Add Worker role.

This was originally added at Wekan v3.58,
reverted at Wekan v3.60 because of bugs,
and now after fixes added back.

Thanks to xet7 !

Closes #2788
This commit is contained in:
Lauri Ojansivu 2020-01-05 21:28:14 +02:00
parent 6209b792aa
commit f6f7705f23
19 changed files with 346 additions and 205 deletions

View file

@ -38,18 +38,20 @@ template(name="attachmentsGalery")
| {{_ 'download'}} | {{_ 'download'}}
if currentUser.isBoardMember if currentUser.isBoardMember
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
if isImage unless currentUser.isWorker
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}") if isImage
i.fa.fa-thumb-tack a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
if($eq ../coverId _id) i.fa.fa-thumb-tack
| {{_ 'remove-cover'}} if($eq ../coverId _id)
else | {{_ 'remove-cover'}}
| {{_ 'add-cover'}} else
a.js-confirm-delete | {{_ 'add-cover'}}
i.fa.fa-close a.js-confirm-delete
| {{_ 'delete'}} i.fa.fa-close
| {{_ 'delete'}}
if currentUser.isBoardMember if currentUser.isBoardMember
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
li.attachment-item.add-attachment unless currentUser.isWorker
a.js-add-attachment {{_ 'add-attachment' }} li.attachment-item.add-attachment
a.js-add-attachment {{_ 'add-attachment' }}

View file

@ -97,7 +97,8 @@ Template.dateBadge.helpers({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}); });

View file

