diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade
index f9f5b72a9..5b171b744 100644
--- a/client/components/settings/peopleBody.jade
+++ b/client/components/settings/peopleBody.jade
@@ -5,28 +5,95 @@ template(name="people")
else
.content-title.ext-box
.ext-box-left
- span
- i.fa.fa-users
- | {{_ 'people'}}
- input#searchInput(placeholder="{{_ 'search'}}")
- button#searchButton
- i.fa.fa-search
- | {{_ 'search'}}
- .ext-box-right
- span {{_ 'people-number'}} #{peopleNumber}
+ if loading.get
+ +spinner
+ else if orgSetting.get
+ span
+ i.fa.fa-sitemap
+ | {{_ 'organizations'}}
+ input#searchOrgInput(placeholder="{{_ 'search'}}")
+ button#searchOrgButton
+ i.fa.fa-search
+ | {{_ 'search'}}
+ .ext-box-right
+ span {{_ 'org-number'}} #{orgNumber}
+ else if teamSetting.get
+ span
+ i.fa.fa-users
+ | {{_ 'teams'}}
+ input#searchTeamInput(placeholder="{{_ 'search'}}")
+ button#searchTeamButton
+ i.fa.fa-search
+ | {{_ 'search'}}
+ .ext-box-right
+ span {{_ 'team-number'}} #{teamNumber}
+ else if peopleSetting.get
+ span
+ i.fa.fa-user
+ | {{_ 'people'}}
+ input#searchInput(placeholder="{{_ 'search'}}")
+ button#searchButton
+ i.fa.fa-search
+ | {{_ 'search'}}
+ .ext-box-right
+ span {{_ 'people-number'}} #{peopleNumber}
.content-body
.side-menu
ul
li.active
- a.js-setting-menu(data-id="people-setting")
+ a.js-org-menu(data-id="org-setting")
+ i.fa.fa-sitemap
+ | {{_ 'organizations'}}
+ li
+ a.js-team-menu(data-id="team-setting")
i.fa.fa-users
+ | {{_ 'teams'}}
+ li
+ a.js-people-menu(data-id="people-setting")
+ i.fa.fa-user
| {{_ 'people'}}
.main-body
if loading.get
+spinner
- else if people.get
+ else if orgSetting.get
+ +orgGeneral
+ else if teamSetting.get
+ +teamGeneral
+ else if peopleSetting.get
+peopleGeneral
+
+template(name="orgGeneral")
+ table
+ tbody
+ tr
+ th {{_ 'displayName'}}
+ th {{_ 'description'}}
+ th {{_ 'shortName'}}
+ th {{_ 'website'}}
+ th {{_ 'teams'}}
+ th {{_ 'createdAt'}}
+ th {{_ 'active'}}
+ th
+ +newOrgRow
+ each user in orgList
+ +orgRow(orgId=org._id)
+
+template(name="teamGeneral")
+ table
+ tbody
+ tr
+ th {{_ 'displayName'}}
+ th {{_ 'description'}}
+ th {{_ 'shortName'}}
+ th {{_ 'website'}}
+ th {{_ 'createdAt'}}
+ th {{_ 'active'}}
+ th
+ +newTeamRow
+ each team in teamList
+ +teamRow(teamId=team._id)
+
template(name="peopleGeneral")
table
tbody
@@ -44,11 +111,93 @@ template(name="peopleGeneral")
each user in peopleList
+peopleRow(userId=user._id)
+template(name="newOrgRow")
+ a.new-org
+ i.fa.fa-edit
+ | {{_ 'new'}}
+
+template(name="newTeamRow")
+ a.new-team
+ i.fa.fa-edit
+ | {{_ 'new'}}
+
template(name="newUserRow")
a.new-user
i.fa.fa-edit
| {{_ 'new'}}
+template(name="orgRow")
+ tr
+ if orgData.loginDisabled
+ td {{ orgData.displayName }}
+ else
+ td {{ orgData.displayName }}
+ if orgData.loginDisabled
+ td {{ orgData.orgDesc }}
+ else
+ td {{ orgData.desc }}
+ if orgData.loginDisabled
+ td {{ orgData.name }}
+ else
+ td {{ orgData.name }}
+ if orgData.loginDisabled
+ td {{ orgData.website }}
+ else
+ td {{ orgData.website }}
+ if orgData.loginDisabled
+ td {{ orgData.teams }}
+ else
+ td {{ orgData.teams }}
+ if orgData.loginDisabled
+ td {{ moment orgData.createdAt 'LLL' }}
+ else
+ td {{ moment orgData.createdAt 'LLL' }}
+ td
+ if orgData.loginDisabled
+ | {{_ 'no'}}
+ else
+ | {{_ 'yes'}}
+ td
+ a.edit-org
+ i.fa.fa-edit
+ | {{_ 'edit'}}
+ a.more-settings-org
+ i.fa.fa-ellipsis-h
+
+template(name="teamRow")
+ tr
+ if teamData.loginDisabled
+ td {{ teamData.displayName }}
+ else
+ td {{ teamData.displayName }}
+ if teamData.loginDisabled
+ td {{ teamData.desc }}
+ else
+ td {{ teamData.desc }}
+ if teamData.loginDisabled
+ td {{ teamData.dame }}
+ else
+ td {{ teamData.name }}
+ if teamData.loginDisabled
+ td {{ teamData.website }}
+ else
+ td {{ teamData.website }}
+ if orgData.loginDisabled
+ td {{ moment teamData.createdAt 'LLL' }}
+ else
+ td {{ moment teamData.createdAt 'LLL' }}
+ td
+ if teamData.loginDisabled
+ | {{_ 'no'}}
+ else
+ | {{_ 'yes'}}
+ td
+ a.edit-team
+ i.fa.fa-edit
+ | {{_ 'edit'}}
+ a.more-settings-team
+ i.fa.fa-ellipsis-h
+
template(name="peopleRow")
tr
if userData.loginDisabled
@@ -107,6 +256,58 @@ template(name="peopleRow")
a.more-settings-user
i.fa.fa-ellipsis-h
+template(name="editOrgPopup")
+ form
+ label.hide.orgId(type="text" value=org._id)
+ label
+ | {{_ 'orgDisplayName'}}
+ input.js-orgDisplayName(type="text" value=org.orgDisplayName required)
+ span.error.hide.orgname-taken
+ | {{_ 'error-orgname-taken'}}
+ label
+ | {{_ 'orgDesc'}}
+ input.js-orgDesc(type="text" value=org.orgDesc required)
+ label
+ | {{_ 'orgName'}}
+ input.js-orgName(type="text" value=org.orgName required)
+ label
+ | {{_ 'orgWebsite'}}
+ input.js-orgWebsite(type="text" value=org.orgWebsite required)
+ label
+ | {{_ 'active'}}
+ select.select-active.js-org-isactive
+ option(value="false") {{_ 'yes'}}
+ option(value="true" selected="{{org.loginDisabled}}") {{_ 'no'}}
+ hr
+ div.buttonsContainer
+ input.primary.wide(type="submit" value="{{_ 'save'}}")
+
+template(name="editTeamPopup")
+ form
+ label.hide.teamId(type="text" value=team._id)
+ label
+ | {{_ 'displayName'}}
+ input.js-teamDisplayName(type="text" value=team.displayName required)
+ span.error.hide.teamname-taken
+ | {{_ 'error-teamname-taken'}}
+ label
+ | {{_ 'desc'}}
+ input.js-orgDesc(type="text" value=org.desc required)
+ label
+ | {{_ 'name'}}
+ input.js-orgName(type="text" value=org.name required)
+ label
+ | {{_ 'website'}}
+ input.js-orgWebsite(type="text" value=org.website required)
+ label
+ | {{_ 'active'}}
+ select.select-active.js-team-isactive
+ option(value="false") {{_ 'yes'}}
+ option(value="true" selected="{{team.loginDisabled}}") {{_ 'no'}}
+ hr
+ div.buttonsContainer
+ input.primary.wide(type="submit" value="{{_ 'save'}}")
+
template(name="editUserPopup")
form
label.hide.userId(type="text" value=user._id)
@@ -154,6 +355,54 @@ template(name="editUserPopup")
div.buttonsContainer
input.primary.wide(type="submit" value="{{_ 'save'}}")
+template(name="newOrgPopup")
+ form
+ //label.hide.userId(type="text" value=user._id)
+ label
+ | {{_ 'orgDisplayName'}}
+ input.js-orgDisplayName(type="text" value="" required)
+ label
+ | {{_ 'orgDesc'}}
+ input.js-orgDesc(type="text" value="" required)
+ label
+ | {{_ 'orgName'}}
+ input.js-orgName(type="text" value="")
+ label
+ | {{_ 'orgWebsite'}}
+ input.js-orgWebsite(type="text" value="")
+ label
+ | {{_ 'active'}}
+ select.select-active.js-profile-isactive
+ option(value="false" selected="selected") {{_ 'yes'}}
+ option(value="true") {{_ 'no'}}
+ hr
+ div.buttonsContainer
+ input.primary.wide(type="submit" value="{{_ 'save'}}")
+
+template(name="newTeamPopup")
+ form
+ //label.hide.teamId(type="text" value=team._id)
+ label
+ | {{_ 'displayName'}}
+ input.js-teamDisplayName(type="text" value="" required)
+ label
+ | {{_ 'desc'}}
+ input.js-teamDesc(type="text" value="" required)
+ label
+ | {{_ 'shortName'}}
+ input.js-teamName(type="text" value="")
+ label
+ | {{_ 'website'}}
+ input.js-teamWebsite(type="text" value="")
+ label
+ | {{_ 'active'}}
+ select.select-active.js-profile-isactive
+ option(value="false" selected="selected") {{_ 'yes'}}
+ option(value="true") {{_ 'no'}}
+ hr
+ div.buttonsContainer
+ input.primary.wide(type="submit" value="{{_ 'save'}}")
+
template(name="newUserPopup")
form
//label.hide.userId(type="text" value=user._id)
@@ -201,6 +450,31 @@ template(name="newUserPopup")
div.buttonsContainer
input.primary.wide(type="submit" value="{{_ 'save'}}")
+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'}}")
+
template(name="settingsUserPopup")
ul.pop-over-list
li
diff --git a/client/components/settings/peopleBody.js b/client/components/settings/peopleBody.js
index 0cd288d41..571a8540b 100644
--- a/client/components/settings/peopleBody.js
+++ b/client/components/settings/peopleBody.js
@@ -1,3 +1,5 @@
+const orgsPerPage = 25;
+const teamsPerPage = 25;
const usersPerPage = 25;
BlazeComponent.extendComponent({
@@ -7,17 +9,45 @@ BlazeComponent.extendComponent({
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
- this.people = new ReactiveVar(true);
+ this.orgSetting = new ReactiveVar(true);
+ this.teamSetting = new ReactiveVar(true);
+ this.peopleSetting = new ReactiveVar(true);
+ this.findOrgsOptions = new ReactiveVar({});
+ this.findTeamsOptions = new ReactiveVar({});
this.findUsersOptions = new ReactiveVar({});
- this.number = new ReactiveVar(0);
+ this.numberOrgs = new ReactiveVar(0);
+ this.numberTeams = new ReactiveVar(0);
+ this.numberPeople = new ReactiveVar(0);
this.page = new ReactiveVar(1);
this.loadNextPageLocked = false;
this.callFirstWith(null, 'resetNextPeak');
this.autorun(() => {
- const limit = this.page.get() * usersPerPage;
+ const limitOrgs = this.page.get() * orgsPerPage;
+ const limitTeams = this.page.get() * teamsPerPage;
+ const limitUsers = this.page.get() * usersPerPage;
- this.subscribe('people', this.findUsersOptions.get(), limit, () => {
+ this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {
+ this.loadNextPageLocked = false;
+ const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
+ this.calculateNextPeak();
+ const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
+ if (nextPeakBefore === nextPeakAfter) {
+ this.callFirstWith(null, 'resetNextPeak');
+ }
+ });
+
+ this.subscribe('team', this.findTeamsOptions.get(), limitTeams, () => {
+ this.loadNextPageLocked = false;
+ const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
+ this.calculateNextPeak();
+ const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
+ if (nextPeakBefore === nextPeakAfter) {
+ this.callFirstWith(null, 'resetNextPeak');
+ }
+ });
+
+ this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
this.calculateNextPeak();
@@ -31,6 +61,22 @@ BlazeComponent.extendComponent({
events() {
return [
{
+ 'click #searchOrgButton'() {
+ this.filterOrg();
+ },
+ 'keydown #searchOrgInput'(event) {
+ if (event.keyCode === 13 && !event.shiftKey) {
+ this.filterOrg();
+ }
+ },
+ 'click #searchTeamButton'() {
+ this.filterTeam();
+ },
+ 'keydown #searchTeamInput'(event) {
+ if (event.keyCode === 13 && !event.shiftKey) {
+ this.filterTeam();
+ }
+ },
'click #searchButton'() {
this.filterPeople();
},
@@ -39,9 +85,18 @@ BlazeComponent.extendComponent({
this.filterPeople();
}
},
+ 'click #newOrgButton'() {
+ Popup.open('newOrg');
+ },
+ 'click #newTeamButton'() {
+ Popup.open('newTeam');
+ },
'click #newUserButton'() {
Popup.open('newUser');
},
+ 'click a.js-org-menu': this.switchMenu,
+ 'click a.js-team-menu': this.switchMenu,
+ 'click a.js-people-menu': this.switchMenu,
},
];
},
@@ -84,18 +139,63 @@ BlazeComponent.extendComponent({
setLoading(w) {
this.loading.set(w);
},
+ orgList() {
+ const orgs = Org.find(this.findOrgsOptions.get(), {
+ fields: { _id: true },
+ });
+ this.numberOrgs.set(org.count(false));
+ return orgs;
+ },
+ teamList() {
+ const teams = Team.find(this.findTeamsOptions.get(), {
+ fields: { _id: true },
+ });
+ this.numberTeams.set(team.count(false));
+ return teams;
+ },
peopleList() {
const users = Users.find(this.findUsersOptions.get(), {
fields: { _id: true },
});
- this.number.set(users.count(false));
+ this.numberPeople.set(users.count(false));
return users;
},
+ orgNumber() {
+ return this.numberOrgs.get();
+ },
+ teamNumber() {
+ return this.numberTeams.get();
+ },
peopleNumber() {
- return this.number.get();
+ return this.numberPeople.get();
+ },
+ switchMenu(event) {
+ const target = $(event.target);
+ if (!target.hasClass('active')) {
+ $('.side-menu li.active').removeClass('active');
+ target.parent().addClass('active');
+ const targetID = target.data('id');
+ this.orgSetting.set('org-setting' === targetID);
+ this.teamSetting.set('team-setting' === targetID);
+ this.peopleSetting.set('people-setting' === targetID);
+ }
},
}).register('people');
+Template.orgRow.helpers({
+ orgData() {
+ const orgCollection = this.esSearch ? ESSearchResults : Org;
+ return orgCollection.findOne(this.orgId);
+ },
+});
+
+Template.teamRow.helpers({
+ teamData() {
+ const teamCollection = this.esSearch ? ESSearchResults : Team;
+ return teamCollection.findOne(this.teamId);
+ },
+});
+
Template.peopleRow.helpers({
userData() {
const userCollection = this.esSearch ? ESSearchResults : Users;
@@ -122,6 +222,51 @@ Template.editUserPopup.onCreated(function() {
});
});
+Template.editOrgPopup.helpers({
+ org() {
+ return Org.findOne(this.orgId);
+ },
+ /*
+ isSelected(match) {
+ const orgId = Template.instance().data.orgId;
+ const selected = Org.findOne(orgId).authenticationMethod;
+ return selected === match;
+ },
+ isLdap() {
+ const userId = Template.instance().data.userId;
+ const selected = Users.findOne(userId).authenticationMethod;
+ return selected === 'ldap';
+ },
+ */
+ errorMessage() {
+ return Template.instance().errorMessage.get();
+ },
+});
+
+Template.editTeamPopup.helpers({
+ team() {
+ return Team.findOne(this.teamId);
+ },
+ /*
+ authentications() {
+ return Template.instance().authenticationMethods.get();
+ },
+ isSelected(match) {
+ const userId = Template.instance().data.userId;
+ const selected = Users.findOne(userId).authenticationMethod;
+ return selected === match;
+ },
+ isLdap() {
+ const userId = Template.instance().data.userId;
+ const selected = Users.findOne(userId).authenticationMethod;
+ return selected === 'ldap';
+ },
+ */
+ errorMessage() {
+ return Template.instance().errorMessage.get();
+ },
+});
+
Template.editUserPopup.helpers({
user() {
return Users.findOne(this.userId);
@@ -144,6 +289,46 @@ Template.editUserPopup.helpers({
},
});
+Template.newOrgPopup.onCreated(function() {
+ //this.authenticationMethods = new ReactiveVar([]);
+ this.errorMessage = new ReactiveVar('');
+ /*
+ Meteor.call('getAuthenticationsEnabled', (_, result) => {
+ if (result) {
+ // TODO : add a management of different languages
+ // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
+ this.authenticationMethods.set([
+ { value: 'password' },
+ // Gets only the authentication methods availables
+ ...Object.entries(result)
+ .filter(e => e[1])
+ .map(e => ({ value: e[0] })),
+ ]);
+ }
+ });
+*/
+});
+
+Template.newTeamPopup.onCreated(function() {
+ //this.authenticationMethods = new ReactiveVar([]);
+ this.errorMessage = new ReactiveVar('');
+ /*
+ Meteor.call('getAuthenticationsEnabled', (_, result) => {
+ if (result) {
+ // TODO : add a management of different languages
+ // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
+ this.authenticationMethods.set([
+ { value: 'password' },
+ // Gets only the authentication methods availables
+ ...Object.entries(result)
+ .filter(e => e[1])
+ .map(e => ({ value: e[0] })),
+ ]);
+ }
+ });
+*/
+});
+
Template.newUserPopup.onCreated(function() {
this.authenticationMethods = new ReactiveVar([]);
this.errorMessage = new ReactiveVar('');
diff --git a/client/components/settings/peopleBody.styl b/client/components/settings/peopleBody.styl
index 8f3c10c27..028db164c 100644
--- a/client/components/settings/peopleBody.styl
+++ b/client/components/settings/peopleBody.styl
@@ -21,7 +21,7 @@ table
.ext-box-left
display: flex;
- width: 40%
+ width: 100%
span
vertical-align: center;
@@ -47,5 +47,5 @@ table
div
margin: auto
-.more-settings-user
+.more-settings-user,.more-settings-team,.more-settings-org
margin-left: 10px;
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 7027613c8..9fbb4ed34 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -774,6 +774,8 @@
"display-authentication-method": "Display Authentication Method",
"default-authentication-method": "Default Authentication Method",
"duplicate-board": "Duplicate Board",
+ "org-number": "The number of organizations is: ",
+ "team-number": "The number of teams is: ",
"people-number": "The number of people is: ",
"swimlaneDeletePopup-title": "Delete Swimlane ?",
"swimlane-delete-pop": "All actions will be removed from the activity feed and you won't be able to recover the swimlane. There is no undo.",
@@ -836,5 +838,11 @@
"hide-checked-items": "Hide checked items",
"task": "Task",
"create-task": "Create Task",
- "ok": "OK"
+ "ok": "OK",
+ "organizations": "Organizations",
+ "teams": "Teams",
+ "displayName": "Display Name",
+ "shortName": "Short Name",
+ "website": "Website",
+ "person": "Person"
}
diff --git a/models/org.js b/models/org.js
index a24d829db..adbf7a729 100644
--- a/models/org.js
+++ b/models/org.js
@@ -1,7 +1,7 @@
Org = new Mongo.Collection('org');
/**
- * A Organization in wekan
+ * A Organization in Wekan. A Enterprise in Trello.
*/
Org.attachSchema(
new SimpleSchema({
@@ -18,76 +18,96 @@ Org.attachSchema(
}
},
},
- version: {
+ displayName: {
/**
- * the version of the organization
+ * the name to display for the organization
*/
- type: Number,
+ type: String,
optional: true,
},
- name: {
+ desc: {
/**
- * name of the organization
+ * the description the organization
*/
type: String,
optional: true,
max: 190,
},
- address1: {
+ name: {
/**
- * address1 of the organization
+ * short name of the organization
*/
type: String,
optional: true,
max: 255,
},
- address2: {
+ website: {
/**
- * address2 of the organization
+ * website of the organization
*/
type: String,
optional: true,
max: 255,
},
- city: {
+ teams: {
/**
- * city of the organization
+ * List of teams of a organization
*/
- type: String,
- optional: true,
- max: 255,
+ 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,
+ },
+ ];
+ }
+ },
},
- state: {
+ 'teams.$.teamId': {
/**
- * state of the organization
+ * The uniq ID of the team
*/
type: String,
- optional: true,
- max: 255,
},
- zipCode: {
+ 'teams.$.isAdmin': {
/**
- * zipCode of the organization
+ * Is the team an admin of the board?
*/
- type: String,
- optional: true,
- max: 50,
+ type: Boolean,
},
- country: {
+ 'teams.$.isActive': {
/**
- * country of the organization
+ * Is the team active?
*/
- type: String,
- optional: true,
- max: 255,
+ type: Boolean,
},
- billingEmail: {
+ 'teams.$.isNoComments': {
/**
- * billingEmail of the organization
+ * Is the team not allowed to make comments
*/
- type: String,
+ 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,
- max: 255,
},
createdAt: {
/**
diff --git a/models/team.js b/models/team.js
new file mode 100644
index 000000000..dfcbedfcb
--- /dev/null
+++ b/models/team.js
@@ -0,0 +1,90 @@
+Team = new Mongo.Collection('team');
+
+/**
+ * A Team in Wekan. Organization in Trello.
+ */
+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: {
+ /**
+ * the name to display for the team
+ */
+ type: String,
+ optional: true,
+ },
+ desc: {
+ /**
+ * the description the team
+ */
+ type: String,
+ optional: true,
+ max: 190,
+ },
+ name: {
+ /**
+ * short name of the team
+ */
+ type: String,
+ optional: true,
+ max: 255,
+ },
+ website: {
+ /**
+ * website of the team
+ */
+ type: String,
+ optional: true,
+ max: 255,
+ },
+ createdAt: {
+ /**
+ * creation date of the team
+ */
+ type: Date,
+ // eslint-disable-next-line consistent-return
+ autoValue() {
+ if (this.isInsert) {
+ return new Date();
+ } else if (this.isUpsert) {
+ return { $setOnInsert: new Date() };
+ } else {
+ this.unset();
+ }
+ },
+ },
+ modifiedAt: {
+ type: Date,
+ denyUpdate: false,
+ // eslint-disable-next-line consistent-return
+ autoValue() {
+ if (this.isInsert || this.isUpsert || this.isUpdate) {
+ return new Date();
+ } else {
+ this.unset();
+ }
+ },
+ },
+ }),
+);
+
+if (Meteor.isServer) {
+ // Index for Team name.
+ Meteor.startup(() => {
+ Team._collection._ensureIndex({ name: -1 });
+ });
+}
+
+export default Team;
diff --git a/server/publications/org.js b/server/publications/org.js
new file mode 100644
index 000000000..e7dcfdd63
--- /dev/null
+++ b/server/publications/org.js
@@ -0,0 +1,27 @@
+Meteor.publish('org', function(query, limit) {
+ check(query, Match.OneOf(Object, null));
+ check(limit, Number);
+
+ if (!Match.test(this.userId, String)) {
+ return [];
+ }
+
+ const user = Users.findOne(this.userId);
+ if (user && user.isAdmin) {
+ return Org.find(query, {
+ limit,
+ sort: { createdAt: -1 },
+ fields: {
+ displayName: 1,
+ desc: 1,
+ name: 1,
+ website: 1,
+ teams: 1,
+ createdAt: 1,
+ loginDisabled: 1,
+ },
+ });
+ }
+
+ return [];
+});
diff --git a/server/publications/team.js b/server/publications/team.js
new file mode 100644
index 000000000..aa18a5da6
--- /dev/null
+++ b/server/publications/team.js
@@ -0,0 +1,27 @@
+Meteor.publish('team', function(query, limit) {
+ check(query, Match.OneOf(Object, null));
+ check(limit, Number);
+
+ if (!Match.test(this.userId, String)) {
+ return [];
+ }
+
+ const user = Users.findOne(this.userId);
+ if (user && user.isAdmin) {
+ return Team.find(query, {
+ limit,
+ sort: { createdAt: -1 },
+ fields: {
+ displayName: 1,
+ desc: 1,
+ name: 1,
+ website: 1,
+ teams: 1,
+ createdAt: 1,
+ loginDisabled: 1,
+ },
+ });
+ }
+
+ return [];
+});