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,6 +38,7 @@ template(name="attachmentsGalery")
| {{_ 'download'}} | {{_ 'download'}}
if currentUser.isBoardMember if currentUser.isBoardMember
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
unless currentUser.isWorker
if isImage if isImage
a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}") a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}")
i.fa.fa-thumb-tack i.fa.fa-thumb-tack
@ -51,5 +52,6 @@ template(name="attachmentsGalery")
if currentUser.isBoardMember if currentUser.isBoardMember
unless currentUser.isCommentOnly unless currentUser.isCommentOnly
unless currentUser.isWorker
li.attachment-item.add-attachment li.attachment-item.add-attachment
a.js-add-attachment {{_ '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
unless currentUser.isWorker
a.js-received-date {{_ 'add'}} 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
unless currentUser.isWorker
a.js-start-date {{_ 'add'}} 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
unless currentUser.isWorker
a.js-due-date {{_ 'add'}} 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
unless currentUser.isWorker
a.js-end-date {{_ 'add'}} 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
unless currentUser.isWorker
a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}") a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}")
i.fa.fa-plus 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,13 +106,16 @@ 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
unless currentUser.isWorker
a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}") a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}")
i.fa.fa-plus i.fa.fa-plus
@ -118,7 +138,10 @@ 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
h3
i.fa.fa-align-left
card-details-item-title {{_ 'description'}}
+inlinedCardDescription(classNames="card-description js-card-description") +inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true) +editor(autofocus=true)
| {{getUnsavedValue 'cardDescription' _id getDescription}} | {{getUnsavedValue 'cardDescription' _id getDescription}}
@ -145,8 +168,11 @@ 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
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-requester") +inlinedForm(classNames="js-card-details-requester")
+editCardRequesterForm +editCardRequesterForm
else else
@ -161,8 +187,11 @@ template(name="cardDetails")
= 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
unless currentUser.isWorker
+inlinedForm(classNames="js-card-details-assigner") +inlinedForm(classNames="js-card-details-assigner")
+editCardAssignerForm +editCardAssignerForm
else else
@ -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
unless currentUser.isWorker
hr hr
ul.pop-over-list ul.pop-over-list
//li: a.js-members {{_ 'card-edit-members'}} //li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-labels {{_ 'card-edit-labels'}} //li: a.js-labels {{_ 'card-edit-labels'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}} //li: a.js-attachments {{_ 'card-edit-attachments'}}
li: a.js-custom-fields {{_ 'card-edit-custom-fields'}} li
a.js-custom-fields
i.fa.fa-list-alt
| {{_ 'card-edit-custom-fields'}}
//li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}} //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}}
//li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}}
//li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}}
//li: a.js-end-date {{_ 'editCardEndDatePopup-title'}} //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}}
li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}} li
li: a.js-set-card-color {{_ 'setCardColorPopup-title'}} 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 hr
ul.pop-over-list ul.pop-over-list
li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}} li
li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}} a.js-move-card-to-top
i.fa.fa-arrow-up
| {{_ 'moveCardToTop-title'}}
li
a.js-move-card-to-bottom
i.fa.fa-arrow-down
| {{_ 'moveCardToBottom-title'}}
unless currentUser.isWorker
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-move-card {{_ 'moveCardPopup-title'}} li
li: a.js-copy-card {{_ 'copyCardPopup-title'}} a.js-move-card
li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}} i.fa.fa-arrow-right
| {{_ 'moveCardPopup-title'}}
li
a.js-copy-card
i.fa.fa-copy
| {{_ '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,6 +390,7 @@ template(name="cardMembersPopup")
i.fa.fa-check i.fa.fa-check
template(name="cardAssigneesPopup") template(name="cardAssigneesPopup")
unless currentUser.isWorker
ul.pop-over-list.js-card-assignee-list ul.pop-over-list.js-card-assignee-list
each board.activeMembers each board.activeMembers
li.item(class="{{#if isCardAssignee}}active{{/if}}") li.item(class="{{#if isCardAssignee}}active{{/if}}")
@ -322,6 +401,16 @@ template(name="cardAssigneesPopup")
| (<span class="username">{{ user.username }}</span>) | (<span class="username">{{ user.username }}</span>)
if isCardAssignee if isCardAssignee
i.fa.fa-check 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,8 +438,10 @@ 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'}}
unless currentUser.isWorker
if $eq currentUser._id user._id if $eq currentUser._id user._id
with currentUser with currentUser
li: a.js-edit-profile {{_ 'edit-profile'}} li: a.js-edit-profile {{_ 'edit-profile'}}

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 { } else if (cookies.has('showDesktopDragHandles')) {
if (cookies.has('showDesktopDragHandles')) {
return true; return true;
} else { } else {
return false; return false;
} }
}
}, },
hiddenMinicardLabelText() { hiddenMinicardLabelText() {
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText; return (currentUser.profile || {}).hiddenMinicardLabelText;
} else { } else if (cookies.has('hiddenMinicardLabelText')) {
if (cookies.has('hiddenMinicardLabelText')) {
return true; return true;
} else { } else {
return false; 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,13 +176,11 @@ Template.list.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else { } else if (cookies.has('showDesktopDragHandles')) {
if (cookies.has('showDesktopDragHandles')) {
return true; return true;
} else { } else {
return false; 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,6 +116,7 @@ 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' }}
unless currentUser.isWorker
a.js-delete {{_ 'delete'}} a.js-delete {{_ 'delete'}}
template(name="listDeletePopup") template(name="listDeletePopup")

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,13 +110,11 @@ Template.listHeader.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).showDesktopDragHandles; return (currentUser.profile || {}).showDesktopDragHandles;
} else { } else if (cookies.has('showDesktopDragHandles')) {
if (cookies.has('showDesktopDragHandles')) {
return true; return true;
} else { } else {
return false; 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,9 +37,10 @@ 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
unless currentUser.isWorker
a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right
i.board-header-btn-icon.fa.fa-cog i.board-header-btn-icon.fa.fa-cog
@ -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
if isNotWorker
li: a.js-custom-fields {{_ 'custom-fields'}} li: a.js-custom-fields {{_ 'custom-fields'}}
li: a.js-open-archives {{_ 'archived-items'}} 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
a(href="{{exportUrl}}", download="{{exportFilename}}")
i.fa.fa-share-alt
| {{_ 'export-board'}}
li
a.js-outgoing-webhooks
i.fa.fa-globe
| {{_ 'outgoing-webhooks'}}
li
a.js-subtask-settings
i.fa.fa-sitemap
| {{_ 'subtask-settings'}}
unless currentBoard.isTemplatesBoard unless currentBoard.isTemplatesBoard
li: a.js-archive-board {{_ 'archive-board'}}
li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
hr hr
ul.pop-over-list ul.pop-over-list
li: a.js-subtask-settings {{_ 'subtask-settings'}} 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,13 +277,13 @@ template(name="memberPopup")
a.js-change-role a.js-change-role
| {{_ 'change-permissions'}} | {{_ 'change-permissions'}}
span.quiet (#{memberType}) span.quiet (#{memberType})
unless currentUser.isWorker
li li
if $eq currentUser._id userId if $eq currentUser._id userId
a.js-leave-member {{_ 'leave-board'}} a.js-leave-member {{_ 'leave-board'}}
else if currentUser.isBoardAdmin else if currentUser.isBoardAdmin
a.js-remove-member {{_ 'remove-from-board'}} 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}}
button.js-confirm.negate.full(type="submit") {{_ 'remove-member'}} button.js-confirm.negate.full(type="submit") {{_ 'remove-member'}}
@ -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,13 +112,11 @@ BlazeComponent.extendComponent({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
Meteor.call('toggleMinicardLabelText'); Meteor.call('toggleMinicardLabelText');
} else { } else if (cookies.has('hiddenMinicardLabelText')) {
if (cookies.has('hiddenMinicardLabelText')) {
cookies.remove('hiddenMinicardLabelText'); cookies.remove('hiddenMinicardLabelText');
} else { } else {
cookies.set('hiddenMinicardLabelText', 'true'); cookies.set('hiddenMinicardLabelText', 'true');
} }
}
}, },
'click .js-shortcuts'() { 'click .js-shortcuts'() {
FlowRouter.go('shortcuts'); FlowRouter.go('shortcuts');
@ -135,13 +133,11 @@ Template.homeSidebar.helpers({
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).hiddenMinicardLabelText; return (currentUser.profile || {}).hiddenMinicardLabelText;
} else { } else if (cookies.has('hiddenMinicardLabelText')) {
if (cookies.has('hiddenMinicardLabelText')) {
return true; return true;
} else { } else {
return false; 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,6 +2,7 @@ template(name="archivesSidebar")
if isArchiveReady.get if isArchiveReady.get
+basicTabs(tabs=tabs) +basicTabs(tabs=tabs)
+tabContent(slug="cards") +tabContent(slug="cards")
unless isWorker
p.quiet p.quiet
a.js-restore-all-cards {{_ 'restore-all'}} a.js-restore-all-cards {{_ 'restore-all'}}
| - | -
@ -10,6 +11,7 @@ template(name="archivesSidebar")
.minicard-wrapper.js-minicard .minicard-wrapper.js-minicard
+minicard(this) +minicard(this)
if currentUser.isBoardMember if currentUser.isBoardMember
unless isWorker
p.quiet p.quiet
a.js-restore-card {{_ 'restore'}} a.js-restore-card {{_ 'restore'}}
| - | -
@ -20,6 +22,7 @@ template(name="archivesSidebar")
p.no-items-message {{_ 'no-archived-cards'}} p.no-items-message {{_ 'no-archived-cards'}}
+tabContent(slug="lists") +tabContent(slug="lists")
unless isWorker
p.quiet p.quiet
a.js-restore-all-lists {{_ 'restore-all'}} a.js-restore-all-lists {{_ 'restore-all'}}
| - | -
@ -29,6 +32,7 @@ template(name="archivesSidebar")
li.archived-lists-item li.archived-lists-item
= title = title
if currentUser.isBoardMember if currentUser.isBoardMember
unless isWorker
p.quiet p.quiet
a.js-restore-list {{_ 'restore'}} a.js-restore-list {{_ 'restore'}}
| - | -
@ -37,6 +41,7 @@ template(name="archivesSidebar")
li.no-items-message {{_ 'no-archived-lists'}} li.no-items-message {{_ 'no-archived-lists'}}
+tabContent(slug="swimlanes") +tabContent(slug="swimlanes")
unless isWorker
p.quiet p.quiet
a.js-restore-all-swimlanes {{_ 'restore-all'}} a.js-restore-all-swimlanes {{_ 'restore-all'}}
| - | -
@ -46,6 +51,7 @@ template(name="archivesSidebar")
li.archived-lists-item li.archived-lists-item
= title = title
if currentUser.isBoardMember if currentUser.isBoardMember
unless isWorker
p.quiet p.quiet
a.js-restore-swimlane {{_ 'restore'}} a.js-restore-swimlane {{_ 'restore'}}
| - | -

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,6 +117,7 @@ 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
unless currentUser.isWorker
hr hr
a.sidebar-btn.js-move-selection a.sidebar-btn.js-move-selection
i.fa.fa-share i.fa.fa-share

View file

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

View file

@ -43,6 +43,7 @@ template(name="listsGroup")
+addListForm +addListForm
template(name="addListForm") template(name="addListForm")
unless currentUser.isWorker
.list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}") .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}")
.list-header-add .list-header-add
+inlinedForm(autoclose=false) +inlinedForm(autoclose=false)

View file

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

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
a.js-go-setting(href="{{pathFor 'setting'}}")
i.fa.fa-lock
| {{_ 'admin-panel'}}
unless currentUser.isWorker
hr hr
ul.pop-over-list ul.pop-over-list
li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}} 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
unless currentUser.isWorker
li li
label.bold label.bold
i.fa.fa-sort-numeric-asc
| {{_ 'show-cards-minimum-count'}} | {{_ 'show-cards-minimum-count'}}
input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") 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'}}") input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}")
template(name="userDeletePopup") template(name="userDeletePopup")
unless currentUser.isWorker
p {{_ 'delete-user-confirm-popup'}} p {{_ 'delete-user-confirm-popup'}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}} 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,27 +24,23 @@ Utils = {
currentUser = Meteor.user(); currentUser = Meteor.user();
if (currentUser) { if (currentUser) {
return (currentUser.profile || {}).boardView; return (currentUser.profile || {}).boardView;
} else { } else if (cookies.get('boardView') === 'board-view-lists') {
if (cookies.get('boardView') === 'board-view-lists') {
return 'board-view-lists'; return 'board-view-lists';
} else if ( } else if (cookies.get('boardView') === 'board-view-swimlanes') {
cookies.get('boardView') === 'board-view-swimlanes'
) {
return 'board-view-swimlanes'; return 'board-view-swimlanes';
} else if (cookies.get('boardView') === 'board-view-cal') { } else if (cookies.get('boardView') === 'board-view-cal') {
return 'board-view-cal'; return 'board-view-cal';
} else { } else {
return false; return false;
} }
}
}, },
// XXX We should remove these two methods // XXX We should remove these two methods
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;