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 e231d1c..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-language: php
-
-php:
- - 5.4
- - 5.5
- - 5.6
- - 7.0
- - 7.1
- - 7.2
-
-sudo: false
-dist: trusty
-
-env:
- matrix:
- - NETCOMMONS_VERSION=master DB=mysql
- global:
- - secure: "UvZqrQzrJFGPz3z6Pbi8h5S8vEFcQT92eLLmOaapL9R+jkycRMKT2sUUKp/nM7C/TcNC/OeUULcrv/cnLH/0sb/ns6nJyoTGzOqlmViAAn92wxKVocy1kbCRYi0RWSrm8DiC492RqLk7oXhz4x8yTMHfowgw4S55M034VZVxtQg="
- - GIT_COMMITTER_NAME=s-nakajima
- - GIT_COMMITTER_EMAIL=nakajimashouhei@gmail.com
- - GIT_AUTHOR_NAME=s-nakajima
- - GIT_AUTHOR_EMAIL=nakajimashouhei@gmail.com
-
-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
-
-notifications:
- email:
- recipients:
- - netcommons3@googlegroups.com
- on_success: never # default: change
- on_failure: always # default: always
diff --git a/Config/Migration/1570717849_add_column_end_time.php b/Config/Migration/1570717849_add_column_end_time.php
new file mode 100644
index 0000000..9e7e797
--- /dev/null
+++ b/Config/Migration/1570717849_add_column_end_time.php
@@ -0,0 +1,67 @@
+
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('NetCommonsMigration', 'NetCommons.Config/Migration');
+
+/**
+ * ルーム削除時に不要データを削除するために使用するテーブルに終了日時を追加
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Config\Migration
+ */
+class AddColumnEndTime extends CakeMigration {
+
+/**
+ * Migration description
+ *
+ * @var string
+ */
+ public $description = 'add_column_end_time';
+
+/**
+ * Actions to be performed
+ *
+ * @var array $migration
+ */
+ public $migration = array(
+ 'up' => array(
+ 'create_field' => array(
+ 'room_delete_related_tables' => array(
+ 'end_time' => array('type' => 'datetime', 'null' => true, 'default' => null, 'after' => 'start_time'),
+ ),
+ ),
+ ),
+ 'down' => array(
+ 'drop_field' => array(
+ 'room_delete_related_tables' => array('end_time'),
+ ),
+ ),
+ );
+
+/**
+ * Before migration callback
+ *
+ * @param string $direction Direction of migration process (up or down)
+ * @return bool Should process continue
+ */
+ public function before($direction) {
+ return true;
+ }
+
+/**
+ * After migration callback
+ *
+ * @param string $direction Direction of migration process (up or down)
+ * @return bool Should process continue
+ */
+ public function after($direction) {
+ return true;
+ }
+}
diff --git a/Config/Schema/schema.php b/Config/Schema/schema.php
index 59c9052..4cbf0d7 100644
--- a/Config/Schema/schema.php
+++ b/Config/Schema/schema.php
@@ -92,6 +92,31 @@ public function after($event = array()) {
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
+/**
+ * room_delete_related_tables table
+ *
+ * @var array
+ */
+ public $room_delete_related_tables = array(
+ 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'unsigned' => false, 'key' => 'primary'),
+ 'room_id' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false, 'key' => 'index', 'comment' => 'ルームID'),
+ 'delete_table_name' => array('type' => 'string', 'null' => false, 'default' => null, 'collate' => 'utf8_general_ci', 'comment' => '削除するテーブル名 rooms|blokcs|pages|users|frames|roles_rooms', 'charset' => 'utf8'),
+ 'field_name' => array('type' => 'string', 'null' => false, 'default' => 'id', 'collate' => 'utf8_general_ci', 'comment' => 'カラム名', 'charset' => 'utf8'),
+ 'value' => array('type' => 'string', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'comment' => '値', 'charset' => 'utf8'),
+ 'foreign_field_conditions' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 1200, 'collate' => 'utf8_general_ci', 'comment' => '対象とするカラムリストをJSON形式で保持 ex) {"id":["*.block_id"], "key":["*.block_key"]}', 'charset' => 'utf8'),
+ 'start_time' => array('type' => 'datetime', 'null' => true, 'default' => null),
+ 'end_time' => array('type' => 'datetime', 'null' => true, 'default' => null),
+ 'created_user' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false),
+ 'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
+ 'modified_user' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false),
+ 'modified' => array('type' => 'datetime', 'null' => true, 'default' => null),
+ 'indexes' => array(
+ 'PRIMARY' => array('column' => 'id', 'unique' => 1),
+ 'room_id' => array('column' => 'room_id', 'unique' => 0)
+ ),
+ 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
+ );
+
/**
* room_role_permissions table
*
@@ -171,30 +196,6 @@ public function after($event = array()) {
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
-/**
- * room_delete_related_tables table
- *
- * @var array
- */
- public $room_delete_related_tables = array(
- 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'unsigned' => false, 'key' => 'primary'),
- 'room_id' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false, 'key' => 'index', 'comment' => 'ルームID'),
- 'delete_table_name' => array('type' => 'string', 'null' => false, 'default' => null, 'collate' => 'utf8_general_ci', 'comment' => '削除するテーブル名 rooms|blokcs|pages|users|frames|roles_rooms', 'charset' => 'utf8'),
- 'field_name' => array('type' => 'string', 'null' => false, 'default' => 'id', 'collate' => 'utf8_general_ci', 'comment' => 'カラム名', 'charset' => 'utf8'),
- 'value' => array('type' => 'string', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'comment' => '値', 'charset' => 'utf8'),
- 'foreign_field_conditions' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 1200, 'collate' => 'utf8_general_ci', 'comment' => '対象とするカラムリストをJSON形式で保持 ex) {"id":["*.block_id"], "key":["*.block_key"]}', 'charset' => 'utf8'),
- 'start_time' => array('type' => 'datetime', 'null' => true, 'default' => null),
- 'created_user' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false),
- 'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
- 'modified_user' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false),
- 'modified' => array('type' => 'datetime', 'null' => true, 'default' => null),
- 'indexes' => array(
- 'PRIMARY' => array('column' => 'id', 'unique' => 1),
- 'room_id' => array('column' => 'room_id', 'unique' => 0)
- ),
- 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
- );
-
/**
* rooms_languages table
*
diff --git a/Console/Command/DeleteNotUsedUsersShell.php b/Console/Command/DeleteNotUsedUsersShell.php
new file mode 100644
index 0000000..938627b
--- /dev/null
+++ b/Console/Command/DeleteNotUsedUsersShell.php
@@ -0,0 +1,268 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('AppShell', 'Console/Command');
+App::uses('RoomsLibForeignConditionsParser', 'Rooms.Lib');
+App::uses('RoomsLibDeleteRoomTables', 'Rooms.Lib');
+App::uses('RoomsLibLog', 'Rooms.Lib');
+App::uses('SiteSettingUtil', 'SiteManager.Utility');
+
+/**
+ * ルーム削除した際の関連テーブルのデータを削除する
+ *
+ * @property Room $Room
+ * @property RolesRoomsUser $RolesRoomsUser
+ * @property User $User
+ * @property PrivateSpace $PrivateSpace
+ * @property RoomDeleteRelatedTable $RoomDeleteRelatedTable
+ *
+ * @property DeleteRelatedRoomsTask $DeleteRelatedRooms
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Console\Command
+ */
+class DeleteNotUsedUsersShell extends AppShell {
+
+/**
+ * 使用するモデル
+ *
+ * @var array
+ */
+ public $uses = [
+ 'Rooms.Room',
+ 'Rooms.RolesRoomsUser',
+ 'Users.User',
+ 'PrivateSpace.PrivateSpace',
+ 'Rooms.RoomDeleteRelatedTable',
+ ];
+
+/**
+ * 使用するタスク
+ *
+ * @var array
+ */
+ public $tasks = [
+ 'Rooms.DeleteRelatedRooms',
+ ];
+
+/**
+ * 一度の処理する件数
+ *
+ * @var int
+ */
+ const PROCESS_COUNT = 10;
+
+/**
+ * Override startup
+ *
+ * @return void
+ */
+ public function startup() {
+ $this->out(__d('rooms', 'Delete room associations Shell'));
+ $this->hr();
+ }
+
+/**
+ * 処理実行
+ *
+ * @return void
+ */
+ public function main() {
+ //ログ出力
+ RoomsLibLog::shellStartLog($this);
+
+ //プライベートルームのデータ削除
+ $result = $this->Room->query(
+ $this->__makeSqlDeletedPrivateRoom('COUNT(*)')
+ );
+ $totalCount = $result[0][0]['count_num'];
+ $maxLoop = ceil($totalCount / self::PROCESS_COUNT);
+ $procCount = 0;
+
+ for ($i = 0; $i < $maxLoop; $i++) {
+ //ログ出力
+ RoomsLibLog::processStartLog(
+ $this,
+ sprintf('Delete Private Rooms[%s]', ($i * self::PROCESS_COUNT + 1) . '/' . $totalCount)
+ );
+
+ $records = $this->Room->query(
+ $this->__makeSqlDeletedPrivateRoom($this->Room->alias . '.id')
+ );
+ foreach ($records as $record) {
+ //$this->out(sprintf('room_id=%s', $record[$this->Room->alias]['id']), 1, self::VERBOSE);
+ $this->__deleteRoom($record[$this->Room->alias]['id']);
+ $procCount++;
+ }
+
+ //ログ出力
+ RoomsLibLog::processEndLog(
+ $this,
+ sprintf('Delete Private Rooms[%s]', $procCount . '/' . $totalCount)
+ );
+ }
+
+ //ユーザのデータ削除
+ $result = $this->User->query(
+ $this->__makeSqlDeletedUser('COUNT(*)')
+ );
+ $totalCount = $result[0][0]['count_num'];
+ $maxLoop = ceil($totalCount / self::PROCESS_COUNT);
+ $procCount = 0;
+
+ for ($i = 0; $i < $maxLoop; $i++) {
+ //ログ出力
+ RoomsLibLog::processStartLog(
+ $this,
+ sprintf('Delete Users[%s]', ($i * self::PROCESS_COUNT + 1) . '/' . $totalCount)
+ );
+
+ $records = $this->User->query(
+ $this->__makeSqlDeletedUser($this->User->alias . '.id')
+ );
+
+ $this->RoomDeleteRelatedTable->begin();
+ foreach ($records as $record) {
+ try {
+ //プライベートルーム削除
+ //ルーム削除情報を登録する
+ $this->RoomDeleteRelatedTable->insertUser($record[$this->User->alias]['id'], '0');
+
+ //トランザクションCommit
+ $this->RoomDeleteRelatedTable->commit();
+
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->RoomDeleteRelatedTable->rollback($ex);
+ }
+
+ $procCount++;
+ }
+
+ //ログ出力
+ RoomsLibLog::processEndLog(
+ $this,
+ sprintf('Delete Users[%s]', $procCount . '/' . $totalCount)
+ );
+ }
+
+ //関連データの削除
+ $this->DeleteRelatedRooms->execute();
+
+ RoomsLibLog::shellEndLog($this);
+ }
+
+/**
+ * 既に削除されているユーザに対するプライベートルームIDを取得するSQLの生成
+ *
+ * @param string $columnName 取得するカラム名
+ * @return void
+ */
+ private function __makeSqlDeletedPrivateRoom($columnName) {
+ $roomTableName = $this->Room->tablePrefix . $this->Room->table;
+ $roomAliasName = $this->Room->alias;
+ $userRoomTableName = $this->RolesRoomsUser->tablePrefix . $this->RolesRoomsUser->table;
+ $userRoomAliasName = $this->RolesRoomsUser->alias;
+
+ $sqlFromWhere = "{$roomTableName} AS {$roomAliasName} " .
+ "LEFT JOIN {$userRoomTableName} AS {$userRoomAliasName} " .
+ "ON ({$roomAliasName}.id = {$userRoomAliasName}.room_id) " .
+ "WHERE {$roomAliasName}.space_id = " . Space::PRIVATE_SPACE_ID . " " .
+ "AND {$roomAliasName}.page_id_top IS NOT NULL " .
+ "AND {$userRoomAliasName}.id IS NULL";
+
+ if ($columnName === 'COUNT(*)') {
+ $sql = "SELECT {$columnName} count_num FROM {$sqlFromWhere}";
+ } else {
+ $sql = "SELECT {$columnName} FROM {$sqlFromWhere} LIMIT " . self::PROCESS_COUNT;
+ }
+
+ return $sql;
+ }
+
+/**
+ * 既に削除されているユーザに対するプライベートルームIDを取得するSQLの生成
+ *
+ * @param string $columnName 取得するカラム名
+ * @return void
+ */
+ private function __makeSqlDeletedUser($columnName) {
+ $userTableName = $this->User->tablePrefix . $this->User->table;
+ $usetAliasName = $this->User->alias;
+ $delRoomTableName = $this->RoomDeleteRelatedTable->tablePrefix .
+ $this->RoomDeleteRelatedTable->table;
+ $delRoomAliasName = $this->RoomDeleteRelatedTable->alias;
+
+ $sqlFromWhere = "{$userTableName} AS {$usetAliasName} " .
+ "LEFT JOIN {$delRoomTableName} AS {$delRoomAliasName} " .
+ "ON (" .
+ "{$delRoomAliasName}.delete_table_name = 'users' " .
+ "AND {$delRoomAliasName}.field_name = 'id' " .
+ "AND {$delRoomAliasName}.value = {$usetAliasName}.id" .
+ ") " .
+ "WHERE {$usetAliasName}.is_deleted = 1 " .
+ "AND {$delRoomAliasName}.id IS NULL";
+
+ if ($columnName === 'COUNT(*)') {
+ $sql = "SELECT {$columnName} count_num FROM {$sqlFromWhere}";
+ } else {
+ $sql = "SELECT {$columnName} FROM {$sqlFromWhere} LIMIT " . self::PROCESS_COUNT;
+ }
+
+ return $sql;
+ }
+
+/**
+ * ルーム削除
+ *
+ * @param int|string $roomId ルームID
+ * @return void
+ */
+ private function __deleteRoom($roomId) {
+ $this->RoomDeleteRelatedTable->begin();
+ try {
+ //プライベートルーム削除
+ //ルーム削除情報を登録する
+ $this->RoomDeleteRelatedTable->insertByRoomId($roomId);
+
+ //roles_roomsデータ削除
+ $this->Room->deleteRolesRoomByRoom($roomId);
+
+ //frameデータの削除
+ $this->Room->deleteFramesByRoom($roomId);
+
+ //pageデータの削除
+ $this->Room->deletePagesByRoom($roomId);
+
+ //blockデータの削除
+ $this->Room->deleteBlocksByRoom($roomId);
+
+ //トランザクションCommit
+ //$this->RoomDeleteRelatedTable->rollback();
+ $this->RoomDeleteRelatedTable->commit();
+
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->RoomDeleteRelatedTable->rollback($ex);
+ }
+ }
+
+/**
+ * 引数の使い方の取得
+ *
+ * @return ConsoleOptionParser
+ */
+ public function getOptionParser() {
+ $parser = parent::getOptionParser();
+ return $parser->description(__d('rooms', 'The Delete room associations Shell'));
+ }
+
+}
diff --git a/Console/Command/DeleteRelatedRoomsShell.php b/Console/Command/DeleteRelatedRoomsShell.php
new file mode 100644
index 0000000..bf96cf7
--- /dev/null
+++ b/Console/Command/DeleteRelatedRoomsShell.php
@@ -0,0 +1,67 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('AppShell', 'Console/Command');
+App::uses('RoomsLibLog', 'Rooms.Lib');
+
+/**
+ * ルーム削除した際の関連テーブルのデータを削除する
+ *
+ * @property DeleteRelatedRoomsTask $DeleteRelatedRooms
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Console\Command
+ */
+class DeleteRelatedRoomsShell extends AppShell {
+
+/**
+ * 使用するタスク
+ *
+ * @var array
+ */
+ public $tasks = [
+ 'Rooms.DeleteRelatedRooms',
+ ];
+
+/**
+ * Override startup
+ *
+ * @return void
+ */
+ public function startup() {
+ $this->out(__d('rooms', 'Delete room associations Shell'));
+ $this->hr();
+ }
+
+/**
+ * 処理実行
+ *
+ * @return void
+ */
+ public function main() {
+ RoomsLibLog::shellStartLog($this);
+
+ $this->DeleteRelatedRooms->execute();
+
+ RoomsLibLog::shellEndLog($this);
+ }
+
+/**
+ * 引数の使い方の取得
+ *
+ * @return ConsoleOptionParser
+ */
+ public function getOptionParser() {
+ $parser = parent::getOptionParser();
+ return $parser->description(__d('rooms', 'The Delete room associations Shell'));
+ }
+
+}
diff --git a/Console/Command/Task/DeleteRelatedRoomsTask.php b/Console/Command/Task/DeleteRelatedRoomsTask.php
new file mode 100644
index 0000000..eac0e0d
--- /dev/null
+++ b/Console/Command/Task/DeleteRelatedRoomsTask.php
@@ -0,0 +1,141 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('AppShell', 'Console/Command');
+App::uses('RoomsLibDeleteRoomTables', 'Rooms.Lib');
+App::uses('RoomsLibLog', 'Rooms.Lib');
+App::uses('SiteSettingUtil', 'SiteManager.Utility');
+
+/**
+ * 関連テーブルのデータを削除する
+ *
+ * @property RoomDeleteRelatedTable $RoomDeleteRelatedTable
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Console\Command
+ */
+class DeleteRelatedRoomsTask extends AppShell {
+
+/**
+ * 使用するモデル
+ *
+ * @var array
+ */
+ public $uses = [
+ 'Rooms.RoomDeleteRelatedTable',
+ ];
+
+/**
+ * DeleteRoomTablesを操作するライブラリ
+ *
+ * @var RoomsLibDeleteRoomTables
+ */
+ private $__RoomsLibDeleteRoomTables;
+
+/**
+ * 一度の処理する件数
+ *
+ * @var int
+ */
+ const PROCESS_COUNT = 100;
+
+/**
+ * タスク実行
+ *
+ * @return void
+ */
+ public function execute() {
+ $this->__RoomsLibDeleteRoomTables =
+ new RoomsLibDeleteRoomTables($this->RoomDeleteRelatedTable, $this);
+
+ $tableName = $this->RoomDeleteRelatedTable->tablePrefix .
+ $this->RoomDeleteRelatedTable->table;
+ $aliasName = $this->RoomDeleteRelatedTable->alias;
+
+ $result = $this->RoomDeleteRelatedTable->query(
+ "SELECT COUNT(*) count_num FROM {$tableName} AS {$aliasName}" .
+ " WHERE {$aliasName}.end_time IS NULL" .
+ //" AND {$aliasName}.room_id = 23"
+ //" AND {$aliasName}.id = 1420"
+ " FOR UPDATE"
+ );
+ if (empty($result)) {
+ return $this->_stop();
+ }
+
+ $totalCount = $result[0][0]['count_num'];
+ $maxLoop = ceil($totalCount / self::PROCESS_COUNT);
+ $procCount = 0;
+
+ for ($i = 0; $i < $maxLoop; $i++) {
+ //ログ出力
+ RoomsLibLog::processStartLog(
+ $this,
+ sprintf('Delete Related Tables[%s]', ($i * self::PROCESS_COUNT + 1) . '/' . $totalCount)
+ );
+
+ $records = $this->RoomDeleteRelatedTable->query(
+ "SELECT {$aliasName}.* FROM {$tableName} AS {$aliasName}" .
+ " WHERE {$aliasName}.end_time IS NULL" .
+ //" AND {$aliasName}.room_id = 23"
+ //" AND {$aliasName}.id = 1420" .
+ " LIMIT " . self::PROCESS_COUNT
+ );
+
+ try {
+ foreach ($records as $record) {
+ //トランザクションBegin
+ $this->RoomDeleteRelatedTable->begin();
+
+ $recordId = $record['RoomDeleteRelatedTable']['id'];
+ $this->RoomDeleteRelatedTable->updateStartTime($recordId);
+
+ $this->__RoomsLibDeleteRoomTables->deleteRelatedTables(
+ $record['RoomDeleteRelatedTable']['delete_table_name'],
+ $record['RoomDeleteRelatedTable']['field_name'],
+ $record['RoomDeleteRelatedTable']['value'],
+ $record['RoomDeleteRelatedTable']['foreign_field_conditions']
+ );
+
+ $this->RoomDeleteRelatedTable->updateEndTime($recordId);
+
+ $procCount++;
+
+ //トランザクションCommit
+ $this->RoomDeleteRelatedTable->commit();
+ //$this->RoomDeleteRelatedTable->rollback();
+ }
+
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->RoomDeleteRelatedTable->rollback($ex);
+ }
+
+ //ログ出力
+ RoomsLibLog::processEndLog(
+ $this,
+ sprintf('Delete Related Tables[%s]', $procCount . '/' . $totalCount)
+ );
+ }
+ }
+
+/**
+ * 引数の使い方の取得
+ *
+ * @return ConsoleOptionParser
+ */
+ public function getOptionParser() {
+ $parser = parent::getOptionParser();
+
+ return $parser->description(__d('rooms', 'The Delete room associations Shell'));
+ }
+
+}
diff --git a/Controller/Component/RoomsRolesFormComponent.php b/Controller/Component/RoomsRolesFormComponent.php
index 9e52827..38b5a23 100644
--- a/Controller/Component/RoomsRolesFormComponent.php
+++ b/Controller/Component/RoomsRolesFormComponent.php
@@ -208,7 +208,7 @@ public function actionRoomsRolesUser(Controller $controller) {
'displayFields' => self::$displaFields,
'extra' => array(
'selectedUsers' => $controller->Session->read('RoomsRolesUsers'),
- 'plugin' => $seached ? $controller->params['plugin'] : '',
+ 'plugin' => $controller->params['plugin'] ?? '',
'search' => $seached
)
));
diff --git a/Controller/RoomAddController.php b/Controller/RoomAddController.php
index 5237e93..750e1d6 100644
--- a/Controller/RoomAddController.php
+++ b/Controller/RoomAddController.php
@@ -165,6 +165,11 @@ public function basic() {
//他言語が入力されていない場合、Currentの言語データをセット
$this->SwitchLanguage->setM17nRequestValue();
+ $defaultParticipation = $this->request->data['Room']['default_participation'] ?? null;
+ if ($this->Session->read('RoomAdd.Room.default_participation') !== $defaultParticipation) {
+ $this->Session->delete('RoomsRolesUsers');
+ }
+
//登録処理
$this->request->data['Room']['in_draft'] = true;
$room = $this->Room->saveRoom($this->request->data);
diff --git a/Lib/RoomsLibCache.php b/Lib/RoomsLibCache.php
new file mode 100644
index 0000000..d92ceea
--- /dev/null
+++ b/Lib/RoomsLibCache.php
@@ -0,0 +1,65 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('NetCommonsCache', 'NetCommons.Utility');
+
+/**
+ * ルーム削除時の関連テーブル削除処理に関するライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ */
+class RoomsLibCache {
+
+/**
+ * NetCommonsキャッシュオブジェクト
+ *
+ * @var NetCommonsCache
+ */
+ private $__NetCommonsCache;
+
+/**
+ * コンストラクタ
+ *
+ * @param Model $Model モデル(当モデルは、MySQLのModelであれば何でも良い)
+ * @return void
+ */
+ public function __construct(Model $Model) {
+ $cacheName = 'delete_rooms_' . $Model->useDbConfig;
+ $isTest = ($Model->useDbConfig === 'test');
+ $this->__NetCommonsCache = new NetCommonsCache($cacheName, $isTest, 'netcommons_model');
+ $this->__NetCommonsCache->clear();
+ }
+
+/**
+ * キャッシュに登録
+ *
+ * @param string $key キャッシュキー
+ * @param string $subKey キャッシュサブキー
+ * @param array $value キャッシュに保存する値
+ * @return array
+ */
+ public function saveCache($key, $subKey, $value) {
+ $this->__NetCommonsCache->write($value, $key, $subKey);
+ }
+
+/**
+ * カラム名に対するテーブルリストを取得する
+ *
+ * @param string $key キャッシュキー
+ * @param string|null $subKey キャッシュサブキー
+ * @return array
+ */
+ public function readCache($key, $subKey = null) {
+ return $this->__NetCommonsCache->read($key, $subKey);
+ }
+
+}
diff --git a/Lib/RoomsLibCommandExec.php b/Lib/RoomsLibCommandExec.php
new file mode 100644
index 0000000..ad6bc9a
--- /dev/null
+++ b/Lib/RoomsLibCommandExec.php
@@ -0,0 +1,59 @@
+
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+/**
+ * コマンド実行 ライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ * @see MailSend よりコピー
+ */
+class RoomsLibCommandExec {
+
+/**
+ * 関連データの削除処理のシェルを実行
+ *
+ * @return void
+ */
+ public static function deleteRelatedRooms() {
+ // バックグラウンドで実行
+ // コマンド例) Console/cake rooms.delete_related_rooms
+ self::__execInBackground(APP . 'Console' . DS . 'cake rooms.delete_related_rooms -q');
+ }
+
+/**
+ * バックグラウンド実行
+ *
+ * @param string $cmd コマンド
+ * @return void
+ */
+ private static function __execInBackground($cmd) {
+ if (self::__isWindows()) {
+ // Windowsの場合
+ pclose(popen('cd ' . APP . ' && start /B ' . $cmd, 'r'));
+ } else {
+ // Linuxの場合
+ // logrotate問題対応 http://dqn.sakusakutto.jp/2012/08/php_exec_nohup_background.html
+ exec('nohup ' . $cmd . ' > /dev/null &');
+ }
+ }
+
+/**
+ * 動作しているOS がWindows かどうかを返す。
+ *
+ * @return bool
+ */
+ private static function __isWindows() {
+ if (DIRECTORY_SEPARATOR == '\\') {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/Lib/RoomsLibDataSourceExecute.php b/Lib/RoomsLibDataSourceExecute.php
new file mode 100644
index 0000000..42e2f3a
--- /dev/null
+++ b/Lib/RoomsLibDataSourceExecute.php
@@ -0,0 +1,308 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('RoomsLibLog', 'Rooms.Lib');
+
+/**
+ * ルーム削除時の関連テーブル削除処理に関するライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ */
+class RoomsLibDataSourceExecute {
+
+/**
+ * データソースオブジェクト
+ *
+ * @var DataSource
+ */
+ private $__DataSource;
+
+/**
+ * 使用するモデル
+ *
+ * @var Model
+ */
+ private $__Model;
+
+/**
+ * 実行シェル
+ *
+ * @var Shell
+ */
+ private $__Shell;
+
+/**
+ * コンストラクタ
+ *
+ * @param Model $Model モデル(当モデルは、MySQLのModelであれば何でも良い)
+ * @param Shell|null $Shell 実行シェル
+ * @return void
+ */
+ public function __construct(Model $Model, $Shell = null) {
+ $this->__Model = $Model;
+ $this->__Shell = $Shell;
+ $this->__DataSource = $Model->getDataSource();
+ $this->__RoomsLibCache = new RoomsLibCache($Model);
+ }
+
+/**
+ * テーブルリストをキャッシュから取得する。なければ、最新情報から取得し、キャッシュに保存する
+ *
+ * @return array
+ */
+ public function showTables() {
+ //キャッシュから取得
+ $cacheTables = $this->__RoomsLibCache->readCache('tables', null);
+ if ($cacheTables) {
+ return $cacheTables;
+ }
+
+ $schemaTables = $this->__findAllSchemeFileTables();
+ $tablePrefix = $this->__Model->tablePrefix;
+
+ //LIKEで多少の絞り込みを行う。ただし、tablePrefixがない場合は、全テーブルが対象となってしまう。
+ $tables = $this->__Model->query("SHOW TABLES LIKE '{$tablePrefix}%s'");
+
+ $retTables = [];
+ foreach ($tables as $table) {
+ $realTableName = null;
+ if (array_key_exists('TABLE_NAMES', $table)) {
+ $realTableName = array_shift($table['TABLE_NAMES']);
+ } elseif (array_key_exists('TABLES', $table)) {
+ $realTableName = array_shift($table['TABLES']);
+ }
+ $realPrefix = substr($realTableName, 0, strlen($tablePrefix));
+ $tableName = substr($realTableName, strlen($tablePrefix));
+
+ if ($tablePrefix !== $realPrefix ||
+ ! in_array($tableName, $schemaTables)) {
+ continue;
+ }
+ $retTables[$tableName] = $this->showTableColumns($realTableName);
+ }
+
+ //キャッシュに登録
+ $this->__RoomsLibCache->saveCache('tables', null, $retTables);
+
+ return $retTables;
+ }
+
+/**
+ * schemaファイルからテーブルリストを取得する
+ *
+ * @return array
+ */
+ private function __findAllSchemeFileTables() {
+ App::uses('CakeSchema', 'Model');
+ $plugins = App::objects('plugins');
+
+ $allTables = [];
+ foreach ($plugins as $plugin) {
+ $tables = $this->__getSchemeFileTablesByPlugin($plugin);
+ if ($tables) {
+ $allTables = array_merge($allTables, $tables);
+ }
+ }
+
+ return $allTables;
+ }
+
+/**
+ * プラグイン名に対してschemaファイルのテーブルリストを取得する
+ *
+ * @param string $plugin プラグイン
+ * @return array|false
+ */
+ private function __getSchemeFileTablesByPlugin($plugin) {
+ $class = $plugin . 'Schema';
+ if (! CakePlugin::loaded($plugin)) {
+ return false;
+ }
+ $filePath = CakePlugin::path($plugin) . 'Config' . DS . 'Schema' . DS . 'schema.php';
+ if (! file_exists($filePath)) {
+ return false;
+ }
+ include_once $filePath;
+ if (! class_exists($class)) {
+ return false;
+ }
+ $classVars = get_class_vars($class);
+
+ $tables = [];
+ foreach ($classVars as $key => $value) {
+ if (is_array($value) && !empty($value)) {
+ $tables[] = $key;
+ }
+ }
+
+ return $tables;
+ }
+
+/**
+ * DBからカラムリストを取得する
+ *
+ * @param string $realTableName テーブルPrefix付きの実際のテーブル名
+ * @return array
+ */
+ public function showTableColumns($realTableName) {
+ $columns = $this->__Model->query('SHOW COLUMNS FROM `' . $realTableName . '`');
+
+ $retColumns = [];
+ foreach ($columns as $column) {
+ $name = $column['COLUMNS']['Field'];
+ $retColumns[$name] = $column['COLUMNS'];
+ }
+ return $retColumns;
+ }
+
+/**
+ * SELECTのSQLを実行する
+ *
+ * @param string $tableName テーブル名
+ * @param array $selectFieldNames SELECTのカラム名リスト
+ * @param array $wheres WHERE条件リスト
+ * @return array
+ */
+ public function selectQuery($tableName, $selectFieldNames, $wheres) {
+ $tablePrefix = $this->__Model->tablePrefix;
+ $realTableName = $tablePrefix . $tableName;
+ $tableAlias = Inflector::classify($tableName);
+
+ $fields = array_map(function ($fieldName) use ($tableAlias) {
+ return $this->__Model->escapeField($fieldName, $tableAlias);
+ }, $selectFieldNames);
+
+ $sql = sprintf(
+ 'SELECT %s FROM `%s` AS `%s` WHERE %s',
+ implode(', ', $fields),
+ $realTableName,
+ $tableAlias,
+ $this->__makeWhereSql($tableAlias, $wheres)
+ );
+
+ $queryResults = $this->__Model->query($sql);
+ return $this->__flattenForDeleteTargetValues($tableName, $tableAlias, $queryResults);
+ }
+
+/**
+ * SELECT COUNT(*)のSQLを実行する
+ *
+ * @param string $tableName テーブル名
+ * @param array $wheres WHERE条件リスト
+ * @return array
+ */
+ public function countQuery($tableName, $wheres) {
+ $tablePrefix = $this->__Model->tablePrefix;
+ $realTableName = $tablePrefix . $tableName;
+ $tableAlias = Inflector::classify($tableName);
+
+ $sql = sprintf(
+ 'SELECT COUNT(*) AS count_num FROM `%s` AS `%s` WHERE %s',
+ $realTableName,
+ $tableAlias,
+ $this->__makeWhereSql($tableAlias, $wheres)
+ );
+
+ $queryResults = $this->__Model->query($sql);
+ if (isset($queryResults[0][0]['count_num'])) {
+ $count = (int)$queryResults[0][0]['count_num'];
+ } else {
+ $count = 0;
+ }
+
+ return $count;
+ }
+
+/**
+ * DELETEのSQLを実行する
+ *
+ * @param string $tableName テーブル名
+ * @param array $wheres WHERE条件リスト
+ * @return array
+ */
+ public function deleteQuery($tableName, $wheres) {
+ $tablePrefix = $this->__Model->tablePrefix;
+ $realTableName = $tablePrefix . $tableName;
+
+ $sql = sprintf(
+ 'DELETE FROM `%s` WHERE %s',
+ $realTableName,
+ $this->__makeWhereSql($realTableName, $wheres)
+ );
+
+ RoomsLibLog::infoLog($this->__Shell, $sql, 2);
+
+ $queryResults = $this->__Model->query($sql);
+
+ RoomsLibLog::successLog(
+ $this->__Shell,
+ '--> AffectedRows = ' . $this->__Model->getAffectedRows(),
+ 2
+ );
+
+ return $queryResults;
+ }
+
+/**
+ * 外部フィードキーに対して、削除対象データを取得する
+ *
+ * @param string $tableAlias テーブルのAlias名
+ * @param array $wheres WHERE条件リスト
+ * @return array
+ */
+ private function __makeWhereSql($tableAlias, $wheres) {
+ $whereConditions = [];
+
+ foreach ($wheres as $fieldName => $values) {
+ $whereField = $this->__Model->escapeField($fieldName, $tableAlias);
+
+ if (is_array($values)) {
+ $escapeValuesArr = array_map(function ($value) {
+ return $this->__DataSource->value($value, 'string');
+ }, $values);
+ $escapeValues = implode(', ', $escapeValuesArr);
+ $whereConditions[] = $whereField . ' IN (' . $escapeValues . ')';
+ } else {
+ $escapeValues = $this->__DataSource->value($values, 'string');
+ $whereConditions[] = $whereField . '=' . $escapeValues;
+ }
+ }
+ return implode(' AND ', $whereConditions);
+ }
+
+/**
+ * クエリの結果をフラットの配列(一次配列、「.」で連結)にする
+ *
+ * @param string $tableName テーブル名
+ * @param string $tableAlias テーブルのAlias名
+ * @param array $queryResults クエリの結果
+ * @return array
+ */
+ private function __flattenForDeleteTargetValues($tableName, $tableAlias, $queryResults) {
+ $retResults = [];
+ if (empty($queryResults)) {
+ return $retResults;
+ }
+
+ foreach ($queryResults as $result) {
+ foreach ($result[$tableAlias] as $field => $value) {
+ if (! isset($retResults[$tableName . '.' . $field])) {
+ $retResults[$tableName . '.' . $field] = [];
+ }
+ $retResults[$tableName . '.' . $field][] = $value;
+ }
+ }
+
+ return $retResults;
+ }
+
+}
diff --git a/Lib/RoomsLibDeleteRoomTables.php b/Lib/RoomsLibDeleteRoomTables.php
new file mode 100644
index 0000000..ed885d4
--- /dev/null
+++ b/Lib/RoomsLibDeleteRoomTables.php
@@ -0,0 +1,436 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('RoomsLibCache', 'Rooms.Lib');
+App::uses('RoomsLibDataSourceExecute', 'Rooms.Lib');
+App::uses('RoomsLibLog', 'Rooms.Lib');
+
+/**
+ * ルーム削除時の関連テーブル削除処理に関するライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ */
+class RoomsLibDeleteRoomTables {
+
+/**
+ * データソースオブジェクト
+ *
+ * @var DataSource
+ */
+ private $__DataSource;
+
+/**
+ * 使用するモデル
+ *
+ * @var Model
+ */
+ private $__Model;
+
+/**
+ * 実行シェル
+ *
+ * @var Shell
+ */
+ private $__Shell = null;
+
+/**
+ * UploadFileモデル
+ *
+ * @var UploadFile
+ */
+ private $__UploadFile;
+
+/**
+ * キャッシュオブジェクト
+ *
+ * @var RoomsLibCache
+ */
+ private $__RoomsLibCache;
+
+/**
+ * キャッシュオブジェクト
+ *
+ * @var RoomsLibDataSourceExecute
+ */
+ private $__RoomsLibDataSourceExecute;
+
+/**
+ * テーブル情報
+ *
+ * @var array
+ */
+ private $__tables = [];
+
+/**
+ * 除外するテーブル
+ *
+ * ※定数扱いだが、php7からconstに配列が使用できるが、php5はconstに配列が使えないためprivateのメンバー変数とする
+ *
+ * @var array
+ */
+ private $__defIgnoreTables = [
+ 'rooms', 'pages', 'blocks', 'frames', 'room_delete_related_tables',
+ 'roles_rooms', 'users'
+ ];
+
+/**
+ * 除外する外部キー
+ *
+ * ※定数扱いだが、php7からconstに配列が使用できるが、php5はconstに配列が使えないためprivateのメンバー変数とする
+ *
+ * @var array
+ */
+ private $__defIgnoreForeigns = [
+ 'cabinet_file_trees'
+ ];
+
+/**
+ * コンストラクタ
+ *
+ * @param Model $Model モデル(当モデルは、MySQLのModelであれば何でも良い)
+ * @param Shell|null $Shell 実行シェル
+ * @return void
+ */
+ public function __construct($Model = null, $Shell = null) {
+ if (! $Model) {
+ $Model = ClassRegistry::init('Rooms.RoomDeleteRelatedTable');
+ }
+ $this->__Model = $Model;
+ $this->__Shell = $Shell;
+ $this->__DataSource = $Model->getDataSource();
+ $this->__RoomsLibCache = new RoomsLibCache($Model);
+ $this->__RoomsLibDataSourceExecute = new RoomsLibDataSourceExecute($Model, $Shell);
+
+ $this->__tables = $this->__RoomsLibDataSourceExecute->showTables();
+
+ $this->__UploadFile = ClassRegistry::init('Files.UploadFile');
+ }
+
+/**
+ * テーブルリストを取得する
+ *
+ * @return array
+ */
+ public function getTableList() {
+ if ($this->__tables) {
+ return array_keys($this->__tables);
+ } else {
+ return [];
+ }
+ }
+
+/**
+ * カラム情報を取得する
+ *
+ * @param string $tableName テーブル名
+ * @return array
+ */
+ private function __getColumns($tableName) {
+ if (isset($this->__tables[$tableName])) {
+ return $this->__tables[$tableName];
+ } else {
+ return [];
+ }
+ }
+
+/**
+ * テーブルに対する関連するテーブルリストを取得する
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param string $foreignKey 外部キーフィールド名
+ * @return array
+ */
+ public function findDeleteTargetRelatedTables($tableName, $fieldName, $foreignKey) {
+ //キャッシュから取得
+ $cacheTables = $this->__RoomsLibCache->readCache(
+ 'delete_related_tables',
+ $tableName . '_' . $fieldName . '_' . $foreignKey
+ );
+ if ($cacheTables) {
+ return $cacheTables;
+ }
+
+ //戻り値の関連テーブルリストを取得する
+ $retRelatedTables = [];
+ if (isset($this->__tables[$tableName][$fieldName])) {
+ $this->__setRecursiveReletedTables(
+ $tableName, $fieldName, $foreignKey, $retRelatedTables
+ );
+ }
+
+ //キャッシュに登録
+ $this->__RoomsLibCache->saveCache(
+ 'delete_related_tables',
+ $tableName . '_' . $fieldName . '_' . $foreignKey,
+ $retRelatedTables
+ );
+
+ return $retRelatedTables;
+ }
+
+/**
+ * テーブルに対する関連するテーブルリストを再帰的に取得する
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param string $foreignKey 外部キーフィールド名
+ * @param array &$retRelatedTables 戻り値の関連テーブルリスト
+ * @return void
+ */
+ private function __setRecursiveReletedTables(
+ $tableName, $fieldName, $foreignKey, &$retRelatedTables) {
+ $relatedTableNames = array_keys($this->__tables);
+ foreach ($relatedTableNames as $relatedTableName) {
+ if (in_array($relatedTableName, $this->__defIgnoreTables, true)) {
+ continue;
+ }
+
+ $columns = $this->__getColumns($relatedTableName);
+ if (! isset($columns[$foreignKey])) {
+ continue;
+ }
+
+ //戻り値の関連テーブルリストにセットする
+ if (! isset($retRelatedTables[$tableName][$fieldName])) {
+ $retRelatedTables[$tableName][$fieldName] = [];
+ }
+ $retRelatedTables[$tableName][$fieldName][] = $relatedTableName . '.' . $foreignKey;
+
+ if (in_array($relatedTableName, $this->__defIgnoreForeigns, true)) {
+ continue;
+ }
+
+ //idカラムがある場合、再帰的に取得する
+ if (isset($columns['id'])) {
+ $relatedForeignKey = $this->__makeForeignKey($relatedTableName, 'id');
+ $this->__setRecursiveReletedTables(
+ $relatedTableName, 'id', $relatedForeignKey, $retRelatedTables
+ );
+ }
+ //keyカラムがある場合、再帰的に取得する
+ if (isset($columns['key'])) {
+ $relatedForeignKey = $this->__makeForeignKey($relatedTableName, 'key');
+ $this->__setRecursiveReletedTables(
+ $relatedTableName, 'key', $relatedForeignKey, $retRelatedTables
+ );
+ }
+ }
+
+ return $retRelatedTables;
+ }
+
+/**
+ * 外部キーのカラム名を生成する
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @return string
+ */
+ private function __makeForeignKey($tableName, $fieldName) {
+ $foreignKey = Inflector::singularize($tableName) . '_' . $fieldName;
+ if ($tableName === 'bbses') {
+ $foreignKey = 'bbs_' . $fieldName;
+ } else {
+ $foreignKey = Inflector::singularize($tableName) . '_' . $fieldName;
+ }
+ return $foreignKey;
+ }
+
+/**
+ * 外部フィールドキーの条件からテーブルリストに展開する
+ *
+ * @param string $cacheKey キャッシュキー
+ * @param string $tableName テーブル名
+ * @param string $forienConditions 外部フィールドの条件(変換後)
+ * @return array
+ */
+ public function expandToTablesFromForienConditions($cacheKey, $tableName, $forienConditions) {
+ //キャッシュから取得
+ $cacheTables = $this->__RoomsLibCache->readCache('expand_to_tables', $cacheKey);
+ if ($cacheTables) {
+ return $cacheTables;
+ }
+
+ //外部フィールドキーの条件からテーブルリストに展開する
+ $retTables = [];
+ foreach ($forienConditions as $fieldName => $conditions) {
+ foreach ($conditions as $condition) {
+ list($relatedTableName, $foriegnKey) = explode('.', $condition);
+ if ($relatedTableName === '*') {
+ $retTables = array_merge(
+ $retTables,
+ $this->findDeleteTargetRelatedTables($tableName, $fieldName, $foriegnKey)
+ );
+ } else {
+ if (! isset($retTables[$tableName][$fieldName])) {
+ $retTables[$tableName][$fieldName] = [];
+ }
+ $retTables[$tableName][$fieldName][] = $relatedTableName . '.' . $foriegnKey;
+ }
+ }
+ }
+
+ //キャッシュに登録
+ $this->__RoomsLibCache->saveCache('expand_to_tables', $cacheKey, $retTables);
+
+ return $retTables;
+ }
+
+/**
+ * 関連データを削除する
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param string $value 値
+ * @param string $foreignConditions 外部フィールドの条件
+ * @return void
+ */
+ public function deleteRelatedTables($tableName, $fieldName, $value, $foreignConditions) {
+ $targetTableList = $this->expandToTablesFromForienConditions(
+ $tableName . '.' . $fieldName,
+ $tableName,
+ RoomsLibForeignConditionsParser::invertDbValue($foreignConditions)
+ );
+
+ $this->__runDeleteRecursiveRelatedTables(
+ $tableName, $fieldName, [$value], $targetTableList
+ );
+
+ if ($tableName === 'users') {
+ $this->deleteAttachment($tableName, 'avatar', $value);
+ }
+ }
+
+/**
+ * 再帰的に削除処理を実行する
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param array $values 値
+ * @param array $targetTableList 対象テーブルリスト
+ * @return array
+ */
+ private function __runDeleteRecursiveRelatedTables(
+ $tableName, $fieldName, $values, $targetTableList) {
+ //テーブルリストに対象のテーブルがあれば、処理する
+ if (! isset($targetTableList[$tableName][$fieldName])) {
+ return;
+ }
+ foreach ($targetTableList[$tableName][$fieldName] as $relatedTableField) {
+ list($relatedTableName, $relatedFieldName) = explode('.', $relatedTableField);
+ if ($relatedTableName === 'upload_files') {
+ //アップロードファイル関連でデータ削除
+ $this->__runDeleteUploadTables($relatedTableName, $relatedFieldName, $values);
+ } else {
+ if (isset($targetTableList[$relatedTableName])) {
+ //再帰する場合
+ $results = $this->__RoomsLibDataSourceExecute->selectQuery(
+ $relatedTableName,
+ array_keys($targetTableList[$relatedTableName]),
+ [$relatedFieldName => $values]
+ );
+
+ if ($results) {
+ foreach ($results as $recursiveTableField => $recursiveValues) {
+ list($recursiveTableName, $recursiveFieldName) = explode('.', $recursiveTableField);
+ $this->__runDeleteRecursiveRelatedTables(
+ $recursiveTableName,
+ $recursiveFieldName,
+ $recursiveValues,
+ $targetTableList
+ );
+ }
+ }
+ }
+
+ $count = $this->__RoomsLibDataSourceExecute->countQuery(
+ $relatedTableName, [$relatedFieldName => $values]
+ );
+ if ($count) {
+ $this->__RoomsLibDataSourceExecute->deleteQuery(
+ $relatedTableName, [$relatedFieldName => $values]
+ );
+ }
+ }
+ }
+ }
+
+/**
+ * アップロードファイル削除処理を実行する
+ *
+ * アップロードは、物理ファイルも消さないといけないので別処理とする。
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param array $values 値
+ * @return array
+ */
+ private function __runDeleteUploadTables($tableName, $fieldName, $values) {
+ $results = $this->__RoomsLibDataSourceExecute->selectQuery(
+ $tableName, ['id'], [$fieldName => $values]
+ );
+
+ if (! empty($results[$tableName . '.id'])) {
+ $this->deleteUploadTables($results[$tableName . '.id']);
+ }
+ }
+
+/**
+ * アップロードファイル削除処理を実行する
+ *
+ * アップロードは、物理ファイルも消さないといけないので別処理とする。
+ *
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param int|string $contentKey コンテンツキー
+ * @return array
+ */
+ public function deleteAttachment($tableName, $fieldName, $contentKey) {
+ $results = $this->__RoomsLibDataSourceExecute->selectQuery(
+ 'upload_files',
+ ['id'],
+ ['plugin_key' => $tableName, 'field_name' => $fieldName, 'content_key' => $contentKey]
+ );
+
+ if (! empty($results['upload_files.id'])) {
+ $this->deleteUploadTables($results['upload_files.id']);
+ }
+ }
+
+/**
+ * アップロードファイル削除処理を実行する
+ *
+ * @param array $fileIds ファイルIDリスト
+ * @return array
+ */
+ public function deleteUploadTables($fileIds) {
+ try {
+ //トランザクションBegin
+ $this->__UploadFile->begin();
+
+ foreach ($fileIds as $fileId) {
+ RoomsLibLog::infoLog($this->__Shell, 'Delete Upload = ' . $fileId, 2);
+ $this->__UploadFile->deleteUploadFile($fileId);
+ RoomsLibLog::successLog($this->__Shell, '--> Success', 2);
+ }
+
+ //トランザクションCommit
+ $this->__UploadFile->commit();
+
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->__UploadFile->rollback($ex);
+ }
+ }
+
+}
diff --git a/Lib/RoomsLibForeignConditionsParser.php b/Lib/RoomsLibForeignConditionsParser.php
new file mode 100644
index 0000000..0434eab
--- /dev/null
+++ b/Lib/RoomsLibForeignConditionsParser.php
@@ -0,0 +1,88 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('RoomsLibDeleteRoomTables', 'Rooms.Lib');
+
+/**
+ * ルーム削除時の関連テーブルのforeign_field_conditionsのパーサ処理に関するライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ */
+class RoomsLibForeignConditionsParser {
+
+/**
+ * 条件のデフォルト値
+ *
+ * ※定数扱いだが、php7からconstに配列が使用できるが、php5はconstに配列が使えないためprivateのメンバー変数とする
+ *
+ * @var array
+ */
+ private static $__defaultConditions = [
+ 'rooms' => [
+ 'id' => ['*.room_id'],
+ ],
+ 'blocks' => [
+ 'id' => ['*.block_id'],
+ 'key' => ['*.block_key'],
+ ],
+ 'pages' => [
+ 'id' => ['*.page_id'],
+ ],
+ 'users' => [
+ 'id' => [
+ '*.user_id',
+ 'calendar_event_share_users.share_user',
+ 'groups.created_user',
+ 'reservation_event_share_users.share_user'
+ ],
+ ],
+ 'frames' => [
+ 'id' => ['*.frame_id'],
+ 'key' => ['*.frame_key'],
+ ],
+ ];
+
+/**
+ * foreign_field_conditionsのデータ取得する
+ *
+ * @param string $tableName テーブル名
+ * @return array
+ */
+ public static function getForeignCondition($tableName) {
+ if (isset(self::$__defaultConditions[$tableName])) {
+ return self::$__defaultConditions[$tableName];
+ } else {
+ return [];
+ }
+ }
+
+/**
+ * 外部フィールドのDBに登録する値に変換する
+ *
+ * @param array $value 外部フィールドの条件(変換前)
+ * @return string
+ */
+ public static function convertDbValue($value) {
+ return json_encode($value);
+ }
+
+/**
+ * 外部フィールドのDBに登録するから処理できる型に変換する
+ *
+ * @param string $value 外部フィールドの条件(変換後)
+ * @return array
+ */
+ public static function invertDbValue($value) {
+ return json_decode($value, true);
+ }
+
+}
diff --git a/Lib/RoomsLibLog.php b/Lib/RoomsLibLog.php
new file mode 100644
index 0000000..8537716
--- /dev/null
+++ b/Lib/RoomsLibLog.php
@@ -0,0 +1,219 @@
+
+ * @author Shohei Nakajima
+ * @link http://www.netcommons.org NetCommons Project
+ * @license http://www.netcommons.org/license.txt NetCommons License
+ * @copyright Copyright 2014, NetCommons Project
+ */
+
+App::uses('Shell', 'Console/Command');
+
+/**
+ * ルーム削除時の関連テーブル削除処理に関するライブラリ
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\Rooms\Lib
+ */
+class RoomsLibLog {
+
+/**
+ * シェル開始時間
+ *
+ * @var float
+ */
+ private static $__shellStartTime;
+
+/**
+ * 処理開始時間
+ *
+ * @var float
+ */
+ private static $__procStartTime;
+
+/**
+ * debugとしてログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @param string $message 出力するメッセージ
+ * @param int $indentLevel インデントレベル
+ * @return void
+ */
+ public static function debugLog($Shell, $message, $indentLevel = 1) {
+ if (! empty($Shell)) {
+ $Shell->out(
+ "" . str_repeat(' ', $indentLevel) . "{$message}",
+ 1,
+ Shell::VERBOSE
+ );
+ }
+ }
+
+/**
+ * infoとしてログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @param string $message 出力するメッセージ
+ * @param int $indentLevel インデントレベル
+ * @return void
+ */
+ public static function infoLog($Shell, $message, $indentLevel = 1) {
+ if (! empty($Shell)) {
+ $Shell->out(
+ "" . str_repeat(' ', $indentLevel) . "{$message}",
+ 1,
+ Shell::VERBOSE
+ );
+ }
+ }
+
+/**
+ * successとしてログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @param string $message 出力するメッセージ
+ * @param int $indentLevel インデントレベル
+ * @return void
+ */
+ public static function successLog($Shell, $message, $indentLevel = 1) {
+ if (! empty($Shell)) {
+ $Shell->out(
+ "" . str_repeat(' ', $indentLevel) . "{$message}",
+ 1,
+ Shell::VERBOSE
+ );
+ }
+ }
+
+/**
+ * シェル開始のログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @return void
+ */
+ public static function shellStartLog($Shell) {
+ self::$__shellStartTime = microtime(true);
+ if (! empty($Shell)) {
+ $Shell->out(sprintf(
+ "[SHELL START %s] Memory=%s",
+ date('Y-m-d H:i:s'),
+ self::getMemoryUsage()
+ ));
+ }
+ }
+
+/**
+ * シェル終了のログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @return void
+ */
+ public static function shellEndLog($Shell) {
+ $endTime = microtime(true);
+ if (! empty($Shell)) {
+ $Shell->out(sprintf(
+ "[SHELL E N D %s] Time=%.4f, Memory=%s",
+ date('Y-m-d H:i:s'),
+ ($endTime - self::$__shellStartTime),
+ self::getMemoryUsage()
+ ));
+ }
+ }
+
+/**
+ * 処理開始のログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @param string $message 出力するメッセージ
+ * @param int $indentLevel インデントレベル
+ * @return void
+ */
+ public static function processStartLog($Shell, $message = '', $indentLevel = 1) {
+ self::$__procStartTime = microtime(true);
+ if (! empty($Shell)) {
+ $Shell->out(sprintf(
+ str_repeat(' ', $indentLevel) . "[PROCESS START %s] %s Memory=%s",
+ date('Y-m-d H:i:s'),
+ $message,
+ self::getMemoryUsage()
+ ));
+ }
+ }
+
+/**
+ * 処理終了のログ出力
+ *
+ * @param Shell|null $Shell 実行シェル
+ * @param string $message 出力するメッセージ
+ * @param int $indentLevel インデントレベル
+ * @return void
+ */
+ public static function processEndLog($Shell, $message = '', $indentLevel = 1) {
+ $endTime = microtime(true);
+ if (! empty($Shell)) {
+ $Shell->out(sprintf(
+ str_repeat(' ', $indentLevel) . "[PROCESS E N D %s] %s Time=%.4f, Memory=%s",
+ date('Y-m-d H:i:s'),
+ $message,
+ ($endTime - self::$__procStartTime),
+ self::getMemoryUsage()
+ ));
+ }
+ }
+
+/**
+ * メモリー使用量取得 単位付きで取得
+ *
+ * @return string
+ * @see GetMemoryUsageComponent::execute() からコードをコピペ
+ */
+ public static function getMemoryUsage() {
+ $size = memory_get_usage();
+ return self::__formatMemory($size);
+ }
+
+/**
+ * getPeakMemoryUsage
+ *
+ * @return string
+ */
+ public static function getPeakMemoryUsage() {
+ $size = memory_get_peak_usage();
+ return self::__formatMemory($size);
+ }
+
+/**
+ * メモリ使用量のフォーマット
+ *
+ * @param int $size memory使用量
+ * @return string
+ */
+ private static function __formatMemory($size) {
+ $byte = 1024; // バイト
+ $mb = pow($byte, 2); // メガバイト
+ //$gb = pow($byte, 3); // ギガバイト
+
+ switch(true){
+ //case $size >= $gb:
+ // $target = $gb;
+ // $unit = 'GB';
+ // break;
+ case $size >= $mb:
+ $target = $mb;
+ $unit = 'MB';
+ break;
+ default:
+ $target = $byte;
+ $unit = 'KB';
+ break;
+ }
+
+ $newSize = round($size / $target, 3);
+ $fileSize = number_format($newSize, 3, '.', ',') . $unit;
+
+ return $fileSize;
+ }
+
+}
diff --git a/Locale/jpn/LC_MESSAGES/rooms.po b/Locale/jpn/LC_MESSAGES/rooms.po
index 98dac67..7967a25 100644
--- a/Locale/jpn/LC_MESSAGES/rooms.po
+++ b/Locale/jpn/LC_MESSAGES/rooms.po
@@ -233,3 +233,21 @@ msgstr "%sに変更する"
#: Rooms/View/Elements/Rooms/edit_form.ctp:64
msgid "Last access datetime"
msgstr "最終アクセス日時"
+
+#
+# 削除ルームに対する関連テーブル削除シェル
+#
+msgid "Delete room associations Shell"
+msgstr "削除ルームに対する関連テーブルのデータ削除"
+
+msgid "Process limit"
+msgstr "処理件数"
+
+msgid "Process room id"
+msgstr "処理するルームID"
+
+msgid "All records"
+msgstr "全レコード"
+
+msgid "Deleted."
+msgstr "削除しました。"
diff --git a/Model/Behavior/DeleteRoomAssociationsBehavior.php b/Model/Behavior/DeleteRoomAssociationsBehavior.php
index 6ee84a8..8018fd2 100644
--- a/Model/Behavior/DeleteRoomAssociationsBehavior.php
+++ b/Model/Behavior/DeleteRoomAssociationsBehavior.php
@@ -64,18 +64,23 @@ public function deletePagesByRoom(Model $model, $roomId) {
]);
//外部キーがpage_idのデータを削除
- $pageIds = $model->Page->find('list', array(
+ $page = $model->Page->find('first', array(
'recursive' => -1,
//'fields' => array('id', 'key'),
'conditions' => array(
$model->Page->alias . '.room_id' => $roomId,
+ 'root_id = parent_id'
),
));
- $pageIds = array_keys($pageIds);
+ if (! $page) {
+ return true; // すでに対象のページがないということなので
+ }
+
+ $pageId = $page['Page']['id'];
- //Tree構成の関係で、ページの削除はdeleteAllでやる
- if (!$model->Page->deleteAll(array($model->Page->alias . '.id' => $pageIds), false)) {
- CakeLog::error('[room deleting] Page->deleteAll ' . implode(', ', $pageIds));
+ //Tree構成の関係で、ページの削除はdeleteで親だけを消して、あとはCallbackに任せる
+ if (!$model->Page->delete(array($model->Page->alias . '.id' => $pageId), false)) {
+ CakeLog::error('[room deleting] Page->delete ' . $pageId);
throw new InternalErrorException(__d('net_commons', 'Internal Server Error'));
}
@@ -169,8 +174,13 @@ public function queryDeleteAll(Model $model, $field, $value) {
$tables = $model->query("SHOW TABLES LIKE '{$prefix}%'");
foreach ($tables as $table) {
- $tableName = array_shift($table['TABLE_NAMES']);
- $columns = $model->query('SHOW COLUMNS FROM ' . $tableName);
+ $tableName = null;
+ if (array_key_exists('TABLE_NAMES', $table)) {
+ $tableName = array_shift($table['TABLE_NAMES']);
+ } elseif (array_key_exists('TABLES', $table)) {
+ $tableName = array_shift($table['TABLES']);
+ }
+ $columns = $model->query('SHOW COLUMNS FROM `' . $tableName . '`');
if (! Hash::check($columns, '{n}.COLUMNS[Field=' . $field . ']')) {
continue;
}
diff --git a/Model/Room.php b/Model/Room.php
index 32ecc64..f856be4 100644
--- a/Model/Room.php
+++ b/Model/Room.php
@@ -13,6 +13,7 @@
App::uses('Role', 'Roles.Model');
App::uses('Space', 'Rooms.Model');
App::uses('BlockSettingBehavior', 'Blocks.Model/Behavior');
+App::uses('RoomsLibCommandExec', 'Rooms.Lib');
/**
* Room Model
@@ -624,10 +625,12 @@ public function saveMove($room, $moveMethod, $moveStep = 1) {
* ルームの削除処理
*
* @param array $data received post data
+ * @param bool $execDelCommand 削除コマンドを実行するかどうか
* @return bool True on success, false on validation errors
* @throws InternalErrorException
+ * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
- public function deleteRoom($data) {
+ public function deleteRoom($data, $execDelCommand = true) {
$this->loadModels([
'RoomsLanguage' => 'Rooms.RoomsLanguage',
]);
@@ -644,6 +647,12 @@ public function deleteRoom($data) {
//トランザクションCommit
$this->commit();
+ //コマンドライン実行
+ if ($execDelCommand) {
+ //会員の一括削除の場合、毎回実行しないようにするため。
+ RoomsLibCommandExec::deleteRelatedRooms();
+ }
+
} catch (Exception $ex) {
//トランザクションRollback
$this->rollback($ex);
diff --git a/Model/RoomDeleteRelatedTable.php b/Model/RoomDeleteRelatedTable.php
index e3572f5..3601abe 100644
--- a/Model/RoomDeleteRelatedTable.php
+++ b/Model/RoomDeleteRelatedTable.php
@@ -10,6 +10,7 @@
*/
App::uses('RoomsAppModel', 'Rooms.Model');
+App::uses('RoomsLibForeignConditionsParser', 'Rooms.Lib');
/**
* ルーム削除時に関連して削除するテーブル Model
@@ -19,36 +20,6 @@
*/
class RoomDeleteRelatedTable extends RoomsAppModel {
-/**
- * 削除
- *
- * @var array
- */
- private $__defaultConditions = [
- 'rooms' => [
- 'id' => ['*.room_id'],
- ],
- 'blocks' => [
- 'id' => ['*.block_id'],
- 'key' => ['*.block_key'],
- ],
- 'pages' => [
- 'id' => ['*.page_id'],
- ],
- 'users' => [
- 'id' => [
- '*.user_id',
- 'calendar_event_share_users.share_user',
- 'groups.created_user',
- 'reservation_event_share_users.share_user'
- ],
- ],
- 'frames' => [
- 'id' => ['*.frame_id'],
- 'key' => ['*.frame_key'],
- ],
- ];
-
/**
* ルームIDを基にルーム削除関連データを追加
*
@@ -63,6 +34,10 @@ public function insertByRoomId($roomId) {
'Block' => 'Blocks.Block',
]);
+ if ($this->__existTableValue($this->Room->table, 'id', $roomId)) {
+ return;
+ }
+
$targetTables = [
//$this->Room->table,
$this->Page->table,
@@ -76,13 +51,17 @@ public function insertByRoomId($roomId) {
try {
$table = $this->Room->table;
$this->__execInsertQuery(
- $roomId, 'id', $roomId, $table, 'id', $this->__defaultConditions[$table]
+ $roomId, 'id', $roomId, $table, 'id',
+ RoomsLibForeignConditionsParser::getForeignCondition($table)
);
foreach ($targetTables as $table) {
- $this->__execInsertQuery(
- $roomId, 'room_id', $roomId, $table, 'id', $this->__defaultConditions[$table]
- );
+ $foreignConditions = RoomsLibForeignConditionsParser::getForeignCondition($table);
+ foreach ($foreignConditions as $field => $condition) {
+ $this->__execInsertQuery(
+ $roomId, 'room_id', $roomId, $table, $field, [$field => $condition]
+ );
+ }
}
//トランザクションCommit
@@ -106,13 +85,37 @@ public function insertUser($userId, $roomId) {
'User' => 'Users.User',
]);
+ $table = $this->User->table;
+ $this->insert(
+ $roomId, $table, 'id', $userId,
+ RoomsLibForeignConditionsParser::getForeignCondition($table)
+ );
+ }
+
+/**
+ * ルーム削除関連データを追加
+ *
+ * insertByRoomId()やinsertUser()に含まれないものを追加する時に使用する
+ *
+ * @param int|string $roomId ルームID
+ * @param string $tableName テーブル名
+ * @param string $fieldName カラム名
+ * @param string|int $value 値
+ * @param array $foreignConditions 外部キーリスト
+ * @return void
+ */
+ public function insert($roomId, $tableName, $fieldName, $value, $foreignConditions) {
+ //既に登録されているかチェック
+ if ($this->__existTableValue($tableName, $fieldName, $value)) {
+ return;
+ }
+
//トランザクションBegin
$this->begin();
try {
- $table = $this->User->table;
$this->__execInsertQuery(
- $roomId, 'id', $userId, $table, 'id', $this->__defaultConditions[$table]
+ $roomId, $fieldName, $value, $tableName, $fieldName, $foreignConditions
);
//トランザクションCommit
@@ -124,6 +127,27 @@ public function insertUser($userId, $roomId) {
}
}
+/**
+ * 対象テーブルの値が存在するか否か
+ *
+ * @param string $tableName 対象テーブル名
+ * @param string $fieldName 対象カラム名
+ * @param string $value 対象の値
+ *
+ * @return bool
+ */
+ private function __existTableValue($tableName, $fieldName, $value) {
+ $findTableName = $this->tablePrefix . $this->table;
+ $result = $this->query(
+ "SELECT COUNT(*) count_num FROM {$findTableName} AS {$this->alias}" .
+ " WHERE {$this->alias}.delete_table_name = :tableName" .
+ " AND {$this->alias}.field_name = :fieldName" .
+ " AND {$this->alias}.value = :value",
+ ['tableName' => $tableName, 'fieldName' => $fieldName, 'value' => $value]
+ );
+ return $result[0][0]['count_num'] > 0;
+ }
+
/**
* ルーム削除に関する情報を追加する
*
@@ -150,7 +174,8 @@ private function __execInsertQuery(
'delete_table_name' => $db->value($targetTableName, 'string'),
'field_name' => $db->value($targetFieldName, 'string'),
'value' => $this->escapeField($targetFieldName, $targetAlias),
- 'foreign_field_conditions' => $db->value(json_encode($foreignConditions), 'string'),
+ 'foreign_field_conditions' =>
+ $db->value(RoomsLibForeignConditionsParser::convertDbValue($foreignConditions), 'string'),
'created' => $db->value(date('Y-m-d H:i:s'), 'string'),
'created_user' => $db->value($loginUserId, 'string'),
'modified' => $db->value(date('Y-m-d H:i:s'), 'string'),
@@ -161,10 +186,60 @@ private function __execInsertQuery(
' (' . implode(', ', array_keys($values)) . ')' .
' SELECT ' . implode(', ', $values) .
' FROM ' . $fullTargetTableName . ' AS ' . $targetAlias .
- ' WHERE ' . $this->escapeField($findField, $targetAlias) . ' = ' . $findValue;
+ ' WHERE ' . $this->escapeField($findField, $targetAlias) .
+ ' = ' . $db->value($findValue, 'string');
//登録処理
return $this->query($sql);
}
+/**
+ * 開始時間の更新
+ *
+ * @param int|string $id ID
+ * @return bool
+ */
+ public function updateStartTime($id) {
+ $this->__updateField($id, 'start_time', gmdate('Y-m-d H:i:s'));
+ }
+
+/**
+ * 終了日時の更新
+ *
+ * @param int|string $id ID
+ * @return bool
+ */
+ public function updateEndTime($id) {
+ $this->__updateField($id, 'end_time', gmdate('Y-m-d H:i:s'));
+ }
+
+/**
+ * 更新処理
+ *
+ * @param int|string $id ID
+ * @param string $name カラム名
+ * @param string $value 値
+ * @return bool|array See Model::save() False on failure or an array of model data on success.
+ * @see Model::save()
+ * @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false
+ */
+ private function __updateField($id, $name, $value) {
+ //トランザクションBegin
+ $this->begin();
+
+ try {
+ $this->create(false);
+
+ $options = ['validate' => false, 'fieldList' => [$name]];
+ $this->save([$this->alias => [$this->primaryKey => $id, $name => $value]], $options);
+
+ //トランザクションCommit
+ $this->commit();
+
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->rollback($ex);
+ }
+ }
+
}
diff --git a/README.md b/README.md
index 57e329c..dccdb4b 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,6 @@
Rooms
=====
-Rooms for NetComomns3
-
-[](https://travis-ci.org/NetCommons3/Rooms)
-[](https://coveralls.io/r/NetCommons3/Rooms?branch=master)
-
-| dependencies | status |
-| ------------ | ------ |
-| composer.json | [](https://www.versioneye.com/user/projects/53f823dde09da395fc0004e2) |
\ No newline at end of file
+[](https://github.com/NetCommons3/Rooms/actions/workflows/tests.yml)
+[](https://coveralls.io/r/NetCommons3/Rooms?branch=master)
+[](https://packagist.org/packages/netcommons/rooms)
diff --git a/Test/Case/Model/Behavior/DeleteRoomAssociationsBehavior/DeletePagesByRoomTest.php b/Test/Case/Model/Behavior/DeleteRoomAssociationsBehavior/DeletePagesByRoomTest.php
index 60ea29f..004d751 100644
--- a/Test/Case/Model/Behavior/DeleteRoomAssociationsBehavior/DeletePagesByRoomTest.php
+++ b/Test/Case/Model/Behavior/DeleteRoomAssociationsBehavior/DeletePagesByRoomTest.php
@@ -93,7 +93,7 @@ public function testDeletePagesByRoom($roomId) {
* @return void
*/
public function testDeletePagesByRoomOnExceptionError($roomId) {
- $this->_mockForReturnFalse('TestModel', 'Pages.Page', 'deleteAll');
+ $this->_mockForReturnFalse('TestModel', 'Pages.Page', 'delete');
//事前チェック
$this->__assertTable('Page', 3);
@@ -102,7 +102,6 @@ public function testDeletePagesByRoomOnExceptionError($roomId) {
$this->setExpectedException('InternalErrorException');
$this->TestModel->deletePagesByRoom($roomId);
}
-
/**
* テーブルのチェック
*
diff --git a/Test/Case/Model/Behavior/SaveRoomAssociationsBehavior/SaveDefaultPageTest.php b/Test/Case/Model/Behavior/SaveRoomAssociationsBehavior/SaveDefaultPageTest.php
index e2ccdad..1deac3d 100644
--- a/Test/Case/Model/Behavior/SaveRoomAssociationsBehavior/SaveDefaultPageTest.php
+++ b/Test/Case/Model/Behavior/SaveRoomAssociationsBehavior/SaveDefaultPageTest.php
@@ -113,7 +113,7 @@ public function testPageOnExceptionError($data) {
* @return void
*/
public function testRoomOnExceptionError($data) {
- $this->_mockForReturnTrue('TestModel', 'Pages.Page', 'savePage');
+ $this->_mockForReturn('TestModel', 'Pages.Page', 'savePage', ['Page' => ['id' => '5']]);
$this->_mockForReturnFalse('TestModel', 'Rooms.Room', 'updateAll');
//テスト実施
diff --git a/Test/Case/Model/RolesRoomsUser/GetRolesRoomsUsersTest.php b/Test/Case/Model/RolesRoomsUser/GetRolesRoomsUsersTest.php
index a8b60be..bc60f63 100644
--- a/Test/Case/Model/RolesRoomsUser/GetRolesRoomsUsersTest.php
+++ b/Test/Case/Model/RolesRoomsUser/GetRolesRoomsUsersTest.php
@@ -73,7 +73,7 @@ public function testGetRolesRoomsUsers() {
);
//テスト実施
- $result = $this->$model->$methodName($conditions);
+ $result = $this->$model->$methodName($conditions, ['order' => ['Room.id' => 'asc']]);
//チェック
$this->assertCount(3, $result);
diff --git a/Test/Fixture/Page4deleteFixture.php b/Test/Fixture/Page4deleteFixture.php
index 544a114..0dd3c7c 100644
--- a/Test/Fixture/Page4deleteFixture.php
+++ b/Test/Fixture/Page4deleteFixture.php
@@ -43,7 +43,8 @@ class Page4deleteFixture extends PageFixture {
array(
'id' => '1',
'room_id' => '2',
- 'parent_id' => null,
+ 'parent_id' => 1,
+ 'root_id' => 1,
//'lft' => '1',
//'rght' => '2',
'weight' => '1',
@@ -57,12 +58,13 @@ class Page4deleteFixture extends PageFixture {
array(
'id' => '2',
'room_id' => '5',
- 'parent_id' => null,
+ 'parent_id' => 1,
+ 'root_id' => 1,
//'lft' => '3',
//'rght' => '4',
'weight' => '2',
'sort_key' => '~00000002',
- 'child_count' => '0',
+ 'child_count' => '1',
'permalink' => 'delete_page_1',
'slug' => 'delete_page_1',
'is_container_fluid' => true,
@@ -70,11 +72,12 @@ class Page4deleteFixture extends PageFixture {
array(
'id' => '3',
'room_id' => '5',
- 'parent_id' => null,
+ 'parent_id' => 2,
+ 'root_id' => 1,
//'lft' => '5',
//'rght' => '6',
- 'weight' => '3',
- 'sort_key' => '~00000003',
+ 'weight' => '1',
+ 'sort_key' => '~00000002-00000001',
'child_count' => '0',
'permalink' => 'delete_page_2',
'slug' => 'delete_page_2',
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/Helper/RoomsHelper.php b/View/Helper/RoomsHelper.php
index 2d91ec6..05f5ab6 100644
--- a/View/Helper/RoomsHelper.php
+++ b/View/Helper/RoomsHelper.php
@@ -322,7 +322,7 @@ public function roomRoleName($roomRoleKey, $options = []) {
' data-content="' . $roleDesc . '"' .
//' data-trigger="focus">';
' data-trigger="focus">';
- $html .= '';
+ $html .= '';
$html .= '';
$html .= '';
diff --git a/View/Helper/RoomsRolesFormHelper.php b/View/Helper/RoomsRolesFormHelper.php
index 4126234..2a086f4 100644
--- a/View/Helper/RoomsRolesFormHelper.php
+++ b/View/Helper/RoomsRolesFormHelper.php
@@ -196,7 +196,7 @@ public function roomRolesDescription($options = []) {
' data-content="' . h($description) . '"' .
' data-trigger="focus">';
- $html .= '';
+ $html .= '';
$html .= '';
$html .= '';
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 31a3436..b6cfc6d 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,5 +1,8 @@
+
+
+
app/Plugin/Rooms
@@ -14,5 +17,6 @@
+