diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..592d72f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,59 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - '3*' + +name: create_release + +jobs: + build: + name: create_release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Slack Notification on Start + uses: rtCamp/action-slack-notify@v2.2.0 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_RELEASE }} + SLACK_CHANNEL: notify-nc3-release + SLACK_TITLE: "${{ github.repository }}" + SLACK_COLOR: "#f0ad4e" + SLACK_MESSAGE: "Start Job" + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + body: | + NetCommons ${{ github.ref }} released. + draft: false + prerelease: false + + # テスト成功時はこちらのステップが実行される + - name: Slack Notification on Finish + uses: rtCamp/action-slack-notify@v2.2.0 + if: success() + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_RELEASE }} + SLACK_CHANNEL: notify-nc3-release + SLACK_TITLE: "${{ github.repository }}" + SLACK_COLOR: good + SLACK_MESSAGE: "Job Success" + + # テスト失敗時はこちらのステップが実行される + - name: Slack Notification on Failure + uses: rtCamp/action-slack-notify@v2.2.0 + if: failure() + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_RELEASE }} + SLACK_CHANNEL: notify-nc3-tests + SLACK_TITLE: "${{ github.repository }}" + SLACK_COLOR: danger + SLACK_MESSAGE: "Job Failure" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..6da4321 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,168 @@ +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +name: tests + +jobs: + setup: + name: setup + runs-on: ubuntu-latest + steps: + - name: Slack Notification on Start + uses: rtCamp/action-slack-notify@v2.2.0 + if: env.SLACK_WEBHOOK != '' + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_TESTS }} + SLACK_CHANNEL: notify-nc3-tests + SLACK_TITLE: "${{ github.repository }}" + SLACK_COLOR: "#f0ad4e" + + tests: + name: tests + needs: setup + runs-on: ubuntu-latest + strategy: + matrix: + php: [ '7.1', '7.2', '7.3', '7.4' ] + mysql: [ '5.7', '8.0' ] + + env: + NC3_BUILD_DIR: "/opt/nc3" + NC3_DOCKER_DIR: "/opt/docker" + NC3_GIT_URL: "git://github.com/NetCommons3/NetCommons3.git" + NC3_GIT_BRANCH: "master" + PLUGIN_BUILD_DIR: ${{ github.workspace }} + PHP_VERSION: ${{ matrix.php }} + MYSQL_VERSION: ${{ matrix.mysql }} + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: cakephp_test + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v2 + + - name: Fix up git URLs + run: echo -e '[url "https://github.com/"]\n insteadOf = "git://github.com/"' >> ~/.gitconfig + + - name: environment + run: | + echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + echo "PLUGIN_BUILD_DIR=${PLUGIN_BUILD_DIR}" + echo "PHP_VERSION=${PHP_VERSION}" + echo "MYSQL_VERSION=${MYSQL_VERSION}" + ls -al ${PLUGIN_BUILD_DIR} + + - name: docker-compose install + run: | + curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > ~/docker-compose + chmod +x ~/docker-compose + sudo mv ~/docker-compose /usr/local/bin/docker-compose + docker-compose --version + + - name: git clone nc3 + run: git clone -b ${NC3_GIT_BRANCH} ${NC3_GIT_URL} ${NC3_BUILD_DIR} + + - name: git clone nc3_docker + run: git clone https://github.com/NetCommons3/nc3app-docker.git ${NC3_DOCKER_DIR} + + - name: docker-compose start + run: | + cd ${NC3_DOCKER_DIR} + docker-compose up -d + docker-compose start + + - run: docker ps + + - name: check libraries + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/start-on-docker.sh + + - name: nc3 build + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/app-build.sh + + - name: phpcs (PHP CodeSniffer) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/phpcs.sh + + - name: phpmd (PHP Mess Detector) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/phpmd.sh + + - name: phpcpd (PHP Copy/Paste Detector) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/phpcpd.sh + + - name: gjslint (JavaScript Style Check) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/gjslint.sh + + - name: phpdoc (PHP Documentor) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/phpdoc.sh + + - name: phpunit (PHP UnitTest) + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose exec -T nc3app bash /opt/scripts/phpunit.sh + sudo -s chmod a+w -R ${NC3_BUILD_DIR}/build + +# - name: push coveralls +# env: +# COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# COVERALLS_FLAG_NAME: ${{ matrix.php }} +# run: | +# cd ${NC3_BUILD_DIR} +# ls -la ${NC3_BUILD_DIR} +# vendors/bin/php-coveralls --coverage_clover=build/logs/clover.xml -v + + - name: docker-compose remove + if: always() + run: | + cd ${NC3_DOCKER_DIR} + docker-compose rm -f + + # テスト失敗時はこちらのステップが実行される + - name: Slack Notification on Failure + uses: rtCamp/action-slack-notify@v2.2.0 + if: env.SLACK_WEBHOOK != '' && failure() + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_TESTS }} + SLACK_CHANNEL: notify-nc3-tests + SLACK_TITLE: "${{ github.repository }}(php${{ matrix.php }}, mysql${{ matrix.mysql }})" + SLACK_COLOR: danger + + teardown: + name: teardown + runs-on: ubuntu-latest + needs: tests + steps: + # テスト成功時はこちらのステップが実行される + - name: Slack Notification on Success + uses: rtCamp/action-slack-notify@v2.2.0 + if: env.SLACK_WEBHOOK != '' && success() + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_TESTS }} + SLACK_CHANNEL: notify-nc3-tests + SLACK_TITLE: "${{ github.repository }}" + SLACK_COLOR: good diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2c06db5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - -sudo: false - -env: - - NETCOMMONS_VERSION=master DB=mysql - -before_script: - - export NETCOMMONS_BUILD_DIR=`dirname $TRAVIS_BUILD_DIR`/NetCommons3 - - git clone git://github.com/NetCommons3/NetCommons3 $NETCOMMONS_BUILD_DIR - - cd $NETCOMMONS_BUILD_DIR - - git checkout $NETCOMMONS_VERSION - - travis_wait . tools/build/plugins/cakephp/travis/pre.sh - - . tools/build/plugins/cakephp/travis/environment.sh - -script: - - . tools/build/plugins/cakephp/travis/main.sh - -after_script: - - . tools/build/plugins/cakephp/travis/post.sh diff --git a/Config/config.php b/Config/config.php new file mode 100644 index 0000000..818d1e8 --- /dev/null +++ b/Config/config.php @@ -0,0 +1,14 @@ + min(20, ini_get('max_file_uploads')), + /** スライドショーのリミット */ + 'slideShowMaxLimit' => 1000, + /** 1アルバムにアップロードできる最大画像数 */ + 'maxFileInAlbum' => 500, +]; \ No newline at end of file diff --git a/Controller/Component/PhotoAlbumMaxFileUploadsComponent.php b/Controller/Component/PhotoAlbumMaxFileUploadsComponent.php new file mode 100644 index 0000000..e7cb79d --- /dev/null +++ b/Controller/Component/PhotoAlbumMaxFileUploadsComponent.php @@ -0,0 +1,63 @@ +__controller = $controller; + + Configure::load('PhotoAlbums.config'); + $this->__maxFileUploads = Configure::read('PhotoAlbums.maxFileUploads'); + $this->__controller->set('maxFileUploads', $this->__maxFileUploads); + } + +/** + * 写真追加、アルバム追加時にアップロードされるファイル数を__maxFileUploadsまでに制限する + * + * 超えたファイル情報は切り捨てる + * + * @param string $photoPathInData photoの配列パス + * @return void + */ + public function removeOverMaxFileUploads($photoPathInData) { + $photo = Hash::get($this->__controller->request->data, $photoPathInData); + + if (!is_array($photo)) { + return; + } + + if (count($photo) <= $this->__maxFileUploads) { + return; + } + + $photo = array_slice($photo, 0, $this->__maxFileUploads); + $this->__controller->request->data = Hash::insert( + $this->__controller->request->data, + $photoPathInData, + $photo + ); + } + +} \ No newline at end of file diff --git a/Controller/PhotoAlbumFrameSettingsController.php b/Controller/PhotoAlbumFrameSettingsController.php index af8451e..e469eac 100644 --- a/Controller/PhotoAlbumFrameSettingsController.php +++ b/Controller/PhotoAlbumFrameSettingsController.php @@ -100,6 +100,7 @@ public function edit() { $this->NetCommons->handleValidationError($this->PhotoAlbumFrameSetting->validationErrors); } else { $this->request->data = $this->PhotoAlbumFrameSetting->getFrameSetting(); + $this->request->data['Frame'] = Current::read('Frame'); } $query = array( diff --git a/Controller/PhotoAlbumPhotosController.php b/Controller/PhotoAlbumPhotosController.php index 6974871..34d96d1 100644 --- a/Controller/PhotoAlbumPhotosController.php +++ b/Controller/PhotoAlbumPhotosController.php @@ -56,6 +56,7 @@ class PhotoAlbumPhotosController extends PhotoAlbumsAppController { 'Workflow.Workflow', 'PhotoAlbums.PhotoAlbumPhotos', 'PhotoAlbums.PhotoAlbums', + 'PhotoAlbums.PhotoAlbumMaxFileUploads', 'Files.Download' ); @@ -119,12 +120,16 @@ public function slide() { $conditions = $this->PhotoAlbumPhoto->getWorkflowConditions(); $conditions['PhotoAlbumPhoto.album_key'] = $this->request->params['key']; + Configure::load('PhotoAlbums.config'); + $slideShowLimit = Configure::read('PhotoAlbums.slideShowMaxLimit'); + $this->Paginator->settings = array( 'PhotoAlbumPhoto' => array( 'sort' => $frameSetting['PhotoAlbumFrameSetting']['photos_sort'], 'direction' => $frameSetting['PhotoAlbumFrameSetting']['photos_direction'], - 'limit' => $this->Paginator->settings['maxLimit'], - 'conditions' => $conditions + 'conditions' => $conditions, + 'limit' => $slideShowLimit, + 'maxLimit' => $slideShowLimit, ) ); $this->set('photos', $this->Paginator->paginate('PhotoAlbumPhoto')); @@ -154,13 +159,14 @@ public function view($id = null) { * @return void */ public function add() { - $this->layout = 'NetCommons.modal'; $this->view = 'edit'; + $this->set('isAdd', true); $photo = $this->PhotoAlbumPhoto->create(); if ($this->request->is('post')) { + $this->PhotoAlbumMaxFileUploads->removeOverMaxFileUploads('PhotoAlbumPhoto.photo'); $this->request->data['PhotoAlbumPhoto']['status'] = $this->Workflow->parseStatus(); - if ($this->PhotoAlbumPhoto->savePhoto($this->request->data)) { + if ($this->PhotoAlbumPhoto->savePhotos($this->request->data)) { $url = PhotoAlbumsSettingUtility::settingUrl( array( 'plugin' => 'photo_albums', @@ -179,6 +185,7 @@ public function add() { } $this->NetCommons->handleValidationError($this->PhotoAlbumPhoto->validationErrors); } else { + $this->layout = 'NetCommons.modal'; $this->request->data = $photo; $this->request->data['PhotoAlbumPhoto']['album_key'] = $this->request->params['key']; } @@ -343,4 +350,5 @@ public function delete() { ); $this->redirect($url); } + } diff --git a/Controller/PhotoAlbumsController.php b/Controller/PhotoAlbumsController.php index 96ec822..673d934 100644 --- a/Controller/PhotoAlbumsController.php +++ b/Controller/PhotoAlbumsController.php @@ -56,6 +56,7 @@ class PhotoAlbumsController extends PhotoAlbumsAppController { 'Workflow.Workflow', 'Files.Download', 'PhotoAlbums.PhotoAlbums', + 'PhotoAlbums.PhotoAlbumMaxFileUploads', ); /** @@ -217,6 +218,8 @@ public function add() { $album = $this->PhotoAlbum->create(); if ($this->request->is('post')) { + $this->PhotoAlbumMaxFileUploads->removeOverMaxFileUploads('PhotoAlbumPhoto'); + $this->request->data['PhotoAlbum']['status'] = $this->Workflow->parseStatus(); $album = $this->PhotoAlbum->saveAlbumForAdd($this->request->data); if ($album) { diff --git a/Locale/jpn/LC_MESSAGES/photo_albums.po b/Locale/jpn/LC_MESSAGES/photo_albums.po index 07dc67f..98ad2e8 100644 --- a/Locale/jpn/LC_MESSAGES/photo_albums.po +++ b/Locale/jpn/LC_MESSAGES/photo_albums.po @@ -201,3 +201,9 @@ msgstr "スライド写真の高さ(px)" msgid "'0' for default setting." msgstr "「0」を指定すると自動になります" + +msgid "You cannot upload more than %d photos." +msgstr "%dを超える写真をアップロードすることはできません。" + +msgid "You have exceeded the maximum number of images that can be uploaded to this album." +msgstr "このアルバムにアップロードできる最大画像数を超えています。" \ No newline at end of file diff --git a/Model/Behavior/OriginImageResizeBehavior.php b/Model/Behavior/OriginImageResizeBehavior.php new file mode 100644 index 0000000..4cdb0ab --- /dev/null +++ b/Model/Behavior/OriginImageResizeBehavior.php @@ -0,0 +1,115 @@ +__guradBeforeLoadingAttachmentBehavior($model); + + $this->settings[$model->alias] = $config; + + $this->__uploadFileModel = ClassRegistry::init('Files.UploadFile'); + + parent::setup($model, $config); + } + +/** + * afterSave + * + * @param Model $model model + * @param bool $created created + * @param array $options options + * @return bool + */ + public function afterSave(Model $model, $created, $options = array()) { + $setting = $this->settings[$model->alias]; + + foreach ($setting as $fieldName => $fieldSetting) { + $pluginKey = Inflector::underscore($model->plugin); + $uploadFile = $this->__uploadFileModel->getFile($pluginKey, $model->id, $fieldName); + if ($uploadFile) { + $this->overwriteOriginFile($uploadFile, $fieldSetting['resizeImageSizeKey'] . '_'); + } + } + + return parent::afterSave($model, $created, $options); + } + +/** + * 元ファイルをリサイズしたファイルで上書き + * + * @param array $uploadFile UploadFileデータ + * @param string $overwriteFilePrefix リサイズされた画像のprefix このprefixのついたファイルを元画像あつかいにする。 + * @return array|false UploadFile::save()の結果 + * @throws InternalErrorException + */ + public function overwriteOriginFile(array $uploadFile, $overwriteFilePrefix) { + // 元ファイル削除 + $originFilePath = $this->__uploadFileModel->getRealFilePath($uploadFile); + + // origin_resizeからprefix削除 + $directoryPath = substr( + $originFilePath, + 0, + -1 * strlen($uploadFile['UploadFile']['real_file_name']) + ); + $originResizePath = $directoryPath . + $overwriteFilePrefix . $uploadFile['UploadFile']['real_file_name']; + + if (! file_exists($originResizePath)) { + //リネームするファイルがなければ、そのままuploadFileを返す。 + return $uploadFile; + } + + unlink($originFilePath); + rename($originResizePath, $originFilePath); + + // uploadFileのsize更新 + $stat = stat($originFilePath); + $uploadFile['UploadFile']['size'] = $stat['size']; + try { + $uploadFile = $this->__uploadFileModel->save( + $uploadFile, + ['callbacks' => false, 'validate' => false] + ); + } catch (Exception $e) { + throw new InternalErrorException('Failed Update UploadFile.size'); + } + return $uploadFile; + } + +/** + * 事前にAttachmentBehaviorが読みこまれているか + * + * @param Model $model model + * @return void + * @throws CakeException + */ + private function __guradBeforeLoadingAttachmentBehavior(Model $model) { + if (!$model->Behaviors->loaded('Files.Attachment')) { + $error = '"Files.AttachmentBehavior" not loaded in ' . $model->alias . '. '; + $error .= 'Load "Files.AttachmentBehavior" before loading "OriginImageResizeBehavior"'; + throw new CakeException($error); + } + } + +} \ No newline at end of file diff --git a/Model/PhotoAlbum.php b/Model/PhotoAlbum.php index ae8a29a..d544fda 100644 --- a/Model/PhotoAlbum.php +++ b/Model/PhotoAlbum.php @@ -128,9 +128,8 @@ public function afterFind($results, $primary = false) { $photoCount = []; foreach ($countArr as $result) { $result = $result['PhotoAlbumPhoto']; - $photoCount[$result['album_key']] = [ - $result['status'] => $result['photo_count'] - ]; + $photoCount[$result['album_key']][$result['status']] = $result['photo_count']; + } foreach ($results as $index => $result) { @@ -379,6 +378,11 @@ public function deleteAlbum($data) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } + if (!$this->__deleteJacketFile($data['PhotoAlbum']['key'])) { + throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); + } + + // アルバム内の写真削除 $Photo = ClassRegistry::init('PhotoAlbums.PhotoAlbumPhoto'); $conditions = array('PhotoAlbumPhoto.album_key' => $data['PhotoAlbum']['key']); $query = array( @@ -387,9 +391,13 @@ public function deleteAlbum($data) { 'recursive' => -1 ); $contentKeys = $Photo->find('list', $query); + if (!$Photo->deleteAll($conditions, false)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } + if (!$Photo->deleteUploadFileByContentKeys($contentKeys)) { + throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); + } $DisplayAlbum = ClassRegistry::init('PhotoAlbums.PhotoAlbumDisplayAlbum'); $conditions = array('PhotoAlbumDisplayAlbum.album_key' => $data['PhotoAlbum']['key']); @@ -408,4 +416,14 @@ public function deleteAlbum($data) { return true; } + +/** + * __deleteJacketFile + * + * @param string $albumKey album.key + * @return mixed + */ + private function __deleteJacketFile($albumKey) { + return $this->deleteUploadFileByContentKeys([$albumKey]); + } } diff --git a/Model/PhotoAlbumFrameSetting.php b/Model/PhotoAlbumFrameSetting.php index 565bf06..97a955d 100644 --- a/Model/PhotoAlbumFrameSetting.php +++ b/Model/PhotoAlbumFrameSetting.php @@ -162,7 +162,8 @@ public function savePhotoAlbumFrameSetting($data) { foreach ($data['PhotoAlbumDisplayAlbum'] as $displayAlbumData) { $displayAlbumData = $DisplayAlbum->create($displayAlbumData); - $displayAlbumData['frame_key'] = $data['PhotoAlbumFrameSetting']['frame_key']; + $displayAlbumData['PhotoAlbumDisplayAlbum']['frame_key'] = + $data['PhotoAlbumFrameSetting']['frame_key']; if (!$DisplayAlbum->save($displayAlbumData)) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } diff --git a/Model/PhotoAlbumPhoto.php b/Model/PhotoAlbumPhoto.php index be8177a..4e6c750 100644 --- a/Model/PhotoAlbumPhoto.php +++ b/Model/PhotoAlbumPhoto.php @@ -16,6 +16,8 @@ /** * Summary for PhotoAlbumPhoto Model + * + * @noinspection ALL */ class PhotoAlbumPhoto extends PhotoAlbumsAppModel { @@ -33,7 +35,24 @@ class PhotoAlbumPhoto extends PhotoAlbumsAppModel { */ public $actsAs = array( 'NetCommons.OriginalKey', - 'Files.Attachment' => [PhotoAlbumPhoto::ATTACHMENT_FIELD_NAME], + 'Files.Attachment' => [ + PhotoAlbumPhoto::ATTACHMENT_FIELD_NAME => [ + 'thumbnailSizes' => array( + 'origin' => '800ml', + 'big' => '800ml', + 'medium' => '400ml', + //'small' => '152mh', + 'small' => '120mh', + ////'small' => '200ml', + 'thumb' => '80x80', + ), + ] + ], + 'PhotoAlbums.OriginImageResize' => [ + PhotoAlbumPhoto::ATTACHMENT_FIELD_NAME => [ + 'resizeImageSizeKey' => 'origin', + ] + ], 'Workflow.Workflow', 'Workflow.WorkflowComment', //多言語 @@ -73,7 +92,7 @@ public function beforeValidate($options = array()) { $validate['photoExtension'] = array( 'rule' => array( 'extension', - array('gif', 'jpeg', 'png', 'jpg', 'zip') + array('gif', 'jpeg', 'png', 'jpg', 'zip', 'bmp') ), 'message' => array(__d('files', 'It is upload disabled file format')) ); @@ -84,6 +103,13 @@ public function beforeValidate($options = array()) { 'message' => array(__d('files', 'It is upload disabled file format')) ); */ + $validate['maxFileInAlbum'] = [ + 'rule' => ['validationMaxFileInAlbum'], + 'message' => __d( + 'photo_albums', + 'You have exceeded the maximum number of images that can be uploaded to this album.' + ), + ]; } $this->validate = array_merge( @@ -96,6 +122,24 @@ public function beforeValidate($options = array()) { return parent::beforeValidate($options); } +/** + * validationMaxFileInAlbum アルバムの画像数が最大数未満かをチェック + * + * @param array $check check + * @return bool + */ + public function validationMaxFileInAlbum(array $check) { + Configure::load('PhotoAlbums.config'); + $maxFileInAlbum = Configure::read('PhotoAlbums.maxFileInAlbum'); + $count = $this->find('count', [ + 'conditions' => [ + 'PhotoAlbumPhoto.album_key' => $this->data['PhotoAlbumPhoto']['album_key'], + 'PhotoAlbumPhoto.is_latest' => true, + ] + ]); + return ($count < $maxFileInAlbum); + } + /** * Called before each find operation. Return false if you want to halt the find * call, otherwise return the (modified) query data. @@ -110,6 +154,46 @@ public function beforeFind($query) { return $query; } +/** + * savePhotos 複数ファイル保存 + * + * @param array $data CakeRequest::data + * @return bool + */ + public function savePhotos(array $data) { + $this->begin(); + + try { + if (!$this->__savePhotos($data)) { + $this->rollback(); + return false; + }; + } catch (Exception $ex) { + $this->rollback($ex); + } + + $this->commit(); + return true; + } + +/** + * 複数ファイル保存のメイン処理 + * + * @param array $data CakeRequest::data + * @return bool + */ + private function __savePhotos(array $data) { + $base = $data; + + foreach ($data[$this->alias][self::ATTACHMENT_FIELD_NAME] as $photo) { + $base[$this->alias][self::ATTACHMENT_FIELD_NAME] = $photo; + if (!$this->savePhoto($base)) { + return false; + } + } + return true; + } + /** * Save photo * @@ -122,14 +206,10 @@ public function savePhoto($data) { $regenerateData = $this->__regenerateDataForZip($data); - $photo = array(); - - foreach ($regenerateData as $index => $data) { - if ($index > 0) { - $this->create(); - } + foreach ($regenerateData as $datum) { + $this->create(); - $this->set($data); + $this->set($datum); if (!$this->validates()) { $this->rollback(); return false; @@ -140,16 +220,16 @@ public function savePhoto($data) { if (! $result) { throw new InternalErrorException(__d('net_commons', 'Internal Server Error')); } - $photo[] = $result; } catch (Exception $ex) { $this->rollback($ex); + return false; } } $this->commit(); - return $photo; + return true; } /** @@ -272,19 +352,23 @@ public function getWorkflowConditions() { * @throws InternalErrorException */ private function __regenerateDataForZip($data) { - $files = array(); $zipType = ['application/zip', 'application/x-zip-compressed']; - if (in_array($data['PhotoAlbumPhoto']['photo']['type'], $zipType)) { - $zip = new UnZip($data['PhotoAlbumPhoto']['photo']['tmp_name']); - $unzipedFolder = $zip->extract(); - $dir = new Folder($unzipedFolder->path); - $files = $dir->findRecursive('.*\.(jpg|jpeg)'); + if (in_array($data['PhotoAlbumPhoto']['photo']['type'], $zipType) === false) { + return array($data); + } + // ZIP 処理 + $zip = new UnZip($data['PhotoAlbumPhoto']['photo']['tmp_name']); + $unzipedFolder = $zip->extract(); + $dir = new Folder($unzipedFolder->path); + $files = $dir->findRecursive('.*\.(jpg|jpeg|gif|png|bmp)'); + $files = $this->__excludeHiddenFile($files); if (!$files) { - return array($data); + $this->invalidate('photo', __d('photo_albums', 'Select photo.')); } + $regenerateData = []; foreach ($files as $file) { $file = new File($file); $data['PhotoAlbumPhoto']['photo'] = array_merge( @@ -302,4 +386,21 @@ private function __regenerateDataForZip($data) { return $regenerateData; } +/** + * 隠しファイル除外 + * + * @param array $files ファイルリスト + * @return array + */ + private function __excludeHiddenFile($files) { + $excludedFiles = []; + foreach ($files as $file) { + // "."はじまりのファイル(隠しファイル)は除外 + if (substr(basename($file), 0, 1) === '.') { + continue; + } + $excludedFiles[] = $file; + } + return $excludedFiles; + } } diff --git a/README.md b/README.md index 6224646..52a1276 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# PhotoAlbums -PhotoAlbums for NetCommons3 +PhotoAlbums +============ -[](https://travis-ci.org/NetCommons3/PhotoAlbums) -[](https://coveralls.io/github/NetCommons3/PhotoAlbums?branch=master) \ No newline at end of file +[](https://github.com/NetCommons3/PhotoAlbums/actions/workflows/tests.yml) +[](https://coveralls.io/r/NetCommons3/PhotoAlbums?branch=master) +[](https://packagist.org/packages/netcommons/photo-albums) diff --git a/Test/Case/Controller/PhotoAlbumFrameSettingsController/EditTest.php b/Test/Case/Controller/PhotoAlbumFrameSettingsController/EditTest.php index 1e572a1..fb4154d 100644 --- a/Test/Case/Controller/PhotoAlbumFrameSettingsController/EditTest.php +++ b/Test/Case/Controller/PhotoAlbumFrameSettingsController/EditTest.php @@ -110,7 +110,7 @@ public function testEdit($method, $data = null, $validationError = false, $excep TestAuthGeneral::login($this); $frameId = '6'; - if ($validationError) { + if (is_array($validationError)) { $data = Hash::insert($data, $validationError['field'], $validationError['value']); } @@ -148,7 +148,7 @@ public function testEdit($method, $data = null, $validationError = false, $excep ); //バリデーションエラー(エラー表示あり) - if ($validationError) { + if (is_array($validationError)) { if ($validationError['message']) { array_push($asserts, array( 'method' => 'assertNotEmpty', 'value' => $this->controller->validationErrors diff --git a/Test/Case/Model/PhotoAlbumPhoto/GetWorkflowConditionsTest.php b/Test/Case/Model/PhotoAlbumPhoto/GetWorkflowConditionsTest.php index 3406506..80023e5 100644 --- a/Test/Case/Model/PhotoAlbumPhoto/GetWorkflowConditionsTest.php +++ b/Test/Case/Model/PhotoAlbumPhoto/GetWorkflowConditionsTest.php @@ -26,6 +26,34 @@ class PhotoAlbumPhotoGetWorkflowConditionsTest extends NetCommonsCakeTestCase { 'plugin.photo_albums.photo_album_photo', ); +/** + * 元の公開権限データを保持するための変数 + * + * @var array + */ + private $__originPublishablePermission; + +/** + * 元の編集権限データを保持するための変数 + * + * @var array + */ + private $__originEditablePermission; + +/** + * 元の権限データを保持するための変数 + * + * @var array + */ + private $__originPermissions = []; + +/** + * 元のCurrentのデータ + * + * @var array + */ + private $__originCurrent = []; + /** * setUp method * @@ -53,9 +81,8 @@ public function tearDown() { * @return void */ public function testHasContentEditable() { - $currentValue['Permission']['content_editable']['value'] = true; - $currentValue['Language']['id'] = 99; - PhotoAlbumTestCurrentUtility::setValue($currentValue); + Current::write('Language.id', 99); + $this->__permissionEditable(); $expected = array ( array( @@ -74,7 +101,7 @@ public function testHasContentEditable() { $actual = $this->PhotoAlbumPhoto->getWorkflowConditions(); $this->assertEquals($expected, $actual); - PhotoAlbumTestCurrentUtility::setOriginValue(); + $this->__restorePermission(); } /** @@ -83,11 +110,16 @@ public function testHasContentEditable() { * @return void */ public function testHasPhotoCreatable() { - $currentValue['Permission']['content_editable']['value'] = false; - $currentValue['Permission']['photo_albums_photo_creatable']['value'] = true; - $currentValue['Language']['id'] = 99; - $currentValue['User']['id'] = 88; - PhotoAlbumTestCurrentUtility::setValue($currentValue); + //$currentValue['Permission']['content_editable']['value'] = false; + //$currentValue['Permission']['photo_albums_photo_creatable']['value'] = true; + $this->__setPermission('content_editable', false); + $this->__setPermission('photo_albums_photo_creatable', true); + + //$currentValue['Language']['id'] = 99; + //$currentValue['User']['id'] = 88; + //PhotoAlbumTestCurrentUtility::setValue($currentValue); + $this->__setCurrent('Language.id', 99); + $this->__setCurrent('User.id', 88); $expected = array ( array( @@ -112,7 +144,10 @@ public function testHasPhotoCreatable() { $actual = $this->PhotoAlbumPhoto->getWorkflowConditions(); $this->assertEquals($expected, $actual); - PhotoAlbumTestCurrentUtility::setOriginValue(); + //PhotoAlbumTestCurrentUtility::setOriginValue(); + + $this->__restoreCurrent(); + $this->__restorePermission(); } /** @@ -121,11 +156,15 @@ public function testHasPhotoCreatable() { * @return void */ public function testNotHasPhotoCreatable() { - $currentValue['Permission']['content_editable']['value'] = false; - $currentValue['Permission']['photo_albums_photo_creatable']['value'] = false; - $currentValue['Language']['id'] = 99; - $currentValue['User']['id'] = 88; - PhotoAlbumTestCurrentUtility::setValue($currentValue); + //$currentValue['Permission']['content_editable']['value'] = false; + //$currentValue['Permission']['photo_albums_photo_creatable']['value'] = false; + //$currentValue['Language']['id'] = 99; + //$currentValue['User']['id'] = 88; + //PhotoAlbumTestCurrentUtility::setValue($currentValue); + $this->__setPermission('content_editable', false); + $this->__setPermission('photo_albums_photo_creatable', false); + $this->__setCurrent('Language.id', 99); + $this->__setCurrent('User.id', 88); $expected = array ( array( @@ -146,6 +185,67 @@ public function testNotHasPhotoCreatable() { $actual = $this->PhotoAlbumPhoto->getWorkflowConditions(); $this->assertEquals($expected, $actual); - PhotoAlbumTestCurrentUtility::setOriginValue(); + //PhotoAlbumTestCurrentUtility::setOriginValue(); + $this->__restoreCurrent(); + $this->__restorePermission(); + } + +/** + * __permissionPublishable + * + * @return void + */ + private function __permissionEditable() { + $this->__setPermission('content_editable', true); + } + +/** + * __restorePermission + * + * @return void + */ + private function __restorePermission() { + Current::writePermission(null, 'content_publishable', $this->__originPublishablePermission); + Current::writePermission(null, 'content_publishable', $this->__originEditablePermission); + + foreach ($this->__originPermissions as $permission => $permissionValue) { + Current::writePermission(null, $permission, $permissionValue); + } + } + +/** + * __setPermission + * + * @param string $permissionKey + * @param bool $allow + * @return void + */ + private function __setPermission($permissionKey, $allow) { + $this->__originPermissions[$permissionKey] = Current::permission($permissionKey); + Current::writePermission(null, $permissionKey, $allow); } + +/** + * Currentにセットする + * + * @param string $key + * @param mixed $value + * @return void + */ + private function __setCurrent($key, $value) { + $this->__originCurrent[$key] = Current::read($key); + Current::write($key, $value); + } + +/** + * Currentの中身をリストアする + * + * @return void + */ + private function __restoreCurrent() { + foreach ($this->__originCurrent as $key => $value) { + Current::write($key, $value); + } + } + } diff --git a/Test/Case/Model/PhotoAlbumPhoto/MultiCallBeforeValidateTest.php b/Test/Case/Model/PhotoAlbumPhoto/MultiCallBeforeValidateTest.php new file mode 100644 index 0000000..deb05dd --- /dev/null +++ b/Test/Case/Model/PhotoAlbumPhoto/MultiCallBeforeValidateTest.php @@ -0,0 +1,42 @@ +data['PhotoAlbumPhoto'][$field]['name']) + $photoModel->data['PhotoAlbumPhoto'][PhotoAlbumPhoto::ATTACHMENT_FIELD_NAME]['name'] = 'foo.jpg'; + $photoModel->beforeValidate(); + $call1stValidate = $photoModel->validate; + + $photoModel->beforeValidate(); + $call2ndValidate = $photoModel->validate; + + $this->assertSame($call1stValidate, $call2ndValidate); + } +} \ No newline at end of file diff --git a/Test/Case/Model/PhotoAlbumPhoto/PublishTest.php b/Test/Case/Model/PhotoAlbumPhoto/PublishTest.php index 0f9bb5a..ff686fd 100644 --- a/Test/Case/Model/PhotoAlbumPhoto/PublishTest.php +++ b/Test/Case/Model/PhotoAlbumPhoto/PublishTest.php @@ -15,6 +15,8 @@ /** * Summary for PhotoAlbumPhotoPublish Test Case + * + * @property PhotoAlbumPhoto $PhotoAlbumPhoto */ class PhotoAlbumPhotoPublishTest extends NetCommonsCakeTestCase { @@ -30,6 +32,13 @@ class PhotoAlbumPhotoPublishTest extends NetCommonsCakeTestCase { 'plugin.workflow.workflow_comment', ); +/** + * 元のCurrentのデータ + * + * @var array + */ + private $__originPermission; + /** * setUp method * @@ -59,15 +68,19 @@ public function tearDown() { * @return void */ public function testPublish() { - $currentValue['Permission']['content_publishable']['value'] = true; - PhotoAlbumTestCurrentUtility::setValue($currentValue); + //$currentValue['Permission']['content_publishable']['value'] = true; + //PhotoAlbumTestCurrentUtility::setValue($currentValue); + + $this->__permissionPublishable(); $data['PhotoAlbumPhoto']['id'] = 1; $data['PhotoAlbumPhoto']['language_id'] = 1; $actual = $this->PhotoAlbumPhoto->publish([$data]); $this->assertTrue($actual); - PhotoAlbumTestCurrentUtility::setOriginValue(); + //PhotoAlbumTestCurrentUtility::setOriginValue(); + + $this->__restorePermission(); } /** @@ -94,15 +107,36 @@ public function testPublishException() { ->method('save') ->will($this->returnValue(false)); - $currentValue['Permission']['content_publishable']['value'] = true; - PhotoAlbumTestCurrentUtility::setValue($currentValue); + //$currentValue['Permission']['content_publishable']['value'] = true; + //PhotoAlbumTestCurrentUtility::setValue($currentValue); + $this->__permissionPublishable(); $this->setExpectedException('InternalErrorException'); $data['PhotoAlbumPhoto']['id'] = 1; $data['PhotoAlbumPhoto']['language_id'] = 1; $MockPhotoAlbumPhoto->publish([$data]); - PhotoAlbumTestCurrentUtility::setOriginValue(); + //PhotoAlbumTestCurrentUtility::setOriginValue(); + $this->__restorePermission(); + } + +/** + * __permissionPublishable + * + * @return void + */ + private function __permissionPublishable() { + $this->__originPermission = Current::permission('content_publishable'); + Current::writePermission(null, 'content_publishable', true); + } + +/** + * __restorePermission + * + * @return void + */ + private function __restorePermission() { + Current::writePermission(null, 'content_publishable', $this->__originPermission); } } diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..86fb650 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +3.3.7 diff --git a/View/Elements/jacket_select_add.ctp b/View/Elements/jacket_select_add.ctp index 7a60c47..9e7d90b 100644 --- a/View/Elements/jacket_select_add.ctp +++ b/View/Elements/jacket_select_add.ctp @@ -7,9 +7,13 @@ * @link http://www.netcommons.org NetCommons Project * @license http://www.netcommons.org/license.txt NetCommons License */ + +/** + * @var int $maxFileUploads 一度にアップロードできるファイル数 + */ ?> -