Преглед на файлове

Merge pull request #3895 from Emile840/master

Add or remove an organization and team from a board
Lauri Ojansivu преди 3 години
родител
ревизия
2a611a8935

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

@@ -31,6 +31,26 @@ template(name='homeSidebar')
     +activities(mode="board")
 
 template(name="membersWidget")
+  .board-widget.board-widget-members
+    h3
+      i.fa.fa-users
+      | {{_ 'organizations'}}
+
+    .board-widget-content
+      +boardOrgGeneral
+      .clearfix
+  br
+  hr
+  .board-widget.board-widget-members
+    h3
+      i.fa.fa-users
+      | {{_ 'teams'}}
+
+    .board-widget-content
+      +boardTeamGeneral
+      .clearfix
+  br
+  hr
   .board-widget.board-widget-members
     h3
       i.fa.fa-users
@@ -65,6 +85,30 @@ template(name="membersWidget")
     button.js-member-invite-accept.primary {{_ 'accept'}}
     button.js-member-invite-decline {{_ 'decline'}}
 
+template(name="boardOrgGeneral")
+  table
+    tbody
+      tr
+        th {{_ 'displayName'}}
+        th
+          if currentUser.isBoardAdmin
+            a.member.orgOrTeamMember.add-member.js-manage-board-addOrg(title="{{_ 'add-members'}}")
+              i.fa.fa-plus
+      each org in currentBoard.activeOrgs
+        +boardOrgRow(orgId=org.orgId)
+
+template(name="boardTeamGeneral")
+  table
+    tbody
+      tr
+        th {{_ 'displayName'}}
+        th
+          if currentUser.isBoardAdmin
+            a.member.orgOrTeamMember.add-member.js-manage-board-addTeam(title="{{_ 'add-members'}}")
+              i.fa.fa-plus
+      each currentBoard.activeteams
+        +boardTeamRow(teamId=this.teamId)
+
 template(name="boardChangeColorPopup")
   .board-backgrounds-list.clearfix
     each backgroundColors
@@ -409,6 +453,40 @@ template(name="leaveBoardPopup")
   p {{_ 'leave-board-pop' boardTitle=board.title}}
   button.js-confirm.negate.full(type="submit") {{_ 'leave-board'}}
 
+template(name="addBoardOrgPopup")
+  select.js-boardOrgs#jsBoardOrgs
+    option(value="-1") {{_ 'organizations'}} :
+    each value in orgsDatas
+      option(value="{{value._id}}") {{_ value.orgDisplayName}}
+
+template(name="removeBoardOrgPopup")
+  form
+    input.hide#hideOrgId(type="text" value=org._id)
+    label
+      | {{_ 'leave-board'}} ?
+  br
+  hr
+  div.buttonsContainer
+    input.primary.wide.leaveBoardBtn#leaveBoardBtn(type="submit" value="{{_ 'leave-board'}}")
+    input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardBtn(type="submit" value="{{_ 'Cancel'}}")
+
+template(name="addBoardTeamPopup")
+  select.js-boardTeams#jsBoardTeams
+    option(value="-1") {{_ 'teams'}} :
+    each value in teamsDatas
+      option(value="{{value._id}}") {{_ value.teamDisplayName}}
+
+template(name="removeBoardTeamPopup")
+  form
+    input.hide#hideTeamId(type="text" value=team._id)
+    label
+      | {{_ 'leave-board'}} ?
+  br
+  hr
+  div.buttonsContainer
+    input.primary.wide.leaveBoardBtn#leaveBoardTeamBtn(type="submit" value="{{_ 'leave-board'}}")
+    input.primary.wide.cancelLeaveBoardBtn#cancelLeaveBoardTeamBtn(type="submit" value="{{_ 'Cancel'}}")
+
 template(name="addMemberPopup")
   .js-search-member
     +esInput(index="users")

+ 279 - 0
client/components/sidebar/sidebar.js