@ -44,7 +44,8 @@ template(name="cardDetails")
+cardReceivedDate +cardReceivedDate
else else
if canModifyCard if canModifyCard
a.js-received-date {{_ 'add'}} unless currentUser.isWorker
a.js-received-date {{_ 'add'}}
.card-details-item.card-details-item-start .card-details-item.card-details-item-start
h3 h3
@ -54,7 +55,8 @@ template(name="cardDetails")
+cardStartDate +cardStartDate
else else
if canModifyCard if canModifyCard
a.js-start-date {{_ 'add'}} unless currentUser.isWorker
a.js-start-date {{_ 'add'}}
.card-details-item.card-details-item-due .card-details-item.card-details-item-due
h3 h3
@ -64,7 +66,8 @@ template(name="cardDetails")
+cardDueDate +cardDueDate
else else
if canModifyCard if canModifyCard
a.js-due-date {{_ 'add'}} unless currentUser.isWorker
a.js-due-date {{_ 'add'}}
.card-details-item.card-details-item-end .card-details-item.card-details-item-end
h3 h3
@ -74,7 +77,8 @@ template(name="cardDetails")
+cardEndDate +cardEndDate
else else
if canModifyCard if canModifyCard
a.js-end-date {{_ 'add'}} unless currentUser.isWorker
a.js-end-date {{_ 'add'}}
.card-details-items .card-details-items
.card-details-item.card-details-item-members .card-details-item.card-details-item-members
@ -85,8 +89,9 @@ template(name="cardDetails")
+userAvatar(userId=this cardId=../_id) +userAvatar(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }} | {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard if canModifyCard
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}") unless currentUser.isWorker
i.fa.fa-plus a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus
.card-details-item.card-details-item-assignees .card-details-item.card-details-item-assignees
h3 h3
@ -96,10 +101,14 @@ template(name="cardDetails")
+userAvatarAssignee(userId=this cardId=../_id) +userAvatarAssignee(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }} | {{! XXX Hack to hide syntaxic coloration /// }}
if canModifyCard if canModifyCard
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus
if currentUser.isWorker
unless assigneeSelected unless assigneeSelected
a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}") a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}")
i.fa.fa-plus i.fa.fa-plus
.card-details-item.card-details-item-labels .card-details-item.card-details-item-labels
h3 h3
i.fa.fa-tags i.fa.fa-tags
@ -110,8 +119,9 @@ template(name="cardDetails")
+viewer +viewer
= name = name
if canModifyCard if canModifyCard
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}") unless currentUser.isWorker
i.fa.fa-plus a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus
.card-details-items .card-details-items
each customFieldsWD each customFieldsWD
@ -132,28 +142,29 @@ template(name="cardDetails")
//- XXX We should use "editable" to avoid repetiting ourselves //- XXX We should use "editable" to avoid repetiting ourselves
if canModifyCard if canModifyCard
h3 unless currentUser.isWorker
i.fa.fa-align-left h3
card-details-item-title {{_ 'description'}} i.fa.fa-align-left
+inlinedCardDescription(classNames="card-description js-card-description") card-details-item-title {{_ 'description'}}
+editor(autofocus=true) +inlinedCardDescription(classNames="card-description js-card-description")
| {{getUnsavedValue 'cardDescription' _id getDescription}} +editor(autofocus=true)
.edit-controls.clearfix | {{getUnsavedValue 'cardDescription' _id getDescription}}
button.primary(type="submit") {{_ 'save'}} .edit-controls.clearfix
a.fa.fa-times-thin.js-close-inlined-form button.primary(type="submit") {{_ 'save'}}
else a.fa.fa-times-thin.js-close-inlined-form
a.js-open-inlined-form else
if getDescription a.js-open-inlined-form
+viewer if getDescription
= getDescription +viewer
else = getDescription
| {{_ 'edit'}} else
if (hasUnsavedValue 'cardDescription' _id) | {{_ 'edit'}}
p.quiet if (hasUnsavedValue 'cardDescription' _id)
| {{_ 'unsaved-description'}} p.quiet
a.js-open-inlined-form {{_ 'view-it'}} | {{_ 'unsaved-description'}}
= ' - ' a.js-open-inlined-form {{_ 'view-it'}}
a.js-close-inlined-form {{_ 'discard'}} = ' - '
a.js-close-inlined-form {{_ 'discard'}}
else if getDescription else if getDescription
h3.card-details-item-title {{_ 'description'}} h3.card-details-item-title {{_ 'description'}}
+viewer +viewer
@ -165,15 +176,16 @@ template(name="cardDetails")
i.fa.fa-shopping-cart i.fa.fa-shopping-cart
card-details-item-title {{_ 'requested-by'}} card-details-item-title {{_ 'requested-by'}}
if canModifyCard if canModifyCard
+inlinedForm(classNames="js-card-details-requester") unless currentUser.isWorker
+editCardRequesterForm +inlinedForm(classNames="js-card-details-requester")
else +editCardRequesterForm
a.js-open-inlined-form else
if getRequestedBy a.js-open-inlined-form
+viewer if getRequestedBy
= getRequestedBy +viewer
else = getRequestedBy
| {{_ 'add'}} else
| {{_ 'add'}}
else if getRequestedBy else if getRequestedBy
+viewer +viewer
= getRequestedBy = getRequestedBy
@ -183,15 +195,16 @@ template(name="cardDetails")
i.fa.fa-user-plus i.fa.fa-user-plus
card-details-item-title {{_ 'assigned-by'}} card-details-item-title {{_ 'assigned-by'}}
if canModifyCard if canModifyCard
+inlinedForm(classNames="js-card-details-assigner") unless currentUser.isWorker
+editCardAssignerForm +inlinedForm(classNames="js-card-details-assigner")
else +editCardAssignerForm
a.js-open-inlined-form else
if getAssignedBy a.js-open-inlined-form
+viewer if getAssignedBy
= getAssignedBy +viewer
else = getAssignedBy
| {{_ 'add'}} else
| {{_ 'add'}}
else if getRequestedBy else if getRequestedBy
+viewer +viewer
= getAssignedBy = getAssignedBy
@ -266,28 +279,29 @@ template(name="cardDetailsActionsPopup")
i.fa.fa-eye-slash i.fa.fa-eye-slash
| {{_ 'watch'}} | {{_ 'watch'}}
if canModifyCard if canModifyCard
hr unless currentUser.isWorker
ul.pop-over-list hr
//li: a.js-members {{_ 'card-edit-members'}} ul.pop-over-list
//li: a.js-labels {{_ 'card-edit-labels'}} //li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}} //li: a.js-labels {{_ 'card-edit-labels'}}
li //li: a.js-attachments {{_ 'card-edit-attachments'}}
a.js-custom-fields li
i.fa.fa-list-alt a.js-custom-fields
| {{_ 'card-edit-custom-fields'}} i.fa.fa-list-alt
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}} | {{_ 'card-edit-custom-fields'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}} //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
li //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
a.js-spent-time li
i.fa.fa-clock-o a.js-spent-time
| {{_ 'editCardSpentTimePopup-title'}} i.fa.fa-clock-o
li | {{_ 'editCardSpentTimePopup-title'}}
a.js-set-card-color li
i.fa.fa-paint-brush a.js-set-card-color
| {{_ 'setCardColorPopup-title'}} i.fa.fa-paint-brush
hr | {{_ 'setCardColorPopup-title'}}
hr
ul.pop-over-list ul.pop-over-list
li li
a.js-move-card-to-top a.js-move-card-to-top
@ -297,16 +311,17 @@ template(name="cardDetailsActionsPopup")
a.js-move-card-to-bottom a.js-move-card-to-bottom
i.fa.fa-arrow-down i.fa.fa-arrow-down
| {{_ 'moveCardToBottom-title'}} | {{_ 'moveCardToBottom-title'}}
hr unless currentUser.isWorker
ul.pop-over-list hr
li ul.pop-over-list
a.js-move-card li
i.fa.fa-arrow-right a.js-move-card
| {{_ 'moveCardPopup-title'}} i.fa.fa-arrow-right
li | {{_ 'moveCardPopup-title'}}
a.js-copy-card li
i.fa.fa-copy a.js-copy-card
| {{_ 'copyCardPopup-title'}} i.fa.fa-copy
| {{_ 'copyCardPopup-title'}}
hr hr
ul.pop-over-list ul.pop-over-list
li li
@ -379,16 +394,27 @@ template(name="cardMembersPopup")
i.fa.fa-check i.fa.fa-check
template(name="cardAssigneesPopup") template(name="cardAssigneesPopup")
ul.pop-over-list.js-card-assignee-list unless currentUser.isWorker
each board.activeMembers ul.pop-over-list.js-card-assignee-list
li.item(class="{{#if isCardAssignee}}active{{/if}}") each board.activeMembers
a.name.js-select-assignee(href="#") li.item(class="{{#if isCardAssignee}}active{{/if}}")
+userAvatar(userId=user._id) a.name.js-select-assignee(href="#")
span.full-name +userAvatar(userId=user._id)
= user.profile.fullname span.full-name
| (<span class="username">{{ user.username }}</span>) = user.profile.fullname
if isCardAssignee | (<span class="username">{{ user.username }}</span>)
i.fa.fa-check if isCardAssignee
i.fa.fa-check
if currentUser.isWorker
ul.pop-over-list.js-card-assignee-list
li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}")
a.name.js-select-assignee(href="#")
+userAvatar(userId=currentUser._id)
span.full-name
= currentUser.profile.fullname
| (<span class="username">{{ currentUser.username }}</span>)
if currentUser.isCardAssignee
i.fa.fa-check
template(name="userAvatarAssignee") template(name="userAvatarAssignee")
a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})") a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})")
@ -416,11 +442,13 @@ template(name="cardAssigneePopup")
p.quiet @{{ user.username }} p.quiet @{{ user.username }}
ul.pop-over-list ul.pop-over-list
if currentUser.isNotCommentOnly if currentUser.isNotCommentOnly
li: a.js-remove-assignee {{_ 'remove-member-from-card'}} unless currentUser.isWorker
li: a.js-remove-assignee {{_ 'remove-member-from-card'}}
if $eq currentUser._id user._id unless currentUser.isWorker
with currentUser if $eq currentUser._id user._id
li: a.js-edit-profile {{_ 'edit-profile'}} with currentUser
li: a.js-edit-profile {{_ 'edit-profile'}}
template(name="userAvatarAssigneeInitials") template(name="userAvatarAssigneeInitials")
svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15") svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15")

View file

@ -51,7 +51,8 @@ BlazeComponent.extendComponent({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },

View file

@ -67,7 +67,8 @@ BlazeComponent.extendComponent({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}).register('checklistDetail'); }).register('checklistDetail');
@ -120,7 +121,8 @@ BlazeComponent.extendComponent({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
@ -228,7 +230,8 @@ Template.checklistItemDetail.helpers({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}); });

View file

@ -3,7 +3,8 @@ BlazeComponent.extendComponent({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}).register('subtaskDetail'); }).register('subtaskDetail');
@ -55,7 +56,8 @@ BlazeComponent.extendComponent({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
@ -154,7 +156,8 @@ Template.subtaskItemDetail.helpers({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}); });

View file

@ -189,7 +189,8 @@ BlazeComponent.extendComponent({
!this.reachedWipLimit() && !this.reachedWipLimit() &&
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },

View file

@ -10,7 +10,7 @@ template(name="listHeader")
a.list-header-left-icon.fa.fa-angle-left.js-unselect-list a.list-header-left-icon.fa.fa-angle-left.js-unselect-list
h2.list-header-name( h2.list-header-name(
title="{{ moment modifiedAt 'LLL' }}" title="{{ moment modifiedAt 'LLL' }}"
class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}") class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}")
+viewer +viewer
= title = title
if wipLimit.enabled if wipLimit.enabled
@ -65,11 +65,12 @@ template(name="listActionPopup")
i.fa.fa-eye-slash i.fa.fa-eye-slash
| {{_ 'watch'}} | {{_ 'watch'}}
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
ul.pop-over-list unless currentUser.isWorker
li ul.pop-over-list
a.js-set-color-list li
i.fa.fa-paint-brush a.js-set-color-list
| {{_ 'set-color-list'}} i.fa.fa-paint-brush
| {{_ 'set-color-list'}}
ul.pop-over-list ul.pop-over-list
if cards.count if cards.count
li li
@ -82,13 +83,14 @@ template(name="listActionPopup")
a.js-set-wip-limit a.js-set-wip-limit
i.fa.fa-ban i.fa.fa-ban
| {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}} | {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
hr unless currentUser.isWorker
ul.pop-over-list hr
li ul.pop-over-list
a.js-close-list li
i.fa.fa-arrow-right a.js-close-list
i.fa.fa-archive i.fa.fa-arrow-right
| {{_ 'archive-list'}} i.fa.fa-archive
| {{_ 'archive-list'}}
hr hr
ul.pop-over-list ul.pop-over-list
li li
@ -114,7 +116,8 @@ template(name="listMorePopup")
input.inline-input(type="text" readonly value="{{ rootUrl }}") input.inline-input(type="text" readonly value="{{ rootUrl }}")
| {{_ 'added'}} | {{_ 'added'}}
span.date(title=list.createdAt) {{ moment createdAt 'LLL' }} span.date(title=list.createdAt) {{ moment createdAt 'LLL' }}
a.js-delete {{_ 'delete'}} unless currentUser.isWorker
a.js-delete {{_ 'delete'}}
template(name="listDeletePopup") template(name="listDeletePopup")
p {{_ "list-delete-pop"}} p {{_ "list-delete-pop"}}

View file

@ -9,9 +9,10 @@ BlazeComponent.extendComponent({
canSeeAddCard() { canSeeAddCard() {
const list = Template.currentData(); const list = Template.currentData();
return ( return (
!list.getWipLimit('enabled') || (!list.getWipLimit('enabled') ||
list.getWipLimit('soft') || list.getWipLimit('soft') ||
!this.reachedWipLimit() !this.reachedWipLimit()) &&
!Meteor.user().isWorker()
); );
}, },

View file

@ -40,8 +40,9 @@ template(name="membersWidget")
i.fa.fa-users i.fa.fa-users
| {{_ 'members'}} | {{_ 'members'}}
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right unless currentUser.isWorker
i.board-header-btn-icon.fa.fa-cog a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
i.board-header-btn-icon.fa.fa-cog
.board-widget-content .board-widget-content
each currentBoard.activeMembers each currentBoard.activeMembers
@ -164,11 +165,12 @@ template(name="outgoingWebhooksPopup")
template(name="boardMenuPopup") template(name="boardMenuPopup")
ul.pop-over-list ul.pop-over-list
li: a.js-custom-fields {{_ 'custom-fields'}} if isNotWorker
li li: a.js-custom-fields {{_ 'custom-fields'}}
a.js-open-archives li
i.fa.fa-archive a.js-open-archives
| {{_ 'archived-items'}} i.fa.fa-archive
| {{_ 'archived-items'}}
if currentUser.isBoardAdmin if currentUser.isBoardAdmin
li li
a.js-change-board-color a.js-change-board-color
@ -246,7 +248,7 @@ template(name="labelsWidget")
.board-widget-content .board-widget-content
each currentBoard.labels each currentBoard.labels
a.card-label(class="card-label-{{color}}" a.card-label(class="card-label-{{color}}"
class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}") class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}")
span.card-label-name span.card-label-name
+viewer +viewer
= name = name
@ -275,11 +277,12 @@ template(name="memberPopup")
a.js-change-role a.js-change-role
| {{_ 'change-permissions'}} | {{_ 'change-permissions'}}
span.quiet (#{memberType}) span.quiet (#{memberType})
li unless currentUser.isWorker
if $eq currentUser._id userId li
a.js-leave-member {{_ 'leave-board'}} if $eq currentUser._id userId
else if currentUser.isBoardAdmin a.js-leave-member {{_ 'leave-board'}}
a.js-remove-member {{_ 'remove-from-board'}} else if currentUser.isBoardAdmin
a.js-remove-member {{_ 'remove-from-board'}}
template(name="removeMemberPopup") template(name="removeMemberPopup")
p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}} p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}}
@ -343,6 +346,12 @@ template(name="changePermissionsPopup")
if isCommentOnly if isCommentOnly
i.fa.fa-check i.fa.fa-check
span.sub-name {{_ 'comment-only-desc'}} span.sub-name {{_ 'comment-only-desc'}}
li
a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}")
| {{_ 'worker'}}
if isWorker
i.fa.fa-check
span.sub-name {{_ 'worker-desc'}}
if isLastAdmin if isLastAdmin
hr hr
p.quiet.bottom {{_ 'last-admin-desc'}} p.quiet.bottom {{_ 'last-admin-desc'}}

