wekan/client/components/settings/peopleBody.js
Lauri Ojansivu ae0d059b6f Feature: Added brute force login protection settings to Admin Panel/People/Locked Users.
Added filtering of Admin Panel/People/People: All Users/Locked Users Only/Active/Not Active.
Added visual indicators: red lock icon for locked users, green check for active users, and red X for inactive users.
Added "Unlock All" button to quickly unlock all brute force locked users.
Added ability to toggle user active status directly from the People page.
Moved lockout settings from environment variables to database so admins can configure the lockout thresholds directly in the UI.

Thanks to xet7.
2025-08-05 00:31:43 +03:00

1312 lines
40 KiB
JavaScript

import { ReactiveCache } from '/imports/reactiveCache';
import LockoutSettings from '/models/lockoutSettings';
const orgsPerPage = 25;
const teamsPerPage = 25;
const usersPerPage = 25;
let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user
let selectedUserChkBoxUserIds = [];
BlazeComponent.extendComponent({
mixins() {
return [Mixins.InfiniteScrolling];
},
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
this.orgSetting = new ReactiveVar(true);
this.teamSetting = new ReactiveVar(false);
this.peopleSetting = new ReactiveVar(false);
this.lockedUsersSetting = new ReactiveVar(false);
this.findOrgsOptions = new ReactiveVar({});
this.findTeamsOptions = new ReactiveVar({});
this.findUsersOptions = new ReactiveVar({});
this.numberOrgs = new ReactiveVar(0);
this.numberTeams = new ReactiveVar(0);
this.numberPeople = new ReactiveVar(0);
this.userFilterType = new ReactiveVar('all');
this.page = new ReactiveVar(1);
this.loadNextPageLocked = false;
this.callFirstWith(null, 'resetNextPeak');
this.autorun(() => {
const limitOrgs = this.page.get() * orgsPerPage;
const limitTeams = this.page.get() * teamsPerPage;
const limitUsers = this.page.get() * usersPerPage;
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();
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
if (nextPeakBefore === nextPeakAfter) {
this.callFirstWith(null, 'resetNextPeak');
}
});
});
},
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();
},
'click #addOrRemoveTeam'(){
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'block';
},
'keydown #searchInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterPeople();
}
},
'change #userFilterSelect'(event) {
const filterType = $(event.target).val();
this.userFilterType.set(filterType);
this.filterPeople();
},
'click #unlockAllUsers'(event) {
event.preventDefault();
if (confirm(TAPi18n.__('accounts-lockout-confirm-unlock-all'))) {
Meteor.call('unlockAllUsers', (error) => {
if (error) {
console.error('Error unlocking all users:', error);
} else {
// Show a brief success message
const message = document.createElement('div');
message.className = 'unlock-all-success';
message.textContent = TAPi18n.__('accounts-lockout-all-users-unlocked');
document.body.appendChild(message);
// Remove the message after a short delay
setTimeout(() => {
if (message.parentNode) {
message.parentNode.removeChild(message);
}
}, 3000);
}
});
}
},
'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,
'click a.js-locked-users-menu': this.switchMenu,
},
];
},
filterPeople() {
const value = $('#searchInput').first().val();
const filterType = this.userFilterType.get();
const currentTime = Number(new Date());
let query = {};
// Apply text search filter if there's a search value
if (value !== '') {
const regex = new RegExp(value, 'i');
query = {
$or: [
{ username: regex },
{ 'profile.fullname': regex },
{ 'emails.address': regex },
],
};
}
// Apply filter based on selected option
switch (filterType) {
case 'locked':
// Show only locked users
query['services.accounts-lockout.unlockTime'] = { $gt: currentTime };
break;
case 'active':
// Show only active users (loginDisabled is false or undefined)
query['loginDisabled'] = { $ne: true };
break;
case 'inactive':
// Show only inactive users (loginDisabled is true)
query['loginDisabled'] = true;
break;
case 'all':
default:
// Show all users, no additional filter
break;
}
this.findUsersOptions.set(query);
},
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);
},
orgList() {
const orgs = ReactiveCache.getOrgs(this.findOrgsOptions.get(), {
sort: { orgDisplayName: 1 },
fields: { _id: true },
});
this.numberOrgs.set(orgs.length);
return orgs;
},
teamList() {
const teams = ReactiveCache.getTeams(this.findTeamsOptions.get(), {
sort: { teamDisplayName: 1 },
fields: { _id: true },
});
this.numberTeams.set(teams.length);
return teams;
},
peopleList() {
const users = ReactiveCache.getUsers(this.findUsersOptions.get(), {
sort: { username: 1 },
fields: { _id: true },
});
this.numberPeople.set(users.length);
return users;
},
orgNumber() {
return this.numberOrgs.get();
},
teamNumber() {
return this.numberTeams.get();
},
peopleNumber() {
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);
this.lockedUsersSetting.set('locked-users-setting' === targetID);
// When switching to locked users tab, refresh the locked users list
if ('locked-users-setting' === targetID) {
// Find the lockedUsersGeneral component and call refreshLockedUsers
const lockedUsersComponent = Blaze.getView($('.main-body')[0])._templateInstance;
if (lockedUsersComponent && lockedUsersComponent.refreshLockedUsers) {
lockedUsersComponent.refreshLockedUsers();
}
}
}
},
}).register('people');
Template.orgRow.helpers({
orgData() {
return ReactiveCache.getOrg(this.orgId);
},
});
Template.teamRow.helpers({
teamData() {
return ReactiveCache.getTeam(this.teamId);
},
});
Template.peopleRow.helpers({
userData() {
return ReactiveCache.getUser(this.userId);
},
isUserLocked() {
const user = ReactiveCache.getUser(this.userId);
if (!user) return false;
// Check if user has accounts-lockout with unlockTime property
if (user.services &&
user.services['accounts-lockout'] &&
user.services['accounts-lockout'].unlockTime) {
// Check if unlockTime is in the future
const currentTime = Number(new Date());
return user.services['accounts-lockout'].unlockTime > currentTime;
}
return false;
}
});
// Initialize filter dropdown
Template.people.rendered = function() {
const template = this;
// Set the initial value of the dropdown
Tracker.afterFlush(function() {
if (template.findAll('#userFilterSelect').length) {
$('#userFilterSelect').val('all');
}
});
};
Template.editUserPopup.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.editOrgPopup.helpers({
org() {
return ReactiveCache.getOrg(this.orgId);
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
Template.editTeamPopup.helpers({
team() {
return ReactiveCache.getTeam(this.teamId);
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
Template.editUserPopup.helpers({
user() {
return ReactiveCache.getUser(this.userId);
},
authentications() {
return Template.instance().authenticationMethods.get();
},
orgsDatas() {
const ret = ReactiveCache.getOrgs({}, {sort: { orgDisplayName: 1 }});
return ret;
},
teamsDatas() {
const ret = ReactiveCache.getTeams({}, {sort: { teamDisplayName: 1 }});
return ret;
},
isSelected(match) {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === match;
},
isLdap() {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === 'ldap';
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
Template.newOrgPopup.onCreated(function () {
this.errorMessage = new ReactiveVar('');
});
Template.newTeamPopup.onCreated(function () {
this.errorMessage = new ReactiveVar('');
});
Template.newUserPopup.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.newOrgPopup.helpers({
org() {
return ReactiveCache.getOrg(this.orgId);
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
Template.newTeamPopup.helpers({
team() {
return ReactiveCache.getTeam(this.teamId);
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
Template.newUserPopup.helpers({
user() {
return ReactiveCache.getUser(this.userId);
},
authentications() {
return Template.instance().authenticationMethods.get();
},
orgsDatas() {
const ret = ReactiveCache.getOrgs({}, {sort: { orgDisplayName: 1 }});
return ret;
},
teamsDatas() {
const ret = ReactiveCache.getTeams({}, {sort: { teamDisplayName: 1 }});
return ret;
},
isSelected(match) {
const userId = Template.instance().data.userId;
if(userId){
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === match;
}
else{
false;
}
},
isLdap() {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === 'ldap';
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});
BlazeComponent.extendComponent({
onCreated() {},
org() {
return ReactiveCache.getOrg(this.orgId);
},
events() {
return [
{
'click a.edit-org': Popup.open('editOrg'),
'click a.more-settings-org': Popup.open('settingsOrg'),
},
];
},
}).register('orgRow');
BlazeComponent.extendComponent({
onCreated() {},
team() {
return ReactiveCache.getTeam(this.teamId);
},
events() {
return [
{
'click a.edit-team': Popup.open('editTeam'),
'click a.more-settings-team': Popup.open('settingsTeam'),
},
];
},
}).register('teamRow');
BlazeComponent.extendComponent({
onCreated() {},
user() {
return ReactiveCache.getUser(this.userId);
},
events() {
return [
{
'click a.edit-user': Popup.open('editUser'),
'click a.more-settings-user': Popup.open('settingsUser'),
'click .selectUserChkBox': function(ev){
if(ev.currentTarget){
if(ev.currentTarget.checked){
if(!selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
selectedUserChkBoxUserIds.push(ev.currentTarget.id);
}
}
else{
if(selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
let index = selectedUserChkBoxUserIds.indexOf(ev.currentTarget.id);
if(index > -1)
selectedUserChkBoxUserIds.splice(index, 1);
}
}
}
if(selectedUserChkBoxUserIds.length > 0)
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
else
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
},
'click .js-toggle-active-status': function(ev) {
ev.preventDefault();
const userId = this.userId;
const user = ReactiveCache.getUser(userId);
if (!user) return;
// Toggle loginDisabled status
const isActive = !(user.loginDisabled === true);
// Update the user's active status
Users.update(userId, {
$set: {
loginDisabled: isActive
}
});
},
'click .js-toggle-lock-status': function(ev){
ev.preventDefault();
const userId = this.userId;
const user = ReactiveCache.getUser(userId);
if (!user) return;
// Check if user is currently locked
const isLocked = user.services &&
user.services['accounts-lockout'] &&
user.services['accounts-lockout'].unlockTime &&
user.services['accounts-lockout'].unlockTime > Number(new Date());
if (isLocked) {
// Unlock the user
Meteor.call('unlockUser', userId, (error) => {
if (error) {
console.error('Error unlocking user:', error);
}
});
} else {
// Lock the user - this is optional, you may want to only allow unlocking
// If you want to implement locking too, you would need a server method for it
// For now, we'll leave this as a no-op
}
},
},
];
},
}).register('peopleRow');
BlazeComponent.extendComponent({
onCreated() {},
teamsDatas() {
const ret = ReactiveCache.getTeams({}, {sort: { teamDisplayName: 1 }});
return ret;
},
events() {
return [
{
'click #cancelBtn': function(){
let selectedElt = document.getElementById("jsteamsUser");
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
},
'click #addTeamBtn': function(){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
let userTms = [];
let currentUser;
let currUserTeamIndex;
selectedElt = document.getElementById("jsteamsUser");
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
if(document.getElementById('addAction').checked){
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]);
userTms = currentUser.teams;
if(userTms == undefined || userTms.length == 0){
userTms = [];
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
})
}
else if(userTms.length > 0)
{
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
if(currUserTeamIndex == -1){
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
});
}
}
Users.update(selectedUserChkBoxUserIds[i], {
$set:{
teams: userTms
}
});
}
}
else{
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]);
userTms = currentUser.teams;
if(userTms !== undefined || userTms.length > 0)
{
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
if(currUserTeamIndex != -1){
userTms.splice(currUserTeamIndex, 1);
}
}
Users.update(selectedUserChkBoxUserIds[i], {
$set:{
teams: userTms
}
});
}
}
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
},
},
];
},
}).register('modifyTeamsUsers');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-org': Popup.open('newOrg'),
},
];
},
}).register('newOrgRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-team': Popup.open('newTeam'),
},
];
},
}).register('newTeamRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-user': Popup.open('newUser'),
},
];
},
}).register('newUserRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click .allUserChkBox': function(ev){
selectedUserChkBoxUserIds = [];
const checkboxes = document.getElementsByClassName("selectUserChkBox");
if(ev.currentTarget){
if(ev.currentTarget.checked){
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
selectedUserChkBoxUserIds.push(checkboxes[i].id);
checkboxes[i].checked = true;
}
}
}
else{
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
checkboxes[i].checked = false;
}
}
}
}
if(selectedUserChkBoxUserIds.length > 0)
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
else
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
},
},
];
},
}).register('selectAllUser');
Template.editOrgPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const org = ReactiveCache.getOrg(this.orgId);
const orgDisplayName = templateInstance
.find('.js-orgDisplayName')
.value.trim();
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
const orgAutoAddUsersWithDomainName = templateInstance.find('.js-orgAutoAddUsersWithDomainName').value.trim();
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
const orgIsActive = templateInstance.find('.js-org-isactive').value.trim() == 'true';
const isChangeOrgDisplayName = orgDisplayName !== org.orgDisplayName;
const isChangeOrgDesc = orgDesc !== org.orgDesc;
const isChangeOrgShortName = orgShortName !== org.orgShortName;
const isChangeOrgAutoAddUsersWithDomainName = orgAutoAddUsersWithDomainName !== org.orgAutoAddUsersWithDomainName;
const isChangeOrgWebsite = orgWebsite !== org.orgWebsite;
const isChangeOrgIsActive = orgIsActive !== org.orgIsActive;
if (
isChangeOrgDisplayName ||
isChangeOrgDesc ||
isChangeOrgShortName ||
isChangeOrgAutoAddUsersWithDomainName ||
isChangeOrgWebsite ||
isChangeOrgIsActive
) {
Meteor.call(
'setOrgAllFields',
org,
orgDisplayName,
orgDesc,
orgShortName,
orgAutoAddUsersWithDomainName,
orgWebsite,
orgIsActive,
);
}
Popup.back();
},
});
Template.editTeamPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const team = ReactiveCache.getTeam(this.teamId);
const teamDisplayName = templateInstance
.find('.js-teamDisplayName')
.value.trim();
const teamDesc = templateInstance.find('.js-teamDesc').value.trim();
const teamShortName = templateInstance
.find('.js-teamShortName')
.value.trim();
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
const teamIsActive =
templateInstance.find('.js-team-isactive').value.trim() == 'true';
const isChangeTeamDisplayName = teamDisplayName !== team.teamDisplayName;
const isChangeTeamDesc = teamDesc !== team.teamDesc;
const isChangeTeamShortName = teamShortName !== team.teamShortName;
const isChangeTeamWebsite = teamWebsite !== team.teamWebsite;
const isChangeTeamIsActive = teamIsActive !== team.teamIsActive;
if (
isChangeTeamDisplayName ||
isChangeTeamDesc ||
isChangeTeamShortName ||
isChangeTeamWebsite ||
isChangeTeamIsActive
) {
Meteor.call(
'setTeamAllFields',
team,
teamDisplayName,
teamDesc,
teamShortName,
teamWebsite,
teamIsActive,
);
}
Popup.back();
},
});
Template.editUserPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const user = ReactiveCache.getUser(this.userId);
const username = templateInstance.find('.js-profile-username').value.trim();
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
const initials = templateInstance.find('.js-profile-initials').value.trim();
const password = templateInstance.find('.js-profile-password').value;
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
const email = templateInstance.find('.js-profile-email').value.trim();
const verified = templateInstance.find('.js-profile-email-verified').value.trim();
const authentication = templateInstance.find('.js-authenticationMethod').value.trim();
const importUsernames = templateInstance.find('.js-import-usernames').value.trim();
const userOrgs = templateInstance.find('.js-userOrgs').value.trim();
const userOrgsIds = templateInstance.find('.js-userOrgIds').value.trim();
const userTeams = templateInstance.find('.js-userteams').value.trim();
const userTeamsIds = templateInstance.find('.js-userteamIds').value.trim();
const isChangePassword = password.length > 0;
const isChangeUserName = username !== user.username;
const isChangeInitials = initials.length > 0;
const isChangeEmailVerified = verified !== user.emails[0].verified;
// If previously email address has not been set, it is undefined,
// check for undefined, and allow adding email address.
const isChangeEmail =
email.toLowerCase() !==
(typeof user.emails !== 'undefined'
? user.emails[0].address.toLowerCase()
: false);
Users.update(this.userId, {
$set: {
'profile.fullname': fullname,
isAdmin: isAdmin === 'true',
loginDisabled: isActive === 'true',
authenticationMethod: authentication,
importUsernames: Users.parseImportUsernames(importUsernames),
},
});
let userTeamsList = userTeams.split(",");
let userTeamsIdsList = userTeamsIds.split(",");
let userTms = [];
if(userTeams != ''){
for(let i = 0; i < userTeamsList.length; i++){
userTms.push({
"teamId": userTeamsIdsList[i],
"teamDisplayName": userTeamsList[i],
})
}
}
Users.update(this.userId, {
$set:{
teams: userTms
}
});
let userOrgsList = userOrgs.split(",");
let userOrgsIdsList = userOrgsIds.split(",");
let userOrganizations = [];
if(userOrgs != ''){
for(let i = 0; i < userOrgsList.length; i++){
userOrganizations.push({
"orgId": userOrgsIdsList[i],
"orgDisplayName": userOrgsList[i],
})
}
}
Users.update(this.userId, {
$set:{
orgs: userOrganizations
}
});
if (isChangePassword) {
Meteor.call('setPassword', password, this.userId);
}
if (isChangeEmailVerified) {
Meteor.call('setEmailVerified', email, verified === 'true', this.userId);
}
if (isChangeInitials) {
Meteor.call('setInitials', initials, this.userId);
}
if (isChangeUserName && isChangeEmail) {
Meteor.call(
'setUsernameAndEmail',
username,
email.toLowerCase(),
this.userId,
function (error) {
const usernameMessageElement = templateInstance.$('.username-taken');
const emailMessageElement = templateInstance.$('.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.back();
}
},
);
} else if (isChangeUserName) {
Meteor.call('setUsername', username, this.userId, function (error) {
const usernameMessageElement = templateInstance.$('.username-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'username-already-taken') {
usernameMessageElement.show();
}
} else {
usernameMessageElement.hide();
Popup.back();
}
});
} else if (isChangeEmail) {
Meteor.call(
'setEmail',
email.toLowerCase(),
this.userId,
function (error) {
const emailMessageElement = templateInstance.$('.email-taken');
if (error) {
const errorElement = error.error;
if (errorElement === 'email-already-taken') {
emailMessageElement.show();
}
} else {
emailMessageElement.hide();
Popup.back();
}
},
);
} else Popup.back();
},
'click #addUserOrg'(event) {
event.preventDefault();
userOrgsTeamsAction = "addOrg";
document.getElementById("jsOrgs").style.display = 'block';
document.getElementById("jsTeams").style.display = 'none';
},
'click #removeUserOrg'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeOrg";
document.getElementById("jsOrgs").style.display = 'block';
document.getElementById("jsTeams").style.display = 'none';
},
'click #addUserTeam'(event) {
event.preventDefault();
userOrgsTeamsAction = "addTeam";
document.getElementById("jsTeams").style.display = 'block';
document.getElementById("jsOrgs").style.display = 'none';
},
'click #removeUserTeam'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeTeam";
document.getElementById("jsTeams").style.display = 'block';
document.getElementById("jsOrgs").style.display = 'none';
},
'change #jsOrgs'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement();
},
'change #jsTeams'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement();
},
});
UpdateUserOrgsOrTeamsElement = function(isNewUser = false){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
let inputElt;
let inputEltId;
let lstInputValues = [];
let lstInputValuesIds = [];
let index;
let indexId;
switch(userOrgsTeamsAction)
{
case "addOrg":
case "removeOrg":
inputElt = !isNewUser ? document.getElementById("jsUserOrgsInPut") : document.getElementById("jsUserOrgsInPutNewUser");
inputEltId = !isNewUser ? document.getElementById("jsUserOrgIdsInPut") : document.getElementById("jsUserOrgIdsInPutNewUser");
selectedElt = !isNewUser ? document.getElementById("jsOrgs") : document.getElementById("jsOrgsNewUser");
break;
case "addTeam":
case "removeTeam":
inputElt = !isNewUser ? document.getElementById("jsUserTeamsInPut") : document.getElementById("jsUserTeamsInPutNewUser");
inputEltId = !isNewUser ? document.getElementById("jsUserTeamIdsInPut") : document.getElementById("jsUserTeamIdsInPutNewUser");
selectedElt = !isNewUser ? document.getElementById("jsTeams") : document.getElementById("jsTeamsNewUser");
break;
default:
break;
}
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
lstInputValues = inputElt.value.trim().split(",");
if(lstInputValues.length == 1 && lstInputValues[0] == ''){
lstInputValues = [];
}
lstInputValuesIds = inputEltId.value.trim().split(",");
if(lstInputValuesIds.length == 1 && lstInputValuesIds[0] == ''){
lstInputValuesIds = [];
}
index = lstInputValues.indexOf(selectedEltValue);
indexId = lstInputValuesIds.indexOf(selectedEltValueId);
if(userOrgsTeamsAction == "addOrg" || userOrgsTeamsAction == "addTeam"){
if(index <= -1 && selectedEltValueId != "-1"){
lstInputValues.push(selectedEltValue);
}
if(indexId <= -1 && selectedEltValueId != "-1"){
lstInputValuesIds.push(selectedEltValueId);
}
}
else{
if(index > -1 && selectedEltValueId != "-1"){
lstInputValues.splice(index, 1);
}
if(indexId > -1 && selectedEltValueId != "-1"){
lstInputValuesIds.splice(indexId, 1);
}
}
if(lstInputValues.length > 0){
inputElt.value = lstInputValues.join(",");
}
else{
inputElt.value = "";
}
if(lstInputValuesIds.length > 0){
inputEltId.value = lstInputValuesIds.join(",");
}
else{
inputEltId.value = "";
}
selectedElt.value = "-1";
selectedElt.style.display = "none";
}
Template.newOrgPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const orgDisplayName = templateInstance
.find('.js-orgDisplayName')
.value.trim();
const orgDesc = templateInstance.find('.js-orgDesc').value.trim();
const orgShortName = templateInstance.find('.js-orgShortName').value.trim();
const orgAutoAddUsersWithDomainName = templateInstance.find('.js-orgAutoAddUsersWithDomainName').value.trim();
const orgWebsite = templateInstance.find('.js-orgWebsite').value.trim();
const orgIsActive =
templateInstance.find('.js-org-isactive').value.trim() == 'true';
Meteor.call(
'setCreateOrg',
orgDisplayName,
orgDesc,
orgShortName,
orgAutoAddUsersWithDomainName,
orgWebsite,
orgIsActive,
);
Popup.back();
},
});
Template.newTeamPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const teamDisplayName = templateInstance
.find('.js-teamDisplayName')
.value.trim();
const teamDesc = templateInstance.find('.js-teamDesc').value.trim();
const teamShortName = templateInstance
.find('.js-teamShortName')
.value.trim();
const teamWebsite = templateInstance.find('.js-teamWebsite').value.trim();
const teamIsActive =
templateInstance.find('.js-team-isactive').value.trim() == 'true';
Meteor.call(
'setCreateTeam',
teamDisplayName,
teamDesc,
teamShortName,
teamWebsite,
teamIsActive,
);
Popup.back();
},
});
Template.newUserPopup.events({
submit(event, templateInstance) {
event.preventDefault();
const fullname = templateInstance.find('.js-profile-fullname').value.trim();
const username = templateInstance.find('.js-profile-username').value.trim();
const initials = templateInstance.find('.js-profile-initials').value.trim();
const password = templateInstance.find('.js-profile-password').value;
const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
const isActive = templateInstance.find('.js-profile-isactive').value.trim();
const email = templateInstance.find('.js-profile-email').value.trim();
const importUsernames = Users.parseImportUsernames(
templateInstance.find('.js-import-usernames').value,
);
const userOrgs = templateInstance.find('.js-userOrgsNewUser').value.trim();
const userOrgsIds = templateInstance.find('.js-userOrgIdsNewUser').value.trim();
const userTeams = templateInstance.find('.js-userteamsNewUser').value.trim();
const userTeamsIds = templateInstance.find('.js-userteamIdsNewUser').value.trim();
let userTeamsList = userTeams.split(",");
let userTeamsIdsList = userTeamsIds.split(",");
let userTms = [];
for(let i = 0; i < userTeamsList.length; i++){
if(!!userTeamsIdsList[i] && !!userTeamsList[i]) {
userTms.push({
"teamId": userTeamsIdsList[i],
"teamDisplayName": userTeamsList[i],
})
}
}
let userOrgsList = userOrgs.split(",");
let userOrgsIdsList = userOrgsIds.split(",");
let userOrganizations = [];
for(let i = 0; i < userOrgsList.length; i++){
if(!!userOrgsIdsList[i] && !!userOrgsList[i]) {
userOrganizations.push({
"orgId": userOrgsIdsList[i],
"orgDisplayName": userOrgsList[i],
})
}
}
Meteor.call(
'setCreateUser',
fullname,
username,
initials,
password,
isAdmin,
isActive,
email.toLowerCase(),
importUsernames,
userOrganizations,
userTms,
function(error) {
const usernameMessageElement = templateInstance.$('.username-taken');
const emailMessageElement = templateInstance.$('.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.back();
}
},
);
Popup.back();
},
'click #addUserOrgNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "addOrg";
document.getElementById("jsOrgsNewUser").style.display = 'block';
document.getElementById("jsTeamsNewUser").style.display = 'none';
},
'click #removeUserOrgNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeOrg";
document.getElementById("jsOrgsNewUser").style.display = 'block';
document.getElementById("jsTeamsNewUser").style.display = 'none';
},
'click #addUserTeamNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "addTeam";
document.getElementById("jsTeamsNewUser").style.display = 'block';
document.getElementById("jsOrgsNewUser").style.display = 'none';
},
'click #removeUserTeamNewUser'(event) {
event.preventDefault();
userOrgsTeamsAction = "removeTeam";
document.getElementById("jsTeamsNewUser").style.display = 'block';
document.getElementById("jsOrgsNewUser").style.display = 'none';
},
'change #jsOrgsNewUser'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement(true);
},
'change #jsTeamsNewUser'(event) {
event.preventDefault();
UpdateUserOrgsOrTeamsElement(true);
},
});
Template.settingsOrgPopup.events({
'click #deleteButton'(event) {
event.preventDefault();
if (ReactiveCache.getUsers({"orgs.orgId": this.orgId}).length > 0)
{
let orgClassList = document.getElementById("deleteOrgWarningMessage").classList;
if(orgClassList.contains('hide'))
{
orgClassList.remove('hide');
document.getElementById("deleteOrgWarningMessage").style.color = "red";
}
return;
}
Org.remove(this.orgId);
Popup.back();
}
});
Template.settingsTeamPopup.events({
'click #deleteButton'(event) {
event.preventDefault();
if (ReactiveCache.getUsers({"teams.teamId": this.teamId}).length > 0)
{
let teamClassList = document.getElementById("deleteTeamWarningMessage").classList;
if(teamClassList.contains('hide'))
{
teamClassList.remove('hide');
document.getElementById("deleteTeamWarningMessage").style.color = "red";
}
return;
}
Team.remove(this.teamId);
Popup.back();
}
});
Template.settingsUserPopup.events({
'click .impersonate-user'(event) {
event.preventDefault();
Meteor.call('impersonate', this.userId, (err) => {
if (!err) {
FlowRouter.go('/');
Meteor.connection.setUserId(this.userId);
}
});
},
'click #deleteButton'(event) {
event.preventDefault();
Users.remove(this.userId);
/*
// Delete user is enabled, but you should remove user from all boards
// before deleting user, because there is possibility of leaving empty user avatars
// to boards. You can remove non-existing user ids manually from database,
// if that happens.
//. 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
//
//
*/
Popup.back();
},
});
Template.settingsUserPopup.helpers({
user() {
return ReactiveCache.getUser(this.userId);
},
authentications() {
return Template.instance().authenticationMethods.get();
},
isSelected(match) {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === match;
},
isLdap() {
const userId = Template.instance().data.userId;
const selected = ReactiveCache.getUser(userId).authenticationMethod;
return selected === 'ldap';
},
errorMessage() {
return Template.instance().errorMessage.get();
},
});