diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index 180a6e95b..d3dfeb73d 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -268,7 +268,7 @@ template(name="editOrgPopup") form label.hide.orgId(type="text" value=org._id) label - | {{_ 'orgDisplayName'}} + | {{_ 'displayName'}} input.js-orgDisplayName(type="text" value=org.orgDisplayName required) span.error.hide.orgname-taken | {{_ 'error-orgname-taken'}} @@ -478,27 +478,39 @@ template(name="newUserPopup") template(name="settingsOrgPopup") ul.pop-over-list li - a.impersonate-org - i.fa.fa-user - | {{_ 'impersonate-org'}} - // Delete is not enabled yet, because it does leave empty user avatars - // to boards: boards members, card members and assignees have - // empty users. See: - // - wekan/client/components/settings/peopleBody.jade deleteButton - // - wekan/client/components/settings/peopleBody.js deleteButton - // - wekan/client/components/sidebar/sidebar.js Popup.afterConfirm('removeMember' - // that does now remove member from board, card members and assignees correctly, - // but that should be used to remove user from all boards similarly - // - wekan/models/users.js Delete is not enabled - //li - // br - // br - // hr - //li - // form - // label.hide.userId(type="text" value=user._id) - // div.buttonsContainer - // input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}") + br + br + br + br + hr + li + form + label.hide.orgId(type="text" value=org._id) + div.buttonsContainer + input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}") + // It's not yet possible to impersonate organization. Only impersonate user, + // because that changes current user ID. What would it mean in practice + // to impersonate organization? + // li + // a.impersonate-org + // i.fa.fa-user + // | {{_ 'impersonate-org'}} + // + // + +template(name="settingsTeamPopup") + ul.pop-over-list + li + br + br + br + br + hr + li + form + label.hide.teamId(type="text" value=team._id) + div.buttonsContainer + input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}") template(name="settingsUserPopup") ul.pop-over-list @@ -506,6 +518,15 @@ template(name="settingsUserPopup") a.impersonate-user i.fa.fa-user | {{_ 'impersonate-user'}} + li + br + br + //hr + //li + // form + // label.hide.userId(type="text" value=user._id) + // div.buttonsContainer + // input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}") // Delete is not enabled yet, because it does leave empty user avatars // to boards: boards members, card members and assignees have // empty users. See: @@ -515,12 +536,3 @@ template(name="settingsUserPopup") // that does now remove member from board, card members and assignees correctly, // but that should be used to remove user from all boards similarly // - wekan/models/users.js Delete is not enabled - //li - // br - // br - // hr - //li - // form - // label.hide.userId(type="text" value=user._id) - // div.buttonsContainer - // input#deleteButton.card-details-red.right.wide(type="button" value="{{_ 'delete'}}") diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js index 588a2b3a5..3544ecdee 100644 --- a/client/components/settings/peopleBody.js +++ b/client/components/settings/peopleBody.js @@ -101,9 +101,7 @@ BlazeComponent.extendComponent({ ]; }, filterPeople() { - const value = $('#searchInput') - .first() - .val(); + const value = $('#searchInput').first().val(); if (value === '') { this.findUsersOptions.set({}); } else { @@ -203,7 +201,7 @@ Template.peopleRow.helpers({ }, }); -Template.editUserPopup.onCreated(function() { +Template.editUserPopup.onCreated(function () { this.authenticationMethods = new ReactiveVar([]); this.errorMessage = new ReactiveVar(''); @@ -215,8 +213,8 @@ Template.editUserPopup.onCreated(function() { { value: 'password' }, // Gets only the authentication methods availables ...Object.entries(result) - .filter(e => e[1]) - .map(e => ({ value: e[0] })), + .filter((e) => e[1]) + .map((e) => ({ value: e[0] })), ]); } }); @@ -262,15 +260,15 @@ Template.editUserPopup.helpers({ }, }); -Template.newOrgPopup.onCreated(function() { +Template.newOrgPopup.onCreated(function () { this.errorMessage = new ReactiveVar(''); }); -Template.newTeamPopup.onCreated(function() { +Template.newTeamPopup.onCreated(function () { this.errorMessage = new ReactiveVar(''); }); -Template.newUserPopup.onCreated(function() { +Template.newUserPopup.onCreated(function () { this.authenticationMethods = new ReactiveVar([]); this.errorMessage = new ReactiveVar(''); @@ -282,8 +280,8 @@ Template.newUserPopup.onCreated(function() { { value: 'password' }, // Gets only the authentication methods availables ...Object.entries(result) - .filter(e => e[1]) - .map(e => ({ value: e[0] })), + .filter((e) => e[1]) + .map((e) => ({ value: e[0] })), ]); } }); @@ -415,7 +413,8 @@ Template.editOrgPopup.events({ const orgDesc = templateInstance.find('.js-orgDesc').value.trim(); const orgShortName = templateInstance.find('.js-orgShortName').value.trim(); const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim(); - const orgIsActive = templateInstance.find('.js-org-isactive').value.trim() == 'true'; + const orgIsActive = + templateInstance.find('.js-org-isactive').value.trim() == 'true'; const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName; const isChangeOrgDesc = orgDesc !== org.orgDesc; @@ -423,26 +422,24 @@ Template.editOrgPopup.events({ const isChangeOrgWebsite = orgWebsite !== org.orgWebsite; const isChangeOrgIsActive = orgIsActive !== org.orgIsActive; - if(isChangeOrgDisplayName || isChangeOrgDesc || isChangeOrgShortName || isChangeOrgWebsite || isChangeOrgIsActive){ - Meteor.call('setOrgAllFields', org, orgDisplayName, orgDesc, orgShortName, orgWebsite, orgIsActive); + if ( + isChangeOrgDisplayName || + isChangeOrgDesc || + isChangeOrgShortName || + isChangeOrgWebsite || + isChangeOrgIsActive + ) { + Meteor.call( + 'setOrgAllFields', + org, + orgDisplayName, + orgDesc, + orgShortName, + orgWebsite, + orgIsActive, + ); } - // if (isChangeOrgDisplayName) { - // Meteor.call('setOrgDisplayName', org, orgDisplayName); - // } - - // if (isChangeOrgDesc) { - // Meteor.call('setOrgDesc', org, orgDesc); - // } - - // if (isChangeOrgShortName) { - // Meteor.call('setOrgShortName', org, orgShortName); - // } - - // if (isChangeOrgIsActive) { - // Meteor.call('setOrgIsActive', org, orgIsActive); - // } - Popup.close(); }, }); @@ -460,7 +457,8 @@ Template.editTeamPopup.events({ .find('.js-teamShortName') .value.trim(); const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim(); - const teamIsActive = templateInstance.find('.js-team-isactive').value.trim() == 'true'; + const teamIsActive = + templateInstance.find('.js-team-isactive').value.trim() == 'true'; const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName; const isChangeTeamDesc = teamDesc !== team.teamDesc; @@ -468,24 +466,23 @@ Template.editTeamPopup.events({ const isChangeTeamWebsite = teamWebsite !== team.teamWebsite; const isChangeTeamIsActive = teamIsActive !== team.teamIsActive; - if(isChangeTeamDisplayName || isChangeTeamDesc || isChangeTeamShortName || isChangeTeamWebsite || isChangeTeamIsActive){ - Meteor.call('setTeamAllFields', team, teamDisplayName, teamDesc, teamShortName, teamWebsite, teamIsActive); + if ( + isChangeTeamDisplayName || + isChangeTeamDesc || + isChangeTeamShortName || + isChangeTeamWebsite || + isChangeTeamIsActive + ) { + Meteor.call( + 'setTeamAllFields', + team, + teamDisplayName, + teamDesc, + teamShortName, + teamWebsite, + teamIsActive, + ); } - // if (isChangeTeamDisplayName) { - // Meteor.call('setTeamDisplayName', team, teamDisplayName); - // } - - // if (isChangeTeamDesc) { - // Meteor.call('setTeamDesc', team, teamDesc); - // } - - // if (isChangeTeamShortName) { - // Meteor.call('setTeamShortName', team, teamShortName); - // } - - // if (isChangeTeamIsActive) { - // Meteor.call('setTeamIsActive', team, teamIsActive); - // } Popup.close(); }, @@ -553,7 +550,7 @@ Template.editUserPopup.events({ username, email.toLowerCase(), this.userId, - function(error) { + function (error) { const usernameMessageElement = templateInstance.$('.username-taken'); const emailMessageElement = templateInstance.$('.email-taken'); if (error) { @@ -573,7 +570,7 @@ Template.editUserPopup.events({ }, ); } else if (isChangeUserName) { - Meteor.call('setUsername', username, this.userId, function(error) { + Meteor.call('setUsername', username, this.userId, function (error) { const usernameMessageElement = templateInstance.$('.username-taken'); if (error) { const errorElement = error.error; @@ -586,20 +583,23 @@ Template.editUserPopup.events({ } }); } else if (isChangeEmail) { - Meteor.call('setEmail', email.toLowerCase(), this.userId, function( - error, - ) { - const emailMessageElement = templateInstance.$('.email-taken'); - if (error) { - const errorElement = error.error; - if (errorElement === 'email-already-taken') { - emailMessageElement.show(); + Meteor.call( + 'setEmail', + email.toLowerCase(), + this.userId, + function (error) { + const emailMessageElement = templateInstance.$('.email-taken'); + if (error) { + const errorElement = error.error; + if (errorElement === 'email-already-taken') { + emailMessageElement.show(); + } + } else { + emailMessageElement.hide(); + Popup.close(); } - } else { - emailMessageElement.hide(); - Popup.close(); - } - }); + }, + ); } else Popup.close(); }, }); @@ -613,7 +613,8 @@ Template.newOrgPopup.events({ const orgDesc = templateInstance.find('.js-orgDesc').value.trim(); const orgShortName = templateInstance.find('.js-orgShortName').value.trim(); const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim(); - const orgIsActive = templateInstance.find('.js-org-isactive').value.trim() == 'true'; + const orgIsActive = + templateInstance.find('.js-org-isactive').value.trim() == 'true'; Meteor.call( 'setCreateOrg', @@ -638,7 +639,8 @@ Template.newTeamPopup.events({ .find('.js-teamShortName') .value.trim(); const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim(); - const teamIsActive = templateInstance.find('.js-team-isactive').value.trim() == 'true'; + const teamIsActive = + templateInstance.find('.js-team-isactive').value.trim() == 'true'; Meteor.call( 'setCreateTeam', @@ -676,7 +678,7 @@ Template.newUserPopup.events({ isActive, email.toLowerCase(), importUsernames, - function(error) { + function (error) { const usernameMessageElement = templateInstance.$('.username-taken'); const emailMessageElement = templateInstance.$('.email-taken'); if (error) { @@ -699,11 +701,27 @@ Template.newUserPopup.events({ }, }); +Template.settingsOrgPopup.events({ + 'click #deleteButton'(event) { + event.preventDefault(); + Org.remove(this.orgId); + Popup.close(); + }, +}); + +Template.settingsTeamPopup.events({ + 'click #deleteButton'(event) { + event.preventDefault(); + Team.remove(this.teamId); + Popup.close(); + }, +}); + Template.settingsUserPopup.events({ 'click .impersonate-user'(event) { event.preventDefault(); - Meteor.call('impersonate', this.userId, err => { + Meteor.call('impersonate', this.userId, (err) => { if (!err) { FlowRouter.go('/'); Meteor.connection.setUserId(this.userId); @@ -723,21 +741,6 @@ Template.settingsUserPopup.events({ // but that should be used to remove user from all boards similarly // - wekan/models/users.js Delete is not enabled // - //console.log('user id: ' + this.userId); - //Popup.afterConfirm('userDelete', function(event) { - //Boards.find({ members: this.userId }).forEach(board => { - // console.log('board id: ' + board._id); - //Cards.find({ boardId: board._id, members: this.userId }).forEach(card => { - // card.unassignMember(this.userId); - //}); - //Cards.find({ boardId: board._id, members: this.userId }).forEach(card => { - // card.unassignMember(this.userId); - //}); - //Cards.find({ boardId: board._id, assignees: this.userId }).forEach(card => { - // card.unassignAssignee(this.userId); - //}); - //Boards.findOne({ boardId: board._id }).removeMember(this.userId); - //}); //Users.remove(this.userId); */ Popup.close(); diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 6c73383a9..0577dbced 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -443,6 +443,8 @@ "set-color-list": "Set Color", "listActionPopup-title": "List Actions", "settingsUserPopup-title": "User Settings", + "settingsTeamPopup-title": "Team Settings", + "settingsOrgPopup-title": "Organization Settings", "swimlaneActionPopup-title": "Swimlane Actions", "swimlaneAddPopup-title": "Add a Swimlane below", "listImportCardPopup-title": "Import a Trello card", @@ -839,6 +841,8 @@ "act-duenow": "was reminding the current due (__timeValue__) of __card__ is now", "act-atUserComment": "You were mentioned in [__board__] __list__/__card__", "delete-user-confirm-popup": "Are you sure you want to delete this account? There is no undo.", + "delete-team-confirm-popup": "Are you sure you want to delete this team? There is no undo.", + "delete-org-confirm-popup": "Are you sure you want to delete this organization? There is no undo.", "accounts-allowUserDelete": "Allow users to self delete their account", "hide-minicard-label-text": "Hide minicard label text", "show-desktop-drag-handles": "Show desktop drag handles", diff --git a/models/org.js b/models/org.js index 17a7b4388..a4ffffd80 100644 --- a/models/org.js +++ b/models/org.js @@ -76,6 +76,43 @@ Org.attachSchema( ); if (Meteor.isServer) { + Org.allow({ + insert(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + update(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + remove(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + fetch: [], + }); + Meteor.methods({ setCreateOrg( orgDisplayName, @@ -111,7 +148,7 @@ if (Meteor.isServer) { check(org, Object); check(orgDisplayName, String); Org.update(org, { - $set: { orgDisplayName: orgDisplayNameorgShortName}, + $set: { orgDisplayName: orgDisplayNameorgShortName }, }); } }, @@ -146,7 +183,14 @@ if (Meteor.isServer) { } }, - setOrgAllFields(org, orgDisplayName, orgDesc, orgShortName, orgWebsite, orgIsActive) { + setOrgAllFields( + org, + orgDisplayName, + orgDesc, + orgShortName, + orgWebsite, + orgIsActive, + ) { if (Meteor.user() && Meteor.user().isAdmin) { check(org, Object); check(orgDisplayName, String); @@ -155,7 +199,13 @@ if (Meteor.isServer) { check(orgWebsite, String); check(orgIsActive, Boolean); Org.update(org, { - $set: { orgDisplayName : orgDisplayName, orgDesc : orgDesc, orgShortName : orgShortName, orgWebsite : orgWebsite, orgIsActive: orgIsActive }, + $set: { + orgDisplayName: orgDisplayName, + orgDesc: orgDesc, + orgShortName: orgShortName, + orgWebsite: orgWebsite, + orgIsActive: orgIsActive, + }, }); } }, diff --git a/models/team.js b/models/team.js index 949728f84..39733dd39 100644 --- a/models/team.js +++ b/models/team.js @@ -75,6 +75,43 @@ Team.attachSchema( ); if (Meteor.isServer) { + Team.allow({ + insert(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + update(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + remove(userId, doc) { + const user = Users.findOne({ + _id: userId, + }); + if ((user && user.isAdmin) || (Meteor.user() && Meteor.user().isAdmin)) + return true; + if (!user) { + return false; + } + return doc._id === userId; + }, + fetch: [], + }); + Meteor.methods({ setCreateTeam( teamDisplayName, @@ -145,7 +182,14 @@ if (Meteor.isServer) { } }, - setTeamAllFields(team, teamDisplayName, teamDesc, teamShortName, teamWebsite, teamIsActive) { + setTeamAllFields( + team, + teamDisplayName, + teamDesc, + teamShortName, + teamWebsite, + teamIsActive, + ) { if (Meteor.user() && Meteor.user().isAdmin) { check(team, Object); check(teamDisplayName, String); @@ -154,7 +198,13 @@ if (Meteor.isServer) { check(teamWebsite, String); check(teamIsActive, Boolean); Team.update(team, { - $set: { teamDisplayName: teamDisplayName, teamDesc: teamDesc, teamShortName: teamShortName, teamWebsite: teamWebsite, teamIsActive: teamIsActive }, + $set: { + teamDisplayName: teamDisplayName, + teamDesc: teamDesc, + teamShortName: teamShortName, + teamWebsite: teamWebsite, + teamIsActive: teamIsActive, + }, }); } },