View file

@ -161,10 +161,13 @@ Template.memberPopup.helpers({
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
const commentOnly = currentBoard.hasCommentOnly(this.userId); const commentOnly = currentBoard.hasCommentOnly(this.userId);
const noComments = currentBoard.hasNoComments(this.userId); const noComments = currentBoard.hasNoComments(this.userId);
const worker = currentBoard.hasWorker(this.userId);
if (commentOnly) { if (commentOnly) {
return TAPi18n.__('comment-only').toLowerCase(); return TAPi18n.__('comment-only').toLowerCase();
} else if (noComments) { } else if (noComments) {
return TAPi18n.__('no-comments').toLowerCase(); return TAPi18n.__('no-comments').toLowerCase();
} else if (worker) {
return TAPi18n.__('worker').toLowerCase();
} else { } else {
return TAPi18n.__(type).toLowerCase(); return TAPi18n.__(type).toLowerCase();
} }
@ -267,6 +270,14 @@ Template.membersWidget.helpers({
const user = Meteor.user(); const user = Meteor.user();
return user && user.isInvitedTo(Session.get('currentBoard')); return user && user.isInvitedTo(Session.get('currentBoard'));
}, },
isWorker() {
const user = Meteor.user();
if (user) {
return Meteor.call(Boards.hasWorker(user.memberId));
} else {
return false;
}
},
}); });
Template.membersWidget.events({ Template.membersWidget.events({
@ -644,7 +655,7 @@ BlazeComponent.extendComponent({
}).register('addMemberPopup'); }).register('addMemberPopup');
Template.changePermissionsPopup.events({ Template.changePermissionsPopup.events({
'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'( 'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
event, event,
) { ) {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
@ -654,11 +665,13 @@ Template.changePermissionsPopup.events({
'js-set-comment-only', 'js-set-comment-only',
); );
const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments'); const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
const isWorker = $(event.currentTarget).hasClass('js-set-worker');
currentBoard.setMemberPermission( currentBoard.setMemberPermission(
memberId, memberId,
isAdmin, isAdmin,
isNoComments, isNoComments,
isCommentOnly, isCommentOnly,
isWorker,
); );
Popup.back(1); Popup.back(1);
}, },
@ -675,7 +688,8 @@ Template.changePermissionsPopup.helpers({
return ( return (
!currentBoard.hasAdmin(this.userId) && !currentBoard.hasAdmin(this.userId) &&
!currentBoard.hasNoComments(this.userId) && !currentBoard.hasNoComments(this.userId) &&
!currentBoard.hasCommentOnly(this.userId) !currentBoard.hasCommentOnly(this.userId) &&
!currentBoard.hasWorker(this.userId)
); );
}, },
@ -695,6 +709,13 @@ Template.changePermissionsPopup.helpers({
); );
}, },
isWorker() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
},
isLastAdmin() { isLastAdmin() {
const currentBoard = Boards.findOne(Session.get('currentBoard')); const currentBoard = Boards.findOne(Session.get('currentBoard'));
return ( return (

View file

@ -2,54 +2,60 @@ template(name="archivesSidebar")
if isArchiveReady.get if isArchiveReady.get
+basicTabs(tabs=tabs) +basicTabs(tabs=tabs)
+tabContent(slug="cards") +tabContent(slug="cards")
p.quiet unless isWorker
a.js-restore-all-cards {{_ 'restore-all'}} p.quiet
| - a.js-restore-all-cards {{_ 'restore-all'}}
a.js-delete-all-cards {{_ 'delete-all'}} | -
a.js-delete-all-cards {{_ 'delete-all'}}
each archivedCards each archivedCards
.minicard-wrapper.js-minicard .minicard-wrapper.js-minicard
+minicard(this) +minicard(this)
if currentUser.isBoardMember if currentUser.isBoardMember
p.quiet unless isWorker
a.js-restore-card {{_ 'restore'}} p.quiet
| - a.js-restore-card {{_ 'restore'}}
a.js-delete-card {{_ 'delete'}} | -
a.js-delete-card {{_ 'delete'}}
if cardIsInArchivedList if cardIsInArchivedList
p.quiet.small ({{_ 'warn-list-archived'}}) p.quiet.small ({{_ 'warn-list-archived'}})
else else
p.no-items-message {{_ 'no-archived-cards'}} p.no-items-message {{_ 'no-archived-cards'}}
+tabContent(slug="lists") +tabContent(slug="lists")
p.quiet unless isWorker
a.js-restore-all-lists {{_ 'restore-all'}} p.quiet
| - a.js-restore-all-lists {{_ 'restore-all'}}
a.js-delete-all-lists {{_ 'delete-all'}} | -
a.js-delete-all-lists {{_ 'delete-all'}}
ul.archived-lists ul.archived-lists
each archivedLists each archivedLists
li.archived-lists-item li.archived-lists-item
= title = title
if currentUser.isBoardMember if currentUser.isBoardMember
p.quiet unless isWorker
a.js-restore-list {{_ 'restore'}} p.quiet
| - a.js-restore-list {{_ 'restore'}}
a.js-delete-list {{_ 'delete'}} | -
a.js-delete-list {{_ 'delete'}}
else else
li.no-items-message {{_ 'no-archived-lists'}} li.no-items-message {{_ 'no-archived-lists'}}
+tabContent(slug="swimlanes") +tabContent(slug="swimlanes")
p.quiet unless isWorker
a.js-restore-all-swimlanes {{_ 'restore-all'}} p.quiet
| - a.js-restore-all-swimlanes {{_ 'restore-all'}}
a.js-delete-all-swimlanes {{_ 'delete-all'}} | -
a.js-delete-all-swimlanes {{_ 'delete-all'}}
ul.archived-lists ul.archived-lists
each archivedSwimlanes each archivedSwimlanes
li.archived-lists-item li.archived-lists-item
= title = title
if currentUser.isBoardMember if currentUser.isBoardMember
p.quiet unless isWorker
a.js-restore-swimlane {{_ 'restore'}} p.quiet
| - a.js-restore-swimlane {{_ 'restore'}}
a.js-delete-swimlane {{_ 'delete'}} | -
a.js-delete-swimlane {{_ 'delete'}}
else else
li.no-items-message {{_ 'no-archived-swimlanes'}} li.no-items-message {{_ 'no-archived-swimlanes'}}
else else

View file

@ -139,3 +139,12 @@ BlazeComponent.extendComponent({
]; ];
}, },
}).register('archivesSidebar'); }).register('archivesSidebar');
Template.archivesSidebar.helpers({
isWorker() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
},
});

View file

@ -43,19 +43,20 @@ template(name="listsGroup")
+addListForm +addListForm
template(name="addListForm") template(name="addListForm")
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}") unless currentUser.isWorker
.list-header-add .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
+inlinedForm(autoclose=false) .list-header-add
input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}" +inlinedForm(autoclose=false)
autocomplete="off" autofocus) input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}"
.edit-controls.clearfix autocomplete="off" autofocus)
button.primary.confirm(type="submit") {{_ 'save'}} .edit-controls.clearfix
unless currentBoard.isTemplatesBoard button.primary.confirm(type="submit") {{_ 'save'}}
unless currentBoard.isTemplateBoard unless currentBoard.isTemplatesBoard
span.quiet unless currentBoard.isTemplateBoard
| {{_ 'or'}} span.quiet
a.js-list-template {{_ 'template'}} | {{_ 'or'}}
else a.js-list-template {{_ 'template'}}
a.open-list-composer.js-open-inlined-form else
i.fa.fa-plus a.open-list-composer.js-open-inlined-form
| {{_ 'add-list'}} i.fa.fa-plus
| {{_ 'add-list'}}

View file

@ -94,7 +94,8 @@ function initSortable(boardComponent, $listsDom) {
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
} }
@ -131,6 +132,15 @@ function initSortable(boardComponent, $listsDom) {
// MultiSelection.isActive() || !userIsMember(), // MultiSelection.isActive() || !userIsMember(),
); );
} }
$listsDom.sortable(
'option',
'disabled',
// Disable drag-dropping when user is not member
Meteor.user().isWorker(),
// Not disable drag-dropping while in multi-selection mode
// MultiSelection.isActive() || !userIsMember(),
);
}); });
} }
@ -282,7 +292,8 @@ Template.swimlane.helpers({
return ( return (
Meteor.user() && Meteor.user() &&
Meteor.user().isBoardMember() && Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
); );
}, },
}); });

