mirror of
https://github.com/wekan/wekan.git
synced 2026-01-15 05:58:51 +01:00
Fixed Change Avatar. Improved Admin Panel: People columns order, selected tab background color.
Thanks to xet7 !
This commit is contained in:
parent
eca42f32bb
commit
e89f4d260c
10 changed files with 169 additions and 99 deletions
|
|
@ -135,16 +135,15 @@ template(name="peopleGeneral")
|
|||
thead
|
||||
tr
|
||||
th
|
||||
+selectAllUser
|
||||
th {{_ 'accounts-lockout-status'}}
|
||||
th {{_ 'admin-people-active-status'}}
|
||||
+newUserRow
|
||||
th {{_ 'username'}}
|
||||
th {{_ 'fullname'}}
|
||||
th {{_ 'admin'}}
|
||||
th {{_ 'email'}}
|
||||
th {{_ 'admin'}}
|
||||
th {{_ 'admin-people-active-status'}}
|
||||
th {{_ 'accounts-lockout-status'}}
|
||||
th {{_ 'createdAt'}}
|
||||
th
|
||||
+newUserRow
|
||||
+selectAllUser
|
||||
tbody
|
||||
tr
|
||||
each user in peopleList
|
||||
|
|
@ -239,22 +238,12 @@ template(name="teamRow")
|
|||
|
||||
template(name="peopleRow")
|
||||
tr
|
||||
if userData.loginDisabled
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", disabled="disabled", id="{{userData._id}}")
|
||||
else
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", id="{{userData._id}}")
|
||||
td.account-status
|
||||
if isUserLocked
|
||||
span.text-red.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="true", title="{{_ 'accounts-lockout-click-to-unlock'}}") 🔒
|
||||
else
|
||||
span.text-green.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="false", title="{{_ 'accounts-lockout-user-unlocked'}}") 🔓
|
||||
td.account-active-status
|
||||
if userData.loginDisabled
|
||||
span.text-red.js-toggle-active-status(data-user-id=userData._id, data-is-active="false", title="{{_ 'admin-people-user-inactive'}}") 🚫
|
||||
else
|
||||
span.text-green.js-toggle-active-status(data-user-id=userData._id, data-is-active="true", title="{{_ 'admin-people-user-active'}}") ✅
|
||||
td
|
||||
a.edit-user
|
||||
| ✏️
|
||||
| {{_ 'edit'}}
|
||||
a.more-settings-user
|
||||
| ⋯
|
||||
if userData.loginDisabled
|
||||
td.username <s>{{ userData.username }}</s>
|
||||
else if isUserLocked
|
||||
|
|
@ -262,9 +251,9 @@ template(name="peopleRow")
|
|||
else
|
||||
td.username {{ userData.username }}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.profile.fullname }}</s>
|
||||
td <s>{{ userData.emails.[0].address }}</s>
|
||||
else
|
||||
td {{ userData.profile.fullname }}
|
||||
td {{ userData.emails.[0].address }}
|
||||
if userData.loginDisabled
|
||||
td
|
||||
if userData.isAdmin
|
||||
|
|
@ -277,20 +266,26 @@ template(name="peopleRow")
|
|||
| {{_ 'yes'}}
|
||||
else
|
||||
| {{_ 'no'}}
|
||||
if userData.loginDisabled
|
||||
td <s>{{ userData.emails.[0].address }}</s>
|
||||
else
|
||||
td {{ userData.emails.[0].address }}
|
||||
td.account-active-status
|
||||
if userData.loginDisabled
|
||||
span.text-red.js-toggle-active-status(data-user-id=userData._id, data-is-active="false", title="{{_ 'admin-people-user-inactive'}}") 🚫
|
||||
else
|
||||
span.text-green.js-toggle-active-status(data-user-id=userData._id, data-is-active="true", title="{{_ 'admin-people-user-active'}}") ✅
|
||||
td.account-status
|
||||
if isUserLocked
|
||||
span.text-red.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="true", title="{{_ 'accounts-lockout-click-to-unlock'}}") 🔒
|
||||
else
|
||||
span.text-green.js-toggle-lock-status.emoji-icon(data-user-id=userData._id, data-is-locked="false", title="{{_ 'accounts-lockout-user-unlocked'}}") 🔓
|
||||
if userData.loginDisabled
|
||||
td <s>{{ moment userData.createdAt 'LLL' }}</s>
|
||||
else
|
||||
td {{ moment userData.createdAt 'LLL' }}
|
||||
td
|
||||
a.edit-user
|
||||
| ✏️
|
||||
| {{_ 'edit'}}
|
||||
a.more-settings-user
|
||||
| ⋯
|
||||
if userData.loginDisabled
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", disabled="disabled", id="{{userData._id}}")
|
||||
else
|
||||
td
|
||||
input.selectUserChkBox(type="checkbox", id="{{userData._id}}")
|
||||
|
||||
template(name="editOrgPopup")
|
||||
form
|
||||
|
|
|
|||
|
|
@ -840,16 +840,7 @@ Template.editUserPopup.events({
|
|||
? 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),
|
||||
},
|
||||
});
|
||||
|
||||
// Build user teams list
|
||||
let userTeamsList = userTeams.split(",");
|
||||
let userTeamsIdsList = userTeamsIds.split(",");
|
||||
let userTms = [];
|
||||
|
|
@ -862,12 +853,7 @@ Template.editUserPopup.events({
|
|||
}
|
||||
}
|
||||
|
||||
Users.update(this.userId, {
|
||||
$set:{
|
||||
teams: userTms
|
||||
}
|
||||
});
|
||||
|
||||
// Build user orgs list
|
||||
let userOrgsList = userOrgs.split(",");
|
||||
let userOrgsIdsList = userOrgsIds.split(",");
|
||||
let userOrganizations = [];
|
||||
|
|
@ -880,9 +866,20 @@ Template.editUserPopup.events({
|
|||
}
|
||||
}
|
||||
|
||||
Users.update(this.userId, {
|
||||
$set:{
|
||||
orgs: userOrganizations
|
||||
// Update user via Meteor method (for admin to edit other users)
|
||||
const updateData = {
|
||||
fullname: fullname,
|
||||
isAdmin: isAdmin === 'true',
|
||||
loginDisabled: isActive === 'true',
|
||||
authenticationMethod: authentication,
|
||||
importUsernames: Users.parseImportUsernames(importUsernames),
|
||||
teams: userTms,
|
||||
orgs: userOrganizations,
|
||||
};
|
||||
|
||||
Meteor.call('editUser', this.userId, updateData, (error) => {
|
||||
if (error) {
|
||||
console.error('Error updating user:', error);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#header #header-main-bar .setting-header-btn {
|
||||
border-radius: 3px;
|
||||
color: #f2f2f2;
|
||||
margin-left: 20px;
|
||||
padding-right: 10px;
|
||||
|
|
|
|||
|
|
@ -4,27 +4,27 @@ template(name="settingHeaderBar")
|
|||
|
||||
.setting-header-btns.left
|
||||
if currentUser
|
||||
a.setting-header-btn.settings(href="{{pathFor 'setting'}}")
|
||||
a.setting-header-btn.settings(class=isSettingsActive href="{{pathFor 'setting'}}")
|
||||
span.emoji-icon ⚙️
|
||||
span {{_ 'settings'}}
|
||||
|
||||
a.setting-header-btn.people(href="{{pathFor 'people'}}")
|
||||
a.setting-header-btn.people(class=isPeopleActive href="{{pathFor 'people'}}")
|
||||
span.emoji-icon 👥
|
||||
span {{_ 'people'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'admin-reports'}}")
|
||||
a.setting-header-btn.informations(class=isAdminReportsActive href="{{pathFor 'admin-reports'}}")
|
||||
span.emoji-icon 📋
|
||||
span {{_ 'reports'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'attachments'}}")
|
||||
a.setting-header-btn.informations(class=isAttachmentsActive href="{{pathFor 'attachments'}}")
|
||||
span.emoji-icon 📎
|
||||
span {{_ 'attachments'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'translation'}}")
|
||||
a.setting-header-btn.informations(class=isTranslationActive href="{{pathFor 'translation'}}")
|
||||
span.emoji-icon 🔤
|
||||
span {{_ 'translation'}}
|
||||
|
||||
a.setting-header-btn.informations(href="{{pathFor 'information'}}")
|
||||
a.setting-header-btn.informations(class=isInformationActive href="{{pathFor 'information'}}")
|
||||
span.emoji-icon ℹ️
|
||||
span {{_ 'info'}}
|
||||
|
||||
|
|
|
|||
20
client/components/settings/settingHeader.js
Normal file
20
client/components/settings/settingHeader.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Template.settingHeaderBar.helpers({
|
||||
isSettingsActive() {
|
||||
return FlowRouter.getRouteName() === 'setting' ? 'active' : '';
|
||||
},
|
||||
isPeopleActive() {
|
||||
return FlowRouter.getRouteName() === 'people' ? 'active' : '';
|
||||
},
|
||||
isAdminReportsActive() {
|
||||
return FlowRouter.getRouteName() === 'admin-reports' ? 'active' : '';
|
||||
},
|
||||
isAttachmentsActive() {
|
||||
return FlowRouter.getRouteName() === 'attachments' ? 'active' : '';
|
||||
},
|
||||
isTranslationActive() {
|
||||
return FlowRouter.getRouteName() === 'translation' ? 'active' : '';
|
||||
},
|
||||
isInformationActive() {
|
||||
return FlowRouter.getRouteName() === 'information' ? 'active' : '';
|
||||
},
|
||||
});
|
||||
|
|
@ -23,6 +23,9 @@
|
|||
background-color: #dbdbdb;
|
||||
color: #444;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.member .avatar.avatar-image {
|
||||
object-fit: cover;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ template(name="userAvatar")
|
|||
|
||||
template(name="userAvatarInitials")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{viewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= initials
|
||||
text(x="50%" y="11" text-anchor="middle" dominant-baseline="middle" font-size="16")= initials
|
||||
|
||||
template(name="orgAvatar")
|
||||
a.member.orgOrTeamMember(class="js-member" title="{{orgData.orgDisplayName}}")
|
||||
|
|
@ -53,7 +53,7 @@ template(name="boardTeamRow")
|
|||
|
||||
template(name="boardOrgName")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{orgViewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= orgName
|
||||
text(x="50%" y="11" text-anchor="middle" dominant-baseline="middle" font-size="16")= orgName
|
||||
|
||||
template(name="teamAvatar")
|
||||
a.member.orgOrTeamMember(class="js-member" title="{{teamData.teamDisplayName}}")
|
||||
|
|
@ -61,7 +61,7 @@ template(name="teamAvatar")
|
|||
|
||||
template(name="boardTeamName")
|
||||
svg.avatar.avatar-initials(viewBox="0 0 {{teamViewPortWidth}} 15")
|
||||
text(x="50%" y="13" text-anchor="middle")= teamName
|
||||
text(x="50%" y="11" text-anchor="middle" dominant-baseline="middle" font-size="16")= teamName
|
||||
|
||||
template(name="userPopup")
|
||||
.board-member-menu
|
||||
|
|
@ -88,13 +88,11 @@ template(name="changeAvatarPopup")
|
|||
li: a.js-select-avatar
|
||||
.member
|
||||
img.avatar.avatar-image(src="{{link}}")
|
||||
| {{_ 'uploaded-avatar'}}
|
||||
if isSelected
|
||||
| ✅
|
||||
p.sub-name
|
||||
unless isSelected
|
||||
a.js-delete-avatar {{_ 'delete'}}
|
||||
| -
|
||||
a.js-delete-avatar {{_ 'delete'}}
|
||||
| -
|
||||
= name
|
||||
li: a.js-select-initials
|
||||
.member
|
||||
|
|
@ -102,7 +100,7 @@ template(name="changeAvatarPopup")
|
|||
| {{_ 'initials' }}
|
||||
if noAvatarUrl
|
||||
| ✅
|
||||
p.sub-name {{_ 'default-avatar'}}
|
||||
p.sub-name {{_ 'default-avatar'}}
|
||||
input.hide.js-upload-avatar-input(accept="image/*;capture=camera" type="file")
|
||||
if Meteor.settings.public.avatarsUploadMaxSize
|
||||
| {{_ 'max-avatar-filesize'}} {{Meteor.settings.public.avatarsUploadMaxSize}}
|
||||
|
|
@ -113,7 +111,11 @@ template(name="changeAvatarPopup")
|
|||
| {{_ 'invalid-file'}}
|
||||
button.full.js-upload-avatar
|
||||
| 📤
|
||||
| {{_ 'upload-avatar'}}
|
||||
| {{_ 'upload-avatar' }}
|
||||
|
||||
template(name="deleteAvatarPopup")
|
||||
p {{_ 'delete-avatar-confirm'}}
|
||||
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
|
||||
|
||||
template(name="cardMemberPopup")
|
||||
.board-member-menu
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
uploadedAvatars() {
|
||||
const ret = ReactiveCache.getAvatars({ userId: Meteor.userId() }, {}, true).each();
|
||||
const ret = ReactiveCache.getAvatars({ userId: Meteor.userId() }, {}, true);
|
||||
return ret;
|
||||
},
|
||||
|
||||
|
|
@ -205,7 +205,11 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
|
||||
setAvatar(avatarUrl) {
|
||||
ReactiveCache.getCurrentUser().setAvatarUrl(avatarUrl);
|
||||
Meteor.call('setAvatarUrl', avatarUrl, (err) => {
|
||||
if (err) {
|
||||
this.setError(err.reason || 'Error setting avatar');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setError(error) {
|
||||
|
|
@ -234,44 +238,30 @@ BlazeComponent.extendComponent({
|
|||
uploader.start();
|
||||
}
|
||||
},
|
||||
'click .js-select-avatar'() {
|
||||
const avatarUrl = this.currentData().link();
|
||||
this.setAvatar(avatarUrl);
|
||||
'click .js-select-avatar'(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const data = Blaze.getData(event.currentTarget);
|
||||
if (data && typeof data.link === 'function') {
|
||||
const avatarUrl = data.link();
|
||||
this.setAvatar(avatarUrl);
|
||||
}
|
||||
},
|
||||
'click .js-select-initials'() {
|
||||
'click .js-select-initials'(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.setAvatar('');
|
||||
},
|
||||
'click .js-delete-avatar'(event) {
|
||||
Avatars.remove(this.currentData()._id);
|
||||
'click .js-delete-avatar': Popup.afterConfirm('deleteAvatar', function(event) {
|
||||
Avatars.remove(this._id);
|
||||
Popup.back();
|
||||
event.stopPropagation();
|
||||
},
|
||||
}),
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('changeAvatarPopup');
|
||||
|
||||
Template.cardMembersPopup.helpers({
|
||||
isCardMember() {
|
||||
const card = Template.parentData();
|
||||
const cardMembers = card.getMembers();
|
||||
|
||||
return _.contains(cardMembers, this.userId);
|
||||
},
|
||||
|
||||
user() {
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardMembersPopup.events({
|
||||
'click .js-select-member'(event) {
|
||||
const card = Utils.getCurrentCard();
|
||||
const memberId = this.userId;
|
||||
card.toggleMember(memberId);
|
||||
event.preventDefault();
|
||||
},
|
||||
});
|
||||
|
||||
Template.cardMemberPopup.helpers({
|
||||
user() {
|
||||
return ReactiveCache.getUser(this.userId);
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@
|
|||
"change-permissions": "Change permissions",
|
||||
"change-settings": "Change Settings",
|
||||
"changeAvatarPopup-title": "Change Avatar",
|
||||
"delete-avatar-confirm": "Are you sure you want to delete this avatar?",
|
||||
"deleteAvatarPopup-title": "Delete Avatar?",
|
||||
"changeLanguagePopup-title": "Change Language",
|
||||
"changePasswordPopup-title": "Change Password",
|
||||
"changePermissionsPopup-title": "Change Permissions",
|
||||
|
|
|
|||
|
|
@ -1970,10 +1970,70 @@ Meteor.methods({
|
|||
Users.remove(targetUserId);
|
||||
return { success: true, message: 'User deleted successfully' };
|
||||
},
|
||||
editUser(targetUserId, updateData) {
|
||||
check(targetUserId, String);
|
||||
check(updateData, Object);
|
||||
|
||||
const currentUserId = Meteor.userId();
|
||||
if (!currentUserId) {
|
||||
throw new Meteor.Error('not-authorized', 'User must be logged in');
|
||||
}
|
||||
|
||||
const currentUser = ReactiveCache.getUser(currentUserId);
|
||||
if (!currentUser) {
|
||||
throw new Meteor.Error('not-authorized', 'Current user not found');
|
||||
}
|
||||
|
||||
// Check if current user is admin
|
||||
if (!currentUser.isAdmin) {
|
||||
throw new Meteor.Error('not-authorized', 'Only administrators can edit other users');
|
||||
}
|
||||
|
||||
const targetUser = ReactiveCache.getUser(targetUserId);
|
||||
if (!targetUser) {
|
||||
throw new Meteor.Error('user-not-found', 'Target user not found');
|
||||
}
|
||||
|
||||
// Only allow updating specific fields
|
||||
const updateObject = {};
|
||||
if (updateData.fullname !== undefined) {
|
||||
updateObject['profile.fullname'] = updateData.fullname;
|
||||
}
|
||||
if (updateData.initials !== undefined) {
|
||||
updateObject['profile.initials'] = updateData.initials;
|
||||
}
|
||||
if (updateData.isAdmin !== undefined) {
|
||||
updateObject.isAdmin = updateData.isAdmin;
|
||||
}
|
||||
if (updateData.loginDisabled !== undefined) {
|
||||
updateObject.loginDisabled = updateData.loginDisabled;
|
||||
}
|
||||
if (updateData.authenticationMethod !== undefined) {
|
||||
updateObject.authenticationMethod = updateData.authenticationMethod;
|
||||
}
|
||||
if (updateData.importUsernames !== undefined) {
|
||||
updateObject.importUsernames = updateData.importUsernames;
|
||||
}
|
||||
if (updateData.teams !== undefined) {
|
||||
updateObject.teams = updateData.teams;
|
||||
}
|
||||
if (updateData.orgs !== undefined) {
|
||||
updateObject.orgs = updateData.orgs;
|
||||
}
|
||||
|
||||
Users.update(targetUserId, { $set: updateObject });
|
||||
},
|
||||
setListSortBy(value) {
|
||||
check(value, String);
|
||||
ReactiveCache.getCurrentUser().setListSortBy(value);
|
||||
},
|
||||
setAvatarUrl(avatarUrl) {
|
||||
check(avatarUrl, String);
|
||||
if (!this.userId) {
|
||||
throw new Meteor.Error('not-logged-in', 'User must be logged in');
|
||||
}
|
||||
Users.update(this.userId, { $set: { 'profile.avatarUrl': avatarUrl } });
|
||||
},
|
||||
toggleBoardStar(boardId) {
|
||||
check(boardId, String);
|
||||
if (!this.userId) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue