瀏覽代碼

New Board Permissions: NormalAssignedOnly, CommentAssignedOnly, ReadOnly, ReadAssignedOnly.

Thanks to xet7 !

Fixes #1122,
fixes #6033,
fixes #3300
Lauri Ojansivu 16 小時之前
父節點
當前提交
c1168d181b
共有 5 個文件被更改,包括 190 次插入4 次删除
  1. 24 0
      client/components/sidebar/sidebar.jade
  2. 62 2
      client/components/sidebar/sidebar.js
  3. 8 0
      imports/i18n/data/en.i18n.json
  4. 87 1
      models/boards.js
  5. 9 1
      models/users.js

+ 24 - 0
client/components/sidebar/sidebar.jade

@@ -809,6 +809,12 @@ template(name="changePermissionsPopup")
         if isNormal
         if isNormal
           | ✅
           | ✅
         span.sub-name {{_ 'normal-desc'}}
         span.sub-name {{_ 'normal-desc'}}
+    li
+      a(class="{{#if isLastAdmin}}disabled{{else}}js-set-normal-assigned-only{{/if}}")
+        | {{_ 'normal-assigned-only'}}
+        if isNormalAssignedOnly
+          | ✅
+        span.sub-name {{_ 'normal-assigned-only-desc'}}
     li
     li
       a(class="{{#if isLastAdmin}}disabled{{else}}js-set-no-comments{{/if}}")
       a(class="{{#if isLastAdmin}}disabled{{else}}js-set-no-comments{{/if}}")
         | {{_ 'no-comments'}}
         | {{_ 'no-comments'}}
@@ -821,6 +827,24 @@ template(name="changePermissionsPopup")
         if isCommentOnly
         if isCommentOnly
           | ✅
           | ✅
         span.sub-name {{_ 'comment-only-desc'}}
         span.sub-name {{_ 'comment-only-desc'}}
+    li
+      a(class="{{#if isLastAdmin}}disabled{{else}}js-set-comment-assigned-only{{/if}}")
+        | {{_ 'comment-assigned-only'}}
+        if isCommentAssignedOnly
+          | ✅
+        span.sub-name {{_ 'comment-assigned-only-desc'}}
+    li
+      a(class="{{#if isLastAdmin}}disabled{{else}}js-set-read-only{{/if}}")
+        | {{_ 'read-only'}}
+        if isReadOnly
+          | ✅
+        span.sub-name {{_ 'read-only-desc'}}
+    li
+      a(class="{{#if isLastAdmin}}disabled{{else}}js-set-read-assigned-only{{/if}}")
+        | {{_ 'read-assigned-only'}}
+        if isReadAssignedOnly
+          | ✅
+        span.sub-name {{_ 'read-assigned-only-desc'}}
     li
     li
       a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
       a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
         | {{_ 'worker'}}
         | {{_ 'worker'}}

+ 62 - 2
client/components/sidebar/sidebar.js

