Merge branch 'devel' of https://github.com/thuanpq/wekan into thuanpq-devel

This commit is contained in:
Lauri Ojansivu 2017-11-18 23:37:15 +02:00
commit 22e43e6591
9 changed files with 363 additions and 60 deletions

1
.gitignore vendored
View file

@ -5,6 +5,7 @@
tmp/ tmp/
node_modules/ node_modules/
.vscode/ .vscode/
.idea/
.build/* .build/*
packages/kadira-flow-router/ packages/kadira-flow-router/
packages/meteor-useraccounts-core/ packages/meteor-useraccounts-core/

View file

@ -0,0 +1,89 @@
template(name="people")
.setting-content
unless currentUser.isAdmin
| {{_ 'error-notAuthorized'}}
else
.content-title
span {{_ 'people'}}
.content-body
.side-menu
ul
li.active
a.js-setting-menu(data-id="people-setting") {{_ 'people'}}
.main-body
if loading.get
+spinner
else if people.get
+peopleGeneral
template(name="peopleGeneral")
table
tbody
tr
th {{_ 'username'}}
th {{_ 'fullname'}}
th {{_ 'isAdmin'}}
th {{_ 'email'}}
th {{_ 'verified'}}
th {{_ 'createdAt'}}
th {{_ 'active'}}
th
each user in peopleList
+peopleRow(userId=user._id)
template(name="peopleRow")
tr
td.username {{ userData.username }}
td {{ userData.profile.fullname }}
td
if userData.isAdmin
| true
else
| false
td {{ userData.emails.[0].address }}
td
if userData.emails.[0].verified
| true
else
| false
td {{ moment userData.createdAt 'LLL' }}
td
if userData.loginDisabled
| false
else
| true
td
a.edit-user
| edit
template(name="editUserPopup")
form
label.hide.userId(type="text" value=user._id)
label
| {{_ 'fullname'}}
input.js-profile-fullname(type="text" value=user.profile.fullname autofocus)
label
| {{_ 'username'}}
span.error.hide.username-taken
| {{_ 'error-username-taken'}}
input.js-profile-username(type="text" value=user.username)
label
| {{_ 'initials'}}
input.js-profile-initials(type="text" value=user.profile.initials)
label
| {{_ 'email'}}
span.error.hide.email-taken
| {{_ 'error-email-taken'}}
input.js-profile-email(type="email" value="{{user.emails.[0].address}}")
label
| {{_ 'isAdmin'}}
select.select-role.js-profile-isadmin
option(value="false") No
option(value="true" selected="{{user.isAdmin}}") Yes
label
| {{_ 'isActive'}}
select.select-active.js-profile-isactive
option(value="false") Yes
option(value="true" selected="{{user.loginDisabled}}") No
input.primary.wide(type="submit" value="{{_ 'save'}}")

View file

@ -0,0 +1,156 @@
const usersPerPage = 25;
BlazeComponent.extendComponent({
mixins() {
return [Mixins.InfiniteScrolling];
},
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
this.people = new ReactiveVar(true);
this.page = new ReactiveVar(1);
this.loadNextPageLocked = false;
this.callFirstWith(null, 'resetNextPeak');
this.autorun(() => {
const limit = this.page.get() * usersPerPage;
this.subscribe('people', limit, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
this.calculateNextPeak();
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
if (nextPeakBefore === nextPeakAfter) {
this.callFirstWith(null, 'resetNextPeak');
}
});
});
},
loadNextPage() {
if (this.loadNextPageLocked === false) {
this.page.set(this.page.get() + 1);
this.loadNextPageLocked = true;
}
},
calculateNextPeak() {
const element = this.find('.main-body');
if (element) {
const altitude = element.scrollHeight;
this.callFirstWith(this, 'setNextPeak', altitude);
}
},
reachNextPeak() {
this.loadNextPage();
},
setError(error) {
this.error.set(error);
},
setLoading(w) {
this.loading.set(w);
},
peopleList() {
return Users.find({}, {
fields: {_id: true},
});
},
}).register('people');
Template.peopleRow.helpers({
userData() {
const userCollection = this.esSearch ? ESSearchResults : Users;
return userCollection.findOne(this.userId);
},
});
Template.editUserPopup.helpers({
user() {
return Users.findOne(this.userId);
},
});
BlazeComponent.extendComponent({
onCreated() {
},
user() {
return Users.findOne(this.userId);
},
events() {
return [{
'click a.edit-user': Popup.open('editUser'),
}];
},
}).register('peopleRow');
Template.editUserPopup.events({
submit(evt, tpl) {
evt.preventDefault();
const user = Users.findOne(this.userId);
const fullname = tpl.find('.js-profile-fullname').value.trim();
const username = tpl.find('.js-profile-username').value.trim();
const initials = tpl.find('.js-profile-initials').value.trim();
const isAdmin = tpl.find('.js-profile-isadmin').value.trim();
const isActive = tpl.find('.js-profile-isactive').value.trim();
const email = tpl.find('.js-profile-email').value.trim();
let isChangeUserName = false;
let isChangeEmail = false;
Users.update(this.userId, {
$set: {
'profile.fullname': fullname,
'profile.initials': initials,
'isAdmin': isAdmin === 'true',
'loginDisabled': isActive === 'true',
},
});
isChangeUserName = username !== user.username;
isChangeEmail = email.toLowerCase() !== user.emails[0].address.toLowerCase();
if (isChangeUserName && isChangeEmail) {
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), this.userId, function (error) {
const usernameMessageElement = tpl.$('.username-taken');
const emailMessageElement = tpl.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
emailMessageElement.hide();
} else if (errorElement === 'email-already-taken') {
usernameMessageElement.hide();
emailMessageElement.show();
}
} else {
usernameMessageElement.hide();
emailMessageElement.hide();
Popup.close();
}
});
} else if (isChangeUserName) {
Meteor.call('setUsername', username, this.userId, function (error) {
const usernameMessageElement = tpl.$('.username-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
}
} else {
usernameMessageElement.hide();
Popup.close();
}
});
} else if (isChangeEmail) {
Meteor.call('setEmail', email.toLowerCase(), this.userId, function (error) {
const emailMessageElement = tpl.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'email-already-taken') {
emailMessageElement.show();
}
} else {
emailMessageElement.hide();
Popup.close();
}
});
} else Popup.close();
},
});

View file

@ -0,0 +1,15 @@
.main-body
overflow: scroll;
table
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
td, th
border: 1px solid #d2d0d0;
text-align: left;
padding: 8px;
tr:nth-child(even)
background-color: #dddddd;

View file

@ -9,13 +9,14 @@ template(name="settingHeaderBar")
a.setting-header-btn.settings(href="{{pathFor 'setting'}}") a.setting-header-btn.settings(href="{{pathFor 'setting'}}")
i.fa(class="fa-cog") i.fa(class="fa-cog")
span {{_ 'settings'}} span {{_ 'settings'}}
a.setting-header-btn.informations(href="{{pathFor 'information'}}") a.setting-header-btn.informations(href="{{pathFor 'information'}}")
i.fa(class="fa-info-circle") i.fa(class="fa-info-circle")
span {{_ 'info'}} span {{_ 'info'}}
//TODO
// a.setting-header-btn.people a.setting-header-btn.people(href="{{pathFor 'people'}}")
// i.fa(class="fa-users") i.fa(class="fa-users")
// span {{_ 'people'}} span {{_ 'people'}}
else else
a.setting-header-btn.js-log-in( a.setting-header-btn.js-log-in(

View file

@ -42,7 +42,7 @@ Template.editProfilePopup.events({
isChangeUserName = username !== Meteor.user().username; isChangeUserName = username !== Meteor.user().username;
isChangeEmail = email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase(); isChangeEmail = email.toLowerCase() !== Meteor.user().emails[0].address.toLowerCase();
if (isChangeUserName && isChangeEmail) { if (isChangeUserName && isChangeEmail) {
Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), function(error) { Meteor.call('setUsernameAndEmail', username, email.toLowerCase(), Meteor.userId(), function (error) {
const usernameMessageElement = tpl.$('.username-taken'); const usernameMessageElement = tpl.$('.username-taken');
const emailMessageElement = tpl.$('.email-taken'); const emailMessageElement = tpl.$('.email-taken');
if (error) { if (error) {
@ -61,7 +61,7 @@ Template.editProfilePopup.events({
} }
}); });
} else if (isChangeUserName) { } else if (isChangeUserName) {
Meteor.call('setUsername', username, function(error) { Meteor.call('setUsername', username, Meteor.userId(), function (error) {
const messageElement = tpl.$('.username-taken'); const messageElement = tpl.$('.username-taken');
if (error) { if (error) {
messageElement.show(); messageElement.show();
@ -71,7 +71,7 @@ Template.editProfilePopup.events({
} }
}); });
} else if (isChangeEmail) { } else if (isChangeEmail) {
Meteor.call('setEmail', email.toLowerCase(), function(error) { Meteor.call('setEmail', email.toLowerCase(), Meteor.userId(), function (error) {
const messageElement = tpl.$('.email-taken'); const messageElement = tpl.$('.email-taken');
if (error) { if (error) {
messageElement.show(); messageElement.show();

View file

@ -140,6 +140,26 @@ FlowRouter.route('/information', {
}, },
}); });
FlowRouter.route('/people', {
name: 'people',
triggersEnter: [
AccountsTemplates.ensureSignedIn,
() => {
Session.set('currentBoard', null);
Session.set('currentCard', null);
Filter.reset();
EscapeActions.executeAll();
},
],
action() {
BlazeLayout.render('defaultLayout', {
headerBar: 'settingHeaderBar',
content: 'people',
});
},
});
FlowRouter.notFound = { FlowRouter.notFound = {
action() { action() {
BlazeLayout.render('defaultLayout', { content: 'notFound' }); BlazeLayout.render('defaultLayout', { content: 'notFound' });

View file

@ -118,6 +118,13 @@ Users.attachSchema(new SimpleSchema({
}, },
})); }));
Users.allow({
update(userId) {
const user = Users.findOne(userId);
return user && Meteor.user().isAdmin;
},
});
// Search a user in the complete server database by its name or username. This // Search a user in the complete server database by its name or username. This
// is used for instance to add a new user to a board. // is used for instance to add a new user to a board.
const searchInFields = ['username', 'profile.fullname']; const searchInFields = ['username', 'profile.fullname'];
@ -325,13 +332,13 @@ Users.mutations({
}); });
Meteor.methods({ Meteor.methods({
setUsername(username) { setUsername(username, userId) {
check(username, String); check(username, String);
const nUsersWithUsername = Users.find({username}).count(); const nUsersWithUsername = Users.find({username}).count();
if (nUsersWithUsername > 0) { if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken'); throw new Meteor.Error('username-already-taken');
} else { } else {
Users.update(this.userId, { $set: { username } }); Users.update(userId, {$set: {username}});
} }
}, },
toggleSystemMessages() { toggleSystemMessages() {
@ -342,13 +349,13 @@ Meteor.methods({
check(limit, Number); check(limit, Number);
Meteor.user().setShowCardsCountAt(limit); Meteor.user().setShowCardsCountAt(limit);
}, },
setEmail(email) { setEmail(email, userId) {
check(email, String); check(email, String);
const existingUser = Users.findOne({'emails.address': email}, {fields: {_id: 1}}); const existingUser = Users.findOne({'emails.address': email}, {fields: {_id: 1}});
if (existingUser) { if (existingUser) {
throw new Meteor.Error('email-already-taken'); throw new Meteor.Error('email-already-taken');
} else { } else {
Users.update(this.userId, { Users.update(userId, {
$set: { $set: {
emails: [{ emails: [{
address: email, address: email,
@ -358,11 +365,12 @@ Meteor.methods({
}); });
} }
}, },
setUsernameAndEmail(username, email) { setUsernameAndEmail(username, email, userId) {
check(username, String); check(username, String);
check(email, String); check(email, String);
Meteor.call('setUsername', username); check(userId, String);
Meteor.call('setEmail', email); Meteor.call('setUsername', username, userId);
Meteor.call('setEmail', email, userId);
}, },
}); });
@ -457,7 +465,11 @@ if (Meteor.isServer) {
if (!options || !options.profile) { if (!options || !options.profile) {
throw new Meteor.Error('error-invitation-code-blank', 'The invitation code is required'); throw new Meteor.Error('error-invitation-code-blank', 'The invitation code is required');
} }
const invitationCode = InvitationCodes.findOne({ code: options.profile.invitationcode, email: options.email, valid: true }); const invitationCode = InvitationCodes.findOne({
code: options.profile.invitationcode,
email: options.email,
valid: true,
});
if (!invitationCode) { if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist', 'The invitation code doesn\'t exist'); throw new Meteor.Error('error-invitation-code-not-exist', 'The invitation code doesn\'t exist');
} else { } else {
@ -492,6 +504,7 @@ if (Meteor.isServer) {
function getStarredBoardsIds(doc) { function getStarredBoardsIds(doc) {
return doc.profile && doc.profile.starredBoards; return doc.profile && doc.profile.starredBoards;
} }
const oldIds = getStarredBoardsIds(this.previous); const oldIds = getStarredBoardsIds(this.previous);
const newIds = getStarredBoardsIds(user); const newIds = getStarredBoardsIds(user);
@ -503,6 +516,7 @@ if (Meteor.isServer) {
Boards.update(boardId, {$inc: {stars: inc}}); Boards.update(boardId, {$inc: {stars: inc}});
}); });
} }
incrementBoards(_.difference(oldIds, newIds), -1); incrementBoards(_.difference(oldIds, newIds), -1);
incrementBoards(_.difference(newIds, oldIds), +1); incrementBoards(_.difference(newIds, oldIds), +1);
}); });

View file

@ -0,0 +1,7 @@
Meteor.publish('people', (limit) => {
check(limit, Number);
return Users.find({}, {
limit,
sort: {createdAt: -1},
});
});