Add Worker role.

Add more Font Awesome icons.
Fix browser console errors when editing user profile name etc.

Thanks to xet7 !

Closes #2788
This commit is contained in:
Lauri Ojansivu 2020-01-03 06:49:35 +02:00
parent 0709b5abc8
commit 2bf004120d
37 changed files with 683 additions and 320 deletions

View file

@ -145,6 +145,7 @@
"allowIsBoardMemberByCard": true, "allowIsBoardMemberByCard": true,
"allowIsBoardMemberCommentOnly": true, "allowIsBoardMemberCommentOnly": true,
"allowIsBoardMemberNoComments": true, "allowIsBoardMemberNoComments": true,
"allowIsBoardMemberWorker": true,
"Emoji": true, "Emoji": true,
"Checklists": true, "Checklists": true,
"Settings": true, "Settings": true,

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

@ -1,7 +1,7 @@
template(name="cardDetails") template(name="cardDetails")
section.card-details.js-card-details.js-perfect-scrollbar: .card-details-canvas section.card-details.js-card-details.js-perfect-scrollbar: .card-details-canvas
.card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}') .card-details-header(class='{{#if colorClass}}card-details-{{colorClass}}{{/if}}')
+inlinedForm(classNames="js-card-details-title") +inlinedForm(classNames="{{#if canModifyCardWorker}}js-card-details-title{{/if}}")
+editCardTitleForm +editCardTitleForm
else else
unless isMiniScreen unless isMiniScreen
@ -13,11 +13,11 @@ template(name="cardDetails")
if currentUser.isBoardMember if currentUser.isBoardMember
a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu
h2.card-details-title.js-card-title( h2.card-details-title.js-card-title(
class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}") class="{{#if canModifyCardWorker}}js-open-inlined-form is-editable{{/if}}")
+viewer +viewer
= getTitle = getTitle
if isWatching if isWatching
i.fa.fa-eye.card-details-watch i.card-details-watch.fa.fa-eye
.card-details-path .card-details-path
each parentList each parentList
|   >   |   >  
@ -37,49 +37,66 @@ template(name="cardDetails")
.card-details-items .card-details-items
.card-details-item.card-details-item-received .card-details-item.card-details-item-received
h3.card-details-item-title {{_ 'card-received'}} h3
i.fa.fa-sign-out
card-details-item-title {{_ 'card-received'}}
if getReceived if getReceived
+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.card-details-item-title {{_ 'card-start'}} h3
i.fa.fa-hourglass-start
card-details-item-title {{_ 'card-start'}}
if getStart if getStart
+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.card-details-item-title {{_ 'card-due'}} h3
i.fa.fa-sign-in
card-details-item-title {{_ 'card-due'}}
if getDue if getDue
+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.card-details-item-title {{_ 'card-end'}} h3
i.fa.fa-hourglass-end
card-details-item-title {{_ 'card-end'}}
if getEnd if getEnd
+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
h3.card-details-item-title {{_ 'members'}} h3
i.fa.fa-users
card-details-item-title {{_ 'members'}}
each getMembers each getMembers
+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.card-details-item-title {{_ 'assignee'}} h3
i.fa.fa-user
card-details-item-title {{_ 'assignee'}}
each getAssignees each getAssignees
+userAvatarAssignee(userId=this cardId=../_id) +userAvatarAssignee(userId=this cardId=../_id)
| {{! XXX Hack to hide syntaxic coloration /// }} | {{! XXX Hack to hide syntaxic coloration /// }}
@ -89,15 +106,18 @@ template(name="cardDetails")
i.fa.fa-plus i.fa.fa-plus
.card-details-item.card-details-item-labels .card-details-item.card-details-item-labels
h3.card-details-item-title {{_ 'labels'}} h3
i.fa.fa-tags
card-details-item-title {{_ 'labels'}}
a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}") a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}")
each labels each labels
span.card-label(class="card-label-{{color}}" title=name) span.card-label(class="card-label-{{color}}" title=name)
+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
@ -118,26 +138,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.card-details-item-title {{_ 'description'}} unless currentUser.isWorker
+inlinedCardDescription(classNames="card-description js-card-description") h3
+editor(autofocus=true) i.fa.fa-align-left
| {{getUnsavedValue 'cardDescription' _id getDescription}} card-details-item-title {{_ 'description'}}
.edit-controls.clearfix +inlinedCardDescription(classNames="card-description js-card-description")
button.primary(type="submit") {{_ 'save'}} +editor(autofocus=true)
a.fa.fa-times-thin.js-close-inlined-form | {{getUnsavedValue 'cardDescription' _id getDescription}}
else .edit-controls.clearfix
a.js-open-inlined-form button.primary(type="submit") {{_ 'save'}}
if getDescription a.fa.fa-times-thin.js-close-inlined-form
+viewer else
= getDescription a.js-open-inlined-form
else if getDescription
| {{_ 'edit'}} +viewer
if (hasUnsavedValue 'cardDescription' _id) = getDescription
p.quiet else
| {{_ 'unsaved-description'}} | {{_ 'edit'}}
a.js-open-inlined-form {{_ 'view-it'}} if (hasUnsavedValue 'cardDescription' _id)
= ' - ' p.quiet
a.js-close-inlined-form {{_ 'discard'}} | {{_ 'unsaved-description'}}
a.js-open-inlined-form {{_ 'view-it'}}
= ' - '
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
@ -145,33 +168,39 @@ template(name="cardDetails")
.card-details-items .card-details-items
.card-details-item.card-details-item-name .card-details-item.card-details-item-name
h3.card-details-item-title {{_ 'requested-by'}} h3
i.fa.fa-shopping-cart
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
.card-details-item.card-details-item-name .card-details-item.card-details-item-name
h3.card-details-item-title {{_ 'assigned-by'}} h3
i.fa.fa-user-plus
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
@ -193,7 +222,9 @@ template(name="cardDetails")
hr hr
unless currentUser.isNoComments unless currentUser.isNoComments
.activity-title .activity-title
h3 {{ _ 'activity'}} h3
i.fa.fa-history
| {{ _ 'activity'}}
if currentUser.isBoardMember if currentUser.isBoardMember
.material-toggle-switch .material-toggle-switch
span.toggle-switch-title {{_ 'hide-system-messages'}} span.toggle-switch-title {{_ 'hide-system-messages'}}
@ -235,32 +266,79 @@ template(name="editCardAssignerForm")
template(name="cardDetailsActionsPopup") template(name="cardDetailsActionsPopup")
ul.pop-over-list ul.pop-over-list
li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}} li
a.js-toggle-watch-card
if isWatching
i.fa.fa-eye
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| {{_ 'watch'}}
if canModifyCard if canModifyCard
hr unless currentUser.isWorker
hr
ul.pop-over-list
//li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-labels {{_ 'card-edit-labels'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}}
li
a.js-custom-fields
i.fa.fa-list-alt
| {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
li
a.js-spent-time
i.fa.fa-clock-o
| {{_ 'editCardSpentTimePopup-title'}}
li
a.js-set-card-color
i.fa.fa-paint-brush
| {{_ 'setCardColorPopup-title'}}
hr
ul.pop-over-list ul.pop-over-list
//li: a.js-members {{_ 'card-edit-members'}} li
//li: a.js-labels {{_ 'card-edit-labels'}} a.js-move-card-to-top
//li: a.js-attachments {{_ 'card-edit-attachments'}} i.fa.fa-arrow-up
li: a.js-custom-fields {{_ 'card-edit-custom-fields'}} | {{_ 'moveCardToTop-title'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}} li
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} a.js-move-card-to-bottom
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} i.fa.fa-arrow-down
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}} | {{_ 'moveCardToBottom-title'}}
li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}} unless currentUser.isWorker
li: a.js-set-card-color {{_ 'setCardColorPopup-title'}} hr
hr ul.pop-over-list
ul.pop-over-list li
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}} a.js-move-card
li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}} i.fa.fa-arrow-right
hr | {{_ 'moveCardPopup-title'}}
ul.pop-over-list li
li: a.js-move-card {{_ 'moveCardPopup-title'}} a.js-copy-card
li: a.js-copy-card {{_ 'copyCardPopup-title'}} i.fa.fa-copy
li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}} | {{_ 'copyCardPopup-title'}}
hr
ul.pop-over-list
li
a.js-copy-checklist-cards
i.fa.fa-list
i.fa.fa-copy
| {{_ 'copyChecklistToManyCardsPopup-title'}}
unless archived unless archived
li: a.js-archive {{_ 'archive-card'}} hr
li: a.js-more {{_ 'cardMorePopup-title'}} ul.pop-over-list
li
a.js-archive
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-card'}}
hr
ul.pop-over-list
li
a.js-more
i.fa.fa-link
| {{_ 'cardMorePopup-title'}}
template(name="moveCardPopup") template(name="moveCardPopup")
+boardsAndLists +boardsAndLists
@ -312,16 +390,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-assigneeWorker(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}})")
@ -349,11 +438,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
unless currentUser.isWorker
li: a.js-remove-assignee {{_ 'remove-member-from-card'}} 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

@ -26,6 +26,7 @@ BlazeComponent.extendComponent({
onCreated() { onCreated() {
this.currentBoard = Boards.findOne(Session.get('currentBoard')); this.currentBoard = Boards.findOne(Session.get('currentBoard'));
this.currentUser = Meteor.user();
this.isLoaded = new ReactiveVar(false); this.isLoaded = new ReactiveVar(false);
const boardBody = this.parentComponent().parentComponent(); const boardBody = this.parentComponent().parentComponent();
//in Miniview parent is Board, not BoardBody. //in Miniview parent is Board, not BoardBody.
@ -55,6 +56,15 @@ BlazeComponent.extendComponent({
); );
}, },
canModifyCardWorker() {
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
scrollParentContainer() { scrollParentContainer() {
const cardPanelWidth = 510; const cardPanelWidth = 510;
const bodyBoardComponent = this.parentComponent().parentComponent(); const bodyBoardComponent = this.parentComponent().parentComponent();
@ -322,7 +332,12 @@ BlazeComponent.extendComponent({
'click .js-assignee': Popup.open('cardAssignee'), 'click .js-assignee': Popup.open('cardAssignee'),
'click .js-add-assignees': Popup.open('cardAssignees'), 'click .js-add-assignees': Popup.open('cardAssignees'),
'click .js-add-labels': Popup.open('cardLabels'), 'click .js-add-labels': Popup.open('cardLabels'),
'click .js-received-date': Popup.open('editCardReceivedDate'), 'click .js-received-date'(event) {
event.preventDefault();
if (!Meteor.user().isWorker) {
Popup.open('editCardReceivedDate');
}
},
'click .js-start-date': Popup.open('editCardStartDate'), 'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'), 'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'), 'click .js-end-date': Popup.open('editCardEndDate'),
@ -383,6 +398,13 @@ Template.cardDetails.helpers({
return user && user.isBoardAdmin() ? 'admin' : 'normal'; return user && user.isBoardAdmin() ? 'admin' : 'normal';
}, },
isWorker() {
const currentBoard = Boards.findOne(Session.get('currentBoard'));
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
},
presenceStatusClassName() { presenceStatusClassName() {
const user = Users.findOne(this.userId); const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId }); const userPresence = presences.findOne({ userId: this.userId });
@ -459,6 +481,15 @@ Template.cardDetailsActionsPopup.helpers({
!Meteor.user().isCommentOnly() !Meteor.user().isCommentOnly()
); );
}, },
canModifyCardWorker() {
return (
Meteor.user() &&
Meteor.user().isBoardMember() &&
!Meteor.user().isCommentOnly() &&
!Meteor.user().isWorker()
);
},
}); });
Template.cardDetailsActionsPopup.events({ Template.cardDetailsActionsPopup.events({
@ -467,7 +498,12 @@ Template.cardDetailsActionsPopup.events({
'click .js-labels': Popup.open('cardLabels'), 'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'), 'click .js-attachments': Popup.open('cardAttachments'),
'click .js-custom-fields': Popup.open('cardCustomFields'), 'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-received-date': Popup.open('editCardReceivedDate'), 'click .js-received-date'(event) {
event.preventDefault();
if (!Meteor.user().isWorker) {
Popup.open('editCardReceivedDate');
}
},
'click .js-start-date': Popup.open('editCardStartDate'), 'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'), 'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'), 'click .js-end-date': Popup.open('editCardEndDate'),
@ -879,6 +915,12 @@ Template.cardAssigneesPopup.events({
card.toggleAssignee(assigneeId); card.toggleAssignee(assigneeId);
event.preventDefault(); event.preventDefault();
}, },
'click .js-select-assigneeWorker'(event) {
const card = Cards.findOne(Session.get('currentCard'));
const assigneeId = currentUser._id;
card.toggleAssignee(assigneeId);
event.preventDefault();
},
}); });
Template.cardAssigneesPopup.helpers({ Template.cardAssigneesPopup.helpers({

View file

@ -1,5 +1,7 @@
template(name="checklists") template(name="checklists")
h3 {{_ 'checklists'}} h3
i.fa.fa-check
| {{_ 'checklists'}}
if toggleDeleteDialog.get if toggleDeleteDialog.get
.board-overlay#card-details-overlay .board-overlay#card-details-overlay
+checklistDeleteDialog(checklist = checklistToDelete) +checklistDeleteDialog(checklist = checklistToDelete)

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

@ -36,24 +36,20 @@ Template.minicard.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { return false;
return true;
} else {
return false;
}
} }
}, },
hiddenMinicardLabelText() { hiddenMinicardLabelText() {
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText; return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (cookies.has('hiddenMinicardLabelText')) {
return true;
} else { } else {
if (cookies.has('hiddenMinicardLabelText')) { return false;
return true;
} else {
return false;
}
} }
}, },
}); });