@@ -239,8 +239,20 @@ Template.memberPopup.helpers({
       const commentOnly = currentBoard.hasCommentOnly(this.userId);
       const commentOnly = currentBoard.hasCommentOnly(this.userId);
       const noComments = currentBoard.hasNoComments(this.userId);
       const noComments = currentBoard.hasNoComments(this.userId);
       const worker = currentBoard.hasWorker(this.userId);
       const worker = currentBoard.hasWorker(this.userId);
-      if (commentOnly) {
+      const normalAssignedOnly = currentBoard.hasNormalAssignedOnly(this.userId);
+      const commentAssignedOnly = currentBoard.hasCommentAssignedOnly(this.userId);
+      const readOnly = currentBoard.hasReadOnly(this.userId);
+      const readAssignedOnly = currentBoard.hasReadAssignedOnly(this.userId);
+      if (readAssignedOnly) {
+        return TAPi18n.__('read-assigned-only');
+      } else if (readOnly) {
+        return TAPi18n.__('read-only');
+      } else if (commentAssignedOnly) {
+        return TAPi18n.__('comment-assigned-only');
+      } else if (commentOnly) {
         return TAPi18n.__('comment-only');
         return TAPi18n.__('comment-only');
+      } else if (normalAssignedOnly) {
+        return TAPi18n.__('normal-assigned-only');
       } else if (noComments) {
       } else if (noComments) {
         return TAPi18n.__('no-comments');
         return TAPi18n.__('no-comments');
       } else if (worker) {
       } else if (worker) {
@@ -1925,7 +1937,7 @@ Template.removeBoardTeamPopup.helpers({
 });
 });
 
 
 Template.changePermissionsPopup.events({
 Template.changePermissionsPopup.events({
-  'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
+  'click .js-set-admin, click .js-set-normal, click .js-set-normal-assigned-only, click .js-set-no-comments, click .js-set-comment-only, click .js-set-comment-assigned-only, click .js-set-read-only, click .js-set-read-assigned-only, click .js-set-worker'(
     event,
     event,
   ) {
   ) {
     const currentBoard = Utils.getCurrentBoard();
     const currentBoard = Utils.getCurrentBoard();
@@ -1934,6 +1946,14 @@ Template.changePermissionsPopup.events({
     const isCommentOnly = $(event.currentTarget).hasClass(
     const isCommentOnly = $(event.currentTarget).hasClass(
       'js-set-comment-only',
       'js-set-comment-only',
     );
     );
+    const isNormalAssignedOnly = $(event.currentTarget).hasClass(
+      'js-set-normal-assigned-only',
+    );
+    const isCommentAssignedOnly = $(event.currentTarget).hasClass(
+      'js-set-comment-assigned-only',
+    );
+    const isReadOnly = $(event.currentTarget).hasClass('js-set-read-only');
+    const isReadAssignedOnly = $(event.currentTarget).hasClass('js-set-read-assigned-only');
     const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
     const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
     const isWorker = $(event.currentTarget).hasClass('js-set-worker');
     const isWorker = $(event.currentTarget).hasClass('js-set-worker');
     currentBoard.setMemberPermission(
     currentBoard.setMemberPermission(
@@ -1942,6 +1962,10 @@ Template.changePermissionsPopup.events({
       isNoComments,
       isNoComments,
       isCommentOnly,
       isCommentOnly,
       isWorker,
       isWorker,
+      isNormalAssignedOnly,
+      isCommentAssignedOnly,
+      isReadOnly,
+      isReadAssignedOnly,
     );
     );
     Popup.back(1);
     Popup.back(1);
   },
   },
@@ -1959,10 +1983,22 @@ Template.changePermissionsPopup.helpers({
       !currentBoard.hasAdmin(this.userId) &&
       !currentBoard.hasAdmin(this.userId) &&
       !currentBoard.hasNoComments(this.userId) &&
       !currentBoard.hasNoComments(this.userId) &&
       !currentBoard.hasCommentOnly(this.userId) &&
       !currentBoard.hasCommentOnly(this.userId) &&
+      !currentBoard.hasNormalAssignedOnly(this.userId) &&
+      !currentBoard.hasCommentAssignedOnly(this.userId) &&
+      !currentBoard.hasReadOnly(this.userId) &&
+      !currentBoard.hasReadAssignedOnly(this.userId) &&
       !currentBoard.hasWorker(this.userId)
       !currentBoard.hasWorker(this.userId)
     );
     );
   },
   },
 
 
+  isNormalAssignedOnly() {
+    const currentBoard = Utils.getCurrentBoard();
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasNormalAssignedOnly(this.userId)
+    );
+  },
+
   isNoComments() {
   isNoComments() {
     const currentBoard = Utils.getCurrentBoard();
     const currentBoard = Utils.getCurrentBoard();
     return (
     return (
@@ -1979,6 +2015,30 @@ Template.changePermissionsPopup.helpers({
     );
     );
   },
   },
 
 
+  isCommentAssignedOnly() {
+    const currentBoard = Utils.getCurrentBoard();
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasCommentAssignedOnly(this.userId)
+    );
+  },
+
+  isReadOnly() {
+    const currentBoard = Utils.getCurrentBoard();
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasReadOnly(this.userId)
+    );
+  },
+
+  isReadAssignedOnly() {
+    const currentBoard = Utils.getCurrentBoard();
+    return (
+      !currentBoard.hasAdmin(this.userId) &&
+      currentBoard.hasReadAssignedOnly(this.userId)
+    );
+  },
+
   isWorker() {
   isWorker() {
     const currentBoard = Utils.getCurrentBoard();
     const currentBoard = Utils.getCurrentBoard();
     return (
     return (

+ 8 - 0
imports/i18n/data/en.i18n.json

@@ -328,10 +328,16 @@
   "comment-placeholder": "Write Comment",
   "comment-placeholder": "Write Comment",
   "comment-only": "Comment only",
   "comment-only": "Comment only",
   "comment-only-desc": "Can comment on cards only.",
   "comment-only-desc": "Can comment on cards only.",
+  "comment-assigned-only": "Comment only (Assigned Only)",
+  "comment-assigned-only-desc": "Can comment on assigned cards only.",
   "comment-delete": "Are you sure you want to delete the comment?",
   "comment-delete": "Are you sure you want to delete the comment?",
   "deleteCommentPopup-title": "Delete comment?",
   "deleteCommentPopup-title": "Delete comment?",
   "no-comments": "No comments",
   "no-comments": "No comments",
   "no-comments-desc": "Can not see comments and activities.",
   "no-comments-desc": "Can not see comments and activities.",
+  "read-only": "Read Only",
+  "read-only-desc": "Can view cards only. Can not comment or edit.",
+  "read-assigned-only": "Read Only (Assigned Only)",
+  "read-assigned-only-desc": "Can view assigned cards only. Can not comment or edit.",
   "worker": "Worker",
   "worker": "Worker",
   "worker-desc": "Can only move cards, assign itself to card and comment.",
   "worker-desc": "Can only move cards, assign itself to card and comment.",
   "computer": "Computer",
   "computer": "Computer",
@@ -568,6 +574,8 @@
   "no-results": "No results",
   "no-results": "No results",
   "normal": "Normal",
   "normal": "Normal",
   "normal-desc": "Can view and edit cards. Can't change settings.",
   "normal-desc": "Can view and edit cards. Can't change settings.",
+  "normal-assigned-only": "Normal (Assigned Only)",
+  "normal-assigned-only-desc": "Can view and edit only assigned cards. Can't change settings.",
   "not-accepted-yet": "Invitation not accepted yet",
   "not-accepted-yet": "Invitation not accepted yet",
   "notify-participate": "Receive updates to any cards you participate as creator or member",
   "notify-participate": "Receive updates to any cards you participate as creator or member",
   "notify-watch": "Receive updates to any boards, lists, or cards you’re watching",
   "notify-watch": "Receive updates to any boards, lists, or cards you’re watching",

+ 87 - 1
models/boards.js

@@ -225,6 +225,34 @@ Boards.attachSchema(
       type: Boolean,
       type: Boolean,
       optional: true,
       optional: true,
     },
     },
+    'members.$.isNormalAssignedOnly': {
+      /**
+       * Is the member only allowed to see assigned cards (Normal permission)
+       */
+      type: Boolean,
+      optional: true,
+    },
+    'members.$.isCommentAssignedOnly': {
+      /**
+       * Is the member only allowed to comment on assigned cards
+       */
+      type: Boolean,
+      optional: true,
+    },
+    'members.$.isReadOnly': {
+      /**
+       * Is the member only allowed to read the board (no comments, no editing)
+       */
+      type: Boolean,
+      optional: true,
+    },
+    'members.$.isReadAssignedOnly': {
+      /**
+       * Is the member only allowed to read assigned cards (no comments, no editing)
+       */
+      type: Boolean,
+      optional: true,
+    },
     permission: {
     permission: {
       /**
       /**
        * visibility of the board
        * visibility of the board
@@ -979,6 +1007,44 @@ Boards.helpers({
     });
     });
   },
   },
 
 
+  hasNormalAssignedOnly(memberId) {
+    return !!_.findWhere(this.members, {
+      userId: memberId,
+      isActive: true,
+      isAdmin: false,
+      isNormalAssignedOnly: true,
+      isCommentAssignedOnly: false,
+    });
+  },
+
+  hasCommentAssignedOnly(memberId) {
+    return !!_.findWhere(this.members, {
+      userId: memberId,
+      isActive: true,
+      isAdmin: false,
+      isNormalAssignedOnly: false,
+      isCommentAssignedOnly: true,
+    });
+  },
+
+  hasReadOnly(memberId) {
+    return !!_.findWhere(this.members, {
+      userId: memberId,
+      isActive: true,
+      isAdmin: false,
+      isReadOnly: true,
+    });
+  },
+
+  hasReadAssignedOnly(memberId) {
+    return !!_.findWhere(this.members, {
+      userId: memberId,
+      isActive: true,
+      isAdmin: false,
+      isReadAssignedOnly: true,
+    });
+  },
+
   hasAnyAllowsDate() {
   hasAnyAllowsDate() {
     const ret = this.allowsReceivedDate || this.allowsStartDate || this.allowsDueDate || this.allowsEndDate;
     const ret = this.allowsReceivedDate || this.allowsStartDate || this.allowsDueDate || this.allowsEndDate;
     return ret;
     return ret;
@@ -1416,6 +1482,10 @@ Boards.mutations({
           isNoComments: false,
           isNoComments: false,
           isCommentOnly: false,
           isCommentOnly: false,
           isWorker: false,
           isWorker: false,
+          isNormalAssignedOnly: false,
+          isCommentAssignedOnly: false,
+          isReadOnly: false,
+          isReadAssignedOnly: false,
         },
         },
       },
       },
     };
     };
@@ -1449,6 +1519,10 @@ Boards.mutations({
     isNoComments,
     isNoComments,
     isCommentOnly,
     isCommentOnly,
     isWorker,
     isWorker,
+    isNormalAssignedOnly = false,
+    isCommentAssignedOnly = false,
+    isReadOnly = false,
+    isReadAssignedOnly = false,
     currentUserId = Meteor.userId(),
     currentUserId = Meteor.userId(),
   ) {
   ) {
     const memberIndex = this.memberIndex(memberId);
     const memberIndex = this.memberIndex(memberId);
@@ -1463,6 +1537,10 @@ Boards.mutations({
         [`members.${memberIndex}.isNoComments`]: isNoComments,
         [`members.${memberIndex}.isNoComments`]: isNoComments,
         [`members.${memberIndex}.isCommentOnly`]: isCommentOnly,
         [`members.${memberIndex}.isCommentOnly`]: isCommentOnly,
         [`members.${memberIndex}.isWorker`]: isWorker,
         [`members.${memberIndex}.isWorker`]: isWorker,
+        [`members.${memberIndex}.isNormalAssignedOnly`]: isNormalAssignedOnly,
+        [`members.${memberIndex}.isCommentAssignedOnly`]: isCommentAssignedOnly,
+        [`members.${memberIndex}.isReadOnly`]: isReadOnly,
+        [`members.${memberIndex}.isReadAssignedOnly`]: isReadAssignedOnly,
       },
       },
     };
     };
   },
   },
@@ -2372,6 +2450,10 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
    * @param {boolean} isNoComments NoComments capability
    * @param {boolean} isNoComments NoComments capability
    * @param {boolean} isCommentOnly CommentsOnly capability
    * @param {boolean} isCommentOnly CommentsOnly capability
    * @param {boolean} isWorker Worker capability
    * @param {boolean} isWorker Worker capability
+   * @param {boolean} isNormalAssignedOnly NormalAssignedOnly capability
+   * @param {boolean} isCommentAssignedOnly CommentAssignedOnly capability
+   * @param {boolean} isReadOnly ReadOnly capability
+   * @param {boolean} isReadAssignedOnly ReadAssignedOnly capability
    */
    */
   JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
   JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
     req,
     req,
@@ -2381,7 +2463,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
       Authentication.checkUserId(req.userId);
       Authentication.checkUserId(req.userId);
       const boardId = req.params.boardId;
       const boardId = req.params.boardId;
       const memberId = req.params.memberId;
       const memberId = req.params.memberId;
-      const { isAdmin, isNoComments, isCommentOnly, isWorker } = req.body;
+      const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
       const board = ReactiveCache.getBoard(boardId);
       const board = ReactiveCache.getBoard(boardId);
       function isTrue(data) {
       function isTrue(data) {
         try {
         try {
@@ -2396,6 +2478,10 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
         isTrue(isNoComments),
         isTrue(isNoComments),
         isTrue(isCommentOnly),
         isTrue(isCommentOnly),
         isTrue(isWorker),
         isTrue(isWorker),
+        isTrue(isNormalAssignedOnly),
+        isTrue(isCommentAssignedOnly),
+        isTrue(isReadOnly),
+        isTrue(isReadAssignedOnly),
         req.userId,
         req.userId,
       );
       );
 
 

+ 9 - 1
models/users.js

@@ -2947,6 +2947,10 @@ if (Meteor.isServer) {
    * @param {boolean} isNoComments disable comments
    * @param {boolean} isNoComments disable comments
    * @param {boolean} isCommentOnly only enable comments
    * @param {boolean} isCommentOnly only enable comments
    * @param {boolean} isWorker is the user a board worker
    * @param {boolean} isWorker is the user a board worker
+   * @param {boolean} isNormalAssignedOnly only see assigned cards (Normal permission)
+   * @param {boolean} isCommentAssignedOnly only comment on assigned cards
+   * @param {boolean} isReadOnly read-only access (no comments or editing)
+   * @param {boolean} isReadAssignedOnly read-only assigned cards only
    * @return_type {_id: string,
    * @return_type {_id: string,
    *               title: string}
    *               title: string}
    */
    */
@@ -2959,7 +2963,7 @@ if (Meteor.isServer) {
         const userId = req.params.userId;
         const userId = req.params.userId;
         const boardId = req.params.boardId;
         const boardId = req.params.boardId;
         const action = req.body.action;
         const action = req.body.action;
-        const { isAdmin, isNoComments, isCommentOnly, isWorker } = req.body;
+        const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
         let data = ReactiveCache.getUser(userId);
         let data = ReactiveCache.getUser(userId);
         if (data !== undefined) {
         if (data !== undefined) {
           if (action === 'add') {
           if (action === 'add') {
@@ -2978,6 +2982,10 @@ if (Meteor.isServer) {
                   isTrue(isNoComments),
                   isTrue(isNoComments),
                   isTrue(isCommentOnly),
                   isTrue(isCommentOnly),
                   isTrue(isWorker),
                   isTrue(isWorker),
+                  isTrue(isNormalAssignedOnly),
+                  isTrue(isCommentAssignedOnly),
+                  isTrue(isReadOnly),
+                  isTrue(isReadAssignedOnly),
                   userId,
                   userId,
                 );
                 );
               }
               }