mirror of
https://github.com/wekan/wekan.git
synced 2025-12-18 16:30:13 +01:00
Merge branch 'devel' of https://github.com/thuanpq/wekan into thuanpq-devel
This commit is contained in:
commit
22e43e6591
9 changed files with 363 additions and 60 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -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/
|
||||||
|
|
|
||||||
89
client/components/settings/peopleBody.jade
Normal file
89
client/components/settings/peopleBody.jade
Normal 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'}}")
|
||||||
156
client/components/settings/peopleBody.js
Normal file
156
client/components/settings/peopleBody.js
Normal 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();
|
||||||
|
},
|
||||||
|
});
|
||||||
15
client/components/settings/peopleBody.styl
Normal file
15
client/components/settings/peopleBody.styl
Normal 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;
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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' });
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
7
server/publications/people.js
Normal file
7
server/publications/people.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
Meteor.publish('people', (limit) => {
|
||||||
|
check(limit, Number);
|
||||||
|
return Users.find({}, {
|
||||||
|
limit,
|
||||||
|
sort: {createdAt: -1},
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue