瀏覽代碼

# Feature/196, 198, 199 Grouping users
* Refactor UserGroup model.
* Fix some logics of UserGroupRelation model methods.
* Refactor admin route logics for GroupACL.

Tatsuya Ise 8 年之前
父節點
當前提交
c7bbb87cda
共有 3 個文件被更改,包括 273 次插入234 次删除
  1. 34 12
      lib/models/user-group-relation.js
  2. 122 118
      lib/models/user-group.js
  3. 117 104
      lib/routes/admin.js

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

@@ -155,6 +155,28 @@ class UserGroupRelation {
       .exec();
   }
 
+  /**
+   * find all "not" related user for UserGroup
+   *
+   * @static
+   * @param {UserGroup} userGroup for find users not related
+   * @returns {Promise<User>}
+   * @memberof UserGroupRelation
+   */
+  static findUserByNotRelatedGroup(userGroup) {
+    const User = UserGroupRelation.crowi.model('User');
+
+    return this.findAllRelationForUserGroup(userGroup)
+      .then((relations) => {
+        const relatedUserIds = relations.map((relation) => {
+          return relation.relatedUser.id;
+        });
+        const query = { _id: { $nin: relatedUserIds } };
+
+        return User.find(query).exec();
+      });
+  }
+
   /**
    * get if the user has relation for group
    *
@@ -233,18 +255,18 @@ class UserGroupRelation {
   static removeById(id) {
 
     return this.findById(id)
-    .then((relationData) => {
-      if (relationData == null) {
-        throw new Exception('UserGroupRelation data is not exists. id:', id);
-      }
-      else {
-        relationData.remove();
-      }
-    })
-    .catch((err) => {
-      debug('Error on find a removing user-group-relation', err);
-      reject(err);
-    });
+      .then((relationData) => {
+        if (relationData == null) {
+          throw new Exception('UserGroupRelation data is not exists. id:', id);
+        }
+        else {
+          relationData.remove();
+        }
+      })
+      .catch((err) => {
+        debug('Error on find a removing user-group-relation', err);
+        reject(err);
+      });
   }
 
 }

+ 122 - 118
lib/models/user-group.js

@@ -1,147 +1,151 @@
-module.exports = function(crowi) {
-  var debug = require('debug')('crowi:models:userGroup')
-    , mongoose = require('mongoose')
-    , mongoosePaginate = require('mongoose-paginate')
-    , ObjectId = mongoose.Schema.Types.ObjectId
-
-    , USER_GROUP_PUBLIC_FIELDS = '_id image name createdAt'
-
-    , PAGE_ITEMS = 10
-
-    , userGroupSchema;
-
-  userGroupSchema = new mongoose.Schema({
-    userGroupId: String,
-    image: String,
-    name: { type: String, required: true, unique: true },
-    createdAt: { type: Date, default: Date.now },
-  });
-  userGroupSchema.plugin(mongoosePaginate);
-
-
-  // グループ画像の更新
-  userGroupSchema.methods.updateImage = function(image, callback) {
-    this.image = image;
-    this.save(function(err, userGroupData) {
-      return callback(err, userGroupData);
-    });
-  };
-
-  // グループ画像の削除
-  userGroupSchema.methods.deleteImage = function(callback) {
-    return this.updateImage(null, callback);
-  };
-
-  // グループ画像パスの生成
-  userGroupSchema.statics.createUserGroupPictureFilePath = function (userGroup, name) {
+const debug = require('debug')('crowi:models:userGroup');
+const mongoose = require('mongoose');
+const mongoosePaginate = require('mongoose-paginate');
+const ObjectId = mongoose.Schema.Types.ObjectId;
+
+
+/*
+ * define schema
+ */
+const schema = new mongoose.Schema({
+  userGroupId: String,
+  image: String,
+  name: { type: String, required: true, unique: true },
+  createdAt: { type: Date, default: Date.now },
+});
+schema.plugin(mongoosePaginate);
+
+class UserGroup {
+
+  /**
+   * public fields for UserGroup model
+   *
+   * @readonly
+   * @static
+   * @memberof UserGroup
+   */
+  static get USER_GROUP_PUBLIC_FIELDS() {
+    return '_id image name createdAt';
+  }
+
+  /**
+   * limit items num for pagination
+   *
+   * @readonly
+   * @static
+   * @memberof UserGroup
+   */
+  static get PAGE_ITEMS() {
+    return 10;
+  }
+
+  /*
+   * model static methods
+   */
+
+   // グループ画像パスの生成
+  static createUserGroupPictureFilePath(userGroup, name) {
     var ext = '.' + name.match(/(.*)(?:\.([^.]+$))/)[2];
 
     return 'userGroup/' + userGroup._id + ext;
   };
 
   // すべてのグループを取得(オプション指定可)
-  userGroupSchema.statics.findAllGroups = function(option) {
-    var UserGroup = this;
-
-    return new Promise(function(resolve, reject) {
-      UserGroup
-        .find()
-        .exec(function (err, userGroupData) {
-          if (err) {
-            return reject(err);
-          }
-
-          return resolve(userGroupData);
-        });
-    });
-  };
+  static findAllGroups(option) {
 
-  // ページネーション利用のグループ検索
-  userGroupSchema.statics.findUserGroupsWithPagination = function(options, callback) {
-    var sort = options.sort || {name: 1, createdAt: 1};
+    return this.find().exec();
+  };
 
-    this.paginate({}, { page: options.page || 1, limit: options.limit || PAGE_ITEMS }, function(err, result) {
-      if (err) {
+  /**
+   * find all entities with pagination
+   *
+   * @see https://github.com/edwardhotchkiss/mongoose-paginate
+   *
+   * @static
+   * @param {any} opts mongoose-paginate options object
+   * @returns {Promise<any>} mongoose-paginate result object
+   * @memberof UserGroup
+   */
+  static findUserGroupsWithPagination(opts) {
+    const query = {};
+    const options = Object.assign({}, opts);
+    if (options.page == null) {
+      options.page = 1;
+    }
+    if (options.limit == null) {
+      options.limit = UserGroup.PAGE_ITEMS;
+    }
+
+    return this.paginate(query, options)
+      .catch((err) => {
         debug('Error on pagination:', err);
-        return callback(err, null);
-      }
-
-      return callback(err, result);
-    }, { sortBy : sort });
+      });
   };
 
   // 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);
-        }
-
-        return resolve(userGroupData);
-      });
-    });
+  static findUserGroupByName(name) {
+    const query = { name: name };
+    return this.findOne(query);
   };
 
   // 登録可能グループ名確認