View file

@ -1,5 +1,7 @@
template(name="subtasks") template(name="subtasks")
h3 {{_ 'subtasks'}} h3
i.fa.fa-sitemap
| {{_ 'subtasks'}}
if toggleDeleteDialog.get if toggleDeleteDialog.get
.board-overlay#card-details-overlay .board-overlay#card-details-overlay
+subtaskDeleteDialog(subtask = subtaskToDelete) +subtaskDeleteDialog(subtask = subtaskToDelete)

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

@ -176,12 +176,10 @@ Template.list.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { return false;
return true;
} else {
return false;
}
} }
}, },
}); });

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

@ -56,25 +56,47 @@ template(name="editListTitleForm")
template(name="listActionPopup") template(name="listActionPopup")
ul.pop-over-list ul.pop-over-list
li: a.js-toggle-watch-list {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}} li
a.js-toggle-watch-list
if isWatching
i.fa.fa-eye
| {{_ 'unwatch'}}
else
i.fa.fa-eye-slash
| {{_ 'watch'}}
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
hr unless currentUser.isWorker
ul.pop-over-list ul.pop-over-list
li: a.js-set-color-list {{_ 'set-color-list'}} li
hr a.js-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: a.js-select-cards {{_ 'list-select-cards'}} li
hr a.js-select-cards
i.fa.fa-check-square
| {{_ 'list-select-cards'}}
if currentUser.isBoardAdmin if currentUser.isBoardAdmin
ul.pop-over-list ul.pop-over-list
li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}} li
a.js-set-wip-limit
i.fa.fa-ban
| {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}}
unless currentUser.isWorker
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-close-list {{_ 'archive-list'}} li
a.js-close-list
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-list'}}
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-more {{_ 'listMorePopup-title'}} li
a.js-more
i.fa.fa-link
| {{_ 'listMorePopup-title'}}
template(name="boardLists") template(name="boardLists")
ul.pop-over-list ul.pop-over-list
@ -94,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()
); );
}, },
@ -109,12 +110,10 @@ Template.listHeader.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { return false;
return true;
} else {
return false;
}
} }
}, },
}); });