@@ -313,6 +313,8 @@ Template.membersWidget.events({
   'click .js-member': Popup.open('member'),
   'click .js-open-board-menu': Popup.open('boardMenu'),
   'click .js-manage-board-members': Popup.open('addMember'),
+  'click .js-manage-board-addOrg': Popup.open('addBoardOrg'),
+  'click .js-manage-board-addTeam': Popup.open('addBoardTeam'),
   'click .js-import': Popup.open('boardImportBoard'),
   submit: this.onSubmit,
   'click .js-import-board': Popup.open('chooseBoardSource'),
@@ -1168,6 +1170,283 @@ BlazeComponent.extendComponent({
   },
 }).register('addMemberPopup');
 
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'change #jsBoardOrgs'() {
+          let currentBoard = Boards.findOne(Session.get('currentBoard'));
+          let selectElt = document.getElementById("jsBoardOrgs");
+          let selectedOrgId = selectElt.options[selectElt.selectedIndex].value;
+          let selectedOrgDisplayName = selectElt.options[selectElt.selectedIndex].text;
+          let boardOrganizations = [];
+          if(currentBoard.orgs !== undefined){
+            for(let i = 0; i < currentBoard.orgs.length; i++){
+              boardOrganizations.push(currentBoard.orgs[i]);
+            }
+          }
+
+          if(!boardOrganizations.some((org) => org.orgDisplayName == selectedOrgDisplayName)){
+            boardOrganizations.push({
+              "orgId": selectedOrgId,
+              "orgDisplayName": selectedOrgDisplayName,
+              "isActive" : true,
+            })
+
+            if (selectedOrgId != "-1") {
+              Meteor.call('setBoardOrgs', boardOrganizations, currentBoard._id);
+            }
+          }
+
+          Popup.close();
+        },
+      },
+    ];
+  },
+}).register('addBoardOrgPopup');
+
+Template.addBoardOrgPopup.helpers({
+  orgsDatas() {
+    // return Org.find({}, {sort: { createdAt: -1 }});
+    let orgs = Org.find({}, {sort: { createdAt: -1 }});
+    return orgs;
+  },
+});
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'click #leaveBoardBtn'(){
+          let stringOrgId = document.getElementById('hideOrgId').value;
+          let currentBoard = Boards.findOne(Session.get('currentBoard'));
+          let boardOrganizations = [];
+          if(currentBoard.orgs !== undefined){
+            for(let i = 0; i < currentBoard.orgs.length; i++){
+              if(currentBoard.orgs[i].orgId != stringOrgId){
+                boardOrganizations.push(currentBoard.orgs[i]);
+              }
+            }
+          }
+
+          Meteor.call('setBoardOrgs', boardOrganizations, currentBoard._id);
+
+          Popup.close();
+        },
+        'click #cancelLeaveBoardBtn'(){
+          Popup.close();
+        },
+      },
+    ];
+  },
+}).register('removeBoardOrgPopup');
+
+Template.removeBoardOrgPopup.helpers({
+  org() {
+    return Org.findOne(this.orgId);
+  },
+});
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'change #jsBoardTeams'() {
+          let currentBoard = Boards.findOne(Session.get('currentBoard'));
+          let selectElt = document.getElementById("jsBoardTeams");
+          let selectedTeamId = selectElt.options[selectElt.selectedIndex].value;
+          let selectedTeamDisplayName = selectElt.options[selectElt.selectedIndex].text;
+          let boardTeams = [];
+          if(currentBoard.teams !== undefined){
+            for(let i = 0; i < currentBoard.teams.length; i++){
+              boardTeams.push(currentBoard.teams[i]);
+            }
+          }
+
+          if(!boardTeams.some((team) => team.teamDisplayName == selectedTeamDisplayName)){
+            boardTeams.push({
+              "teamId": selectedTeamId,
+              "teamDisplayName": selectedTeamDisplayName,
+              "isActive" : true,
+            })
+
+            if (selectedTeamId != "-1") {
+              Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
+            }
+          }
+
+          Popup.close();
+        },
+      },
+    ];
+  },
+}).register('addBoardTeamPopup');
+
+Template.addBoardTeamPopup.helpers({
+  teamsDatas() {
+    let teams = Team.find({}, {sort: { createdAt: -1 }});
+    return teams;
+  },
+});
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'click #leaveBoardTeamBtn'(){
+          let stringTeamId = document.getElementById('hideTeamId').value;
+          let currentBoard = Boards.findOne(Session.get('currentBoard'));
+          let boardTeams = [];
+          if(currentBoard.teams !== undefined){
+            for(let i = 0; i < currentBoard.teams.length; i++){
+              if(currentBoard.teams[i].teamId != stringTeamId){
+                boardTeams.push(currentBoard.teams[i]);
+              }
+            }
+          }
+
+          Meteor.call('setBoardTeams', boardTeams, currentBoard._id);
+
+          Popup.close();
+        },
+        'click #cancelLeaveBoardTeamBtn'(){
+          Popup.close();
+        },
+      },
+    ];
+  },
+}).register('removeBoardTeamPopup');
+
+Template.removeBoardTeamPopup.helpers({
+  team() {
+    return Team.findOne(this.teamId);
+  },
+});
+
 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'(
     event,

+ 10 - 0
client/components/sidebar/sidebar.styl

@@ -214,3 +214,13 @@
     i.fa
       padding: 8px 0px 8px 16px
       font-weight: bold
+
+#jsBoardOrgs, #jsBoardTeams
+  width: 90%
+
+.leaveBoardBtn
+  background-color: green !important
+
+.cancelLeaveBoardBtn
+  margin-left: 5% !important
+  background-color: red !important

+ 38 - 0
client/components/users/userAvatar.jade

@@ -19,6 +19,44 @@ template(name="userAvatarInitials")
   svg.avatar.avatar-initials(viewBox="0 0 {{viewPortWidth}} 15")
     text(x="50%" y="13" text-anchor="middle")= initials
 
+template(name="orgAvatar")
+  a.member.orgOrTeamMember(class="js-member" title="{{orgData.orgDisplayName}}")
+    +boardOrgName(orgId=orgData._id)
+
+template(name="boardOrgRow")
+  tr
+    if orgData.orgIsActive
+      td <s>{{ orgData.orgDisplayName }}</s>
+    else
+      td {{ orgData.orgDisplayName }}
+    td
+      if currentUser.isBoardAdmin
+        a.member.orgOrTeamMember.add-member.js-manage-board-removeOrg(title="{{_ 'remove-from-board'}}")
+          i.fa.fa-minus
+
+template(name="boardTeamRow")
+  tr
+    if teamData.teamIsActive
+      td <s>{{ teamData.teamDisplayName }}</s>
+    else
+      td {{ teamData.teamDisplayName }}
+    td
+      if currentUser.isBoardAdmin
+        a.member.orgOrTeamMember.add-member.js-manage-board-removeTeam(title="{{_ 'remove-from-board'}}")
+          i.fa.fa-minus
+
+template(name="boardOrgName")
+  svg.avatar.avatar-initials(viewBox="0 0 {{orgViewPortWidth}} 15")
+    text(x="50%" y="13" text-anchor="middle")= orgName
+
+template(name="teamAvatar")
+  a.member.orgOrTeamMember(class="js-member" title="{{teamData.teamDisplayName}}")
+    +boardTeamName(orgId=orgData._id)
+
+template(name="boardTeamName")
+  svg.avatar.avatar-initials(viewBox="0 0 {{teamViewPortWidth}} 15")
+    text(x="50%" y="13" text-anchor="middle")= teamName
+
 template(name="userPopup")
   .board-member-menu
     .mini-profile-info

+ 128 - 0
client/components/users/userAvatar.js

@@ -1,6 +1,8 @@
 import Cards from '/models/cards';
 import Avatars from '/models/avatars';
 import Users from '/models/users';
