From 1dfb6ef477dba4d0faf4bf86026647f43fb2f5f6 Mon Sep 17 00:00:00 2001 From: lkisme Date: Fri, 24 Feb 2017 22:10:38 +0800 Subject: 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 --- models/invitationCodes.js | 45 +++++++++++++++++++ models/settings.js | 111 ++++++++++++++++++++++++++++++++++++++++++++++ models/users.js | 45 ++++++++++++++++++- 3 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 models/invitationCodes.js create mode 100644 models/settings.js (limited to 'models') 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}}); + } + } + }); } + -- cgit v1.2.3-1-g7c22 From e3c3cc0d8df14c84852a854383efa1b4bb83f218 Mon Sep 17 00:00:00 2001 From: lkisme Date: Sun, 26 Feb 2017 14:25:29 +0800 Subject: Make mailServer setting optional --- models/settings.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'models') diff --git a/models/settings.js b/models/settings.js index 77bf8d24..b160c2a4 100644 --- a/models/settings.js +++ b/models/settings.js @@ -6,18 +6,23 @@ Settings.attachSchema(new SimpleSchema({ }, 'mailServer.username': { type: String, + optional: true, }, 'mailServer.password': { type: String, + optional: true, }, 'mailServer.host': { type: String, + optional: true, }, 'mailServer.port': { type: String, + optional: true, }, 'mailServer.from': { type: String, + optional: true, defaultValue: 'Kanban', }, createdAt: { -- cgit v1.2.3-1-g7c22 From 39f2837838ba30ec02bfe9f33c9fa0dfca05d1a6 Mon Sep 17 00:00:00 2001 From: lkisme Date: Sun, 26 Feb 2017 21:11:15 +0800 Subject: wording change, email sending optimization, add texts to i18n --- models/settings.js | 6 +++--- models/users.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'models') diff --git a/models/settings.js b/models/settings.js index b160c2a4..b9ff1b37 100644 --- a/models/settings.js +++ b/models/settings.js @@ -1,7 +1,7 @@ Settings = new Mongo.Collection('settings'); Settings.attachSchema(new SimpleSchema({ - strict: { + disableRegistration: { type: Boolean, }, 'mailServer.username': { @@ -23,7 +23,7 @@ Settings.attachSchema(new SimpleSchema({ 'mailServer.from': { type: String, optional: true, - defaultValue: 'Kanban', + defaultValue: 'Wekan', }, createdAt: { type: Date, @@ -56,7 +56,7 @@ if (Meteor.isServer) { const setting = Settings.findOne({}); if(!setting){ const now = new Date(); - const defaultSetting = {strict: false, mailServer: { + const defaultSetting = {disableRegistration: false, mailServer: { username: '', password:'', host: '', port:'', from: '', }, createdAt: now, modifiedAt: now}; Settings.insert(defaultSetting); diff --git a/models/users.js b/models/users.js index c7db8fff..b77c7a84 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'); + if (Settings.findOne().disableRegistration) throw new Meteor.Error('error-user-notCreated'); const email = username; username = email.substring(0, posAt); const newUserId = Accounts.createUser({ username, email }); @@ -395,8 +395,8 @@ if (Meteor.isServer) { user.isAdmin = true; return user; } - const strict = Settings.findOne().strict; - if (!strict) { + const disableRegistration = Settings.findOne().disableRegistration; + if (!disableRegistration) { return user; } @@ -484,8 +484,8 @@ if (Meteor.isServer) { Users.after.insert((userId, doc) => { //invite user to corresponding boards - const strict = Settings.findOne().strict; - if (strict) { + const disableRegistration = Settings.findOne().disableRegistration; + if (disableRegistration) { const user = Users.findOne(doc._id); const invitationCode = InvitationCodes.findOne({code: user.profile.icode, valid:true}); if (!invitationCode) { -- cgit v1.2.3-1-g7c22