From 1bc07b1b4a3e8cd1a177f3f1776ed8e189bc627a Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Thu, 11 Feb 2021 19:07:34 +0200 Subject: [PATCH] Teams/Organizations: Added more code to Admin Panel for saving and editing. In Progress. Thanks to xet7 ! Related #802 --- client/components/cards/cardDate.js | 19 ++- client/components/settings/peopleBody.jade | 78 +++++------ client/components/settings/peopleBody.js | 151 ++++++++++++++++---- client/lib/datepicker.js | 19 ++- i18n/en.i18n.json | 2 + models/cards.js | 6 +- models/org.js | 154 ++++++++++----------- models/team.js | 94 ++++++++++--- 8 files changed, 345 insertions(+), 178 deletions(-) diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index d247fe6d8..250ad0aa7 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -1,6 +1,9 @@ // Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours function adjustedTimeFormat() { - return moment.localeData().longDateFormat('LT').replace(/HH/i, 'H'); + return moment + .localeData() + .longDateFormat('LT') + .replace(/HH/i, 'H'); } // Edit received, start, due & end dates @@ -64,7 +67,11 @@ BlazeComponent.extendComponent({ }, 'keyup .js-time-field'() { // parse for localized time format in strict mode - const dateMoment = moment(this.find('#time').value, adjustedTimeFormat(), true); + const dateMoment = moment( + this.find('#time').value, + adjustedTimeFormat(), + true, + ); if (dateMoment.isValid()) { this.error.set(''); } @@ -79,7 +86,11 @@ BlazeComponent.extendComponent({ const newTime = moment(time, adjustedTimeFormat(), true); const newDate = moment(evt.target.date.value, 'L', true); const dateString = `${evt.target.date.value} ${time}`; - const newCompleteDate = moment(dateString, 'L ' + adjustedTimeFormat(), true); + const newCompleteDate = moment( + dateString, + 'L ' + adjustedTimeFormat(), + true, + ); if (!newTime.isValid()) { this.error.set('invalid-time'); evt.target.time.focus(); @@ -92,7 +103,7 @@ BlazeComponent.extendComponent({ this._storeDate(newCompleteDate.toDate()); Popup.close(); } else { - if (!this.error){ + if (!this.error) { this.error.set('invalid'); } } diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index 81a48c774..b4c41c406 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -74,7 +74,6 @@ template(name="orgGeneral") th {{_ 'description'}} th {{_ 'shortName'}} th {{_ 'website'}} - th {{_ 'teams'}} th {{_ 'createdAt'}} th {{_ 'active'}} th @@ -134,25 +133,21 @@ template(name="newUserRow") template(name="orgRow") tr if orgData.loginDisabled - td {{ orgData.displayName }} + td {{ orgData.orgDisplayName }} else - td {{ orgData.displayName }} + td {{ orgData.orgDisplayName }} if orgData.loginDisabled td {{ orgData.orgDesc }} else - td {{ orgData.desc }} + td {{ orgData.orgDesc }} if orgData.loginDisabled - td {{ orgData.name }} + td {{ orgData.orgName }} else - td {{ orgData.name }} + td {{ orgData.orgName }} if orgData.loginDisabled - td {{ orgData.website }} + td {{ orgData.orgWebsite }} else - td {{ orgData.website }} - if orgData.loginDisabled - td {{ orgData.teams }} - else - td {{ orgData.teams }} + td {{ orgData.orgWebsite }} if orgData.loginDisabled td {{ moment orgData.createdAt 'LLL' }} else @@ -172,21 +167,21 @@ template(name="orgRow") template(name="teamRow") tr if teamData.loginDisabled - td {{ teamData.displayName }} + td {{ teamData.teamDisplayName }} else - td {{ teamData.displayName }} + td {{ teamData.teamDisplayName }} if teamData.loginDisabled - td {{ teamData.desc }} + td {{ teamData.teamDesc }} else - td {{ teamData.desc }} + td {{ teamData.teamDesc }} if teamData.loginDisabled - td {{ teamData.dame }} + td {{ teamData.teamName }} else - td {{ teamData.name }} + td {{ teamData.teamName }} if teamData.loginDisabled - td {{ teamData.website }} + td {{ teamData.teamWebsite }} else - td {{ teamData.website }} + td {{ teamData.teamWebsite }} if orgData.loginDisabled td {{ moment teamData.createdAt 'LLL' }} else @@ -273,19 +268,19 @@ template(name="editOrgPopup") form label.hide.orgId(type="text" value=org._id) label - | {{_ 'orgDisplayName'}} + | {{_ 'displayName'}} input.js-orgDisplayName(type="text" value=org.displayName required) span.error.hide.orgname-taken | {{_ 'error-orgname-taken'}} label - | {{_ 'orgDesc'}} - input.js-orgDesc(type="text" value=org.desc required) + | {{_ 'description'}} + input.js-orgDesc(type="text" value=org.orgDesc required) label - | {{_ 'orgName'}} - input.js-orgName(type="text" value=org.name required) + | {{_ 'shortName'}} + input.js-orgShortName(type="text" value=org.orgShortName required) label - | {{_ 'orgWebsite'}} - input.js-orgWebsite(type="text" value=org.website required) + | {{_ 'website'}} + input.js-orgWebsite(type="text" value=org.orgWebsite required) label | {{_ 'active'}} select.select-active.js-org-isactive @@ -300,18 +295,18 @@ template(name="editTeamPopup") label.hide.teamId(type="text" value=team._id) label | {{_ 'displayName'}} - input.js-teamDisplayName(type="text" value=team.displayName required) + input.js-teamDisplayName(type="text" value=team.teamDisplayName required) span.error.hide.teamname-taken | {{_ 'error-teamname-taken'}} label | {{_ 'description'}} - input.js-orgDesc(type="text" value=org.desc required) + input.js-teamDesc(type="text" value=team.teamDesc required) label - | {{_ 'name'}} - input.js-orgName(type="text" value=org.name required) + | {{_ 'shortName'}} + input.js-teamShortName(type="text" value=team.teamShortName required) label | {{_ 'website'}} - input.js-orgWebsite(type="text" value=org.website required) + input.js-teamWebsite(type="text" value=team.teamWebsite required) label | {{_ 'active'}} select.select-active.js-team-isactive @@ -384,22 +379,19 @@ template(name="newOrgPopup") //label.hide.userId(type="text" value=user._id) label | {{_ 'displayName'}} - input.js-displayName(type="text" value="" required) + input.js-orgDisplayName(type="text" value="" required) label | {{_ 'description'}} - input.js-desc(type="text" value="" required) + input.js-orgDesc(type="text" value="" required) label | {{_ 'shortName'}} - input.js-name(type="text" value="") - label - | {{_ 'teams'}} - input.js-teams(type="text" value="") + input.js-orgName(type="text" value="" required) label | {{_ 'website'}} - input.js-website(type="text" value="") + input.js-orgWebsite(type="text" value="" required) label | {{_ 'active'}} - select.select-active.js-profile-isactive + select.select-active.js-org-isactive option(value="false" selected="selected") {{_ 'yes'}} option(value="true") {{_ 'no'}} hr @@ -417,13 +409,13 @@ template(name="newTeamPopup") input.js-teamDesc(type="text" value="" required) label | {{_ 'shortName'}} - input.js-teamName(type="text" value="") + input.js-teamName(type="text" value="" required) label | {{_ 'website'}} - input.js-teamWebsite(type="text" value="") + input.js-teamWebsite(type="text" value="" required) label | {{_ 'active'}} - select.select-active.js-profile-isactive + select.select-active.js-team-isactive option(value="false" selected="selected") {{_ 'yes'}} option(value="true") {{_ 'no'}} hr diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js index e6629867b..f2846f440 100644 --- a/client/components/settings/peopleBody.js +++ b/client/components/settings/peopleBody.js @@ -404,6 +404,88 @@ BlazeComponent.extendComponent({ }, }).register('newUserRow'); +Template.editOrgPopup.events({ + submit(event, templateInstance) { + event.preventDefault(); + const org = Orgs.findOne(this.orgId); + + const orgDisplayName = templateInstance + .find('.js-orgDisplayName') + .value.trim(); + 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(); + + const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName; + const isChangeOrgDesc = orgDesc !== org.orgDesc; + const isChangeOrgShortName = orgShortName !== org.orgShortName; + const isChangeOrgWebsite = orgWebsite !== org.orgWebsite; + const isChangeOrgIsActive = orgIsActive !== org.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(); + }, +}); + +Template.editTeamPopup.events({ + submit(event, templateInstance) { + event.preventDefault(); + const team = Teams.findOne(this.teamId); + + const teamDisplayName = templateInstance + .find('.js-teamDisplayName') + .value.trim(); + const teamDesc = templateInstance.find('.js-teamDesc').value.trim(); + const teamShortName = templateInstance + .find('.js-teamShortName') + .value.trim(); + const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim(); + const teamIsActive = templateInstance + .find('.js-team-isactive') + .value.trim(); + + const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName; + const isChangeTeamDesc = teamDesc !== team.teamDesc; + const isChangeTeamShortName = teamShortName !== team.teamShortName; + const isChangeTeamWebsite = teamWebsite !== team.teamWebsite; + const isChangeTeamIsActive = teamIsActive !== team.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(); + }, +}); + Template.editUserPopup.events({ submit(event, templateInstance) { event.preventDefault(); @@ -520,39 +602,48 @@ Template.editUserPopup.events({ Template.newOrgPopup.events({ submit(event, templateInstance) { event.preventDefault(); - const displayName = templateInstance.find('.js-displayName').value.trim(); - const desc = templateInstance.find('.js-desc').value.trim(); - const name = templateInstance.find('.js-name').value.trim(); - const teams = templateInstance.find('.js-teams').value.trim(); - const website = templateInstance.find('.js-website').value.trim(); - const isActive = templateInstance.find('.js-profile-isactive').value.trim(); + const orgDisplayName = templateInstance + .find('.js-orgDisplayName') + .value.trim(); + 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(); Meteor.call( 'setCreateOrg', - displayName, - desc, - name, - teams, - website, - isActive, - email.toLowerCase(), - function(error) { - const nameMessageElement = templateInstance.$('.name-taken'); - if (error) { - const errorElement = error.error; - if (errorElement === 'name-already-taken') { - nameMessageElement.show(); - emailMessageElement.hide(); - } else if (errorElement === 'email-already-taken') { - usernameMessageElement.hide(); - emailMessageElement.show(); - } - } else { - usernameMessageElement.hide(); - emailMessageElement.hide(); - Popup.close(); - } - }, + orgDisplayName, + orgDesc, + orgShortName, + orgWebsite, + orgIsActive, + ); + Popup.close(); + }, +}); + +Template.newTeamPopup.events({ + submit(event, templateInstance) { + event.preventDefault(); + const teamDisplayName = templateInstance + .find('.js-teamDisplayName') + .value.trim(); + const teamDesc = templateInstance.find('.js-teamDesc').value.trim(); + const teamShortName = templateInstance + .find('.js-teamShortName') + .value.trim(); + const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim(); + const teamIsActive = templateInstance + .find('.js-team-isactive') + .value.trim(); + + Meteor.call( + 'setCreateTeam', + teamDisplayName, + teamDesc, + teamShortName, + teamWebsite, + teamIsActive, ); Popup.close(); }, diff --git a/client/lib/datepicker.js b/client/lib/datepicker.js index de3986157..5b62a27e5 100644 --- a/client/lib/datepicker.js +++ b/client/lib/datepicker.js @@ -1,6 +1,9 @@ // Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours function adjustedTimeFormat() { - return moment.localeData().longDateFormat('LT').replace(/HH/i, 'H'); + return moment + .localeData() + .longDateFormat('LT') + .replace(/HH/i, 'H'); } DatePicker = BlazeComponent.extendComponent({ @@ -82,7 +85,11 @@ DatePicker = BlazeComponent.extendComponent({ }, 'keyup .js-time-field'() { // parse for localized time format in strict mode - const dateMoment = moment(this.find('#time').value, adjustedTimeFormat(), true); + const dateMoment = moment( + this.find('#time').value, + adjustedTimeFormat(), + true, + ); if (dateMoment.isValid()) { this.error.set(''); } @@ -97,7 +104,11 @@ DatePicker = BlazeComponent.extendComponent({ const newTime = moment(time, adjustedTimeFormat(), true); const newDate = moment(evt.target.date.value, 'L', true); const dateString = `${evt.target.date.value} ${time}`; - const newCompleteDate = moment(dateString, 'L ' + adjustedTimeFormat(), true); + const newCompleteDate = moment( + dateString, + 'L ' + adjustedTimeFormat(), + true, + ); if (!newTime.isValid()) { this.error.set('invalid-time'); evt.target.time.focus(); @@ -110,7 +121,7 @@ DatePicker = BlazeComponent.extendComponent({ this._storeDate(newCompleteDate.toDate()); Popup.close(); } else { - if (!this.error){ + if (!this.error) { this.error.set('invalid'); } } diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 6606e4f04..22fb31f97 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -322,6 +322,8 @@ "error-user-notAllowSelf": "You can not invite yourself", "error-user-notCreated": "This user is not created", "error-username-taken": "This username is already taken", + "error-orgname-taken": "This organization name is already taken", + "error-teamname-taken": "This team name is already taken", "error-email-taken": "Email has already been taken", "export-board": "Export board", "export-board-json": "Export board to JSON", diff --git a/models/cards.js b/models/cards.js index 953ce020b..28be0e989 100644 --- a/models/cards.js +++ b/models/cards.js @@ -736,11 +736,11 @@ Cards.helpers({ // at linked cards custom fields definition is not found ret.sort( (a, b) => - a.definition !== undefined && - b.definition !== undefined && + a.definition !== undefined && + b.definition !== undefined && a.definition.name !== undefined && b.definition.name !== undefined && - a.definition.name.localeCompare(b.definition.name) + a.definition.name.localeCompare(b.definition.name), ); return ret; }, diff --git a/models/org.js b/models/org.js index adbf7a729..5e14daead 100644 --- a/models/org.js +++ b/models/org.js @@ -5,27 +5,14 @@ Org = new Mongo.Collection('org'); */ Org.attachSchema( new SimpleSchema({ - _id: { - /** - * the organization id - */ - type: Number, - optional: true, - // eslint-disable-next-line consistent-return - autoValue() { - if (this.isInsert && !this.isSet) { - return incrementCounter('counters', 'orgId', 1); - } - }, - }, - displayName: { + orgDisplayName: { /** * the name to display for the organization */ type: String, optional: true, }, - desc: { + orgDesc: { /** * the description the organization */ @@ -33,7 +20,7 @@ Org.attachSchema( optional: true, max: 190, }, - name: { + orgShortName: { /** * short name of the organization */ @@ -41,7 +28,7 @@ Org.attachSchema( optional: true, max: 255, }, - website: { + orgWebsite: { /** * website of the organization */ @@ -49,66 +36,6 @@ Org.attachSchema( optional: true, max: 255, }, - teams: { - /** - * List of teams of a organization - */ - type: [Object], - // eslint-disable-next-line consistent-return - autoValue() { - if (this.isInsert && !this.isSet) { - return [ - { - teamId: this.teamId, - isAdmin: true, - isActive: true, - isNoComments: false, - isCommentOnly: false, - isWorker: false, - }, - ]; - } - }, - }, - 'teams.$.teamId': { - /** - * The uniq ID of the team - */ - type: String, - }, - 'teams.$.isAdmin': { - /** - * Is the team an admin of the board? - */ - type: Boolean, - }, - 'teams.$.isActive': { - /** - * Is the team active? - */ - type: Boolean, - }, - 'teams.$.isNoComments': { - /** - * Is the team not allowed to make comments - */ - type: Boolean, - optional: true, - }, - 'teams.$.isCommentOnly': { - /** - * Is the team only allowed to comment on the board - */ - type: Boolean, - optional: true, - }, - 'teams.$.isWorker': { - /** - * Is the team only allowed to move card, assign himself to card and comment - */ - type: Boolean, - optional: true, - }, createdAt: { /** * creation date of the organization @@ -140,6 +67,79 @@ Org.attachSchema( }), ); +if (Meteor.isServer) { + Meteor.methods({ + setCreateOrg( + orgDisplayName, + orgDesc, + orgShortName, + orgWebsite, + orgIsActive, + ) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(orgDisplayName, String); + check(orgDesc, String); + check(orgShortName, String); + check(orgWebsite, String); + check(orgIsActive, String); + + const nOrgNames = Org.find({ orgShortName }).count(); + if (nOrgNames > 0) { + throw new Meteor.Error('orgname-already-taken'); + } else { + Org.insert({ + orgDisplayName, + orgDesc, + orgShortName, + orgWebsite, + orgIsActive, + }); + } + } + }, + + setOrgDisplayName(org, orgDisplayName) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(org, String); + check(orgDisplayName, String); + Org.update(org, { + $set: { orgDisplayName: orgDisplayName }, + }); + } + }, + + setOrgDesc(org, orgDesc) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(org, String); + check(orgDesc, String); + Org.update(org, { + $set: { orgDesc: orgDesc }, + }); + } + }, + + setOrgShortName(org, orgShortName) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(org, String); + check(orgShortName, String); + Org.update(org, { + $set: { orgShortName: orgShortName }, + }); + } + }, + + setOrgIsActive(org, orgIsActive) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(org, String); + check(orgIsActive, String); + Org.update(org, { + $set: { orgIsActive: orgIsActive }, + }); + } + }, + }); +} + if (Meteor.isServer) { // Index for Organization name. Meteor.startup(() => { diff --git a/models/team.js b/models/team.js index dfcbedfcb..656daa756 100644 --- a/models/team.js +++ b/models/team.js @@ -5,27 +5,14 @@ Team = new Mongo.Collection('team'); */ Team.attachSchema( new SimpleSchema({ - _id: { - /** - * the organization id - */ - type: Number, - optional: true, - // eslint-disable-next-line consistent-return - autoValue() { - if (this.isInsert && !this.isSet) { - return incrementCounter('counters', 'orgId', 1); - } - }, - }, - displayName: { + teamDisplayName: { /** * the name to display for the team */ type: String, optional: true, }, - desc: { + teamDesc: { /** * the description the team */ @@ -33,7 +20,7 @@ Team.attachSchema( optional: true, max: 190, }, - name: { + teamShortName: { /** * short name of the team */ @@ -41,7 +28,7 @@ Team.attachSchema( optional: true, max: 255, }, - website: { + teamWebsite: { /** * website of the team */ @@ -80,6 +67,79 @@ Team.attachSchema( }), ); +if (Meteor.isServer) { + Meteor.methods({ + setCreateTeam( + teamDisplayName, + teamDesc, + teamShortName, + teamWebsite, + teamIsActive, + ) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(teamDisplayName, String); + check(teamDesc, String); + check(teamShortName, String); + check(teamWebsite, String); + check(teamIsActive, String); + + const nTeamNames = Team.find({ teamShortName }).count(); + if (nTeamNames > 0) { + throw new Meteor.Error('teamname-already-taken'); + } else { + Team.insert({ + teamDisplayName, + teamDesc, + teamShortName, + teamWebsite, + teamIsActive, + }); + } + } + }, + + setTeamDisplayName(team, teamDisplayName) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(team, String); + check(teamDisplayName, String); + Team.update(team, { + $set: { teamDisplayName: teamDisplayName }, + }); + } + }, + + setTeamDesc(team, teamDesc) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(team, String); + check(teamDesc, String); + Team.update(team, { + $set: { teamDesc: teamDesc }, + }); + } + }, + + setTeamShortName(team, teamShortName) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(team, String); + check(teamShortName, String); + Team.update(team, { + $set: { teamShortName: teamShortName }, + }); + } + }, + + setTeamIsActive(team, teamIsActive) { + if (Meteor.user() && Meteor.user().isAdmin) { + check(team, String); + check(teamIsActive, String); + Team.update(team, { + $set: { teamIsActive: teamIsActive }, + }); + } + }, + }); +} + if (Meteor.isServer) { // Index for Team name. Meteor.startup(() => {