diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index 180a6e95b..b20fb9716 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -110,6 +110,8 @@ template(name="peopleGeneral") th {{_ 'active'}} th {{_ 'authentication-method'}} th {{_ 'import-usernames'}} + th {{_ 'organizations'}} + th {{_ 'teams'}} th +newUserRow each user in peopleList @@ -257,6 +259,14 @@ template(name="peopleRow") td {{ userData.importUsernamesString }} else td {{ userData.importUsernamesString }} + if userData.loginDisabled + td {{ userData.orgsUserBelongs }} + else + td {{ userData.orgsUserBelongs }} + if userData.loginDisabled + td {{ userData.teamsUserBelongs }} + else + td {{ userData.teamsUserBelongs }} td a.edit-user i.fa.fa-edit @@ -367,6 +377,27 @@ template(name="editUserPopup") option(value="{{value}}" selected) {{_ value}} else option(value="{{value}}") {{_ value}} + label + | {{_ 'organizations'}} + i.fa.fa-plus-square#addUserOrg + i.fa.fa-minus-square#removeUserOrg + select.js-orgs#jsOrgs + option(value="-1") {{_ 'organizations'}} : + each value in orgsDatas + option(value="{{value._id}}") {{_ value.orgDisplayName}} + input#jsUserOrgsInPut.js-userOrgs(type="text" value=user.orgsUserBelongs, disabled) + input#jsUserOrgIdsInPut.js-userOrgIds.hide(type="text" value=user.orgIdsUserBelongs) + label + | {{_ 'teams'}} + i.fa.fa-plus-square#addUserTeam + i.fa.fa-minus-square#removeUserTeam + select.js-teams#jsTeams + option(value="-1") {{_ 'teams'}} : + each value in teamsDatas + option(value="{{value._id}}") {{_ value.teamDisplayName}} + input#jsUserTeamsInPut.js-userteams(type="text" value=user.teamsUserBelongs, disabled) + input#jsUserTeamIdsInPut.js-userteamIds.hide(type="text" value=user.teamIdsUserBelongs) + hr label | {{_ 'password'}} @@ -468,6 +499,27 @@ template(name="newUserPopup") option(value="{{value}}" selected) {{_ value}} else option(value="{{value}}") {{_ value}} + label + | {{_ 'organizations'}} + i.fa.fa-plus-square#addUserOrgNewUser + i.fa.fa-minus-square#removeUserOrgNewUser + select.js-orgsNewUser#jsOrgsNewUser + option(value="-1") {{_ 'organizations'}} : + each value in orgsDatas + option(value="{{value._id}}") {{_ value.orgDisplayName}} + input#jsUserOrgsInPutNewUser.js-userOrgsNewUser(type="text" value=user.orgsUserBelongs, disabled) + input#jsUserOrgIdsInPutNewUser.js-userOrgIdsNewUser.hide(type="text" value=user.orgIdsUserBelongs) + label + | {{_ 'teams'}} + i.fa.fa-plus-square#addUserTeamNewUser + i.fa.fa-minus-square#removeUserTeamNewUser + select.js-teamsNewUser#jsTeamsNewUser + option(value="-1") {{_ 'teams'}} : + each value in teamsDatas + option(value="{{value._id}}") {{_ value.teamDisplayName}} + input#jsUserTeamsInPutNewUser.js-userteamsNewUser(type="text" value=user.teamsUserBelongs, disabled) + input#jsUserTeamIdsInPutNewUser.js-userteamIdsNewUser.hide(type="text" value=user.teamIdsUserBelongs) + hr label | {{_ 'password'}} diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js index 588a2b3a5..95b561cd7 100644 --- a/client/components/settings/peopleBody.js +++ b/client/components/settings/peopleBody.js @@ -1,6 +1,7 @@ const orgsPerPage = 25; const teamsPerPage = 25; const usersPerPage = 25; +let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user BlazeComponent.extendComponent({ mixins() { @@ -247,6 +248,12 @@ Template.editUserPopup.helpers({ authentications() { return Template.instance().authenticationMethods.get(); }, + orgsDatas() { + return Org.find({}, {sort: { createdAt: -1 }}); + }, + teamsDatas() { + return Team.find({}, {sort: { createdAt: -1 }}); + }, isSelected(match) { const userId = Template.instance().data.userId; const selected = Users.findOne(userId).authenticationMethod; @@ -314,10 +321,21 @@ Template.newUserPopup.helpers({ authentications() { return Template.instance().authenticationMethods.get(); }, + orgsDatas() { + return Org.find({}, {sort: { createdAt: -1 }}); + }, + teamsDatas() { + return Team.find({}, {sort: { createdAt: -1 }}); + }, isSelected(match) { const userId = Template.instance().data.userId; - const selected = Users.findOne(userId).authenticationMethod; - return selected === match; + if(userId){ + const selected = Users.findOne(userId).authenticationMethod; + return selected === match; + } + else{ + false; + } }, isLdap() { const userId = Template.instance().data.userId; @@ -502,15 +520,13 @@ Template.editUserPopup.events({ const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim(); const isActive = templateInstance.find('.js-profile-isactive').value.trim(); const email = templateInstance.find('.js-profile-email').value.trim(); - const verified = templateInstance - .find('.js-profile-email-verified') - .value.trim(); - const authentication = templateInstance - .find('.js-authenticationMethod') - .value.trim(); - const importUsernames = templateInstance - .find('.js-import-usernames') - .value.trim(); + const verified = templateInstance.find('.js-profile-email-verified').value.trim(); + const authentication = templateInstance.find('.js-authenticationMethod').value.trim(); + const importUsernames = templateInstance.find('.js-import-usernames').value.trim(); + const userOrgs = templateInstance.find('.js-userOrgs').value.trim(); + const userOrgsIds = templateInstance.find('.js-userOrgIds').value.trim(); + const userTeams = templateInstance.find('.js-userteams').value.trim(); + const userTeamsIds = templateInstance.find('.js-userteamIds').value.trim(); const isChangePassword = password.length > 0; const isChangeUserName = username !== user.username; @@ -535,6 +551,36 @@ Template.editUserPopup.events({ }, }); + let userTeamsList = userTeams.split(","); + let userTeamsIdsList = userTeamsIds.split(","); + let userTms = []; + for(let i = 0; i < userTeamsList.length; i++){ + userTms.push({ + "teamId": userTeamsIdsList[i], + "teamDisplayName": userTeamsList[i], + }) + } + Users.update(this.userId, { + $set:{ + teams: userTms + } + }); + + let userOrgsList = userOrgs.split(","); + let userOrgsIdsList = userOrgsIds.split(","); + let userOrganizations = []; + for(let i = 0; i < userOrgsList.length; i++){ + userOrganizations.push({ + "orgId": userOrgsIdsList[i], + "orgDisplayName": userOrgsList[i], + }) + } + Users.update(this.userId, { + $set:{ + orgs: userOrganizations + } + }); + if (isChangePassword) { Meteor.call('setPassword', password, this.userId); } @@ -602,8 +648,119 @@ Template.editUserPopup.events({ }); } else Popup.close(); }, + 'click #addUserOrg'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "addOrg"; + document.getElementById("jsOrgs").style.display = 'block'; + document.getElementById("jsTeams").style.display = 'none'; + }, + 'click #removeUserOrg'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "removeOrg"; + document.getElementById("jsOrgs").style.display = 'block'; + document.getElementById("jsTeams").style.display = 'none'; + }, + 'click #addUserTeam'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "addTeam"; + document.getElementById("jsTeams").style.display = 'block'; + document.getElementById("jsOrgs").style.display = 'none'; + }, + 'click #removeUserTeam'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "removeTeam"; + document.getElementById("jsTeams").style.display = 'block'; + document.getElementById("jsOrgs").style.display = 'none'; + }, + 'change #jsOrgs'(event) { + event.preventDefault(); + UpdateUserOrgsOrTeamsElement(); + }, + 'change #jsTeams'(event) { + event.preventDefault(); + UpdateUserOrgsOrTeamsElement(); + }, }); +UpdateUserOrgsOrTeamsElement = function(isNewUser = false){ + let selectedElt; + let selectedEltValue; + let selectedEltValueId; + let inputElt; + let inputEltId; + let lstInputValues = []; + let lstInputValuesIds = []; + let index; + let indexId; + switch(userOrgsTeamsAction) + { + case "addOrg": + case "removeOrg": + inputElt = !isNewUser ? document.getElementById("jsUserOrgsInPut") : document.getElementById("jsUserOrgsInPutNewUser"); + inputEltId = !isNewUser ? document.getElementById("jsUserOrgIdsInPut") : document.getElementById("jsUserOrgIdsInPutNewUser"); + selectedElt = !isNewUser ? document.getElementById("jsOrgs") : document.getElementById("jsOrgsNewUser"); + break; + case "addTeam": + case "removeTeam": + inputElt = !isNewUser ? document.getElementById("jsUserTeamsInPut") : document.getElementById("jsUserTeamsInPutNewUser"); + inputEltId = !isNewUser ? document.getElementById("jsUserTeamIdsInPut") : document.getElementById("jsUserTeamIdsInPutNewUser"); + selectedElt = !isNewUser ? document.getElementById("jsTeams") : document.getElementById("jsTeamsNewUser"); + break; + default: + break; + } + selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text; + selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value; + lstInputValues = inputElt.value.trim().split(","); + if(lstInputValues.length == 1 && lstInputValues[0] == ''){ + lstInputValues = []; + } + lstInputValuesIds = inputEltId.value.trim().split(","); + if(lstInputValuesIds.length == 1 && lstInputValuesIds[0] == ''){ + lstInputValuesIds = []; + } + index = lstInputValues.indexOf(selectedEltValue); + indexId = lstInputValuesIds.indexOf(selectedEltValue); + if(userOrgsTeamsAction == "addOrg" || userOrgsTeamsAction == "addTeam"){ + if(index <= -1 && selectedEltValueId != "-1"){ + lstInputValues.push(selectedEltValue); + } + + if(indexId <= -1 && selectedEltValueId != "-1"){ + lstInputValuesIds.push(selectedEltValueId); + } + } + else{ + if(index > -1 && selectedEltValueId != "-1"){ + lstInputValues.splice(index, 1); + } + + if(indexId > -1 && selectedEltValueId != "-1"){ + lstInputValuesIds.splice(indexId, 1); + } + } + + if(lstInputValues.length > 0){ + inputElt.value = lstInputValues.join(","); + } + else{ + inputElt.value = ""; + } + + if(lstInputValuesIds.length > 0){ + inputEltId.value = lstInputValuesIds.join(","); + } + else{ + inputEltId.value = ""; + } + selectedElt.value = "-1"; + selectedElt.style.display = "none"; +} + Template.newOrgPopup.events({ submit(event, templateInstance) { event.preventDefault(); @@ -665,6 +822,30 @@ Template.newUserPopup.events({ const importUsernames = Users.parseImportUsernames( templateInstance.find('.js-import-usernames').value, ); + const userOrgs = templateInstance.find('.js-userOrgsNewUser').value.trim(); + const userOrgsIds = templateInstance.find('.js-userOrgIdsNewUser').value.trim(); + const userTeams = templateInstance.find('.js-userteamsNewUser').value.trim(); + const userTeamsIds = templateInstance.find('.js-userteamIdsNewUser').value.trim(); + + let userTeamsList = userTeams.split(","); + let userTeamsIdsList = userTeamsIds.split(","); + let userTms = []; + for(let i = 0; i < userTeamsList.length; i++){ + userTms.push({ + "teamId": userTeamsIdsList[i], + "teamDisplayName": userTeamsList[i], + }) + } + + let userOrgsList = userOrgs.split(","); + let userOrgsIdsList = userOrgsIds.split(","); + let userOrganizations = []; + for(let i = 0; i < userOrgsList.length; i++){ + userOrganizations.push({ + "orgId": userOrgsIdsList[i], + "orgDisplayName": userOrgsList[i], + }) + } Meteor.call( 'setCreateUser', @@ -676,6 +857,8 @@ Template.newUserPopup.events({ isActive, email.toLowerCase(), importUsernames, + userOrganizations, + userTms, function(error) { const usernameMessageElement = templateInstance.$('.username-taken'); const emailMessageElement = templateInstance.$('.email-taken'); @@ -697,6 +880,42 @@ Template.newUserPopup.events({ ); Popup.close(); }, + 'click #addUserOrgNewUser'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "addOrg"; + document.getElementById("jsOrgsNewUser").style.display = 'block'; + document.getElementById("jsTeamsNewUser").style.display = 'none'; + }, + 'click #removeUserOrgNewUser'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "removeOrg"; + document.getElementById("jsOrgsNewUser").style.display = 'block'; + document.getElementById("jsTeamsNewUser").style.display = 'none'; + }, + 'click #addUserTeamNewUser'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "addTeam"; + document.getElementById("jsTeamsNewUser").style.display = 'block'; + document.getElementById("jsOrgsNewUser").style.display = 'none'; + }, + 'click #removeUserTeamNewUser'(event) { + event.preventDefault(); + + userOrgsTeamsAction = "removeTeam"; + document.getElementById("jsTeamsNewUser").style.display = 'block'; + document.getElementById("jsOrgsNewUser").style.display = 'none'; + }, + 'change #jsOrgsNewUser'(event) { + event.preventDefault(); + UpdateUserOrgsOrTeamsElement(true); + }, + 'change #jsTeamsNewUser'(event) { + event.preventDefault(); + UpdateUserOrgsOrTeamsElement(true); + }, }); Template.settingsUserPopup.events({ diff --git a/client/components/settings/peopleBody.styl b/client/components/settings/peopleBody.styl index 028db164c..8ef33c9e0 100644 --- a/client/components/settings/peopleBody.styl +++ b/client/components/settings/peopleBody.styl @@ -49,3 +49,9 @@ table .more-settings-user,.more-settings-team,.more-settings-org margin-left: 10px; + +.js-orgs,.js-orgsNewUser + display: none; + +.js-teams,.js-teamsNewUser + display: none; diff --git a/models/users.js b/models/users.js index d2f2d6ef9..58cad3bf7 100644 --- a/models/users.js +++ b/models/users.js @@ -38,6 +38,44 @@ Users.attachSchema( } }, }, + orgs: { + /** + * the list of organizations that a user 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, + }, + teams: { + /** + * the list of teams that a user 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, + }, emails: { /** * the list of emails attached to a user @@ -329,13 +367,7 @@ Users.attachSchema( }, 'sessionData.totalHits': { /** - * Total hits from last search - */ - type: Number, - optional: true, - }, - 'sessionData.lastHit': { - /** + * Total hits from last searchquery['members.userId'] = Meteor.userId(); * last hit that was returned */ type: Number, @@ -464,7 +496,30 @@ Users.helpers({ } return ''; }, - + orgsUserBelongs() { + if (this.orgs) { + return this.orgs.map(function(org){return org.orgDisplayName}).join(','); + } + return ''; + }, + orgIdsUserBelongs() { + if (this.orgs) { + return this.orgs.map(function(org){return org.orgId}).join(','); + } + return ''; + }, + teamsUserBelongs() { + if (this.teams) { + return this.teams.map(function(team){ return team.teamDisplayName}).join(','); + } + return ''; + }, + teamIdsUserBelongs() { + if (this.teams) { + return this.teams.map(function(team){ return team.teamId}).join(','); + } + return ''; + }, boards() { return Boards.find( { @@ -894,6 +949,8 @@ if (Meteor.isServer) { isActive, email, importUsernames, + userOrgsArray, + userTeamsArray, ) { if (Meteor.user() && Meteor.user().isAdmin) { check(fullname, String); @@ -904,6 +961,8 @@ if (Meteor.isServer) { check(isActive, String); check(email, String); check(importUsernames, Array); + check(userOrgsArray, Array); + check(userTeamsArray, Array); const nUsersWithUsername = Users.find({ username, @@ -935,6 +994,8 @@ if (Meteor.isServer) { 'profile.fullname': fullname, importUsernames, 'profile.initials': initials, + orgs: userOrgsArray, + teams: userTeamsArray, }, }); } diff --git a/server/publications/people.js b/server/publications/people.js index b2318287c..af8dfcea5 100644 --- a/server/publications/people.js +++ b/server/publications/people.js @@ -21,6 +21,8 @@ Meteor.publish('people', function(query, limit) { loginDisabled: 1, authenticationMethod: 1, importUsernames: 1, + orgs: 1, + teams: 1, }, }); }