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 dddd247..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-language: php
-
-php:
- - 5.4
- - 5.5
- - 5.6
- - 7.0
- - 7.1
-
-sudo: false
-
-env:
- matrix:
- - NETCOMMONS_VERSION=master DB=mysql
- global:
- - secure: "lpymvB/8q92fy5b52dCYUYvBuSLWORjqEGuHWKmEHXu6KbV1oMN0bp1BMryETafujZHscvaYgtho8gG5x+7EpucVU/ib1NWHeN3lrbDGrgwqTOVPnkEuBlzT2lUKw7ZwY4VtlScxmI8KRGj/larf1Ii5Tei4aNM7X4sWXO73AuZJBQLVgWP1E8DBtoHUcpaPPQPelr0q7HYUfaLakGpqzK4UXCtI8QASXV47C+3OUBJVLvl+PzNsMsAwHYPKj9as/OUwA5TgMiNXaWDyi4WolznJRHGK6ctM6XaVX7QPXbk3GfX1DpIJJIBioFKv4m3vQLmTRq76h4brZhe/OkY2q0a5q6MeNXS6VN7qopM+N160KTao98BseKaANwOyYxD9gPVFi2XXGvYlo0wbFh/Xj4by2UBuBIoyHJ9yR2wsAmohyCicHXOvPd0wZoJYx0y8Q3lg01vw+ZbOCAQtzURXIOxjUtQDlKYE6tuBnz4cEyAp0xFIVgDf1rHJ5fovMzu60345GU8/yBztaZnVlefp7Re4hBYggbdRy2s5VxmT66NVxSeHF4eOBsI73yq5fbUMmYDa13n/lZhJOzE/A49GEFnLhMAMbBpZtOHD7Opg/CdA64QrjO0CYcrpoflwhgc5UOGhuMiH0hO0BN+zXk01KmOHGe9XbX9EXjjUyBDrlUo="
- - 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/routes.php b/Config/routes.php
index ceac0bf..643847e 100644
--- a/Config/routes.php
+++ b/Config/routes.php
@@ -27,7 +27,7 @@
Router::connect(
'/user_manager/user_manager/download/:user_id/:field_name/:size',
['plugin' => 'user_manager', 'controller' => 'user_manager_avatar', 'action' => 'download'],
- ['user_id' => '[0-9]+', 'size' => 'big|medium|small|thumb']
+ ['user_id' => '[0-9]+', 'field_name' => 'avatar', 'size' => 'big|medium|small|thumb']
);
Router::connect(
'/user_manager/user_manager/download/:user_id/:field_name',
@@ -37,7 +37,13 @@
'action' => 'download',
'size' => 'medium'
],
- ['user_id' => '[0-9]+', 'size' => 'medium']
+ ['user_id' => '[0-9]+', 'field_name' => 'avatar', 'size' => 'medium']
+);
+Router::connect(
+ '/user_manager/user_manager/download/*',
+ array(
+ 'plugin' => 'user_manager', 'controller' => 'user_manager', 'action' => 'throwBadRequest',
+ )
);
Router::connect(
diff --git a/Controller/Component/UserManagerBulkComponent.php b/Controller/Component/UserManagerBulkComponent.php
new file mode 100644
index 0000000..8fff260
--- /dev/null
+++ b/Controller/Component/UserManagerBulkComponent.php
@@ -0,0 +1,282 @@
+
+ * @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('Component', 'Controller');
+\App::uses('UserSearchCompComponent', 'Users.Controller/Component');
+\App::uses('UserManagerSearchLib', 'UserManager.Lib');
+\App::uses('RoomsLibCommandExec', 'Rooms.Lib');
+\App::uses('UserAttributeChoice', 'UserAttributes.Model');
+\App::uses('RoomsLibCommandExec', 'Rooms.Lib');
+
+/**
+ * UserManager Component
+ *
+ * @author Shohei Nakajima
+ * @package NetCommons\UserManager\Controller\Component
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class UserManagerBulkComponent extends Component {
+
+/**
+ * コントローラ
+ *
+ * @var UserManagerController|Controller
+ */
+ public $controller;
+
+/**
+ * Userモデル
+ *
+ * @var User
+ */
+ public $User;
+
+/**
+ * UserRoleSettingモデル
+ *
+ * @var UserRoleSetting
+ */
+ public $UserRoleSetting;
+
+/**
+ * Called after the Controller::beforeFilter() and before the controller action
+ *
+ * @param Controller $controller Controller with components to startup
+ * @return void
+ */
+ public function startup(Controller $controller) {
+ $this->controller = $controller;
+ $this->User = ClassRegistry::init('Users.User');
+ $this->UserRoleSetting = ClassRegistry::init('UserRoles.UserRoleSetting');
+ }
+
+/**
+ * 選択した会員を一括で利用不可に設定する
+ *
+ * @return CakeResponse|null
+ * @throws BadRequestException
+ */
+ public function bulkNonactive() {
+ $controller = $this->controller;
+ $data = $controller->request->data['UserManagerBulk'];
+
+ $displayIds = explode(',', $data['displayIds']);
+ $checkedIds = explode(',', $data['checkedIds']);
+
+ //バリデーション
+ try {
+ if (! $this->__validateBulk($checkedIds, $displayIds)) {
+ //通常この条件に来ない。
+ //ただし、jsonで処理された場合、当処理に入ってくるため、念のため抜けておく。
+ return;
+ }
+ } catch (Exception $ex) {
+ throw $ex;
+ }
+
+ //登録処理
+ $users = $this->__findBulkUsers($checkedIds);
+ try {
+ //トランザクションBegin
+ $this->User->begin();
+
+ foreach ($users as $user) {
+ if (! UserManagerSearchLib::hasEditableBulkUser($user)) {
+ throw new BadRequestException(__d('net_commons', 'Bad Request'));
+ }
+
+ //ステータス更新処理
+ $this->User->updateStatus($user['User']['id'], \UserAttributeChoice::STATUS_CODE_NONACTIVE);
+ }
+
+ //トランザクションCommit
+ $this->User->commit();
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->User->rollback($ex);
+ }
+
+ //リダイレクト
+ $controller->NetCommons->setFlashNotification(
+ __d('net_commons', 'Successfully saved.'), array('class' => 'success')
+ );
+ return $controller->redirect($controller->referer('/user_manager/user_manager/index/'));
+ }
+
+/**
+ * 選択した会員を一括で削除する
+ *
+ * @return CakeResponse|null
+ * @throws BadRequestException
+ * @throws InternalErrorException
+ */
+ public function bulkDelete() {
+ $controller = $this->controller;
+ $data = $controller->request->data['UserManagerBulk'];
+
+ $displayIds = explode(',', $data['displayIds']);
+ $checkedIds = explode(',', $data['checkedIds']);
+
+ //バリデーション
+ if (! $this->__validateBulk($checkedIds, $displayIds)) {
+ //通常この条件に来ない。
+ //ただし、jsonで処理された場合、当処理に入ってくるため、念のため抜けておく。
+ return;
+ }
+
+ //削除処理
+ $users = $this->__findBulkUsers($checkedIds);
+ try {
+ //トランザクションBegin
+ $this->User->begin();
+
+ foreach ($users as $user) {
+ if (! UserManagerSearchLib::hasEditableBulkUser($user)) {
+ throw new BadRequestException(__d('net_commons', 'Bad Request'));
+ }
+
+ //削除処理
+ if (! $this->User->deleteUser($user, false)) {
+ //本来あり得ないが、この処理に入ってきたら、エラーログに出力して、
+ //throwを投げる
+ $error = [
+ 'user' => $user,
+ 'validationErrors' => $this->User->validationErrors,
+ ];
+ \CakeLog::error(__METHOD__ . '(' . __LINE__ . ') ' . var_export($error, true));
+ throw new InternalErrorException(__d('net_commons', 'Internal Server Error'));
+ }
+ }
+
+ //トランザクションCommit
+ $this->User->commit();
+
+ //全てが削除されたら、シェルを起動
+ RoomsLibCommandExec::deleteRelatedRooms();
+ } catch (Exception $ex) {
+ //トランザクションRollback
+ $this->User->rollback($ex);
+ }
+
+ //リダイレクト
+ $controller->NetCommons->setFlashNotification(
+ __d('net_commons', 'Successfully deleted.'), array('class' => 'success')
+ );
+
+ return $controller->redirect($this->__makeDeleteRedirectUrl($data));
+ }
+
+/**
+ * 一括削除のリダイレクトURLを生成する
+ *
+ * @param array $data リクエストデータ
+ * @return string
+ */
+ private function __makeDeleteRedirectUrl($data) {
+ $controller = $this->controller;
+
+ $referer = $controller->referer('/user_manager/user_manager/index/');
+ $hasPrev = $data['hasPrev'] ?? false;
+ $hasNext = $data['hasNext'] ?? false;
+ $hasAdminUser = $data['hasAdminUser'] ?? false;
+ if (! $hasPrev ||
+ $hasNext ||
+ $hasAdminUser ||
+ $data['displayIds'] !== $data['checkedIds']) {
+ //先頭ページか、最終ページではない、全選択されていない場合
+ return $referer;
+ } else {
+ //先頭ページではなく、最終ページで、全選択されている場合
+ $urlPath = parse_url($referer, PHP_URL_PATH);
+ if (! $urlPath) {
+ $urlPath = $referer;
+ }
+ $match = [];
+ if (preg_match('#/page:([0-9]+)#iu', $urlPath, $match)) {
+ $urlPath = preg_replace('#/page:([0-9]+)#iu', '', $urlPath);
+ if (substr($urlPath, -1) !== '/') {
+ $urlPath .= '/';
+ }
+ $urlPath .= 'page:' . ((int)$match[1] - 1);
+ }
+
+ $urlQuery = parse_url($referer, PHP_URL_QUERY);
+ return $urlPath . ($urlQuery ? '?' . $urlQuery : '');
+ }
+ }
+
+/**
+ * 選択した会員を一括処理できるかどうかチェックする
+ *
+ * @param array $checkedIds チェックしているユーザIDリスト
+ * @param array $displayIds 画面に表示しているユーザIDリスト
+ *
+ * @return bool
+ */
+ private function __validateBulk($checkedIds, $displayIds) {
+ if (count(array_diff($checkedIds, $displayIds)) > 0) {
+ $this->controller->throwBadRequest();
+ return false;
+ }
+
+ $count = $this->User->find('count', [
+ 'recursive' => -1,
+ 'conditions' => [
+ 'id' => $checkedIds
+ ],
+ ]);
+ if ($count !== count($checkedIds)) {
+ $this->controller->throwBadRequest();
+ return false;
+ }
+
+ return true;
+ }
+
+/**
+ * 一括処理するユーザを取得
+ *
+ * @param array $checkedIds チェックしているユーザIDリスト
+ *
+ * @return array
+ */
+ private function __findBulkUsers($checkedIds) {
+ $users = $this->User->find('all', [
+ 'recursive' => -1,
+ 'fields' => [
+ $this->User->alias . '.id',
+ $this->User->alias . '.handlename',
+ $this->User->alias . '.role_key',
+ $this->UserRoleSetting->alias . '.origin_role_key',
+ ],
+ 'conditions' => [
+ $this->User->alias . '.id' => $checkedIds,
+ ],
+ 'joins' => [
+ [
+ 'table' => $this->UserRoleSetting->table,
+ 'alias' => $this->UserRoleSetting->alias,
+ 'type' => 'INNER',
+ 'conditions' => [
+ $this->User->alias . '.role_key' . ' = ' . $this->UserRoleSetting->alias . '.role_key'
+ ],
+ ]
+ ]
+ ]);
+ if (empty($users)) {
+ $this->controller->throwBadRequest();
+ return false;
+ }
+
+ return $users;
+ }
+
+}
diff --git a/Controller/UserAddController.php b/Controller/UserAddController.php
index 23c56b7..f48f700 100644
--- a/Controller/UserAddController.php
+++ b/Controller/UserAddController.php
@@ -120,7 +120,8 @@ public function beforeFilter() {
//メール通知の場合、NetCommonsMailUtilityをメンバー変数にセットする。Mockであれば、newをしない。
//テストでMockに差し替えが必要なための処理であるので、カバレッジレポートから除外する。
//@codeCoverageIgnoreStart
- if ($this->params['action'] === 'notify' && substr(get_class($this->mail), 0, 4) !== 'Mock') {
+ if ($this->params['action'] === 'notify' &&
+ (empty($this->mail) || substr(get_class($this->mail), 0, 4) !== 'Mock')) {
$this->mail = new NetCommonsMail();
}
//@codeCoverageIgnoreEnd
diff --git a/Controller/UserManagerAvatarController.php b/Controller/UserManagerAvatarController.php
index c4bc4b4..5331170 100644
--- a/Controller/UserManagerAvatarController.php
+++ b/Controller/UserManagerAvatarController.php
@@ -75,7 +75,7 @@ public function download() {
//会員管理が使えない場合、NoImageを出力する
$PluginsRole = $this->_getSimpleModel('PluginsRole');
$query = $this->_getQueryForPluginsRole();
- if (! $PluginsRole->find('count', $query)) {
+ if (! $PluginsRole->cacheFindQuery('count', $query)) {
return $this->_downloadNoImage($User, $user);
}
@@ -83,7 +83,7 @@ public function download() {
'size' => $this->params['size'],
];
- return $this->Download->doDownloadByUploadFileId($user['UploadFile']['id'], $options);
+ return $this->Download->doDownloadByUploadFileId($user['UploadFile']['id'], $options, 'users');
}
/**
@@ -117,7 +117,19 @@ protected function _downloadNoImage($User, $user) {
protected function _getSimpleModel($modelName) {
// TestでAvatarBehavior::temporaryAvatar をMock にしているため、removeObjectしない。
// ClassRegistry::removeObject($modelName);
- $Model = ClassRegistry::init($modelName);
+
+ //@codeCoverageIgnoreStart
+ if (empty($this->$modelName) ||
+ substr(get_class($this->$modelName), 0, 5) !== 'Mock_') {
+ $Model = ClassRegistry::init($modelName);
+ } else {
+ $Model = $this->$modelName;
+ if (! ClassRegistry::getObject($modelName)) {
+ ClassRegistry::addObject($modelName, $Model);
+ }
+ }
+ //@codeCoverageIgnoreEnd
+
$params = [
'belongsTo' => [
'TrackableCreator',
@@ -127,6 +139,9 @@ protected function _getSimpleModel($modelName) {
$Model->unbindModel($params);
$Model->Behaviors->unload('Trackable');
+ if ($modelName === 'PluginsRole') {
+ $Model->Behaviors->load('NetCommons.NetCommonsCache');
+ }
return $Model;
}
@@ -142,7 +157,7 @@ protected function _getBindParamsForUser() {
'className' => 'UploadFile',
'foreignKey' => false,
'conditions' => [
- 'UploadFile.plugin_key' => $this->plugin,
+ 'UploadFile.plugin_key' => 'users',
'UploadFile.content_key = User.id',
'UploadFile.field_name' => $this->request->params['field_name'],
],
@@ -183,7 +198,7 @@ protected function _getQueryForPluginsRole() {
$query = [
'conditions' => [
'PluginsRole.role_key' => AuthComponent::user('role_key'),
- 'PluginsRole.plugin_key' => $this->plugin,
+ 'PluginsRole.plugin_key' => 'user_manager',
],
'recursive' => -1,
'callbacks' => false,
diff --git a/Controller/UserManagerController.php b/Controller/UserManagerController.php
index f191184..eb91096 100644
--- a/Controller/UserManagerController.php
+++ b/Controller/UserManagerController.php
@@ -17,6 +17,19 @@
/**
* UserManager Controller
*
+ * @property AutoUserRegist $AutoUserRegist
+ * @property AutoUserRegistMail $AutoUserRegistMail
+ * @property User $User
+ * @property UserSearch $UserSearch
+ * @property DownloadComponent $Download
+ * @property FileUploadComponent $FileUpload
+ * @property SwitchLanguageComponent $SwitchLanguage
+ * @property RoomsComponent $Rooms
+ * @property UserAttributeLayoutComponent $UserAttributeLayout
+ * @property UserManagerComponent $UserManager
+ * @property UserManagerBulkComponent $UserManagerBulk
+ * @property UserSearchCompComponent $UserSearchComp
+ *
* @author Shohei Nakajima
* @package NetCommons\UserManager\Controller
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
@@ -62,6 +75,7 @@ class UserManagerController extends UserManagerAppController {
'Rooms.Rooms',
'UserAttributes.UserAttributeLayout',
'UserManager.UserManager',
+ 'UserManager.UserManagerBulk',
'Users.UserSearchComp',
);
@@ -86,7 +100,8 @@ public function index() {
//ユーザ一覧データ取得
$this->UserSearchComp->search([
- 'fields' => self::$displaFields,
+ 'fields' => array_merge(['origin_role_key'], self::$displaFields),
+ 'displayFields' => self::$displaFields,
'conditions' => array('space_id !=' => Space::PRIVATE_SPACE_ID),
'joins' => array('Room' => array(
'conditions' => array(
@@ -261,7 +276,7 @@ public function status() {
), 400);
}
- $this->redirect($this->request->referer());
+ $this->redirect($this->request->referer(true));
}
/**
@@ -326,7 +341,7 @@ public function export() {
$csvWriter = $this->User->exportUsers(
array(
'conditions' => array(
- 'space_id' => Space::PRIVATE_SPACE_ID,
+ 'space_id !=' => Space::PRIVATE_SPACE_ID,
'User.role_key NOT' => array(
UserRole::USER_ROLE_KEY_SYSTEM_ADMINISTRATOR,
UserRole::USER_ROLE_KEY_ADMINISTRATOR
@@ -360,4 +375,28 @@ public function export() {
}
}
+/**
+ * bulkアクション
+ *
+ * @return void
+ */
+ public function bulk() {
+ //タイムアウト発生するなら適宜設定
+ set_time_limit(1800);
+ if (! $this->request->is('post')) {
+ return $this->throwBadRequest();
+ }
+
+ if ($this->request->data['UserManagerBulk']['submit'] === 'nonactive') {
+ //利用不可に設定
+ return $this->UserManagerBulk->bulkNonactive();
+ } elseif ($this->request->data['UserManagerBulk']['submit'] === 'delete') {
+ //削除する
+ return $this->UserManagerBulk->bulkDelete();
+ } else {
+ //それ以外
+ return $this->throwBadRequest();
+ }
+ }
+
}
diff --git a/Lib/UserManagerSearchLib.php b/Lib/UserManagerSearchLib.php
new file mode 100644
index 0000000..5fb8c9d
--- /dev/null
+++ b/Lib/UserManagerSearchLib.php
@@ -0,0 +1,32 @@
+
+ * @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('UserRole', 'UserRoles.Model');
+
+/**
+ * 会員管理の一覧に関するライブラリ
+ *
+ * @package NetCommons\UserManager\Lib
+ */
+class UserManagerSearchLib {
+
+/**
+ * 一括操作できるユーザかどうか
+ *
+ * @param array $user ユーザデータ
+ * @return bool
+ */
+ public static function hasEditableBulkUser($user) {
+ $roleKey = $user['UserRoleSetting']['origin_role_key'] ?? null;
+ return ! in_array($roleKey, \UserRole::$systemRoles, true);
+ }
+
+}
diff --git a/Locale/eng/LC_MESSAGES/user_manager.po b/Locale/eng/LC_MESSAGES/user_manager.po
index a3c4620..91fe256 100644
--- a/Locale/eng/LC_MESSAGES/user_manager.po
+++ b/Locale/eng/LC_MESSAGES/user_manager.po
@@ -271,14 +271,6 @@ msgstr ""
msgid "Required. "
msgstr ""
-#: Users/View/Helper/UserSearchFormHelper.php:140
-msgid "Not specified"
-msgstr ""
-
-#: Users/View/Helper/UserSearchFormHelper.php:186;303;333
-msgid "-- Not specify --"
-msgstr ""
-
#: Users/View/Helper/UserSearchFormHelper.php:215
msgid "Not logged more than X days ago"
msgstr ""
@@ -299,6 +291,28 @@ msgstr ""
msgid "Do you approve?"
msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Change to nonactive"
+msgstr ""
+
+msgid "Selected row ..."
+msgstr ""
+
+msgid "Selected members"
+msgstr ""
+
+msgid "Is it really okay to delete it?"
+msgstr ""
+
+msgid "Nonactive the %s. Are you sure to proceed?"
+msgstr ""
+
+msgid "Not found the select user."
+msgstr ""
+
#: Users/View/Helper/UserSearchHelper.php:267
msgid "Approval"
msgstr ""
diff --git a/Locale/jpn/LC_MESSAGES/user_manager.po b/Locale/jpn/LC_MESSAGES/user_manager.po
index 22c0c9d..fe78cf4 100644
--- a/Locale/jpn/LC_MESSAGES/user_manager.po
+++ b/Locale/jpn/LC_MESSAGES/user_manager.po
@@ -79,6 +79,28 @@ msgid "Do you approve?"
msgstr "承認しますか。"
+msgid "Delete"
+msgstr "削除する"
+
+msgid "Change to nonactive"
+msgstr "利用不可にする"
+
+msgid "Selected row ..."
+msgstr "選択した行を・・・"
+
+msgid "Selected members"
+msgstr "選択した会員"
+
+msgid "Is it really okay to delete it?"
+msgstr "削除すると元に戻すことは出来ません。本当に削除して良いですか。"
+
+msgid "Nonactive the %s. Are you sure to proceed?"
+msgstr "%sを利用不可に設定します。本当によろしいですか。"
+
+msgid "Not found the select user."
+msgstr "一つも選択されていません。"
+
+
#
# 入力画面
#
@@ -281,14 +303,6 @@ msgstr "日以内にログインしている"
msgid "Have logged in within %s days"
msgstr "%s日以内にログインしている"
-#: UserManager/View/Elements/setting_tabs.ctp:30
-msgid "Not specified"
-msgstr "条件に含まない"
-
-#: UserManager/View/Elements/setting_tabs.ctp:30
-msgid "-- Not specify --"
-msgstr "-- 条件に含まない --"
-
#: UserManager/View/Elements/setting_tabs.ctp:30
msgid "No avatar."
msgstr "なし"
diff --git a/Locale/user_manager.pot b/Locale/user_manager.pot
index a3c4620..cc1b7aa 100644
--- a/Locale/user_manager.pot
+++ b/Locale/user_manager.pot
@@ -271,14 +271,6 @@ msgstr ""
msgid "Required. "
msgstr ""
-#: Users/View/Helper/UserSearchFormHelper.php:140
-msgid "Not specified"
-msgstr ""
-
-#: Users/View/Helper/UserSearchFormHelper.php:186;303;333
-msgid "-- Not specify --"
-msgstr ""
-
#: Users/View/Helper/UserSearchFormHelper.php:215
msgid "Not logged more than X days ago"
msgstr ""
diff --git a/Model/UserMail.php b/Model/UserMail.php
index 8f2b34b..dfb736b 100644
--- a/Model/UserMail.php
+++ b/Model/UserMail.php
@@ -50,7 +50,7 @@ class UserMail extends UserManagerAppModel {
* @see Model::save()
*/
public function beforeValidate($options = array()) {
- $this->validate = Hash::merge($this->validate, array(
+ $this->validate = ValidateMerge::merge($this->validate, array(
'user_id' => array(
'notBlank' => array(
'rule' => array('notBlank'),
diff --git a/README.md b/README.md
index 015f7a9..b28b15b 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,6 @@
UserManager
==============
-UserManager for NetComomns3
-
-[](https://travis-ci.org/NetCommons3/UserManager)
-[](https://coveralls.io/r/NetCommons3/UserManager?branch=master)
-
-| dependencies | status |
-| ------------- | ------ |
-| composer.json | [](https://www.versioneye.com/user/projects/55cbc36bb7d70b000d000265) |
+[](https://github.com/NetCommons3/UserManager/actions/workflows/tests.yml)
+[](https://coveralls.io/r/NetCommons3/UserManager?branch=master)
+[](https://packagist.org/packages/netcommons/user-manager)
diff --git a/Test/Case/Controller/UserAddController/BasicTest.php b/Test/Case/Controller/UserAddController/BasicTest.php
index 1a1180d..0e378f1 100644
--- a/Test/Case/Controller/UserAddController/BasicTest.php
+++ b/Test/Case/Controller/UserAddController/BasicTest.php
@@ -86,13 +86,8 @@ public function testTmpAvatarDelete() {
//ログイン
TestAuthGeneral::login($this);
- if (Configure::read('debug')) {
- $exactly = 2;
- } else {
- $exactly = 1;
- }
$this->controller->Session
- ->expects($this->exactly($exactly))->method('read')
+ ->expects($this->any())->method('read')
->will($this->returnCallback(function ($key) use ($avatarPath) {
if ($key === 'UserAdd.User.avatar.tmp_name') {
return $avatarPath;
@@ -127,13 +122,9 @@ public function testReferer() {
TestAuthGeneral::login($this);
$data = $this->__data();
- if (Configure::read('debug')) {
- $exactly = 2;
- } else {
- $exactly = 1;
- }
+
$this->controller->Session
- ->expects($this->exactly($exactly))->method('read')
+ ->expects($this->any())->method('read')
->will($this->returnCallback(function ($key) use ($data) {
if ($key === 'UserAdd') {
return $data;
@@ -146,7 +137,7 @@ public function testReferer() {
$this->_testGetAction(array('action' => 'basic'), array('method' => 'assertNotEmpty'), null, 'view');
//チェック
- $this->assertEquals($this->controller->data, $this->__data());
+ $this->__assertCreateUser();
$this->__assertInputs();
}
diff --git a/Test/Case/Controller/UserAddController/BeforeFilterTest.php b/Test/Case/Controller/UserAddController/BeforeFilterTest.php
index ebf80aa..0e654a3 100644
--- a/Test/Case/Controller/UserAddController/BeforeFilterTest.php
+++ b/Test/Case/Controller/UserAddController/BeforeFilterTest.php
@@ -119,13 +119,9 @@ public function testNotify() {
'handlename' => '',
'username' => '',
);
- if (Configure::read('debug')) {
- $exactly = 2;
- } else {
- $exactly = 1;
- }
+
$this->controller->Session
- ->expects($this->exactly($exactly))->method('read')
+ ->expects($this->any())->method('read')
->will($this->returnCallback(function ($key) use ($user) {
if ($key === 'UserAdd') {
return array('User' => $user);
diff --git a/Test/Case/Controller/UserAddController/NotifyTest.php b/Test/Case/Controller/UserAddController/NotifyTest.php
index 7d940d5..81c0d1b 100644
--- a/Test/Case/Controller/UserAddController/NotifyTest.php
+++ b/Test/Case/Controller/UserAddController/NotifyTest.php
@@ -27,6 +27,7 @@ class UserAddControllerNotifyTest extends NetCommonsControllerTestCase {
public $fixtures = array(
'plugin.mails.mail_setting_fixed_phrase',
'plugin.user_attributes.user_attribute_layout',
+ 'plugin.user_attributes.plugins_role4test',
);
/**
diff --git a/Test/Case/Controller/UserAddController/UserRolesRoomsTest.php b/Test/Case/Controller/UserAddController/UserRolesRoomsTest.php
index a29faf5..97a6f9a 100644
--- a/Test/Case/Controller/UserAddController/UserRolesRoomsTest.php
+++ b/Test/Case/Controller/UserAddController/UserRolesRoomsTest.php
@@ -171,7 +171,9 @@ private function __assert() {
$this->view = preg_replace('/[\s]+<', $this->view);
$expected = array('2', '5', '11', '12');
- $this->assertEquals(array_keys($this->vars['rolesRooms']), $expected);
+ $actual = array_keys($this->vars['rolesRooms']);
+ sort($actual);
+ $this->assertEquals($actual, $expected);
$expected = array('1', '2', '3', '4', '5');
$this->assertEquals($expected, Hash::extract($this->vars['rolesRooms'], '2.{s}.RolesRoom.id'));
diff --git a/Test/Case/Controller/UserManagerController/IndexTest.php b/Test/Case/Controller/UserManagerController/IndexTest.php
index b28ce5e..1b261ff 100644
--- a/Test/Case/Controller/UserManagerController/IndexTest.php
+++ b/Test/Case/Controller/UserManagerController/IndexTest.php
@@ -54,6 +54,7 @@ public function tearDown() {
* index()アクションのGetリクエストテスト
*
* @return void
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function testIndexGet() {
//テスト実行
@@ -85,6 +86,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => '2016-09-14 12:18:45',
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'system_administrator'
+ ],
'UsersLanguage' => array(
'name' => 'System Administrator Name',
),
@@ -96,6 +100,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => null,
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'administrator'
+ ],
'UsersLanguage' => array(
'name' => 'Site Manager Name',
),
@@ -107,6 +114,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => null,
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'common_user'
+ ],
'UsersLanguage' => array(
'name' => 'Chief Editor Name',
),
@@ -118,6 +128,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => null,
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'common_user'
+ ],
'UsersLanguage' => array(
'name' => 'Editor Name',
),
@@ -129,6 +142,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => null,
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'common_user'
+ ],
'UsersLanguage' => array(
'name' => 'General User Name',
),
@@ -140,6 +156,9 @@ public function testIndexGet() {
'modified' => '2015-08-15 06:12:30',
'last_login' => null,
),
+ 'UserRoleSetting' => [
+ 'origin_role_key' => 'common_user'
+ ],
'UsersLanguage' => array(
'name' => 'Visitor Name',
),
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/UserManager/index.ctp b/View/UserManager/index.ctp
index 0f46547..41662d2 100644
--- a/View/UserManager/index.ctp
+++ b/View/UserManager/index.ctp
@@ -9,6 +9,8 @@
* @copyright Copyright 2014, NetCommons Project
*/
+\App::uses('UserManagerSearchLib', 'UserManager.Lib');
+
echo $this->NetCommonsHtml->css(array(
'/user_manager/css/style.css',
'/users/css/style.css',
@@ -39,34 +41,140 @@ echo $this->NetCommonsHtml->script('/user_manager/js/user_manager.js');
end(); ?>
-MessageFlash->description(
- __d('user_manager', 'Click the handle name to read his/her data. And to edit the user data. And delete user data, please go from editing screen.')
-); ?>
+
+ MessageFlash->description(
+ __d('user_manager', 'Click the handle name to read his/her data. And to edit the user data. And delete user data, please go from editing screen.')
+ ); ?>
+
+
+ UserSearchForm->displaySearchButton(__d('user_manager', 'Search for the members'), [], true); ?>
+
+ Paginator->counter('{:count}') > 0) : ?>
+
+
+
+
+
+
+
-
- UserSearchForm->displaySearchButton(__d('user_manager', 'Search for the members'), [], true); ?>
+
+
+
+
-
- Button->addLink(__d('user_manager', 'Add user'), ['controller' => 'user_add', 'action' => 'basic']); ?>
+
+ Button->addLink(__d('user_manager', 'Add user'), ['controller' => 'user_add', 'action' => 'basic']); ?>
+
-
-
-
-
+ NetCommonsForm->create('UserManagerBulk', [
+ 'url' => NetCommonsUrl::actionUrlAsArray(['controller' => 'user_manager', 'action' => 'bulk'])
+ ]);
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.displayIds', ['value' => implode(',', $bulkUserIds)]);
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.hasPrev', ['value' => $this->Paginator->hasPrev()]);
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.hasNext', ['value' => $this->Paginator->hasNext()]);
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.hasAdminUser', ['value' => $hasAdminUser]);
+
+ echo $this->NetCommonsForm->unlockField('UserManagerBulk.checkedIds');
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.checkedIds', ['value' => '']);
+
+ echo $this->NetCommonsForm->unlockField('UserManagerBulk.submit');
+ echo $this->NetCommonsForm->hidden('UserManagerBulk.submit', ['value' => '']);
+
+ echo $this->NetCommonsForm->end();
+ ?>
+
-element('NetCommons.paginator');
+ element('NetCommons.paginator'); ?>
+
\ No newline at end of file
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 76c939f..447b7aa 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,5 +1,8 @@
+
+
+
app/Plugin/UserManager
@@ -14,6 +17,6 @@
-
+
diff --git a/webroot/js/user_manager.js b/webroot/js/user_manager.js
index bdec69f..1cf9eda 100644
--- a/webroot/js/user_manager.js
+++ b/webroot/js/user_manager.js
@@ -10,6 +10,106 @@
NetCommonsApp.controller('UserManagerController',
['$scope', 'NetCommonsModal', 'NC3_URL', function($scope, NetCommonsModal, NC3_URL) {
+ /**
+ * 選択したIDリスト
+ */
+ $scope.checkedIds = [];
+
+ /**
+ * チェックボックスの全選択・全解除
+ */
+ $scope.allCheck = function($event) {
+ var elements = $('input[type="checkbox"]');
+
+ for (var i = 0; i < elements.length; i++) {
+ if (elements[i].name) {
+ $scope._changeCheck(elements[i], $event.currentTarget.checked);
+ }
+ }
+ };
+
+ /**
+ * チェックボックスクリック
+ */
+ $scope.check = function($event) {
+ $scope._changeCheck($event.currentTarget, $event.currentTarget.checked);
+ };
+
+ /**
+ * チェックボックス変更処理
+ */
+ $scope._changeCheck = function(element, checked) {
+ var id = element.value;
+ var domId = element.id;
+ element.checked = checked;
+ $scope.checkedIds[id] = checked;
+
+ var trElement = $('#Tr' + domId);
+
+ if (checked) {
+ if (trElement.hasClass('warning')) {
+ trElement.removeClass('warning');
+ trElement.addClass('_warning');
+ } else if (trElement.hasClass('danger')) {
+ trElement.removeClass('danger');
+ trElement.addClass('_danger');
+ }
+
+ if (! trElement.hasClass('success')) {
+ trElement.addClass('success');
+ }
+ } else {
+ if (trElement.hasClass('_warning')) {
+ trElement.removeClass('_warning');
+ trElement.addClass('warning');
+ } else if (trElement.hasClass('_danger')) {
+ trElement.removeClass('_danger');
+ trElement.addClass('danger');
+ }
+
+ if (trElement.hasClass('success')) {
+ trElement.removeClass('success');
+ }
+ }
+ };
+
+ /**
+ * 一括登録処理
+ */
+ $scope.bulk = function($event, action, firstMessage, secondMessage, notSelectMessage) {
+ var checkedIds = [];
+ angular.forEach($scope.checkedIds, function(checked, id) {
+ if (checked) {
+ checkedIds.push(id);
+ }
+ }, checkedIds);
+ if (! checkedIds.length) {
+ alert(notSelectMessage);
+ $event.preventDefault();
+ return;
+ }
+
+ if (! confirm(firstMessage)) {
+ $event.preventDefault();
+ return;
+ }
+
+ if (secondMessage && ! confirm(secondMessage)) {
+ $event.preventDefault();
+ return;
+ }
+
+ var checkedElement = $('#UserManagerBulkCheckedIds');
+ checkedElement[0].value = checkedIds.join(',');
+
+ var submitElement = $('#UserManagerBulkSubmit');
+ submitElement[0].value = action;
+
+ $scope.sending = true;
+ var formElement = $('#UserManagerBulkBulkForm');
+ formElement.submit();
+ };
+
/**
* 検索ダイアログ表示
*