View file

@ -4,12 +4,16 @@ template(name='information')
| {{_ 'error-notAuthorized'}} | {{_ 'error-notAuthorized'}}
else else
.content-title .content-title
span {{_ 'info'}} span
i.fa.fa-info-circle
| {{_ 'info'}}
.content-body .content-body
.side-menu .side-menu
ul ul
li.active li.active
a.js-setting-menu(data-id="information-display") {{_ 'info'}} a.js-setting-menu(data-id="information-display")
i.fa.fa-info-circle
| {{_ 'info'}}
.main-body .main-body
+statistics +statistics

View file

@ -5,16 +5,22 @@ template(name="people")
else else
.content-title.ext-box .content-title.ext-box
.ext-box-left .ext-box-left
span {{_ 'people'}} span
i.fa.fa-users
| {{_ 'people'}}
input#searchInput(placeholder="{{_ 'search'}}") input#searchInput(placeholder="{{_ 'search'}}")
button#searchButton {{_ 'search'}} button#searchButton
i.fa.fa-search
| {{_ 'search'}}
.ext-box-right .ext-box-right
span {{_ 'people-number'}} #{peopleNumber} span {{_ 'people-number'}} #{peopleNumber}
.content-body .content-body
.side-menu .side-menu
ul ul
li.active li.active
a.js-setting-menu(data-id="people-setting") {{_ 'people'}} a.js-setting-menu(data-id="people-setting")
i.fa.fa-users
| {{_ 'people'}}
.main-body .main-body
if loading.get if loading.get
+spinner +spinner
@ -90,6 +96,7 @@ template(name="peopleRow")
td {{_ userData.authenticationMethod }} td {{_ userData.authenticationMethod }}
td td
a.edit-user a.edit-user
i.fa.fa-edit
| {{_ 'edit'}} | {{_ 'edit'}}
template(name="editUserPopup") template(name="editUserPopup")

View file

@ -33,7 +33,7 @@ table
padding: 0; padding: 0;
button button
min-width: 60px; min-width: 90px;
.content-wrapper .content-wrapper
margin-top: 10px margin-top: 10px

View file

@ -4,22 +4,35 @@ template(name="setting")
| {{_ 'error-notAuthorized'}} | {{_ 'error-notAuthorized'}}
else else
.content-title .content-title
i.fa.fa-cog
span {{_ 'settings'}} span {{_ 'settings'}}
.content-body .content-body
.side-menu .side-menu
ul ul
li.active li.active
a.js-setting-menu(data-id="registration-setting") {{_ 'registration'}} a.js-setting-menu(data-id="registration-setting")
i.fa.fa-sign-in
| {{_ 'registration'}}
li li
a.js-setting-menu(data-id="email-setting") {{_ 'email'}} a.js-setting-menu(data-id="email-setting")
i.fa.fa-envelope
| {{_ 'email'}}
li li
a.js-setting-menu(data-id="account-setting") {{_ 'accounts'}} a.js-setting-menu(data-id="account-setting")
i.fa.fa-users
| {{_ 'accounts'}}
li li
a.js-setting-menu(data-id="announcement-setting") {{_ 'admin-announcement'}} a.js-setting-menu(data-id="announcement-setting")
i.fa.fa-bullhorn
| {{_ 'admin-announcement'}}
li li
a.js-setting-menu(data-id="layout-setting") {{_ 'layout'}} a.js-setting-menu(data-id="layout-setting")
i.fa.fa-object-group
| {{_ 'layout'}}
li li
a.js-setting-menu(data-id="webhook-setting") {{_ 'global-webhook'}} a.js-setting-menu(data-id="webhook-setting")
i.fa.fa-globe
| {{_ 'global-webhook'}}
.main-body .main-body
if loading.get if loading.get
+spinner +spinner

View file

@ -41,15 +41,18 @@
&:hover &:hover
background #fff background #fff
box-shadow 0 1px 2px rgba(0,0,0,0.15); box-shadow 0 1px 2px rgba(0,0,0,0.15);
a a
@extends .flex @extends .flex
padding: 1rem 0 1rem 1rem padding: 1rem 0 1rem 1rem
width: 100% - 5rem width: 100% - 5rem
span span
font-size: 13px font-size: 13px
i
margin-right: 20px
.main-body .main-body
padding: 0.1em 1em padding: 0.1em 1em
-webkit-user-select: text // Safari 3.1+ -webkit-user-select: text // Safari 3.1+

View file

@ -37,11 +37,12 @@ template(name='homeSidebar')
template(name="membersWidget") template(name="membersWidget")
.board-widget.board-widget-members .board-widget.board-widget-members
h3 h3
i.fa.fa-user 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
@ -130,7 +131,9 @@ template(name="chooseBoardSource")
template(name="archiveBoardPopup") template(name="archiveBoardPopup")
p {{_ 'close-board-pop'}} p {{_ 'close-board-pop'}}
button.js-confirm.negate.full(type="submit") {{_ 'archive'}} button.js-confirm.negate.full(type="submit")
i.fa.fa-archive
| {{_ 'archive'}}
template(name="outgoingWebhooksPopup") template(name="outgoingWebhooksPopup")
each integrations each integrations
@ -162,38 +165,80 @@ 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: a.js-open-archives {{_ 'archived-items'}} li: a.js-custom-fields {{_ 'custom-fields'}}
li
a.js-open-archives
i.fa.fa-archive
| {{_ 'archived-items'}}
if currentUser.isBoardAdmin if currentUser.isBoardAdmin
li: a.js-change-board-color {{_ 'board-change-color'}} li
a.js-change-board-color
i.fa.fa-paint-brush
| {{_ 'board-change-color'}}
//- //-
XXX Language should be handled by sandstorm, but for now display a XXX Language should be handled by sandstorm, but for now display a
language selection link in the board menu. This link is normally present language selection link in the board menu. This link is normally present
in the header bar that is not displayed on sandstorm. in the header bar that is not displayed on sandstorm.
if isSandstorm if isSandstorm
li: a.js-change-language {{_ 'language'}} li
a.js-change-language
i.fa.fa-flag
| {{_ 'language'}}
unless isSandstorm unless isSandstorm
if currentUser.isBoardAdmin if currentUser.isBoardAdmin
hr hr
ul.pop-over-list ul.pop-over-list
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} li
unless currentBoard.isTemplatesBoard a(href="{{exportUrl}}", download="{{exportFilename}}")
li: a.js-archive-board {{_ 'archive-board'}} i.fa.fa-share-alt
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}} | {{_ 'export-board'}}
hr li
ul.pop-over-list a.js-outgoing-webhooks
li: a.js-subtask-settings {{_ 'subtask-settings'}} i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
li
a.js-subtask-settings
i.fa.fa-sitemap
| {{_ 'subtask-settings'}}
unless currentBoard.isTemplatesBoard
hr
ul.pop-over-list
li
a.js-archive-board
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-board'}}
if isSandstorm if isSandstorm
hr hr
ul.pop-over-list ul.pop-over-list
li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} li
li: a.js-import-board {{_ 'import-board-c'}} a(href="{{exportUrl}}", download="{{exportFilename}}")
li: a.js-archive-board {{_ 'archive-board'}} i.fa.fa-share-alt
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}} i.fa.fa-sign-out
| {{_ 'export-board'}}
li
a.js-import-board
i.fa.fa-share-alt
i.fa.fa-sign-in
| {{_ 'import-board-c'}}
li
a.js-archive-board
i.fa.fa-arrow-right
i.fa.fa-archive
| {{_ 'archive-board'}}
li
a.js-outgoing-webhooks
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-subtask-settings {{_ 'subtask-settings'}} li
a.js-subtask-settings
i.fa.fa-sitemap
| {{_ 'subtask-settings'}}
template(name="labelsWidget") template(name="labelsWidget")
.board-widget.board-widget-labels .board-widget.board-widget-labels
@ -203,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
@ -232,12 +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}}
@ -301,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

@ -112,12 +112,10 @@ BlazeComponent.extendComponent({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
Meteor.call('toggleMinicardLabelText'); Meteor.call('toggleMinicardLabelText');
} else if (cookies.has('hiddenMinicardLabelText')) {
cookies.remove('hiddenMinicardLabelText');
} else { } else {
if (cookies.has('hiddenMinicardLabelText')) { cookies.set('hiddenMinicardLabelText', 'true');
cookies.remove('hiddenMinicardLabelText');
} else {
cookies.set('hiddenMinicardLabelText', 'true');
}
} }
}, },
'click .js-shortcuts'() { 'click .js-shortcuts'() {
@ -135,12 +133,10 @@ Template.homeSidebar.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText; return (currentUser.profile || {}).hiddenMinicardLabelText;
} else if (cookies.has('hiddenMinicardLabelText')) {
return true;
} else { } else {
if (cookies.has('hiddenMinicardLabelText')) { return false;
return true;
} else {
return false;
}
} }
}, },
}); });
@ -165,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();
} }
@ -271,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({
@ -648,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'));
@ -658,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);
}, },
@ -679,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)
); );
}, },
@ -699,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

@ -117,13 +117,14 @@ template(name="multiselectionSidebar")
i.fa.fa-check i.fa.fa-check
else if someSelectedElementHave 'member' _id else if someSelectedElementHave 'member' _id
i.fa.fa-ellipsis-h i.fa.fa-ellipsis-h
hr unless currentUser.isWorker
a.sidebar-btn.js-move-selection hr
i.fa.fa-share a.sidebar-btn.js-move-selection
span {{_ 'move-selection'}} i.fa.fa-share
a.sidebar-btn.js-archive-selection span {{_ 'move-selection'}}
i.fa.fa-archive a.sidebar-btn.js-archive-selection
span {{_ 'archive-selection'}} i.fa.fa-archive
span {{_ 'archive-selection'}}
template(name="disambiguateMultiLabelPopup") template(name="disambiguateMultiLabelPopup")
p {{_ 'what-to-do'}} p {{_ 'what-to-do'}}

View file

@ -35,12 +35,10 @@ Template.swimlaneHeader.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { return false;
return true;
} else {
return false;
}
} }
}, },
}); });

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

@ -104,12 +104,10 @@ function initSortable(boardComponent, $listsDom) {
if (currentUser) { if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {}) showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles; .showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { showDesktopDragHandles = false;
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
} }
if (!Utils.isMiniScreen() && showDesktopDragHandles) { if (!Utils.isMiniScreen() && showDesktopDragHandles) {
@ -182,12 +180,10 @@ BlazeComponent.extendComponent({
if (currentUser) { if (currentUser) {
showDesktopDragHandles = (currentUser.profile || {}) showDesktopDragHandles = (currentUser.profile || {})
.showDesktopDragHandles; .showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
showDesktopDragHandles = true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { showDesktopDragHandles = false;
showDesktopDragHandles = true;
} else {
showDesktopDragHandles = false;
}
} }
const noDragInside = ['a', 'input', 'textarea', 'p'].concat( const noDragInside = ['a', 'input', 'textarea', 'p'].concat(
@ -276,12 +272,10 @@ Template.swimlane.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else if (cookies.has('showDesktopDragHandles')) {
return true;
} else { } else {
if (cookies.has('showDesktopDragHandles')) { return false;
return true;
} else {
return false;
}
} }
}, },
canSeeAddList() { canSeeAddList() {

View file

@ -73,6 +73,7 @@ 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
if currentUser.isNotWorker
li: a.js-remove-member {{_ 'remove-member-from-card'}} li: a.js-remove-member {{_ 'remove-member-from-card'}}
if $eq currentUser._id user._id if $eq currentUser._id user._id

View file

@ -13,21 +13,46 @@ template(name="headerUserBar")
template(name="memberMenuPopup") template(name="memberMenuPopup")
ul.pop-over-list ul.pop-over-list
with currentUser with currentUser
li: a.js-edit-profile {{_ 'edit-profile'}} li
li: a.js-change-settings {{_ 'change-settings'}} a.js-edit-profile
li: a.js-change-avatar {{_ 'edit-avatar'}} i.fa.fa-user
| {{_ 'edit-profile'}}
li
a.js-change-settings
i.fa.fa-cog
| {{_ 'change-settings'}}
li
a.js-change-avatar
i.fa.fa-picture-o
| {{_ 'edit-avatar'}}
unless isSandstorm unless isSandstorm
li: a.js-change-password {{_ 'changePasswordPopup-title'}} li
li: a.js-change-language {{_ 'changeLanguagePopup-title'}} a.js-change-password
i.fa.fa-key
| {{_ 'changePasswordPopup-title'}}
li
a.js-change-language
i.fa.fa-flag
| {{_ 'changeLanguagePopup-title'}}
if currentUser.isAdmin if currentUser.isAdmin
li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}} li
hr a.js-go-setting(href="{{pathFor 'setting'}}")
ul.pop-over-list i.fa.fa-lock
li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}} | {{_ 'admin-panel'}}
unless currentUser.isWorker
hr
ul.pop-over-list
li
a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}")
i.fa.fa-clone
| {{_ 'templates'}}
unless isSandstorm unless isSandstorm
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-logout {{_ 'log-out'}} li
a.js-logout
i.fa.fa-sign-out
| {{_ 'log-out'}}
template(name="editProfilePopup") template(name="editProfilePopup")
form form
@ -75,21 +100,25 @@ template(name="changeSettingsPopup")
ul.pop-over-list ul.pop-over-list
li li
a.js-toggle-system-messages a.js-toggle-system-messages
i.fa.fa-comments-o
| {{_ 'hide-system-messages'}} | {{_ 'hide-system-messages'}}
if hiddenSystemMessages if hiddenSystemMessages
i.fa.fa-check i.fa.fa-check
li li
a.js-toggle-desktop-drag-handles a.js-toggle-desktop-drag-handles
i.fa.fa-arrows
| {{_ '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
| {{_ 'show-cards-minimum-count'}} label.bold
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") i.fa.fa-sort-numeric-asc
input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") | {{_ 'show-cards-minimum-count'}}
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

@ -45,13 +45,31 @@ Template.memberMenuPopup.events({
Template.editProfilePopup.helpers({ Template.editProfilePopup.helpers({
allowEmailChange() { allowEmailChange() {
return AccountSettings.findOne('accounts-allowEmailChange').booleanValue; Meteor.call('AccountSettings.allowEmailChange', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
}, },
allowUserNameChange() { allowUserNameChange() {
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue; Meteor.call('AccountSettings.allowUserNameChange', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
}, },
allowUserDelete() { allowUserDelete() {
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue; Meteor.call('AccountSettings.allowUserDelete', (_, result) => {
if (result) {
return true;
} else {
return false;
}
});
}, },
}); });

View file

@ -24,18 +24,14 @@ Utils = {
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).boardView; return (currentUser.profile || {}).boardView;
} else if (cookies.get('boardView') === 'board-view-lists') {
return 'board-view-lists';
} else if (cookies.get('boardView') === 'board-view-swimlanes') {
return 'board-view-swimlanes';
} else if (cookies.get('boardView') === 'board-view-cal') {
return 'board-view-cal';
} else { } else {
if (cookies.get('boardView') === 'board-view-lists') { return false;
return 'board-view-lists';
} else if (
cookies.get('boardView') === 'board-view-swimlanes'
) {
return 'board-view-swimlanes';
} else if (cookies.get('boardView') === 'board-view-cal') {
return 'board-view-cal';
} else {
return false;
}
} }
}, },
@ -43,8 +39,8 @@ Utils = {
goBoardId(_id) { goBoardId(_id) {
const board = Boards.findOne(_id); const board = Boards.findOne(_id);
return ( return (
board board &&
&& FlowRouter.go('board', { FlowRouter.go('board', {
id: board._id, id: board._id,
slug: board.slug, slug: board.slug,
}) })
@ -55,8 +51,8 @@ Utils = {
const card = Cards.findOne(_id); const card = Cards.findOne(_id);
const board = Boards.findOne(card.boardId); const board = Boards.findOne(card.boardId);
return ( return (
board board &&
&& FlowRouter.go('card', { FlowRouter.go('card', {
cardId: card._id, cardId: card._id,
boardId: board._id, boardId: board._id,
slug: board.slug, slug: board.slug,
@ -227,8 +223,8 @@ Utils = {
}; };
if ( if (
'ontouchstart' in window 'ontouchstart' in window ||
|| (window.DocumentTouch && document instanceof window.DocumentTouch) (window.DocumentTouch && document instanceof window.DocumentTouch)
) { ) {
return true; return true;
} }
@ -249,8 +245,8 @@ Utils = {
calculateTouchDistance(touchA, touchB) { calculateTouchDistance(touchA, touchB) {
return Math.sqrt( return Math.sqrt(
Math.pow(touchA.screenX - touchB.screenX, 2) Math.pow(touchA.screenX - touchB.screenX, 2) +
+ Math.pow(touchA.screenY - touchB.screenY, 2), Math.pow(touchA.screenY - touchB.screenY, 2),
); );
}, },
@ -267,9 +263,9 @@ Utils = {
}); });
$(document).on('touchend', selector, function(e) { $(document).on('touchend', selector, function(e) {
if ( if (
touchStart touchStart &&
&& lastTouch lastTouch &&
&& Utils.calculateTouchDistance(touchStart, lastTouch) <= 20 Utils.calculateTouchDistance(touchStart, lastTouch) <= 20
) { ) {
e.preventDefault(); e.preventDefault();
const clickEvent = document.createEvent('MouseEvents'); const clickEvent = document.createEvent('MouseEvents');

View file

@ -82,4 +82,16 @@ if (Meteor.isServer) {
}); });
} }
AccountSettings.helpers({
allowEmailChange() {
return AccountSettings.findOne('accounts-allowEmailChange').booleanValue;
},
allowUserNameChange() {
return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue;
},
allowUserDelete() {
return AccountSettings.findOne('accounts-allowUserDelete').booleanValue;
},
});
export default AccountSettings; export default AccountSettings;

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

@ -2008,7 +2008,7 @@ if (Meteor.isServer) {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
// Check user has permission to add card to the board // Check user has permission to add card to the board
const board = Boards.findOne({ const board = Boards.findOne({
_id: paramBoardId _id: paramBoardId,
}); });
const addPermission = allowIsBoardMemberCommentOnly(req.userId, board); const addPermission = allowIsBoardMemberCommentOnly(req.userId, board);
Authentication.checkAdminOrCondition(req.userId, addPermission); Authentication.checkAdminOrCondition(req.userId, addPermission);

View file

@ -288,7 +288,7 @@ if (Meteor.isServer) {
const paramBoardId = req.params.boardId; const paramBoardId = req.params.boardId;
// Check user has permission to add checklist to the card // Check user has permission to add checklist to the card
const board = Boards.findOne({ const board = Boards.findOne({
_id: paramBoardId _id: paramBoardId,
}); });
const addPermission = allowIsBoardMemberCommentOnly(req.userId, board); const addPermission = allowIsBoardMemberCommentOnly(req.userId, board);
Authentication.checkAdminOrCondition(req.userId, addPermission); Authentication.checkAdminOrCondition(req.userId, addPermission);

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);

View file

@ -236,12 +236,14 @@ if (isSandstorm && Meteor.isServer) {
const isAdmin = permissions.indexOf('configure') > -1; const isAdmin = permissions.indexOf('configure') > -1;
const isCommentOnly = false; const isCommentOnly = false;
const isNoComments = false; const isNoComments = false;
const isWorker = false;
const permissionDoc = { const permissionDoc = {
userId, userId,
isActive, isActive,
isAdmin, isAdmin,
isNoComments, isNoComments,
isCommentOnly, isCommentOnly,
isWorker,
}; };
const boardMembers = Boards.findOne(sandstormBoard._id).members; const boardMembers = Boards.findOne(sandstormBoard._id).members;