diff --git a/.travis.yml b/.travis.yml index 1e56838..1d20efc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,9 +39,6 @@ before_script: - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" - - sh -c "if [ '$PHPCS' = '1' ]; then pear channel-discover pear.cakephp.org; fi" - - sh -c "if [ '$PHPCS' = '1' ]; then pear install --alldeps cakephp/CakePHP_CodeSniffer; fi" - - sh -c "if [ '$COVERALLS' = '1' ]; then composer require --dev satooshi/php-coveralls:dev-master; fi" - sh -c "if [ '$COVERALLS' = '1' ]; then mkdir -p build/logs; fi" @@ -53,7 +50,7 @@ script: - sh -c "if [ '$COVERALLS' = '1' ]; then phpunit --stderr --coverage-clover build/logs/clover.xml; fi" - sh -c "if [ '$COVERALLS' = '1' ]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi" - sh -c "if [ '$DEFAULT' = '1' ]; then phpunit --stderr; fi" - - sh -c "if [ '$PHPCS' = '1' ]; then phpcs -p --extensions=php --standard=CakePHP --ignore=vendor --ignore=docs . ; fi" + - sh -c "if [ '$PHPCS' = '1' ]; then vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi" notifications: email: false diff --git a/composer.json b/composer.json index 7fae9bd..899df2a 100644 --- a/composer.json +++ b/composer.json @@ -1,37 +1,38 @@ { - "name": "xety/cake3-upload", - "type": "cakephp-plugin", - "description": "Cake3 plugin to upload files.", - "keywords": ["cakephp", "cake3", "plugin", "upload", "file"], - "homepage": "https://github.com/Xety/Cake3-Upload", - "license": "MIT", - "support":{ - "source":"https://github.com/Xety/Cake3-Upload", - "issues":"https://github.com/Xety/Cake3-Upload/issues" - }, - "authors": [ - { - "name": "Xety", - "email": "zoro.fmt@gmail.com", - "homepage": "https://github.com/Xety" - } - ], - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "cakephp/cakephp": "3.0.*-dev", - "phpunit/phpunit": "4.1.*" - }, - "autoload": { - "psr-4": { - "Xety\\Cake3Upload\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Xety\\Cake3Upload\\Test\\": "tests" - } - } + "name": "xety/cake3-upload", + "type": "cakephp-plugin", + "description": "Cake3 plugin to upload files.", + "keywords": ["cakephp", "cake3", "plugin", "upload", "file"], + "homepage": "https://github.com/Xety/Cake3-Upload", + "license": "MIT", + "support":{ + "source":"https://github.com/Xety/Cake3-Upload", + "issues":"https://github.com/Xety/Cake3-Upload/issues" + }, + "authors": [ + { + "name": "Xety", + "email": "zoro.fmt@gmail.com", + "homepage": "https://github.com/Xety" + } + ], + "require": { + "php": ">=5.4.16", + "cakephp/cakephp": "~3.0" + }, + "require-dev": { + "phpunit/phpunit": "*", + "cakephp/cakephp-codesniffer": "dev-master" + }, + "autoload": { + "psr-4": { + "Xety\\Cake3Upload\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Xety\\Cake3Upload\\Test\\": "tests" + } + } } diff --git a/src/Model/Behavior/UploadBehavior.php b/src/Model/Behavior/UploadBehavior.php index e826b98..24c9929 100644 --- a/src/Model/Behavior/UploadBehavior.php +++ b/src/Model/Behavior/UploadBehavior.php @@ -7,208 +7,213 @@ use Cake\ORM\Behavior; use Cake\ORM\Entity; -class UploadBehavior extends Behavior { - -/** - * Default config. - * - * @var array - */ - protected $_defaultConfig = [ - 'root' => WWW_ROOT, - 'suffix' => '_file', - 'fields' => [] - ]; - -/** - * Overwrite all file on upload. - * - * @var bool - */ - protected $_overwrite = true; - -/** - * The prefix of the file. - * - * @var bool|string - */ - protected $_prefix = false; - -/** - * The default file of the field. - * - * @var bool|string - */ - protected $_defaultFile = false; - -/** - * Check if there is some files to upload and modify the entity before - * it is saved. - * - * At the end, for each files to upload, unset their "virtual" property. - * - * @param Event $event The beforeSave event that was fired. - * @param Entity $entity The entity that is going to be saved. - * - * @throws \LogicException When the path configuration is not set. - * @throws \ErrorException When the function to get the upload path failed. - * - * @return void - */ - public function beforeSave(Event $event, Entity $entity) { - $config = $this->_config; - - foreach ($config['fields'] as $field => $fieldOption) { - $data = $entity->toArray(); - $virtualField = $field . $config['suffix']; - - if (!isset($data[$virtualField]) || !is_array($data[$virtualField])) { - continue; - } - - $file = $entity->get($virtualField); - if ((int)$file['error'] === UPLOAD_ERR_NO_FILE) { - continue; - } - - if (!isset($fieldOption['path'])) { - throw new \LogicException(__('The path for the {0} field is required.', $field)); - } - - if (isset($fieldOption['prefix']) && (is_bool($fieldOption['prefix']) || is_string($fieldOption['prefix']))) { - $this->_prefix = $fieldOption['prefix']; - } - - $extension = (new File($file['name'], false))->ext(); - $uploadPath = $this->_getUploadPath($entity, $fieldOption['path'], $extension); - if (!$uploadPath) { - throw new \ErrorException(__('Error to get the uploadPath.')); - } - - $folder = new Folder($this->_config['root']); - $folder->create($this->_config['root'] . dirname($uploadPath)); - - if ($this->_moveFile($entity, $file['tmp_name'], $uploadPath, $field, $fieldOption)) { - if (!$this->_prefix) { - $this->_prefix = ''; - } - - $entity->set($field, $this->_prefix . $uploadPath); - } - - $entity->unsetProperty($virtualField); - } - } - -/** - * Move the temporary source file to the destination file. - * - * @param \Cake\ORM\Entity $entity The entity that is going to be saved. - * @param bool|string $source The temporary source file to copy. - * @param bool|string $destination The destination file to copy. - * @param bool|string $field The current field to process. - * @param array $options The configuration options defined by the user. - * - * @return bool - */ - protected function _moveFile(Entity $entity, $source = false, $destination = false, $field = false, array $options = []) { - if ($source === false || $destination === false || $field === false) { - return false; - } - - if (isset($options['overwrite']) && is_bool($options['overwrite'])) { - $this->_overwrite = $options['overwrite']; - } - - if ($this->_overwrite) { - $this->_deleteOldUpload($entity, $field, $destination, $options); - } - - $file = new File($source, false, 0755); - - if ($file->copy($this->_config['root'] . $destination, $this->_overwrite)) { - return true; - } - - return false; - } - -/** - * Delete the old upload file before to save the new file. - * - * We can not just rely on the copy file with the overwrite, because if you use - * an identifier like :md5 (Who use a different name for each file), the copy - * function will not delete the old file. - * - * @param \Cake\ORM\Entity $entity The entity that is going to be saved. - * @param bool|string $field The current field to process. - * @param bool|string $newFile The new file path. - * @param array $options The configuration options defined by the user. - * - * @return bool - */ - protected function _deleteOldUpload(Entity $entity, $field = false, $newFile = false, array $options = []) { - if ($field === false || $newFile === false) { - return true; - } - - $fileInfo = pathinfo($entity->$field); - $newFileInfo = pathinfo($newFile); - - if (isset($options['defaultFile']) && (is_bool($options['defaultFile']) || is_string($options['defaultFile']))) { - $this->_defaultFile = $options['defaultFile']; - } - - if ($fileInfo['basename'] == $newFileInfo['basename'] || - $fileInfo['basename'] == pathinfo($this->_defaultFile)['basename']) { - return true; - } - - if ($this->_prefix) { - $entity->$field = str_replace($this->_prefix, "", $entity->$field); - } - - $file = new File($this->_config['root'] . $entity->$field, false); - - if ($file->exists()) { - $file->delete(); - return true; - } - - return false; - } - -/** - * Get the path formatted without its identifiers to upload the file. - * - * Identifiers : - * :id : Id of the Entity. - * :md5 : A random and unique identifier with 32 characters. - * :y : Based on the current year. - * :m : Based on the current month. - * - * i.e : upload/:id/:md5 -> upload/2/5e3e0d0f163196cb9526d97be1b2ce26.jpg - * - * @param \Cake\ORM\Entity $entity The entity that is going to be saved. - * @param bool|string $path The path to upload the file with its identifiers. - * @param bool|string $extension The extension of the file. - * - * @return bool|string - */ - protected function _getUploadPath(Entity $entity, $path = false, $extension = false) { - if ($extension === false || $path === false) { - return false; - } - - $path = trim($path, DS); - - $identifiers = [ - ':id' => $entity->id, - ':md5' => md5(rand() . uniqid() . time()), - ':y' => date('Y'), - ':m' => date('m') - ]; - - return strtr($path, $identifiers) . '.' . strtolower($extension); - } +class UploadBehavior extends Behavior +{ + + /** + * Default config. + * + * @var array + */ + protected $_defaultConfig = [ + 'root' => WWW_ROOT, + 'suffix' => '_file', + 'fields' => [] + ]; + + /** + * Overwrite all file on upload. + * + * @var bool + */ + protected $_overwrite = true; + + /** + * The prefix of the file. + * + * @var bool|string + */ + protected $_prefix = false; + + /** + * The default file of the field. + * + * @var bool|string + */ + protected $_defaultFile = false; + + /** + * Check if there is some files to upload and modify the entity before + * it is saved. + * + * At the end, for each files to upload, unset their "virtual" property. + * + * @param Event $event The beforeSave event that was fired. + * @param Entity $entity The entity that is going to be saved. + * + * @throws \LogicException When the path configuration is not set. + * @throws \ErrorException When the function to get the upload path failed. + * + * @return void + */ + public function beforeSave(Event $event, Entity $entity) + { + $config = $this->_config; + + foreach ($config['fields'] as $field => $fieldOption) { + $data = $entity->toArray(); + $virtualField = $field . $config['suffix']; + + if (!isset($data[$virtualField]) || !is_array($data[$virtualField])) { + continue; + } + + $file = $entity->get($virtualField); + if ((int)$file['error'] === UPLOAD_ERR_NO_FILE) { + continue; + } + + if (!isset($fieldOption['path'])) { + throw new \LogicException(__('The path for the {0} field is required.', $field)); + } + + if (isset($fieldOption['prefix']) && (is_bool($fieldOption['prefix']) || is_string($fieldOption['prefix']))) { + $this->_prefix = $fieldOption['prefix']; + } + + $extension = (new File($file['name'], false))->ext(); + $uploadPath = $this->_getUploadPath($entity, $fieldOption['path'], $extension); + if (!$uploadPath) { + throw new \ErrorException(__('Error to get the uploadPath.')); + } + + $folder = new Folder($this->_config['root']); + $folder->create($this->_config['root'] . dirname($uploadPath)); + + if ($this->_moveFile($entity, $file['tmp_name'], $uploadPath, $field, $fieldOption)) { + if (!$this->_prefix) { + $this->_prefix = ''; + } + + $entity->set($field, $this->_prefix . $uploadPath); + } + + $entity->unsetProperty($virtualField); + } + } + + /** + * Move the temporary source file to the destination file. + * + * @param \Cake\ORM\Entity $entity The entity that is going to be saved. + * @param bool|string $source The temporary source file to copy. + * @param bool|string $destination The destination file to copy. + * @param bool|string $field The current field to process. + * @param array $options The configuration options defined by the user. + * + * @return bool + */ + protected function _moveFile(Entity $entity, $source = false, $destination = false, $field = false, array $options = []) + { + if ($source === false || $destination === false || $field === false) { + return false; + } + + if (isset($options['overwrite']) && is_bool($options['overwrite'])) { + $this->_overwrite = $options['overwrite']; + } + + if ($this->_overwrite) { + $this->_deleteOldUpload($entity, $field, $destination, $options); + } + + $file = new File($source, false, 0755); + + if ($file->copy($this->_config['root'] . $destination, $this->_overwrite)) { + return true; + } + + return false; + } + + /** + * Delete the old upload file before to save the new file. + * + * We can not just rely on the copy file with the overwrite, because if you use + * an identifier like :md5 (Who use a different name for each file), the copy + * function will not delete the old file. + * + * @param \Cake\ORM\Entity $entity The entity that is going to be saved. + * @param bool|string $field The current field to process. + * @param bool|string $newFile The new file path. + * @param array $options The configuration options defined by the user. + * + * @return bool + */ + protected function _deleteOldUpload(Entity $entity, $field = false, $newFile = false, array $options = []) + { + if ($field === false || $newFile === false) { + return true; + } + + $fileInfo = pathinfo($entity->$field); + $newFileInfo = pathinfo($newFile); + + if (isset($options['defaultFile']) && (is_bool($options['defaultFile']) || is_string($options['defaultFile']))) { + $this->_defaultFile = $options['defaultFile']; + } + + if ($fileInfo['basename'] == $newFileInfo['basename'] || + $fileInfo['basename'] == pathinfo($this->_defaultFile)['basename']) { + return true; + } + + if ($this->_prefix) { + $entity->$field = str_replace($this->_prefix, "", $entity->$field); + } + + $file = new File($this->_config['root'] . $entity->$field, false); + + if ($file->exists()) { + $file->delete(); + return true; + } + + return false; + } + + /** + * Get the path formatted without its identifiers to upload the file. + * + * Identifiers : + * :id : Id of the Entity. + * :md5 : A random and unique identifier with 32 characters. + * :y : Based on the current year. + * :m : Based on the current month. + * + * i.e : upload/:id/:md5 -> upload/2/5e3e0d0f163196cb9526d97be1b2ce26.jpg + * + * @param \Cake\ORM\Entity $entity The entity that is going to be saved. + * @param bool|string $path The path to upload the file with its identifiers. + * @param bool|string $extension The extension of the file. + * + * @return bool|string + */ + protected function _getUploadPath(Entity $entity, $path = false, $extension = false) + { + if ($extension === false || $path === false) { + return false; + } + + $path = trim($path, DS); + + $identifiers = [ + ':id' => $entity->id, + ':md5' => md5(rand() . uniqid() . time()), + ':y' => date('Y'), + ':m' => date('m') + ]; + + return strtr($path, $identifiers) . '.' . strtolower($extension); + } } diff --git a/tests/Fixture/UsersFixture.php b/tests/Fixture/UsersFixture.php index d089120..776aaad 100644 --- a/tests/Fixture/UsersFixture.php +++ b/tests/Fixture/UsersFixture.php @@ -3,42 +3,43 @@ use Cake\TestSuite\Fixture\TestFixture; -class UsersFixture extends TestFixture { +class UsersFixture extends TestFixture +{ -/** - * Fields - * - * @var array - */ - public $fields = [ - 'id' => ['type' => 'integer'], - 'username' => ['type' => 'string', 'length' => 20], - 'avatar' => ['type' => 'string', 'length' => 255, 'default' => '../img/avatar.png'], - 'banner' => ['type' => 'string', 'length' => 255, 'default' => '../img/banner.png'], - '_constraints' => [ - 'primary' => ['type' => 'primary', 'columns' => ['id']], - 'username' => ['type' => 'unique', 'columns' => ['username']] - ], - '_options' => [ - 'engine' => 'InnoDB', 'collation' => 'utf8_general_ci' - ], - ]; + /** + * Fields + * + * @var array + */ + public $fields = [ + 'id' => ['type' => 'integer'], + 'username' => ['type' => 'string', 'length' => 20], + 'avatar' => ['type' => 'string', 'length' => 255, 'default' => '../img/avatar.png'], + 'banner' => ['type' => 'string', 'length' => 255, 'default' => '../img/banner.png'], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id']], + 'username' => ['type' => 'unique', 'columns' => ['username']] + ], + '_options' => [ + 'engine' => 'InnoDB', 'collation' => 'utf8_general_ci' + ], + ]; -/** - * Records - * - * @var array - */ - public $records = [ - [ - 'username' => 'mariano', - 'avatar' => '../img/avatar.png', - 'banner' => '../img/banner.png' - ], - [ - 'username' => 'larry', - 'avatar' => '../img/avatar.png', - 'banner' => '../img/banner.png' - ] - ]; + /** + * Records + * + * @var array + */ + public $records = [ + [ + 'username' => 'mariano', + 'avatar' => '../img/avatar.png', + 'banner' => '../img/banner.png' + ], + [ + 'username' => 'larry', + 'avatar' => '../img/avatar.png', + 'banner' => '../img/banner.png' + ] + ]; } diff --git a/tests/TestCase/Model/Behavior/UploadBehaviorTest.php b/tests/TestCase/Model/Behavior/UploadBehaviorTest.php index d6792dc..20127ad 100644 --- a/tests/TestCase/Model/Behavior/UploadBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/UploadBehaviorTest.php @@ -7,331 +7,342 @@ use Cake\ORM\TableRegistry; use Cake\TestSuite\TestCase; -class UploadBehaviorTest extends TestCase { - -/** - * Fixtures - * - * @var array - */ - public $fixtures = ['plugin.Xety\Cake3Upload.users']; - -/** - * setUp - * - * @return void - */ - public function setUp() { - $this->Model = TableRegistry::get('Users'); - } - -/** - * tearDown - * - * @return void - */ - public function tearDown() { - parent::tearDown(); - - unset($this->Model); - TableRegistry::clear(); - - $folder = new Folder(WWW_ROOT . 'upload'); - $folder->delete(WWW_ROOT . 'upload'); - } - -/** - * test beforeSaveErrorNoFile - * - * @return void - */ - public function testBeforeSaveErrorNoFile() { - $file = [ - 'name' => '', - 'tmp_name' => '', - 'error' => UPLOAD_ERR_NO_FILE, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':id' - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - - $this->Model->save($entity); - - $this->assertFalse(file_exists(WWW_ROOT . 'upload' . DS . $entity->id . DS . $entity->id)); - } - -/** - * test beforeSaveErrorNoPathConfig - * - * @return void - */ - public function testBeforeSaveErrorNoPathConfig() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - - $this->setExpectedException('LogicException'); - $this->Model->save($entity); - } - -/** - * test testBeforeSaveUploadOk - * - * @return void - */ - public function testBeforeSaveUploadOk() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':id' - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $result = $this->Model->save($entity); - - $this->assertTrue(file_exists(WWW_ROOT . 'upload' . DS . $entity->id . DS . $entity->id . '.png')); - $this->assertEquals('upload' . DS . '1' . DS . '1.png', $result->avatar); - } - -/** - * test testBeforeSaveUploadOkWithPrefix - * - * @return void - */ - public function testBeforeSaveUploadOkWithPrefix() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':id', - 'prefix' => '..' . DS . '..' . DS - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $result = $this->Model->save($entity); - - $this->assertEquals('..' . DS . '..' . DS . 'upload' . DS . '1' . DS . '1.png', $result->avatar); - } - -/** - * test testBeforeSaveUploadOkWithOverwrite - * - * @return void - */ - public function testBeforeSaveUploadOkWithOverwrite() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':md5', - 'overwrite' => true - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $before = $this->Model->save($entity); - - $this->assertTrue(file_exists(WWW_ROOT . $before->avatar), 'The new file should be created.'); - - $newEntity = $this->Model->get(1); - $this->Model->patchEntity($newEntity, ['id' => 1, 'avatar_file' => $file]); - $after = $this->Model->save($newEntity); - - $this->assertFalse(file_exists(WWW_ROOT . $before->avatar), 'The old file should be deleted when overwrite is true'); - $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The new file should be created.'); - - $this->Model->removeBehavior('Upload'); - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':md5', - 'overwrite' => false - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $last = $this->Model->save($entity); - - $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The old file should not be deleted when overwrite is - false.'); - $this->assertTrue(file_exists(WWW_ROOT . $last->avatar), 'The new file should be created.'); - } - -/** - * test testBeforeSaveWithAvatarWithoutExtension - * - * @return void - */ - public function testBeforeSaveWithAvatarWithoutExtension() { - $file = [ - 'name' => 'avatar', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':id' - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - - $this->setExpectedException('ErrorException'); - $this->Model->save($entity); - } - -/** - * test testBeforeSaveUploadOkWithOverwrite - * - * @return void - */ - public function testBeforeSaveUploadOkWithOverwriteAndDefaultFile() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':md5' - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $before = $this->Model->save($entity); - - $this->Model->removeBehavior('Upload'); - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':md5', - 'overwrite' => true, - 'defaultFile' => $before->avatar - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_file' => $file]); - $entity->isNew(false); - $after = $this->Model->save($entity); - - $this->assertTrue(file_exists(WWW_ROOT . $before->avatar), 'The old file should not be deleted because it\'s the - defaultFile.'); - $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The new file should be created.'); - } - -/** - * test testBeforeSaveUploadOkWithCustomSuffix - * - * @return void - */ - public function testBeforeSaveUploadOkWithCustomSuffix() { - $file = [ - 'name' => 'avatar.png', - 'tmp_name' => TMP . 'avatar.png', - 'error' => UPLOAD_ERR_OK, - 'type' => 'image/png', - 'size' => 201 - ]; - - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'suffix' => '_test', - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':id' - ] - ] - ]); - - $entity = new Entity(['id' => 1, 'avatar_test' => $file]); - $entity->isNew(false); - $result = $this->Model->save($entity); - - $this->assertEquals('upload' . DS . '1' . DS . '1.png', $result->avatar, 'The avatar should be created because both - suffix match.'); - - $this->Model->removeBehavior('Upload'); - $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ - 'suffix' => '_testFail', - 'fields' => [ - 'avatar' => [ - 'path' => 'upload' . DS . ':id' . DS . ':md5' - ] - ] - ]); - - $entity = $this->Model->get(1); - $this->Model->patchEntity($entity, ['id' => 1, 'avatar_file' => $file]); - $result = $this->Model->save($entity); - - $this->assertEquals($entity->avatar, $result->avatar, 'The avatar field should not be changed because the suffix does - not match with the field suffix.'); - } +class UploadBehaviorTest extends TestCase +{ + + /** + * Fixtures + * + * @var array + */ + public $fixtures = ['plugin.Xety\Cake3Upload.users']; + + /** + * setUp + * + * @return void + */ + public function setUp() + { + $this->Model = TableRegistry::get('Users'); + } + + /** + * tearDown + * + * @return void + */ + public function tearDown() + { + parent::tearDown(); + + unset($this->Model); + TableRegistry::clear(); + + $folder = new Folder(WWW_ROOT . 'upload'); + $folder->delete(WWW_ROOT . 'upload'); + } + + /** + * test beforeSaveErrorNoFile + * + * @return void + */ + public function testBeforeSaveErrorNoFile() + { + $file = [ + 'name' => '', + 'tmp_name' => '', + 'error' => UPLOAD_ERR_NO_FILE, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':id' + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + + $this->Model->save($entity); + + $this->assertFalse(file_exists(WWW_ROOT . 'upload' . DS . $entity->id . DS . $entity->id)); + } + + /** + * test beforeSaveErrorNoPathConfig + * + * @return void + */ + public function testBeforeSaveErrorNoPathConfig() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + + $this->setExpectedException('LogicException'); + $this->Model->save($entity); + } + + /** + * test testBeforeSaveUploadOk + * + * @return void + */ + public function testBeforeSaveUploadOk() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':id' + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $result = $this->Model->save($entity); + + $this->assertTrue(file_exists(WWW_ROOT . 'upload' . DS . $entity->id . DS . $entity->id . '.png')); + $this->assertEquals('upload' . DS . '1' . DS . '1.png', $result->avatar); + } + + /** + * test testBeforeSaveUploadOkWithPrefix + * + * @return void + */ + public function testBeforeSaveUploadOkWithPrefix() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':id', + 'prefix' => '..' . DS . '..' . DS + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $result = $this->Model->save($entity); + + $this->assertEquals('..' . DS . '..' . DS . 'upload' . DS . '1' . DS . '1.png', $result->avatar); + } + + /** + * test testBeforeSaveUploadOkWithOverwrite + * + * @return void + */ + public function testBeforeSaveUploadOkWithOverwrite() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':md5', + 'overwrite' => true + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $before = $this->Model->save($entity); + + $this->assertTrue(file_exists(WWW_ROOT . $before->avatar), 'The new file should be created.'); + + $newEntity = $this->Model->get(1); + $this->Model->patchEntity($newEntity, ['id' => 1, 'avatar_file' => $file]); + $after = $this->Model->save($newEntity); + + $this->assertFalse(file_exists(WWW_ROOT . $before->avatar), 'The old file should be deleted when overwrite is true'); + $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The new file should be created.'); + + $this->Model->removeBehavior('Upload'); + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':md5', + 'overwrite' => false + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $last = $this->Model->save($entity); + + $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The old file should not be deleted when overwrite is + false.'); + $this->assertTrue(file_exists(WWW_ROOT . $last->avatar), 'The new file should be created.'); + } + + /** + * test testBeforeSaveWithAvatarWithoutExtension + * + * @return void + */ + public function testBeforeSaveWithAvatarWithoutExtension() + { + $file = [ + 'name' => 'avatar', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':id' + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + + $this->setExpectedException('ErrorException'); + $this->Model->save($entity); + } + + /** + * test testBeforeSaveUploadOkWithOverwrite + * + * @return void + */ + public function testBeforeSaveUploadOkWithOverwriteAndDefaultFile() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':md5' + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $before = $this->Model->save($entity); + + $this->Model->removeBehavior('Upload'); + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':md5', + 'overwrite' => true, + 'defaultFile' => $before->avatar + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_file' => $file]); + $entity->isNew(false); + $after = $this->Model->save($entity); + + $this->assertTrue(file_exists(WWW_ROOT . $before->avatar), 'The old file should not be deleted because it\'s the + defaultFile.'); + $this->assertTrue(file_exists(WWW_ROOT . $after->avatar), 'The new file should be created.'); + } + + /** + * test testBeforeSaveUploadOkWithCustomSuffix + * + * @return void + */ + public function testBeforeSaveUploadOkWithCustomSuffix() + { + $file = [ + 'name' => 'avatar.png', + 'tmp_name' => TMP . 'avatar.png', + 'error' => UPLOAD_ERR_OK, + 'type' => 'image/png', + 'size' => 201 + ]; + + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'suffix' => '_test', + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':id' + ] + ] + ]); + + $entity = new Entity(['id' => 1, 'avatar_test' => $file]); + $entity->isNew(false); + $result = $this->Model->save($entity); + + $this->assertEquals('upload' . DS . '1' . DS . '1.png', $result->avatar, 'The avatar should be created because both + suffix match.'); + + $this->Model->removeBehavior('Upload'); + $this->Model->addBehavior('Xety/Cake3Upload.Upload', [ + 'suffix' => '_testFail', + 'fields' => [ + 'avatar' => [ + 'path' => 'upload' . DS . ':id' . DS . ':md5' + ] + ] + ]); + + $entity = $this->Model->get(1); + $this->Model->patchEntity($entity, ['id' => 1, 'avatar_file' => $file]); + $result = $this->Model->save($entity); + + $this->assertEquals($entity->avatar, $result->avatar, 'The avatar field should not be changed because the suffix does + not match with the field suffix.'); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9069986..c38f7dc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -34,71 +34,71 @@ Configure::write('debug', true); Configure::write('App', [ - 'namespace' => 'App', - 'encoding' => 'UTF-8', - 'base' => false, - 'baseUrl' => false, - 'dir' => 'src', - 'webroot' => 'webroot', - 'www_root' => APP . 'webroot', - 'fullBaseUrl' => 'http://localhost', - 'imageBaseUrl' => 'img/', - 'jsBaseUrl' => 'js/', - 'cssBaseUrl' => 'css/', - 'paths' => [ - 'plugins' => [APP . 'Plugin' . DS], - 'templates' => [APP . 'Template' . DS] - ] + 'namespace' => 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'baseUrl' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'www_root' => APP . 'webroot', + 'fullBaseUrl' => 'http://localhost', + 'imageBaseUrl' => 'img/', + 'jsBaseUrl' => 'js/', + 'cssBaseUrl' => 'css/', + 'paths' => [ + 'plugins' => [APP . 'Plugin' . DS], + 'templates' => [APP . 'Template' . DS] + ] ]); Configure::write('Session', [ - 'defaults' => 'php' + 'defaults' => 'php' ]); Cache::config([ - '_cake_core_' => [ - 'engine' => 'File', - 'prefix' => 'cake_core_', - 'serialize' => true - ], - '_cake_model_' => [ - 'engine' => 'File', - 'prefix' => 'cake_model_', - 'serialize' => true - ], - 'default' => [ - 'engine' => 'File', - 'prefix' => 'default_', - 'serialize' => true - ] + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true + ], + 'default' => [ + 'engine' => 'File', + 'prefix' => 'default_', + 'serialize' => true + ] ]); // Ensure default test connection is defined if (!getenv('db_class')) { - putenv('db_class=Cake\Database\Driver\Sqlite'); - putenv('db_dsn=sqlite::memory:'); + putenv('db_class=Cake\Database\Driver\Sqlite'); + putenv('db_dsn=sqlite::memory:'); } ConnectionManager::config('test', [ - 'className' => 'Cake\Database\Connection', - 'driver' => getenv('db_class'), - 'dsn' => getenv('db_dsn'), - 'database' => getenv('db_database'), - 'username' => getenv('db_login'), - 'password' => getenv('db_password'), - 'timezone' => 'UTC' + 'className' => 'Cake\Database\Connection', + 'driver' => getenv('db_class'), + 'dsn' => getenv('db_dsn'), + 'database' => getenv('db_database'), + 'username' => getenv('db_login'), + 'password' => getenv('db_password'), + 'timezone' => 'UTC' ]); Log::config([ - 'debug' => [ - 'engine' => 'Cake\Log\Engine\FileLog', - 'levels' => ['notice', 'info', 'debug'], - 'file' => 'debug', - ], - 'error' => [ - 'engine' => 'Cake\Log\Engine\FileLog', - 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], - 'file' => 'error', - ] + 'debug' => [ + 'engine' => 'Cake\Log\Engine\FileLog', + 'levels' => ['notice', 'info', 'debug'], + 'file' => 'debug', + ], + 'error' => [ + 'engine' => 'Cake\Log\Engine\FileLog', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + 'file' => 'error', + ] ]); Plugin::load('Xety/Cake3Upload', ['path' => ROOT]);