summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlkisme <lkisme@me.com>2017-02-24 22:10:38 +0800
committerlkisme <lkisme@me.com>2017-02-24 22:10:38 +0800
commit1dfb6ef477dba4d0faf4bf86026647f43fb2f5f6 (patch)
tree2c97395901ac5789975a65d5efc6eaf0e37c1e5d
parent29fdfb9c886f1a906a27398fd546ad6cdb929ff9 (diff)
downloadwekan-1dfb6ef477dba4d0faf4bf86026647f43fb2f5f6.tar.gz
wekan-1dfb6ef477dba4d0faf4bf86026647f43fb2f5f6.tar.bz2
wekan-1dfb6ef477dba4d0faf4bf86026647f43fb2f5f6.zip
Admin panel:
Only invited user can register in strict mode, Set mail server in admin panel, Switch strict mode in admin panel, Invite people to system in admin panel
-rw-r--r--.eslintrc.json4
-rw-r--r--client/components/main/layouts.js2
-rw-r--r--client/components/settings/invitationCode.jade5
-rw-r--r--client/components/settings/invitationCode.js6
-rw-r--r--client/components/settings/settingBody.jade72
-rw-r--r--client/components/settings/settingBody.js126
-rw-r--r--client/components/settings/settingBody.styl112
-rw-r--r--client/components/settings/settingHeader.jade21
-rw-r--r--client/components/settings/settingHeader.styl25
-rw-r--r--client/components/users/userHeader.jade2
-rw-r--r--client/components/users/userHeader.js3
-rw-r--r--config/accounts.js15
-rw-r--r--config/router.js10
-rw-r--r--i18n/en.i18n.json12
-rw-r--r--i18n/zh-CN.i18n.json11
-rw-r--r--models/invitationCodes.js45
-rw-r--r--models/settings.js111
-rw-r--r--models/users.js45
-rw-r--r--server/publications/settings.js13
-rw-r--r--server/publications/users.js8
20 files changed, 638 insertions, 10 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index 4808d873..0caa7a01 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -119,6 +119,8 @@
"allowIsBoardMember": true,
"allowIsBoardMemberByCard": true,
"Emoji": true,
- "Checklists": true
+ "Checklists": true,
+ "Settings": true,
+ "InvitationCodes": true
}
}
diff --git a/client/components/main/layouts.js b/client/components/main/layouts.js
index 3df17f41..1e50b01a 100644
--- a/client/components/main/layouts.js
+++ b/client/components/main/layouts.js
@@ -1,4 +1,6 @@
Meteor.subscribe('boards');
+Meteor.subscribe('setting');
+Meteor.subscribe('user-admin');
BlazeLayout.setRoot('body');
diff --git a/client/components/settings/invitationCode.jade b/client/components/settings/invitationCode.jade
new file mode 100644
index 00000000..171a2663
--- /dev/null
+++ b/client/components/settings/invitationCode.jade
@@ -0,0 +1,5 @@
+template(name='invitationCode')
+ .at-input#invitationcode
+ label(for='at-field-code') {{_ 'invitation-code'}}
+
+ input#at-field-invitationcode(type="text" name='at-field-invitationcode' placeholder="{{_ 'invitation-code'}}")
diff --git a/client/components/settings/invitationCode.js b/client/components/settings/invitationCode.js
new file mode 100644
index 00000000..8143d5af
--- /dev/null
+++ b/client/components/settings/invitationCode.js
@@ -0,0 +1,6 @@
+Template.invitationCode.onRendered(() => {
+ const strict = Settings.findOne().strict;
+ if(!strict){
+ $('#invitationcode').hide();
+ }
+});
diff --git a/client/components/settings/settingBody.jade b/client/components/settings/settingBody.jade
new file mode 100644
index 00000000..5d77bc60
--- /dev/null
+++ b/client/components/settings/settingBody.jade
@@ -0,0 +1,72 @@
+template(name="setting")
+ .setting-content
+ .content-title
+ span Settings
+ .content-body
+ .side-menu
+ ul
+ li.active
+ a.js-setting-menu(data-id="general-setting") System
+ li
+ a.js-setting-menu(data-id="email-setting") Email
+ .main-body
+ if loading.get
+ +spinner
+ else if generalSetting.get
+ +general
+ else if emailSetting.get
+ +email
+
+template(name="general")
+ ul#general-setting.setting-detail
+ li
+ a.flex.js-toggle-strict-mode
+ .materialCheckBox(class="{{#if currentSetting.strict}}is-checked{{/if}}")
+
+ span Use Strict Mode
+ li
+ .invite-people(class="{{#if currentSetting.strict}}{{else}}hide{{/if}}")
+ ul
+ li
+ .title Invite People
+ textarea#email-to-invite.form-control(rows='5', placeholder="Email Adresses")
+ li
+ .title To board(s)
+ .bg-white
+ each boards
+ a.option.flex.js-toggle-board-choose(id= _id)
+ .materialCheckBox(data-id= _id)
+
+ span= title
+
+ li
+ button.js-email-invite.primary Invite
+
+template(name='email')
+ ul#email-setting.setting-detail
+ li.smtp-form
+ .title SMTP Host {{currentSetting.mailServer.port}}
+ .description The address of the SMTP server that handles your emails.
+ .form-group
+ input.form-control#mail-server-host(type="text", placeholder="smtp.domain.com" value="{{currentSetting.mailServer.host}}")
+ li.smtp-form
+ .title SMTP Port
+ .description The port your SMTP server uses for outgoing emails.
+ .form-group
+ input.form-control#mail-server-port(type="text", placeholder="25" value="{{currentSetting.mailServer.port}}")
+ li.smtp-form
+ .title SMTP user name
+ .form-group
+ input.form-control#mail-server-username(type="text", placeholder="user name" value="{{currentSetting.mailServer.username}}")
+ li.smtp-form
+ .title SMTP password
+ .form-group
+ input.form-control#mail-server-password(type="text", placeholder="password" value="{{currentSetting.mailServer.password}}")
+ li.smtp-form
+ .title From
+ .Email address you want to use to send emails.
+ .form-group
+ input.form-control#mail-server-from(type="email", placeholder="no-reply@domain.com" value="{{currentSetting.mailServer.from}}")
+
+ li
+ button.js-save.primary Save
diff --git a/client/components/settings/settingBody.js b/client/components/settings/settingBody.js
new file mode 100644
index 00000000..047bbd1c
--- /dev/null
+++ b/client/components/settings/settingBody.js
@@ -0,0 +1,126 @@
+Meteor.subscribe('setting');
+Meteor.subscribe('mailServer');
+
+BlazeComponent.extendComponent({
+ onCreated() {
+ this.error = new ReactiveVar('');
+ this.loading = new ReactiveVar(false);
+ this.generalSetting = new ReactiveVar(true);
+ this.emailSetting = new ReactiveVar(false);
+ },
+
+ setError(error) {
+ this.error.set(error);
+ },
+
+ setLoading(w) {
+ this.loading.set(w);
+ },
+
+ checkField(selector) {
+ const value = $(selector).val();
+ if(!value || value.trim() === ''){
+ $(selector).parents('li.smtp-form').addClass('has-error');
+ throw Error('blank field');
+ } else {
+ return value;
+ }
+ },
+
+ currentSetting(){
+ return Settings.findOne();
+ },
+
+ boards() {
+ return Boards.find({
+ archived: false,
+ 'members.userId': Meteor.userId(),
+ 'members.isAdmin': true,
+ }, {
+ sort: ['title'],
+ });
+ },
+ toggleStrictMode(){
+ this.setLoading(true);
+ const isStrictMode = this.currentSetting().strict;
+ Settings.update(Settings.findOne()._id, {$set:{strict: !isStrictMode}});
+ this.setLoading(false);
+ if(isStrictMode){
+ $('.invite-people').slideUp();
+ }else{
+ $('.invite-people').slideDown();
+ }
+ },
+
+ 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.generalSetting.set('general-setting' === targetID);
+ this.emailSetting.set('email-setting' === targetID);
+ }
+ },
+
+ checkBoard(event){
+ let target = $(event.target);
+ if(!target.hasClass('js-toggle-board-choose')){
+ target = target.parent();
+ }
+ const checkboxId = target.attr('id');
+ $(`#${checkboxId} .materialCheckBox`).toggleClass('is-checked');
+ $(`#${checkboxId}`).toggleClass('is-checked');
+ },
+
+ inviteThroughEmail(){
+ this.setLoading(true);
+ const emails = $('#email-to-invite').val().trim().split('\n').join(',').split(',');
+ const boardsToInvite = [];
+ $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function () {
+ boardsToInvite.push($(this).data('id'));
+ });
+ const validEmails = [];
+ emails.forEach((email) => {
+ if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
+ validEmails.push(email.trim());
+ }
+ });
+ Meteor.call('sendInvitation', validEmails, boardsToInvite, () => {
+ // if (!err) {
+ // TODO - show more info to user
+ // }
+ this.setLoading(false);
+ });
+ },
+
+ saveMailServerInfo(){
+ this.setLoading(true);
+ $('li').removeClass('has-error');
+
+ try{
+ const host = this.checkField('#mail-server-host');
+ const port = this.checkField('#mail-server-port');
+ const username = this.checkField('#mail-server-username');
+ const password = this.checkField('#mail-server-password');
+ const from = this.checkField('#mail-server-from');
+ Settings.update(Settings.findOne()._id, {$set:{'mailServer.host':host, 'mailServer.port': port, 'mailServer.username': username,
+ 'mailServer.password': password, 'mailServer.from': from}});
+ } catch (e) {
+ return;
+ } finally {
+ this.setLoading(false);
+ }
+
+ },
+
+ events(){
+ return [{
+ 'click a.js-toggle-strict-mode': this.toggleStrictMode,
+ 'click a.js-setting-menu': this.switchMenu,
+ 'click a.js-toggle-board-choose': this.checkBoard,
+ 'click button.js-email-invite': this.inviteThroughEmail,
+ 'click button.js-save': this.saveMailServerInfo,
+ }];
+ },
+}).register('setting');
diff --git a/client/components/settings/settingBody.styl b/client/components/settings/settingBody.styl
new file mode 100644
index 00000000..118d364c
--- /dev/null
+++ b/client/components/settings/settingBody.styl
@@ -0,0 +1,112 @@
+.flex
+ display: -webkit-box
+ display: -moz-box
+ display: -webkit-flex
+ display: -moz-flex
+ display: -ms-flexbox
+ display: flex
+
+.setting-content
+ padding 30px
+ color: #727479
+ background: #dedede
+ width 100%
+ height 100%
+ position: absolute;
+
+ .content-title
+ font-size 20px
+
+ .content-body
+ display flex
+ padding-top 15px
+ height 100%
+
+ .side-menu
+ background-color: #f7f7f7;
+ border: 1px solid #f0f0f0;
+ border-radius: 4px;
+ width: 250px;
+ box-shadow: inset -1px -1px 3px rgba(0,0,0,.05);
+
+ ul
+
+ li
+ margin: 0.1rem 0.2rem;
+
+ &.active
+ background #fff
+ box-shadow 0 1px 2px rgba(0,0,0,0.15);
+
+ &:hover
+ background #fff
+ box-shadow 0 1px 2px rgba(0,0,0,0.15);
+ a
+ @extends .flex
+ padding: 1rem 0 1rem 1rem
+ width: 100% - 5rem
+
+
+ span
+ font-size: 13px
+
+ .main-body
+ padding: 0.1em 1em
+
+ ul
+ li
+ padding: 0.5rem 0.5rem;
+
+ a
+ .is-checked
+ border-bottom: 2px solid #2980b9;
+ border-right: 2px solid #2980b9;
+
+ span
+ padding: 0 0.5rem
+
+ .invite-people
+ padding-left 20px;
+ li
+ min-width: 500px;
+
+ ul.no-margin-bottom
+ margin-bottom: 0;
+
+ .bg-white
+ a
+ background #f7f7f7
+ &.is-checked
+ background #fff
+
+
+.option
+ @extends .flex
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: #fff;
+ text-decoration: none;
+ -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2);
+ box-shadow: 0 1px 2px rgba(0,0,0,0.2);
+ margin-top: 5px;
+ padding: 5px;
+
+.title
+ font-weight 700;
+ margin-bottom 0.5rem;
+.description
+ margin-bottom 0.5rem;
+.bg-white
+ background #f9fbfc;
+
+.form-control.has-error
+ border-color: #a94442;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+
+li.has-error
+ color #a94442
+ .form-group
+ .form-control
+ border-color: #a94442;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
+
diff --git a/client/components/settings/settingHeader.jade b/client/components/settings/settingHeader.jade
new file mode 100644
index 00000000..01873eae
--- /dev/null
+++ b/client/components/settings/settingHeader.jade
@@ -0,0 +1,21 @@
+template(name="settingHeaderBar")
+ h1.header-setting-menu
+ span {{_ 'admin-panel'}}
+
+ .setting-header-btns.left
+ unless isMiniScreen
+ unless isSandstorm
+ if currentUser
+ a.setting-header-btn.settings.active
+ i.fa(class="fa-cog")
+ span {{_ 'option-setting'}}
+//TODO
+// a.setting-header-btn.people
+// i.fa(class="fa-users")
+// span {{_ 'option-people'}}
+
+ else
+ a.setting-header-btn.js-log-in(
+ title="{{_ 'log-in'}}")
+ i.fa.fa-sign-in
+ span {{_ 'log-in'}}
diff --git a/client/components/settings/settingHeader.styl b/client/components/settings/settingHeader.styl
new file mode 100644
index 00000000..995ed26d
--- /dev/null
+++ b/client/components/settings/settingHeader.styl
@@ -0,0 +1,25 @@
+#header #header-main-bar .setting-header-btn
+ &.active,
+ &:hover:not(.is-disabled)
+ background: rgba(0, 0, 0, .15)
+ color: darken(white, 5%)
+ margin-left: 20px;
+ padding-right: 10px;
+ height: 28px;
+ font-size: 13px;
+ float: left;
+ overflow: hidden;
+ line-height: @height;
+ margin: 0 2px;
+
+ i.fa
+ float: left
+ display: block
+ line-height: 28px
+ color: darken(white, 5%)
+ margin: 0 10px
+
+ + span
+ display: inline-block
+ margin-top: 1px
+ margin-right: 10px \ No newline at end of file
diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade
index ad41e8aa..51b0888b 100644
--- a/client/components/users/userHeader.jade
+++ b/client/components/users/userHeader.jade
@@ -17,6 +17,8 @@ template(name="memberMenuPopup")
li: a.js-change-password {{_ 'changePasswordPopup-title'}}
li: a.js-change-language {{_ 'changeLanguagePopup-title'}}
li: a.js-edit-notification {{_ 'editNotificationPopup-title'}}
+ if currentUser.isAdmin
+ li: a.js-go-setting(href='/setting') {{_ 'admin-panel'}}
hr
ul.pop-over-list
li: a.js-logout {{_ 'log-out'}}
diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js
index 98053ed1..73a11fc0 100644
--- a/client/components/users/userHeader.js
+++ b/client/components/users/userHeader.js
@@ -15,6 +15,9 @@ Template.memberMenuPopup.events({
AccountsTemplates.logout();
},
+ 'click .js-go-setting'() {
+ Popup.close();
+ },
});
Template.editProfilePopup.events({
diff --git a/config/accounts.js b/config/accounts.js
index 9ab26b33..51c0f49e 100644
--- a/config/accounts.js
+++ b/config/accounts.js
@@ -1,12 +1,21 @@
const passwordField = AccountsTemplates.removeField('password');
const emailField = AccountsTemplates.removeField('email');
+
AccountsTemplates.addFields([{
_id: 'username',
type: 'text',
displayName: 'username',
required: true,
minLength: 2,
-}, emailField, passwordField]);
+}, emailField, passwordField, {
+ _id: 'invitationcode',
+ type: 'text',
+ displayName: 'Invitation Code',
+ required: false,
+ minLength: 6,
+ errStr: 'Invitation code doesn\'t exist',
+ template: 'invitationCode',
+}]);
AccountsTemplates.configure({
defaultLayout: 'userFormsLayout',
@@ -48,9 +57,6 @@ AccountsTemplates.configureRoute('changePwd', {
});
if (Meteor.isServer) {
- if (process.env.MAIL_FROM) {
- Accounts.emailTemplates.from = process.env.MAIL_FROM;
- }
['resetPassword-subject', 'resetPassword-text', 'verifyEmail-subject', 'verifyEmail-text', 'enrollAccount-subject', 'enrollAccount-text'].forEach((str) => {
const [templateName, field] = str.split('-');
@@ -63,3 +69,4 @@ if (Meteor.isServer) {
};
});
}
+
diff --git a/config/router.js b/config/router.js
index 7194621b..f136f8cc 100644
--- a/config/router.js
+++ b/config/router.js
@@ -99,6 +99,16 @@ FlowRouter.route('/import', {
},
});
+FlowRouter.route('/setting', {
+ name: 'setting',
+ action() {
+ BlazeLayout.render('defaultLayout', {
+ headerBar: 'settingHeaderBar',
+ content: 'setting',
+ });
+ },
+});
+
FlowRouter.notFound = {
action() {
BlazeLayout.render('defaultLayout', { content: 'notFound' });
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index ad74a0e0..b5c1b55e 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -323,5 +323,13 @@
"welcome-board": "Welcome Board",
"welcome-list1": "Basics",
"welcome-list2": "Advanced",
- "what-to-do": "What do you want to do?"
-} \ No newline at end of file
+ "what-to-do": "What do you want to do?",
+ "admin-panel": "Admin Panel",
+ "system-setting": "System Setting",
+ "option-setting": "Settings",
+ "option-people": "People",
+ "invitation-code": "Invitation Code",
+ "email-invite-register-subject": "__inviter__ sent you an invitation",
+ "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.\n",
+ "error-invitation-code-not-exist": "Invitation code doesn't exist"
+}
diff --git a/i18n/zh-CN.i18n.json b/i18n/zh-CN.i18n.json
index 859cb2b7..c6bbf7f9 100644
--- a/i18n/zh-CN.i18n.json
+++ b/i18n/zh-CN.i18n.json
@@ -322,5 +322,12 @@
"welcome-board": "“欢迎”看板",
"welcome-list1": "基本",
"welcome-list2": "高阶",
- "what-to-do": "要做什么?"
-} \ No newline at end of file
+ "what-to-do": "要做什么?",
+ "system-setting": "系统设置",
+ "option-setting": "设置",
+ "option-people": "成员",
+ "invitation-code": "邀请码",
+ "email-invite-register-subject": "__inviter__ 向您发出邀请",
+ "email-invite-register-text": "尊敬的 __user__,\n\n__inviter__ 邀请您加入看板参与协作。\n\n请点击下面的链接访问进行注册:\n\n__url__\n您的邀请码是: __icode__\n\n谢谢。\n",
+ "error-invitation-code-not-exist": "验证码不存在"
+}
diff --git a/models/invitationCodes.js b/models/invitationCodes.js
new file mode 100644
index 00000000..5761977a
--- /dev/null
+++ b/models/invitationCodes.js
@@ -0,0 +1,45 @@
+InvitationCodes = new Mongo.Collection('invitation_codes');
+
+InvitationCodes.attachSchema(new SimpleSchema({
+ code: {
+ type: String,
+ },
+ email: {
+ type: String,
+ unique: true,
+ regEx: SimpleSchema.RegEx.Email,
+ },
+ createdAt: {
+ type: Date,
+ denyUpdate: false,
+ },
+ // always be the admin if only one admin
+ authorId: {
+ type: String,
+ },
+ boardsToBeInvited: {
+ type: [String],
+ optional: true,
+ },
+ valid: {
+ type: Boolean,
+ defaultValue: true,
+ },
+}));
+
+InvitationCodes.helpers({
+ author(){
+ return Users.findOne(this.authorId);
+ },
+});
+
+// InvitationCodes.before.insert((userId, doc) => {
+ // doc.createdAt = new Date();
+ // doc.authorId = userId;
+// });
+
+if (Meteor.isServer) {
+ Boards.deny({
+ fetch: ['members'],
+ });
+}
diff --git a/models/settings.js b/models/settings.js
new file mode 100644
index 00000000..77bf8d24
--- /dev/null
+++ b/models/settings.js
@@ -0,0 +1,111 @@
+Settings = new Mongo.Collection('settings');
+
+Settings.attachSchema(new SimpleSchema({
+ strict: {
+ type: Boolean,
+ },
+ 'mailServer.username': {
+ type: String,
+ },
+ 'mailServer.password': {
+ type: String,
+ },
+ 'mailServer.host': {
+ type: String,
+ },
+ 'mailServer.port': {
+ type: String,
+ },
+ 'mailServer.from': {
+ type: String,
+ defaultValue: 'Kanban',
+ },
+ createdAt: {
+ type: Date,
+ denyUpdate: true,
+ },
+ modifiedAt: {
+ type: Date,
+ },
+}));
+Settings.helpers({
+ mailUrl () {
+ const mailUrl = `smtp://${this.mailServer.username}:${this.mailServer.password}@${this.mailServer.host}:${this.mailServer.port}/`;
+ return mailUrl;
+ },
+});
+Settings.allow({
+ update(userId) {
+ const user = Users.findOne(userId);
+ return user && user.isAdmin;
+ },
+});
+
+Settings.before.update((userId, doc, fieldNames, modifier) => {
+ modifier.$set = modifier.$set || {};
+ modifier.$set.modifiedAt = new Date();
+});
+
+if (Meteor.isServer) {
+ Meteor.startup(() => {
+ const setting = Settings.findOne({});
+ if(!setting){
+ const now = new Date();
+ const defaultSetting = {strict: false, mailServer: {
+ username: '', password:'', host: '', port:'', from: '',
+ }, createdAt: now, modifiedAt: now};
+ Settings.insert(defaultSetting);
+ }
+ const newSetting = Settings.findOne();
+ process.env.MAIL_URL = newSetting.mailUrl();
+ Accounts.emailTemplates.from = newSetting.mailServer.from;
+ });
+
+ function getRandomNum (min, max) {
+ const range = max - min;
+ const rand = Math.random();
+ return (min + Math.round(rand * range));
+ }
+
+ function sendInvitationEmail (_id){
+ const icode = InvitationCodes.findOne(_id);
+ const author = Users.findOne(Meteor.userId());
+ try {
+ const params = {
+ email: icode.email,
+ inviter: Users.findOne(icode.authorId).username,
+ user: icode.email.split('@')[0],
+ icode: icode.code,
+ url: FlowRouter.url('sign-up'),
+ };
+ const lang = author.getLanguage();
+ Email.send({
+ to: icode.email,
+ from: Accounts.emailTemplates.from,
+ subject: TAPi18n.__('email-invite-register-subject', params, lang),
+ text: TAPi18n.__('email-invite-register-text', params, lang),
+ });
+ } catch (e) {
+ throw new Meteor.Error('email-fail', e.message);
+ }
+ }
+
+ Meteor.methods({
+ sendInvitation(emails, boards) {
+ check(emails, [String]);
+ check(boards, [String]);
+ const user = Users.findOne(Meteor.userId());
+ if(!user.isAdmin){
+ throw new Meteor.Error('not-allowed');
+ }
+ emails.forEach((email) => {
+ if (email && SimpleSchema.RegEx.Email.test(email)) {
+ const code = getRandomNum(100000, 999999);
+ InvitationCodes.insert({code, email, boardsToBeInvited: boards, createdAt: new Date(), authorId: Meteor.userId()}, function(err, _id){
+ if(!err && _id) sendInvitationEmail(_id);
+ });
+ }
+ });
+ },
+ });
+}
diff --git a/models/users.js b/models/users.js
index 58513231..c7db8fff 100644
--- a/models/users.js
+++ b/models/users.js
@@ -348,7 +348,7 @@ if (Meteor.isServer) {
if (user._id === inviter._id) throw new Meteor.Error('error-user-notAllowSelf');
} else {
if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist');
-
+ if (Settings.findOne().strict) throw new Meteor.Error('error-user-notCreated');
const email = username;
username = email.substring(0, posAt);
const newUserId = Accounts.createUser({ username, email });
@@ -389,6 +389,28 @@ if (Meteor.isServer) {
return { username: user.username, email: user.emails[0].address };
},
});
+ Accounts.onCreateUser((options, user) => {
+ const userCount = Users.find().count();
+ if (userCount === 0){
+ user.isAdmin = true;
+ return user;
+ }
+ const strict = Settings.findOne().strict;
+ if (!strict) {
+ return user;
+ }
+
+ const iCode = options.profile.invitationcode | '';
+
+ const invitationCode = InvitationCodes.findOne({code: iCode, valid:true});
+ if (!invitationCode) {
+ throw new Meteor.Error('error-invitation-code-not-exist');
+ }else{
+ user.profile = {icode: options.profile.invitationcode};
+ }
+
+ return user;
+ });
}
if (Meteor.isServer) {
@@ -458,4 +480,25 @@ if (Meteor.isServer) {
});
});
}
+
+ Users.after.insert((userId, doc) => {
+
+ //invite user to corresponding boards
+ const strict = Settings.findOne().strict;
+ if (strict) {
+ const user = Users.findOne(doc._id);
+ const invitationCode = InvitationCodes.findOne({code: user.profile.icode, valid:true});
+ if (!invitationCode) {
+ throw new Meteor.Error('error-user-notCreated');
+ }else{
+ invitationCode.boardsToBeInvited.forEach((boardId) => {
+ const board = Boards.findOne(boardId);
+ board.addMember(doc._id);
+ });
+ user.profile = {invitedBoards: invitationCode.boardsToBeInvited};
+ InvitationCodes.update(invitationCode._id, {$set: {valid:false}});
+ }
+ }
+ });
}
+
diff --git a/server/publications/settings.js b/server/publications/settings.js
new file mode 100644
index 00000000..e36189e1
--- /dev/null
+++ b/server/publications/settings.js
@@ -0,0 +1,13 @@
+Meteor.publish('setting', () => {
+ return Settings.find({}, {fields:{strict: 1}});
+});
+
+Meteor.publish('mailServer', function () {
+ if (!Match.test(this.userId, String))
+ return [];
+ const user = Users.findOne(this.userId);
+ if(user && user.isAdmin){
+ return Settings.find({}, {fields: {mailServer: 1}});
+ }
+ return [];
+});
diff --git a/server/publications/users.js b/server/publications/users.js
index 4321e32b..4fd98e13 100644
--- a/server/publications/users.js
+++ b/server/publications/users.js
@@ -9,3 +9,11 @@ Meteor.publish('user-miniprofile', function(userId) {
},
});
});
+
+Meteor.publish('user-admin', function() {
+ return Meteor.users.find(this.userId, {
+ fields: {
+ isAdmin: 1,
+ },
+ });
+});