View file

@ -73,7 +73,8 @@ template(name="cardMemberPopup")
p.quiet @{{ user.username }} p.quiet @{{ user.username }}
ul.pop-over-list ul.pop-over-list
if currentUser.isNotCommentOnly if currentUser.isNotCommentOnly
li: a.js-remove-member {{_ 'remove-member-from-card'}} if currentUser.isNotWorker
li: a.js-remove-member {{_ 'remove-member-from-card'}}
if $eq currentUser._id user._id if $eq currentUser._id user._id
with currentUser with currentUser

View file

@ -39,12 +39,13 @@ template(name="memberMenuPopup")
a.js-go-setting(href="{{pathFor 'setting'}}") a.js-go-setting(href="{{pathFor 'setting'}}")
i.fa.fa-lock i.fa.fa-lock
| {{_ 'admin-panel'}} | {{_ 'admin-panel'}}
hr unless currentUser.isWorker
ul.pop-over-list hr
li ul.pop-over-list
a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") li
i.fa.fa-clone a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
| {{_ 'templates'}} i.fa.fa-clone
| {{_ 'templates'}}
unless isSandstorm unless isSandstorm
hr hr
ul.pop-over-list ul.pop-over-list
@ -109,13 +110,15 @@ template(name="changeSettingsPopup")
| {{_ 'show-desktop-drag-handles'}} | {{_ 'show-desktop-drag-handles'}}
if showDesktopDragHandles if showDesktopDragHandles
i.fa.fa-check i.fa.fa-check
li unless currentUser.isWorker
label.bold li
i.fa.fa-sort-numeric-asc label.bold
| {{_ 'show-cards-minimum-count'}} i.fa.fa-sort-numeric-asc
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") | {{_ 'show-cards-minimum-count'}}
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false")
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
template(name="userDeletePopup") template(name="userDeletePopup")
p {{_ 'delete-user-confirm-popup'}} unless currentUser.isWorker
button.js-confirm.negate.full(type="submit") {{_ 'delete'}} p {{_ 'delete-user-confirm-popup'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}

View file

@ -185,6 +185,7 @@ Boards.attachSchema(
isActive: true, isActive: true,
isNoComments: false, isNoComments: false,
isCommentOnly: false, isCommentOnly: false,
isWorker: false,
}, },
]; ];
} }
@ -222,6 +223,13 @@ Boards.attachSchema(
type: Boolean, type: Boolean,
optional: true, optional: true,
}, },
'members.$.isWorker': {
/**
* Is the member only allowed to move card, assign himself to card and comment
*/
type: Boolean,
optional: true,
},
permission: { permission: {
/** /**
* visibility of the board * visibility of the board
@ -538,6 +546,7 @@ Boards.helpers({
isActive: true, isActive: true,
isAdmin: false, isAdmin: false,
isNoComments: true, isNoComments: true,
isWorker: false,
}); });
}, },
@ -547,6 +556,17 @@ Boards.helpers({
isActive: true, isActive: true,
isAdmin: false, isAdmin: false,
isCommentOnly: true, isCommentOnly: true,
isWorker: false,
});
},
hasWorker(memberId) {
return !!_.findWhere(this.members, {
userId: memberId,
isActive: true,
isAdmin: false,
isCommentOnly: false,
isWorker: true,
}); });
}, },
@ -849,6 +869,7 @@ Boards.mutations({
isActive: true, isActive: true,
isNoComments: false, isNoComments: false,
isCommentOnly: false, isCommentOnly: false,
isWorker: false,
}, },
}, },
}; };
@ -881,6 +902,7 @@ Boards.mutations({
isAdmin, isAdmin,
isNoComments, isNoComments,
isCommentOnly, isCommentOnly,
isWorker,
currentUserId = Meteor.userId(), currentUserId = Meteor.userId(),
) { ) {
const memberIndex = this.memberIndex(memberId); const memberIndex = this.memberIndex(memberId);
@ -894,6 +916,7 @@ Boards.mutations({
[`members.${memberIndex}.isAdmin`]: isAdmin, [`members.${memberIndex}.isAdmin`]: isAdmin,
[`members.${memberIndex}.isNoComments`]: isNoComments, [`members.${memberIndex}.isNoComments`]: isNoComments,
[`members.${memberIndex}.isCommentOnly`]: isCommentOnly, [`members.${memberIndex}.isCommentOnly`]: isCommentOnly,
[`members.${memberIndex}.isWorker`]: isWorker,
}, },
}; };
}, },
@ -1281,6 +1304,7 @@ if (Meteor.isServer) {
* @param {boolean} [isActive] is the board active (default true) * @param {boolean} [isActive] is the board active (default true)
* @param {boolean} [isNoComments] disable comments (default false) * @param {boolean} [isNoComments] disable comments (default false)
* @param {boolean} [isCommentOnly] only enable comments (default false) * @param {boolean} [isCommentOnly] only enable comments (default false)
* @param {boolean} [isWorker] only move cards, assign himself to card and comment (default false)
* @param {string} [permission] "private" board <== Set to "public" if you * @param {string} [permission] "private" board <== Set to "public" if you
* want public Wekan board * want public Wekan board
* @param {string} [color] the color of the board * @param {string} [color] the color of the board
@ -1300,6 +1324,7 @@ if (Meteor.isServer) {
isActive: req.body.isActive || true, isActive: req.body.isActive || true,
isNoComments: req.body.isNoComments || false, isNoComments: req.body.isNoComments || false,
isCommentOnly: req.body.isCommentOnly || false, isCommentOnly: req.body.isCommentOnly || false,
isWorker: req.body.isWorker || false,
}, },
], ],
permission: req.body.permission || 'private', permission: req.body.permission || 'private',
@ -1403,6 +1428,7 @@ if (Meteor.isServer) {
* @param {boolean} isAdmin admin capability * @param {boolean} isAdmin admin capability
* @param {boolean} isNoComments NoComments capability * @param {boolean} isNoComments NoComments capability
* @param {boolean} isCommentOnly CommentsOnly capability * @param {boolean} isCommentOnly CommentsOnly capability
* @param {boolean} isWorker Worker capability
*/ */
JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function( JsonRoutes.add('POST', '/api/boards/:boardId/members/:memberId', function(
req, req,
@ -1411,7 +1437,7 @@ if (Meteor.isServer) {
try { try {
const boardId = req.params.boardId; const boardId = req.params.boardId;
const memberId = req.params.memberId; const memberId = req.params.memberId;
const { isAdmin, isNoComments, isCommentOnly } = req.body; const { isAdmin, isNoComments, isCommentOnly, isWorker } = req.body;
Authentication.checkBoardAccess(req.userId, boardId); Authentication.checkBoardAccess(req.userId, boardId);
const board = Boards.findOne({ _id: boardId }); const board = Boards.findOne({ _id: boardId });
function isTrue(data) { function isTrue(data) {
@ -1426,6 +1452,7 @@ if (Meteor.isServer) {
isTrue(isAdmin), isTrue(isAdmin),
isTrue(isNoComments), isTrue(isNoComments),
isTrue(isCommentOnly), isTrue(isCommentOnly),
isTrue(isWorker),
req.userId, req.userId,
); );

View file

@ -352,6 +352,16 @@ if (Meteor.isClient) {
return board && board.hasCommentOnly(this._id); return board && board.hasCommentOnly(this._id);
}, },
isNotWorker() {
const board = Boards.findOne(Session.get('currentBoard'));
return board && board.hasMember(this._id) && !board.hasWorker(this._id);
},
isWorker() {
const board = Boards.findOne(Session.get('currentBoard'));
return board && board.hasWorker(this._id);
},
isBoardAdmin() { isBoardAdmin() {
const board = Boards.findOne(Session.get('currentBoard')); const board = Boards.findOne(Session.get('currentBoard'));
return board && board.hasAdmin(this._id); return board && board.hasAdmin(this._id);