Przeglądaj źródła

# Feature/196, 198, 199 Grouping users
* Prepare user-group detail page
* Prepare add relation UI
* Improve user-group-relation management api(not completed!)

Tatsuya Ise 8 lat temu
rodzic
commit
c364b314f6

+ 34 - 13
lib/models/user-group-relation.js

@@ -9,9 +9,13 @@ module.exports = function(crowi) {
     , userGroupRelationSchema;
 
   userGroupRelationSchema = new mongoose.Schema({
+    userGroupRelationId: String,
     relatedGroup: { type: ObjectId, ref: 'UserGroup' },
     relatedUser: { type: ObjectId, ref: 'User' },
     createdAt: { type: Date, default: Date.now },
+  },{
+    toJSON: { getters: true },
+    toObject: { getters: true }
   });
   userGroupRelationSchema.plugin(mongoosePaginate);
 
@@ -23,6 +27,7 @@ module.exports = function(crowi) {
     return new Promise(function(resolve, reject) {
       UserGroupRelation
         .find({ relatedGroup: group} )
+        .populate('relatedUser')
         .exec(function (err, userGroupRelationData) {
           if (err) {
             return reject(err);
@@ -34,18 +39,18 @@ module.exports = function(crowi) {
   };
 
   // すべてのグループ所属関係を取得
-  userGroupRelationSchema.statics.findAllRelation = function (group) {
-    debug('findAllGroups is called');
+  userGroupRelationSchema.statics.findAllRelationForUserGroup = function (group) {
+    debug('findAllGroups is called', group);
     var UserGroupRelation = this;
 
     return new Promise(function (resolve, reject) {
       UserGroupRelation
         .find({ relatedGroup: group })
+        .populate('relatedUser')
         .exec(function (err, userGroupRelationData) {
           if (err) {
             return reject(err);
           }
-
           return resolve(userGroupRelationData);
         });
     });
@@ -69,33 +74,49 @@ module.exports = function(crowi) {
     var UserGroupRelation = this
       , newUserGroupRelation = new UserGroupRelation();
 
-    newUserGroupRelation.relatedGroup = group;
+    newUserGroupRelation.relatedGroup = userGroup;
     newUserGroupRelation.relatedUser = user;
     newUserGroupRelation.createdAt = Date.now();
 
+    debug('create new user-group-relation ', newUserGroupRelation);
     newUserGroupRelation.save(function(err, userGroupRelationData) {
       return callback(err, userGroupRelationData);
     });
   };
 
   // グループに紐づく関係性の全削除
-  userGroupRelationSchema.statics.removeAllCompletelyByUserGroup = function (userGroup, callback) {
+  userGroupRelationSchema.statics.removeAllByUserGroup = function (userGroup, callback) {
 
-    if (userGroup === null || userGroup === undefined) { return; }
+    if (userGroup === null || userGroup === undefined) { return callback(null); }
     var UserGroupRelation = this
-    var relations = this.findAllRelation(userGroup);
-
-    // TODO 関係性削除の実装
-    relations.array.forEach(element => {
+    var relations = UserGroupRelation.findAllRelation(userGroup);
 
+    // 関係性削除の実装
+    relations.array.forEach(relation => {
+      UserGroupRelation.removeById(relation.id, function(err) {
+        if (err) { return callback(err); }
+      });
     });
+    return callback(null);
   }
 
-  //
-  userGroupRelationSchema.statics.removeCompletely = function (userGroupRelation, callback) {
-    if (userGroupRelation) { return; }
+  // ユーザグループの関係性を削除
+  userGroupRelationSchema.statics.removeById = function (id, callback) {
     var UserGroupRelation = this
+    UserGroupRelation.findById(id, function (err, relationData) {
+      debug(relationData);
+      if (!relationData) {
+        debug('Error on find a removing user-group-relation', err);
+        return callback(err);
+      }
 
+      relationData.remove(function(err) {
+        if (err) {
+          return callback(err);
+        }
+        return callback(null);
+      });
+    });
   }
 
   userGroupRelationSchema.statics.PAGE_ITEMS         = PAGE_ITEMS;

+ 12 - 12
lib/models/user-group.js

@@ -13,7 +13,7 @@ module.exports = function(crowi) {
   userGroupSchema = new mongoose.Schema({
     userGroupId: String,
     image: String,
-    name: { type: String, required: true },
+    name: { type: String, required: true, unique: true },
     createdAt: { type: Date, default: Date.now },
   });
   userGroupSchema.plugin(mongoosePaginate);
@@ -127,18 +127,18 @@ module.exports = function(crowi) {
   };
 
   // TBD: グループ名によるグループ検索
-  // userGroupSchema.statics.findUserGroupByName = function(name) {
-  //   var UserGroup = this;
-  //   return new Promise(function(resolve, reject) {
-  //     UserGroup.findOne({name: name}, function (err, userGroupData) {
-  //       if (err) {
-  //         return reject(err);
-  //       }
+  userGroupSchema.statics.findUserGroupByName = function(name) {
+    var UserGroup = this;
+    return new Promise(function(resolve, reject) {
+      UserGroup.findOne({name: name}, function (err, userGroupData) {
+        if (err) {
+          return reject(err);
+        }
 
-  //       return resolve(userGroupData);
-  //     });
-  //   });
-  // };
+        return resolve(userGroupData);
+      });
+    });
+  };
 
   // TBD: 登録可能グループ名確認
   userGroupSchema.statics.isRegisterableName = function(name, callback) {

+ 106 - 7
lib/routes/admin.js

@@ -6,6 +6,7 @@ module.exports = function(crowi, app) {
     , Page = models.Page
     , User = models.User
     , UserGroup = models.UserGroup
+    , UserGroupRelation = models.UserGroupRelation
     , Config = models.Config
     , PluginUtils = require('../plugins/plugin-utils')
     , pluginUtils = new PluginUtils()
@@ -478,13 +479,58 @@ module.exports = function(crowi, app) {
 
     UserGroup.findUserGroupsWithPagination({ page: page }, function (err, result) {
       const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
+      var userGroups = result.docs
+      var groupRelations = new Map();
+      if (userGroups) {
+        userGroups.forEach(group => {
+          UserGroupRelation.findAllRelationForUserGroup(group)
+          .then(function(relations) {
+            debug(group);
+            debug(relations);
+            groupRelations.set(group, relations);
+            debug('groupRelations is ', groupRelations);
+          }).catch(function(err) {
+            debug('Error on find all relations', err);
+            return res.json(ApiResponse.error('Error'));
+          });
+        });
+      }
       return res.render('admin/user-groups', {
-        userGroups: result.docs,
+        userGroups: userGroups,
+        userGroupRelations: groupRelations,
         pager: pager
       });
     });
   };
 
+  // グループ詳細
+  actions.userGroup.detail = function (req, res) {
+    var name = req.params.name;
+    UserGroup.findUserGroupByName(name)
+    .then( function(data) {
+      var userGroup = data
+      var groupRelations = [];
+      if (userGroup) {
+        UserGroupRelation.findAllRelationForUserGroup(userGroup)
+        .then(function (data) {
+          debug('user-group-detail succeed', data);
+          return res.render('admin/user-group-detail', {
+            userGroup: userGroup,
+            userGroupRelations: data
+          });
+        }).catch(function (err) {
+          debug('Error on find all relations', err);
+          return res.json(ApiResponse.error('Error'));
+        });
+      }
+    })
+    .catch(function(err) {
+      debug('Error on get userGroupDetail', err);
+      return res.json(ApiResponse.error('Error'));
+    });
+  }
+
+  //グループの生成
   actions.userGroup.create = function (req, res) {
     var form = req.form.createGroupForm;
     if (req.form.isValid) {
@@ -568,16 +614,69 @@ module.exports = function(crowi, app) {
   actions.userGroup.removeCompletely = function (req, res) {
     const id = req.body.user_group_id;
 
-    UserGroup.removeCompletelyById(id)
-      .then(function (data) {
-        data.user = User.filterToPublicFields(data.user);
-        return res.json(ApiResponse.success(data));
-      }).catch(function (err) {
-        debug('Error on reseting password', err);
+    UserGroup.removeCompletelyById(id, function (err, removed) {
+      if (err) {
+        debug('Error while removing userGroup.', err, id);
+        req.flash('errorMessage', '完全な削除に失敗しました。');
+      } else {
+        req.flash('successMessage', '削除しました');
+      }
+      return res.redirect('/admin/user-groups');
+    });
+  }
+
+  actions.userGroupRelation = {};
+  actions.userGroupRelation.index = function(req, res) {
+
+  }
+
+  actions.userGroupRelation.create = function(req, res) {
+    const User = crowi.model('User');
+    const UserGroup = crowi.model('UserGroup');
+    const UserGroupRelation = crowi.model('UserGroupRelation');
+
+    // ユーザを名前で検索
+    User.findUserByUsername(req.body.user_name)
+      .then((user) => {
+        // ユーザグループをIDで検索
+        UserGroup.findById(req.body.user_group_id, function(err, userGroup) {
+          if (err) {
+            debug('Error on create user-group relation', err);
+            return res.json(ApiResponse.error('Error'));
+          }
+          // Relation を作成
+          UserGroupRelation.createRelation(userGroup, user, function (err, data) {
+            if (err) {
+              debug('Error on create user-group relation', err);
+              return res.json(ApiResponse.error('Error'));
+            }
+            return res.json(ApiResponse.success(data));
+          });
+
+        });
+
+      }).catch((err) => {
+        debug('Error on create user-group relation', err);
         return res.json(ApiResponse.error('Error'));
       });
   }
 
+  actions.userGroupRelation.remove = function (req, res) {
+    const UserGroupRelation = crowi.model('UserGroupRelation');
+    var name = req.params.name;
+    var relationId = req.params.relationId;
+
+    debug(name, relationId);
+    UserGroupRelation.removeById(relationId, function(err) {
+      if (err) {
+        debug('Error on remove user-group-relation', err);
+        req.flash('errorMessage', 'グループのユーザ削除に失敗しました。');
+      }
+      return res.redirect('/admin/user-group-detail/' + name);
+    });
+
+  }
+
   actions.api = {};
   actions.api.appSetting = function(req, res) {
     var form = req.form.settingForm;

+ 8 - 3
lib/routes/index.js

@@ -101,9 +101,14 @@ module.exports = function(crowi, app) {
   app.post('/_api/admin/users.resetPassword'  , loginRequired(crowi, app) , middleware.adminRequired() , csrf, admin.user.resetPassword);
 
   // user-groups admin
-  app.get('/admin/user-groups'          , loginRequired(crowi, app), middleware.adminRequired(), admin.userGroup.index);
-  app.post('/admin/user-group/create'  , form.admin.userGroupCreate, loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroup.create);
-  app.post('/_api/admin/user-group/delete', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroup.removeCompletely);
+  app.get('/admin/user-groups'             , loginRequired(crowi, app), middleware.adminRequired(), admin.userGroup.index);
+  app.get('/admin/user-group-detail/:name'          , loginRequired(crowi, app), middleware.adminRequired(), admin.userGroup.detail);
+  app.post('/admin/user-group/create'      , form.admin.userGroupCreate, loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroup.create);
+  app.post('/_api/admin/user-group.remove' , loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroup.removeCompletely);
+
+  // user-group-relations admin
+  app.post('/admin/user-group-relation/create', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.create)
+  app.post('/admin/user-group-relation/:name/remove-relation/:relationId', loginRequired(crowi, app), middleware.adminRequired(), csrf, admin.userGroupRelation.remove)
 
   app.get('/me'                       , loginRequired(crowi, app) , me.index);
   app.get('/me/password'              , loginRequired(crowi, app) , me.password);

+ 143 - 0
lib/views/admin/user-group-detail.html

@@ -0,0 +1,143 @@
+{% extends '../layout/admin.html' %}
+
+{% block html_title %}グループ管理 · {% endblock %}
+
+{% block content_head %}
+<div class="header-wrap">
+  <header id="page-header">
+    <h1 class="title" id="">グループ管理(グループ詳細)</h1>
+  </header>
+</div>
+{% endblock %}
+
+{% block content_main %}
+<div class="content-main">
+  {% set smessage = req.flash('successMessage') %}
+  {% if smessage.length %}
+  <div class="alert alert-success">
+    {{ smessage }}
+  </div>
+  {% endif %}
+
+  {% set emessage = req.flash('errorMessage') %}
+  {% if emessage.length %}
+  <div class="alert alert-danger">
+    {{ emessage }}
+  </div>
+  {% endif %}
+
+  <div class="row">
+    <div class="col-md-3">
+      {% include './widget/menu.html' with {current: 'user-group'} %}
+    </div>
+
+    <div class="col-md-9">
+      <div class="modal fade" id="admin-add-user-group-relation-modal">
+        <div class="modal-dialog">
+          <div class="modal-content">
+            <div class="modal-header">
+              <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+              <h4 class="modal-title">
+                グループにユーザを追加します</h4>
+            </div>
+
+            <div class="modal-body">
+              <p>
+                ユーザ名を入力してください。
+              </p>
+              <form role="form" action="/admin/user-group-relation/create" method="post">
+                <p>
+                  Add related user:
+                  <input type="text" name="user_name">
+                </p>
+
+                <input type="hidden" name="user_group_id" value="{{userGroup.id}}">
+                <input type="hidden" name="_csrf" value="{{ csrf() }}">
+                <button type="submit" value="" class="btn btn-danger">
+                  実行
+                </button>
+              </form>
+
+            </div>
+
+          </div>
+          <!-- /.modal-content -->
+        </div>
+        <!-- /.modal-dialog -->
+      </div>
+
+      <h2>ユーザー一覧</h2>
+
+      <table class="table table-hover table-striped table-bordered table-user-list">
+        <thead>
+          <tr>
+            <th width="100px">#</th>
+            <th>
+              <code>username</code>
+            </th>
+            <th>名前</th>
+            <th width="100px">作成日</th>
+            <th width="150px">最終ログイン</th>
+            <th width="90px">操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          {% for sRelation in userGroupRelations %}
+          {% set sUser = sRelation.relatedUser%}
+          <tr>
+            <td>
+              <img src="{{ sRelation.relatedUser|picture }}" class="picture picture-rounded" />
+            </td>
+            <td>
+              <strong>{{ sRelation.relatedUser.username }}</strong>
+            </td>
+            <td>{{ sRelation.relatedUser.name }}</td>
+            <td>{{ sRelation.relatedUser.createdAt|date('Y-m-d', sRelation.relatedUser.createdAt.getTimezoneOffset()) }}</td>
+            <td>
+              {% if sRelation.relatedUser.lastLoginAt %} {{ sRelation.relatedUser.lastLoginAt|date('Y-m-d H:i', sRelation.relatedUser.createdAt.getTimezoneOffset()) }} {% endif %}
+            </td>
+            <td>
+              <div class="btn-group admin-user-menu">
+                <button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
+                  編集
+                  <span class="caret"></span>
+                </button>
+                <ul class="dropdown-menu" role="menu">
+                  <li class="dropdown-header">編集メニュー</li>
+                  <li>
+                    <a href="">編集</a>
+                  </li>
+                  <li class="divider"></li>
+                  <li class="dropdown-button">
+                    <form action="/admin/user-group-relation/{{userGroup.name}}/remove-relation/{{ sRelation.relatedUser._id.toString() }}" method="post">
+                      <input type="hidden" name="_csrf" value="{{ csrf() }}">
+                      <button type="submit" class="btn btn-block btn-danger">グループから外す</button>
+                    </form>
+                  </li>
+                </ul>
+              </div>
+            </td>
+          </tr>
+          {% endfor %}
+          <tr>
+            <td></td>
+            <td><button type="button" class="btn btn-primary" data-target="#admin-add-user-group-relation-modal" data-toggle="modal"><i class="fa fa-plus"></i></button></td>
+            <td></td>
+            <td></td>
+            <td></td>
+            <td></td>
+          </tr>
+        </tbody>
+      </table>
+
+      <!-- {% include '../widget/pager.html' with {path: "/admin/user-group-detail", pager: pager} %} -->
+
+    </div>
+  </div>
+</div>
+{% endblock content_main %}
+
+{% block content_footer %}
+{% endblock content_footer %}
+
+

+ 9 - 7
lib/views/admin/user-groups.html

@@ -80,19 +80,19 @@
 
             <div class="modal-body">
               <p>
-                グループの削除オペレーションを行うと元に戻すことはできませんのでご注意ください。
+                グループの削除を行うと元に戻すことはできませんのでご注意ください。
               </p>
               <p>
                 Related users:
                 <ul class="list-inline">
-                  {% for dGroupRelatedUser in userGroups %}
+                  {% for dGroupRelatedUser in relatedUsers %}
                   <li class="list-inline-item badge badge-primary">{{dGroupRelatedUser.name}}</li>
                   {% endfor %}
                 </ul>
               </p>
 
               <form method="post" id="admin-user-groups-delete">
-                <input type="hidden" name="user-group_id" value="">
+                <input type="hidden" name="user_group_id" value="">
                 <input type="hidden" name="_csrf" value="{{ csrf() }}">
                 <button type="submit" value="" class="btn btn-danger">
                   実行
@@ -127,8 +127,8 @@
             </td>
             <td>{{ sGroup.name }}</td>
             <td><ul class="list-inline">
-              {% for user in userGroups %}
-              <li class="list-inline-item badge badge-primary">{{user.name}}</li>
+              {% for relation in userGroupRelations.get(sGroup) %}
+              <li class="list-inline-item badge badge-primary">{{relation.relatedUser.name}}</li>
               {% endfor %}
             </ul></td>
             <td>{{ sGroup.createdAt|date('Y-m-d', sGroup.createdAt.getTimezoneOffset()) }}</td>
@@ -140,8 +140,10 @@
                 </button>
                 <ul class="dropdown-menu" role="menu">
                   <li class="dropdown-header">編集メニュー</li>
-                  <li>
-                    <a href="">編集</a>
+                  <li class="divider"></li>
+
+                  <li class="dropdown-button">
+                    <a href="/admin/user-group-detail/{{sGroup.name}}" class="btn btn-block btn-default">編集</a>
                   </li>
 
                   <li class="dropdown-button">

+ 8 - 13
resource/js/legacy/crowi-admin.js

@@ -60,23 +60,18 @@ $(function() {
     var relatedUsers = data.data('related-users');
 
     $('#admin-delete-user-group-name').text(userGroupName);
-    $('#admin-user-groups-delete input[name=user-group_id]').val(userGroupId);
+    $('#admin-user-groups-delete input[name=user_group_id]').val(userGroupId);
   });
 
   $('form#admin-user-groups-delete').on('submit', function (e) {
-    $.post('/_api/admin/userGroups.delete', $(this).serialize(), function (res) {
-      if (res.ok) {
-        // TODO Fix
-        //location.reload();
-        $('#admin-delete-user-group-modal').modal('hide');
-        return;
-      }
-
-      // fixme
-      alert('Failed to reset password');
-    });
+    $.post('/_api/admin/user-group.remove', $(this).serialize(), function (res) {});
+  });
 
-    return false;
+  $('form#user-group-relation-create').on('submit', function (e) {
+    $.post('/admin/user-group-relation/create', $(this).serialize(), function (res) {
+      $('#admin-add-user-group-relation-modal').modal('hide');
+      return;
+     });
   });
 
 });