+import Org from '/models/org';
+import Team from '/models/team';
 
 Template.userAvatar.helpers({
   userData() {
@@ -46,6 +48,132 @@ Template.userAvatarInitials.helpers({
   },
 });
 
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitOrgs = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'click .js-manage-board-removeOrg': Popup.open('removeBoardOrg'),
+      },
+    ];
+  },
+}).register('boardOrgRow');
+
+Template.boardOrgRow.helpers({
+  orgData() {
+    const orgCollection = this.esSearch ? ESSearchResults : Org;
+    return orgCollection.findOne(this.orgId);
+  },
+  currentUser(){
+    return Meteor.user();
+  },
+});
+
+Template.boardOrgName.helpers({
+  orgName() {
+    const org = Org.findOne(this.orgId);
+    return org && org.orgDisplayName;
+  },
+
+  orgViewPortWidth() {
+    const org = Org.findOne(this.orgId);
+    return ((org && org.orgDisplayName.length) || 1) * 12;
+  },
+});
+
+BlazeComponent.extendComponent({
+  onCreated() {
+    this.error = new ReactiveVar('');
+    this.loading = new ReactiveVar(false);
+    this.findOrgsOptions = new ReactiveVar({});
+
+    this.page = new ReactiveVar(1);
+    this.autorun(() => {
+      const limitTeams = this.page.get() * Number.MAX_SAFE_INTEGER;
+      this.subscribe('team', this.findOrgsOptions.get(), limitTeams, () => {});
+    });
+  },
+
+  onRendered() {
+    this.setLoading(false);
+  },
+
+  setError(error) {
+    this.error.set(error);
+  },
+
+  setLoading(w) {
+    this.loading.set(w);
+  },
+
+  isLoading() {
+    return this.loading.get();
+  },
+
+  events() {
+    return [
+      {
+        'keyup input'() {
+          this.setError('');
+        },
+        'click .js-manage-board-removeTeam': Popup.open('removeBoardTeam'),
+      },
+    ];
+  },
+}).register('boardTeamRow');
+
+Template.boardTeamRow.helpers({
+  teamData() {
+    const teamCollection = this.esSearch ? ESSearchResults : Team;
+    return teamCollection.findOne(this.teamId);
+  },
+  currentUser(){
+    return Meteor.user();
+  },
+});
+
+Template.boardTeamName.helpers({
+  teamName() {
+    const team = Team.findOne(this.teamId);
+    return team && team.teamDisplayName;
+  },
+
+  teamViewPortWidth() {
+    const team = Team.findOne(this.teamId);
+    return ((team && team.teamDisplayName.length) || 1) * 12;
+  },
+});
+
 BlazeComponent.extendComponent({
   onCreated() {
     this.error = new ReactiveVar('');

+ 85 - 0
models/boards.js

@@ -227,6 +227,56 @@ Boards.attachSchema(
       type: String,
       allowedValues: ['public', 'private'],
     },
+    orgs: {
+      /**
+       * the list of organizations that a board belongs to
+       */
+       type: [Object],
+       optional: true,
+    },
+    'orgs.$.orgId':{
+      /**
+       * The uniq ID of the organization
+       */
+       type: String,
+    },
+    'orgs.$.orgDisplayName':{
+      /**
+       * The display name of the organization
+       */
+       type: String,
+    },
+    'orgs.$.isActive': {
+      /**
+       * Is the organization active?
+       */
+      type: Boolean,
+    },
+    teams: {
+      /**
+       * the list of teams that a board belongs to
+       */
+       type: [Object],
+       optional: true,
+    },
+    'teams.$.teamId':{
+      /**
+       * The uniq ID of the team
+       */
+       type: String,
+    },
+    'teams.$.teamDisplayName':{
+      /**
+       * The display name of the team
+       */
+       type: String,
+    },
+    'teams.$.isActive': {
+      /**
+       * Is the team active?
+       */
+      type: Boolean,
+    },
     color: {
       /**
        * The color of the board.
@@ -695,6 +745,23 @@ Boards.helpers({
     return _.where(this.members, { isActive: true });
   },
 
+
+  activeOrgs() {
+    return _.where(this.orgs, { isActive: true });
+  },
+
+  // hasNotAnyOrg(){
+  //   return this.orgs === undefined || this.orgs.length <= 0;
+  // },
+
+  activeteams() {
+    return _.where(this.teams, { isActive: true });
+  },
+
+  // hasNotAnyTeam(){
+  //   return this.teams === undefined || this.teams.length <= 0;
+  // },
+
   activeAdmins() {
     return _.where(this.members, { isActive: true, isAdmin: true });
   },
@@ -1459,6 +1526,24 @@ if (Meteor.isServer) {
         } else throw new Meteor.Error('error-board-notAMember');
       } else throw new Meteor.Error('error-board-doesNotExist');
     },
+    setBoardOrgs(boardOrgsArray, currBoardId){
+      check(boardOrgsArray, Array);
+      check(currBoardId, String);
+      Boards.update(currBoardId, {
+        $set: {
+          orgs: boardOrgsArray,
+        },
+      });
+    },
+    setBoardTeams(boardTeamsArray, currBoardId){
+      check(boardTeamsArray, Array);
+      check(currBoardId, String);
+      Boards.update(currBoardId, {
+        $set: {
+          teams: boardTeamsArray,
+        },
+      });
+    },
   });
 }
 

+ 2 - 0
models/users.js

@@ -442,6 +442,8 @@ Users.safeFields = {
   'profile.fullname': 1,
   'profile.avatarUrl': 1,
   'profile.initials': 1,
+  orgs: 1,
+  teams: 1,
 };
 
 if (Meteor.isClient) {

+ 2 - 0
server/publications/boards.js

@@ -34,6 +34,8 @@ Meteor.publish('boards', function() {
         description: 1,
         color: 1,
         members: 1,
+        orgs: 1,
+        teams: 1,
         permission: 1,
         type: 1,
         sort: 1,