-  userGroupSchema.statics.isRegisterableName = function(name, callback) {
-    var UserGroup = this;
-    var userGroupnameUsable = true;
-
-    this.findOne({name: name}, function (err, userGroupData) {
-      if (userGroupData) {
-        debug(userGroupData);
-        userGroupnameUsable = false;
-      }
-      return callback(userGroupnameUsable);
-    });
+  static isRegisterableName(name) {
+    const query = { name: name };
+
+    return this.findOne(query)
+      .then((userGroupData) => {
+        return (userGroupData == null);
+      });
   };
 
   // グループの完全削除
-  userGroupSchema.statics.removeCompletelyById = function(id, callback) {
-    var UserGroup = this;
-    UserGroup.findById(id, function (err, userGroupData) {
-      if (!userGroupData) {
-        return callback(err, null);
-      }
-
-      debug('Removing userGroup:', userGroupData);
-
-      userGroupData.remove(function(err) {
-        if (err) {
-          return callback(err, null);
-        }
+  static removeCompletelyById(id) {
 
-        return callback(null, 1);
+    return this.findById(id)
+      .then((userGroupData) => {
+        if (userGroupData == null) {
+          throw new Exception('UserGroup data is not exists. id:', id);
+        }
+        else {
+          userGroupData.remove();
+        }
       });
-    });
-  };
+  }
 
-  // TBD: グループ生成(名前が要る)
-  userGroupSchema.statics.createGroupByName = function(name, callback) {
-    var UserGroup = this
-      , newUserGroup = new UserGroup();
+  // グループ生成(名前が要る)
+  static createGroupByName(name) {
 
-    newUserGroup.name = name;
-    newUserGroup.createdAt = Date.now();
+    return this.create(name);
+  }
 
-    newUserGroup.save(function(err, userGroupData) {
-      return callback(err, userGroupData);
-    });
-  };
+  /*
+   * instance methods
+   */
+
+  // グループ画像の更新
+  updateImage(image) {
+    this.image = image;
+    return this.save();
+  }
+
+  // グループ画像の削除
+  deleteImage() {
+    return this.updateImage(null);
+  }
 
   // グループ名の更新
-  userGroupSchema.methods.updateName = function(name, callback) {
+  updateName(name) {
     // 名前を設定して更新
     this.name = name;
-    this.save(function (err, userGroupData) {
-      return callback(err, this.name);
-    });
-  };
+    return this.save();
+  }
+
+}
+
 
-  // userGroupSchema.statics.USER_GROUP_PUBLIC_FIELDS = USER_GROUP_PUBLIC_FIELDS;
-  userGroupSchema.statics.PAGE_ITEMS         = PAGE_ITEMS;
+module.exports = function (crowi) {
+  UserGroup.crowi = crowi;
+  schema.loadClass(UserGroup);
+  return mongoose.model('UserGroup', schema);
+}
 
-  return mongoose.model('UserGroup', userGroupSchema);
-};

+ 117 - 104
lib/routes/admin.js

@@ -478,29 +478,39 @@ module.exports = function(crowi, app) {
   actions.userGroup = {};
   actions.userGroup.index = function (req, res) {
     var page = parseInt(req.query.page) || 1;
+    var renderVar = {
+      userGroups : [],
+      userGroupRelations : new Map(),
+      pager : null,
+    }
 
-    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();
-      UserGroupRelation.findAllRelationForUserGroups(userGroups)
-        .then( function(relations) {
-          userGroups.map((group) => {
-            groupRelations.set(group, relations.filter( function(target) {
-              return target.relatedGroup.toString() == group._id.toString();
-            }));
-          });
-          return res.render('admin/user-groups', {
-            userGroups: userGroups,
-            userGroupRelations: groupRelations,
-            pager: pager
+    UserGroup.findUserGroupsWithPagination({ page: page })
+      .then((result) => {
+        const pager = createPager(result.total, result.limit, result.page, result.pages, MAX_PAGE_LIST);
+        var userGroups = result.docs;
+        renderVar.userGroups = userGroups;
+        renderVar.pager = pager;
+        return userGroups.map((userGroup) => {
+          return new Promise((resolve, reject) => {
+            UserGroupRelation.findAllRelationForUserGroup(userGroup)
+            .then((relations) => {
+              return resolve([userGroup, relations]);
+            });
           });
-        })
-        .catch( function(err) {
-            debug('Error on find all relations', err);
-            return res.json(ApiResponse.error('Error'));
         });
-    });
+      })
+      .then((allRelationsPromise) => {
+        return Promise.all(allRelationsPromise)
+      })
+      .then((relations) => {
+        renderVar.userGroupRelations = new Map(relations);
+        debug("in findUserGroupsWithPagination findAllRelationForUserGroupResult", renderVar.userGroupRelations);
+        return res.render('admin/user-groups', renderVar);
+      })
+      .catch( function(err) {
+          debug('Error on find all relations', err);
+          return res.json(ApiResponse.error('Error'));
+      });
   };
 
   // グループ詳細
@@ -512,56 +522,39 @@ module.exports = function(crowi, app) {
       pageGroupRelations: [],
       notRelatedusers: []
     }
+    var targetUserGroup = null;
     UserGroup.findUserGroupByName(name)
       .then(function (userGroup) {
-        if (userGroup == null) {
-          req.flash('errorMessage', 'ユーザグループの検索に失敗しました');
-          debug('Error on get userGroupDetail', err);
-          return res.redirect('/admin/user-groups');
-        } else {
-          renderVar.userGroup = userGroup;
-
-          UserGroupRelation.findAllRelationForUserGroup(userGroup)
-          .then(function (relations) {
-            renderVar.userGroupRelations = relations;
-
-            PageGroupRelation.findAllRelationForUserGroup(userGroup)
-            .then(function (pageRelations) {
-              renderVar.pageGroupRelations = pageRelations;
-
-              User.findAllUsers(null)
-                .then(function (users) {
-                  debug('users', users);
-                  users = users.filter( function(user) {
-                    var relation = relations.find( function(relation) {
-                      return relation.relatedUser._id.toString() == user._id.toString();
-                    });
-                    return relation == null;
-                  });
-                  debug('users', users);
-                  debug('user-group-detail succeed', relations);
-                  renderVar.notRelatedusers = users;
-                  return res.render('admin/user-group-detail', renderVar);
-                })
-                .catch(function(err) {
-                  req.flash('errorMessage', 'ユーザの検索に失敗しました');
-                  debug('Error on find all unrelated users', err);
-                  return res.redirect('/admin/user-groups');
-                });
-            })
-            .catch(function (err) {
-              req.flash('errorMessage', 'グループ関連ページの検索に失敗しました');
-              debug('Error on find all relations', err);
-              return res.redirect('/admin/user-groups');
-            });
-          }).catch(function (err) {
-            req.flash('errorMessage', 'グループ関連ユーザの検索に失敗しました');
-            debug('Error on find all relations', err);
-            return res.redirect('/admin/user-groups');
-          });
+        targetUserGroup = userGroup;
+        if (targetUserGroup == null) {
+          req.flash('errorMessage', 'グループがありません');
+          return new Promise();
         }
+        else {
+          renderVar.userGroup = targetUserGroup;
+
+          // get all user and group relations
+          return UserGroupRelation.findAllRelationForUserGroup(targetUserGroup);
+        }
+      })
+      .then((relations) => {
+        renderVar.userGroupRelations = relations;
+
+        // get all page and group relations
+        return PageGroupRelation.findAllRelationForUserGroup(targetUserGroup)
       })
-      .catch(function(err) {
+      .then(function (pageRelations) {
+        renderVar.pageGroupRelations = pageRelations;
+
+        // get all not related users for group
+        return UserGroupRelation.findUserByNotRelatedGroup(targetUserGroup);
+      })
+      .then((notRelatedusers) => {
+        renderVar.notRelatedusers = notRelatedusers;
+
+        return res.render('admin/user-group-detail', renderVar);
+      })
+      .catch((err) => {
         req.flash('errorMessage', 'ユーザグループの検索に失敗しました');
         debug('Error on get userGroupDetail', err);
         return res.redirect('/admin/user-groups');
@@ -572,23 +565,22 @@ module.exports = function(crowi, app) {
   actions.userGroup.create = function (req, res) {
     var form = req.form.createGroupForm;
     if (req.form.isValid) {
-      UserGroup.isRegisterableName(form.userGroupName, function (registerable){
+      UserGroup.isRegisterableName(form.userGroupName)
+      .then((registerable) => {
         if (registerable) {
-          UserGroup.createGroupByName(form.userGroupName, function (err, newUserGroup) {
-            if (err) {
-              req.flash('errorMessage', req.form.errors.join('\n'));
-            } else {
-              req.flash('successMessage', newUserGroup.name)
-              req.flash('createdUserGroup', newUserGroup);
-            }
-            return res.redirect('/admin/user-groups');
+          return UserGroup.createGroupByName(form.userGroupName)
+          .then((newUserGroup) => {
+            req.flash('successMessage', newUserGroup.name)
+            req.flash('createdUserGroup', newUserGroup);
           });
         }
         else {
-          req.flash('errorMessage', '同じグループ名が既に存在します。');
           debug('userGroupName', form.userGroupName);
-          return res.redirect('/admin/user-groups');
+          req.flash('errorMessage', '同じグループ名が既に存在します。');
         }
+      })
+      .then(() => {
+        return res.redirect('/admin/user-groups');
       });
     } else {
       req.flash('errorMessage', req.form.errors.join('\n'));
@@ -602,31 +594,34 @@ module.exports = function(crowi, app) {
     var userGroupId = req.params.userGroupId;
     var name = req.body.name;
 
-    UserGroup.findById(userGroupId, function (err, userGroupData) {
-      if (!userGroupData) {
+    UserGroup.findById(userGroupId)
+    .then((userGroupData) => {
+      if (userGroupData == null) {
         req.flash('errorMessage', 'グループの検索に失敗しました。');
-        return res.redirect('/admin/user-groups');
+        return new Promise();
       }
-
-      // 名前存在チェック
-      UserGroup.isRegisterableName(name, function (isRegisterableName) {
-        // 既に存在するグループ名に更新しようとした場合はエラー
-        if (!isRegisterableName) {
-          req.flash('errorMessage', 'グループ名が既に存在します。');
-          return res.redirect('/admin/user-group-detail/' + userGroupData.name);
-        }
-
-        userGroupData.updateName(name, function (err, updatedName) {
-          if (err) {
-            req.flash('errorMessage', 'グループ名の更新に失敗しました。');
-            return res.redirect('/admin/user-group-detail/' + userGroupData.name);
+      else {
+        // 名前存在チェック
+        return UserGroup.isRegisterableName(name)
+        .then((isRegisterableName) => {
+          // 既に存在するグループ名に更新しようとした場合はエラー
+          if (!isRegisterableName) {
+            req.flash('errorMessage', 'グループ名が既に存在します。');
           }
           else {
-            req.flash('successMessage', 'グループ名を更新しました。');
-            return res.redirect('/admin/user-group-detail/' + name);
+            return userGroupData.updateName(name)
+            .then(() => {
+              req.flash('successMessage', 'グループ名を更新しました。');
+            })
+            .catch((err) => {
+              req.flash('errorMessage', 'グループ名の更新に失敗しました。');
+            });
           }
         });
-      });
+      }
+    })
+    .then(() => {
+      return res.redirect('/admin/user-group-detail/' + userGroupData.name);
     });
   };
 
@@ -669,7 +664,8 @@ module.exports = function(crowi, app) {
       fileUploader.uploadFile(filePath, tmpFile.mimetype, tmpFileStream, {})
         .then(function (data) {
           var imageUrl = fileUploader.generateUrl(filePath);
-          userGroupData.updateImage(imageUrl, function (err, data) {
+          userGroupData.updateImage(imageUrl)
+          .then(() => {
             fs.unlink(tmpPath, function (err) {
               if (err) {
                 debug('Error while deleting tmp file.', err);
@@ -697,16 +693,33 @@ module.exports = function(crowi, app) {
   actions.userGroup.deletePicture = function (req, res) {
 
     var userGroupId = req.params.userGroupId;
+    let userGroupName = null;
 
-    UserGroup.findById(userGroupId, function (err, userGroupData) {
-      if (!userGroupData) {
-        req.flash('errorMessage', 'Error while deleting group picture');
+    UserGroup.findById(userGroupId)
+    .then((userGroupData) => {
+      if (userGroupData == null) {
+        return Promise.reject();
       }
+      else {
+        userGroupName = userGroupData.name;
+        return userGroupData.deleteImage();
+      }
+    })
+    .then((updated) => {
+      req.flash('successMessage', 'Deleted group picture');
 
-      userGroupData.deleteImage(function (err, data) {
-        req.flash('successMessage', 'Deleted group picture');
-      });
-      return res.redirect('/admin/user-group-detail/' + userGroupData.name);
+      return res.redirect('/admin/user-group-detail/' + userGroupName);
+    })
+    .catch((err) => {
+      debug('An error occured.', err);
+
+      req.flash('errorMessage', 'Error while deleting group picture');
+      if (userGroupName == null) {
+        return res.redirect('/admin/user-groups/');
+      }
+      else {
+        return res.redirect('/admin/user-group-detail/' + userGroupName);
+      }
     });
   };