diff --git a/src/Database/Type/FileType.php b/src/Database/Type/FileType.php new file mode 100644 index 0000000..0dfe63e --- /dev/null +++ b/src/Database/Type/FileType.php @@ -0,0 +1,56 @@ + $content) + { + if (!in_array($key, $mustHave)) + { + throw new Exception(__d('upload', "Misconfigured form")); + } + } + } + return $value; + } + +} diff --git a/src/File/Writer/DefaultWriter.php b/src/File/Writer/DefaultWriter.php index c2f0a23..51bf4a4 100644 --- a/src/File/Writer/DefaultWriter.php +++ b/src/File/Writer/DefaultWriter.php @@ -5,13 +5,135 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ + namespace Upload\File\Writer; + +use Cake\ORM\Table; +use Cake\ORM\Entity; +use Cake\Filesystem\File; +use Cake\Filesystem\Folder; +use Cake\Utility\Hash; +use Intervention\Image\ImageManager; + /** * Description of DefaultWriter * * @author allancarvalho */ -class DefaultWriter +class DefaultWriter implements WriterInterface { - //put your code here + + /** + * Table Object + * @var Table + */ + protected $table; + + /** + * Entity Object + * @var Entity + */ + protected $entity; + + /** + * Name of field of file + * @var string + */ + protected $field; + + /** + * Array of settings + * @var array + */ + protected $settings; + + /** + * Info from file + * @var array + */ + protected $fileInfo; + + /** + * Default destination file path + * @var string + */ + protected $defaultPath = ''; + + /** + * Final file name + * @var string + */ + protected $fileName = null; + + /** + * Construct Method + * @param Table $table + * @param Entity $entity + * @param type $field + * @param type $settings + */ + public function __construct(Table $table, Entity $entity, $field, $settings) + { + $this->table = $table; + $this->entity = $entity; + $this->field = $field; + $this->settings = $settings; + $this->fileInfo = $this->entity->get($this->field); + $this->defaultPath = WWW_ROOT . 'files' . DS . $this->table->getAlias() . DS; + } + + public function write() + { + + } + + public function delete() + { + + } + + /** + * Get a path to save file + * @return string + */ + protected function getPath() + { + $path = Hash::get($this->settings, 'path', $this->defaultPath); + + return empty($path) ? $this->defaultPath : (substr($path, -1) === DS ? $path : $path . DS); + } + + /** + * Check if path exist + * @param bool $create Create a path if not exist + */ + protected function checkPath($create = true) + { + if (!new Folder($this->getPath(), $create)) + { + \Cake\Log\Log::error(__d('upload', 'Unable to create directory: {0}', $this->getPath())); + } + } + + /** + * Return a file name + * @return string + */ + protected function getFileName() + { + if (debug_backtrace()[1]['function'] == 'write') + { + if ($this->fileName === null) + { + $filePrefix = Hash::get($this->settings, 'prefix', ''); + $fileUniqidMoreEntropy = Hash::get($this->settings, 'more_entropy', true); + $this->fileName = Hash::get($this->settings, 'filename', uniqid($filePrefix, $fileUniqidMoreEntropy)); + } + }elseif(debug_backtrace()[1]['function'] == 'delete') + { + $this->fileName = $this->entity->get($this->field); + } + return $this->fileName; + } + } diff --git a/src/File/Writer/FileWriter.php b/src/File/Writer/FileWriter.php new file mode 100644 index 0000000..8a16ed4 --- /dev/null +++ b/src/File/Writer/FileWriter.php @@ -0,0 +1,60 @@ +checkPath(); + $file = new File($this->fileInfo['tmp_name']); + + if($file->copy("{$this->getPath()}{$this->getFileName()}{$this->getFileFormat()}")) + { + $this->entity->set($this->field, "{$this->getFileName()}{$this->getFileFormat()}"); + return true; + }else + { + Log::error(__d('upload', 'Unable to save file "{0}" in path "{1}"', $this->getFileName(), $this->getPath())); + return false; + } + + } + + public function delete() + { + $file = new File("{$this->getPath()}{$this->getFileName()}"); + if ($file->exists()) + { + if (!$file->delete()) + { + Log::error(__d('upload', 'Unable to delete file "{0}" in path "{1}"', $this->getFileName(), $this->getPath())); + } + } else + { + Log::error(__d('upload', 'Unable to delete file "{0}" in path "{1}" because it does not exist', $this->getFileName(), $this->getPath())); + } + } + + private function getFileFormat() + { + return '.' . pathinfo($this->fileInfo['name'], PATHINFO_EXTENSION); + } + +} diff --git a/src/File/Writer/ImageWriter.php b/src/File/Writer/ImageWriter.php new file mode 100644 index 0000000..64895cf --- /dev/null +++ b/src/File/Writer/ImageWriter.php @@ -0,0 +1,177 @@ +defaultPath = WWW_ROOT . 'img' . DS . $this->table->getAlias() . DS; + } + + public function write() + { + $this->checkPath(); + $image = $this->getImage($this->fileInfo['tmp_name']); + + $this->maxHeigth = Hash::get($this->settings, 'image.max_height', false); + $this->maxWidth = Hash::get($this->settings, 'image.max_width', false); + $this->watermark = Hash::get($this->settings, 'image.watermark', false); + $this->watermarkPosition = Hash::get($this->settings, 'image.watermark_position', 'bottom-right'); + + $image = $this->modifyImage($image); + + if ($image->save("{$this->getPath()}{$this->getFileName()}{$this->getImageFormat()}", $this->getImageQuality())) + { + return $this->entity->set($this->field, "{$this->getFileName()}{$this->getImageFormat()}"); + } else + { + return false; + } + } + + public function delete() + { + $file = new File("{$this->getPath()}{$this->getFileName()}"); + if ($file->exists()) + { + if (!$file->delete()) + { + \Cake\Log\Log::error(__d('upload', 'Unable to delete file "{0}" in path "{1}"', $this->getFileName(), $this->getPath())); + } + } else + { + \Cake\Log\Log::error(__d('upload', 'Unable to delete file "{0}" in path "{1}" because it does not exist', $this->getFileName(), $this->getPath())); + } + } + + /** + * Modifier function calls + * @param \Intervention\Image\Image $image + * @return \Intervention\Image\Image + */ + private function modifyImage($image) + { + if ($this->maxHeigth !== false) + { + if ($this->maxHeigth < $image->height()) + { + $image = $this->maxHeight($image, $this->maxHeigth); + } + } + + if ($this->maxWidth !== false) + { + if ($this->maxWidth < $image->width()) + { + $image = $this->maxWidth($image, $this->maxWidth); + } + } + + if ($this->watermark !== false) + { + $image = $this->insertWatermark($image, $this->watermark, $this->watermarkPosition); + } + + return $image; + } + + /** + * Get a Intervention image object + * @param string $path + * @return \Intervention\Image\Image + */ + private function getImage($path) + { + $manager = new ImageManager(); + return $manager->make($path); + } + + /** + * Set a max width of image if it is smaller than original + * @param \Intervention\Image\Image $image + * @return \Intervention\Image\Image + */ + private function maxWidth($image, $width) + { + $image->resize($width, null, function ($constraint) + { + $constraint->aspectRatio(); + }); + + return $image; + } + + /** + * Set a max height of image if it is smaller than original + * @param \Intervention\Image\Image $image + * @return \Intervention\Image\Image + */ + private function maxHeight($image, $heigt) + { + + $image->resize(null, $heigt, function ($constraint) + { + $constraint->aspectRatio(); + }); + + return $image; + } + + /** + * Insert a watermark in image + * @param \Intervention\Image\Image $image + * @param string $path + * @param string $position + * @return \Intervention\Image\Image + */ + public function insertWatermark($image, $path, $position) + { + $watermark = $this->getImage($path); + + if($watermark->height() > intval($image->height() * 0.07)) + { + $watermark = $this->maxHeight($watermark, intval($image->height() * 0.07)); + } + + $image->insert($watermark, $position, $image->width() * 0.05, $image->height() * 0.05); + + return $image; + } + + private function getImageFormat() + { + $imageFormat = Hash::get($this->settings, 'image.format', 'jpg'); + return substr($imageFormat, 0, 1) === '.' ? $imageFormat : '.' . $imageFormat; + } + + private function getImageQuality() + { + return Hash::get($this->settings, 'image.quality', 100); + } + +} diff --git a/src/File/Writer/WriterInterface.php b/src/File/Writer/WriterInterface.php new file mode 100644 index 0000000..995190c --- /dev/null +++ b/src/File/Writer/WriterInterface.php @@ -0,0 +1,26 @@ +_config = []; + $configs = []; + foreach ($config as $field => $settings) + { + if (is_int($field)) + { + $configs[$settings] = []; + } else + { + $configs[$field] = $settings; + } + } + $this->setConfig($configs); + + Type::map('upload.file', 'Upload\Database\Type\FileType'); + $schema = $this->_table->getSchema(); + foreach (array_keys($this->getConfig()) as $field) + { + $schema->columnType($field, 'upload.file'); + } + $this->_table->schema($schema); + } + + public function beforeMarshal(Event $event, \ArrayObject $data, \ArrayObject $options) + { + $validator = $this->_table->validator(); + $dataArray = $data->getArrayCopy(); + foreach (array_keys($this->getConfig()) as $field) + { + if (!$validator->isEmptyAllowed($field, false)) + { + continue; + } + if (Hash::get($dataArray, $field . '.error') !== UPLOAD_ERR_NO_FILE) + { + continue; + } + unset($data[$field]); + } + } + + public function beforeSave(Event $event, EntityInterface $entity, \ArrayObject $options) + { + + foreach ($this->getConfig() as $field => $settings) + { + + if ($entity->has($field)) + { + if (Hash::get((array) $entity->get($field), 'error') !== UPLOAD_ERR_OK) + { + \Cake\Log\Log::write(\Psr\Log\LogLevel::ERROR, __d('upload', 'File upload had the following error: {0}', $this->getUploadError($entity->get($field)['error']))); + return false; + } + + $writer = $this->getWriter($entity, $field, $settings); + if(!$writer->write()) + { + return false; + } + } + } + } + + public function afterDelete(Event $event, Entity $entity, \ArrayObject $options) + { + foreach ($this->config() as $field => $settings) + { + if ($entity->has($field)) + { + $writer = $this->getWriter($entity, $field, $settings); + $writer->delete(); + } + } + } + + /** + * + * @param Entity $entity + * @param string $field + * @param array $settings + * @return \Upload\File\Writer\DefaultWriter + * @throws UnexpectedValueException + */ + private function getWriter(Entity $entity, $field, $settings) + { + $default = isset($settings['image']) ? 'Upload\File\Writer\ImageWriter' : 'Upload\File\Writer\FileWriter'; + $writerClass = Hash::get($settings, 'writer', $default); + if (is_subclass_of($writerClass, 'Upload\File\Writer\WriterInterface')) + { + return new $writerClass($this->_table, $entity, $field, $settings); + } + throw new UnexpectedValueException(__d('upload', "'writer' not set to instance of WriterInterface: {0}", $writerClass)); + } + + /** + * Returns the type of upload error generated by PHP + * @param int $error + * @return string + */ + private function getUploadError($error) + { + switch ($error) + { + case UPLOAD_ERR_OK: + $result = __d('upload', 'There is no error, the file uploaded with success'); + break; + case UPLOAD_ERR_INI_SIZE: + $result = __d('upload', 'The uploaded file exceeds the upload_max_filesize directive in php.ini'); + break; + case UPLOAD_ERR_FORM_SIZE: + $result = __d('upload', 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'); + break; + case UPLOAD_ERR_PARTIAL: + $result = __d('upload', 'The uploaded file was only partially uploaded'); + break; + case UPLOAD_ERR_NO_FILE: + $result = __d('upload', 'No file was uploaded'); + break; + case UPLOAD_ERR_NO_TMP_DIR: + $result = __d('upload', 'Missing a temporary folder'); + break; + case UPLOAD_ERR_CANT_WRITE: + $result = __d('upload', 'Failed to write file to disk'); + break; + case UPLOAD_ERR_EXTENSION: + $result = __d('upload', 'A PHP extension stopped the file upload. PHP does not provide a way to ascertain which extension caused the file upload to stop; examining the list of loaded extensions with phpinfo() may help'); + break; + default : + $result = __d('upload', 'Unknown error {0}', $error); + break; + } + return $result; } }