From 78153b4d1d505771a9c924f153f2af47fc1c2cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 13 Oct 2014 13:07:20 +0200 Subject: [PATCH 01/89] Refactor of assigned-to and watchers on details --- app/coffee/modules/common/components.coffee | 96 ++++++++++++--------- app/coffee/modules/related-tasks.coffee | 2 +- app/partials/issues-detail.jade | 3 + app/partials/task-detail.jade | 3 + app/partials/us-detail.jade | 1 + 5 files changed, 61 insertions(+), 44 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 30d3a35c..d0e6ff13 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -106,14 +106,15 @@ module.directive("tgDateSelector", DateSelectorDirective) ## Watchers directive ############################################################################# -WatchersDirective = ($rootscope, $confirm) -> +WatchersDirective = ($rootscope, $confirm, $tgrepo) -> + # You have to include a div with the tg-lb-watchers directive in the page + # where use this directive + # # TODO: i18n template = _.template("""
watchers - <% if (editable) { %> - <% } %>
<% _.each(watchers, function(watcher) { %> @@ -124,42 +125,41 @@ WatchersDirective = ($rootscope, $confirm) ->
- - <%- watcher.full_name_display %> - + <%- watcher.full_name_display %> - <% if (editable) { %> - <% } %>
<% }); %> """) link = ($scope, $el, $attrs, $model) -> - editable = $attrs.editable? + save = (model) -> + promise = $tgrepo.save($model.$modelValue) + promise.then -> + $confirm.notify("success") + watchers = _.map(model.watchers, (watcherId) -> $scope.usersById[watcherId]) + renderWatchers(watchers) + $rootscope.$broadcast("history:reload") + promise.then null, -> + model.revert() + $confirm.notify("error") renderWatchers = (watchers) -> - html = template({watchers: watchers, editable:editable}) + html = template({watchers: watchers}) $el.html(html) if watchers.length == 0 - if editable - $el.find(".title").text("Add watchers") - $el.find(".watchers-header").addClass("no-watchers") - else - $el.find(".watchers-header").hide() + $el.find(".title").text("Add watchers") + $el.find(".watchers-header").addClass("no-watchers") $scope.$watch $attrs.ngModel, (item) -> return if not item? watchers = _.map(item.watchers, (watcherId) -> $scope.usersById[watcherId]) renderWatchers(watchers) - if not editable - $el.find(".add-watcher").remove() - $el.on "click", ".icon-delete", (event) -> event.preventDefault() target = angular.element(event.currentTarget) @@ -176,6 +176,7 @@ WatchersDirective = ($rootscope, $confirm) -> item = $model.$modelValue.clone() item.watchers = watcherIds $model.$setViewValue(item) + save(item) $el.on "click", ".add-watcher", (event) -> event.preventDefault() @@ -191,17 +192,21 @@ WatchersDirective = ($rootscope, $confirm) -> item.watchers = watchers $model.$setViewValue(item) + save(item) return {link:link, require:"ngModel"} -module.directive("tgWatchers", ["$rootScope", "$tgConfirm", WatchersDirective]) +module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", WatchersDirective]) ############################################################################# ## Assigned to directive ############################################################################# -AssignedToDirective = ($rootscope, $confirm) -> +AssignedToDirective = ($rootscope, $confirm, $tgrepo) -> + # You have to include a div with the tg-lb-assignedto directive in the page + # where use this directive + # # TODO: i18n template = _.template(""" <% if (assignedTo) { %> @@ -213,54 +218,59 @@ AssignedToDirective = ($rootscope, $confirm) ->
Assigned to - + <% if (assignedTo) { %> <%- assignedTo.full_name_display %> <% } else { %> Not assigned <% } %> - <% if (editable) { %> - <% } %> - <% if (editable && assignedTo!==null) { %> + <% if (assignedTo!==null) { %> <% } %>
""") link = ($scope, $el, $attrs, $model) -> - editable = $attrs.editable? + save = (model) -> + promise = $tgrepo.save($model.$modelValue) + promise.then -> + $confirm.notify("success") + renderAssignedTo(model) + $rootscope.$broadcast("history:reload") + promise.then null, -> + model.revert() + $confirm.notify("error") renderAssignedTo = (issue) -> assignedToId = issue?.assigned_to assignedTo = null assignedTo = $scope.usersById[assignedToId] if assignedToId? - html = template({assignedTo: assignedTo, editable:editable}) + html = template({assignedTo: assignedTo}) $el.html(html) $scope.$watch $attrs.ngModel, (instance) -> renderAssignedTo(instance) - if editable - $el.on "click", ".user-assigned", (event) -> - event.preventDefault() - $scope.$apply -> - $rootscope.$broadcast("assigned-to:add", $model.$modelValue) + $el.on "click", ".user-assigned", (event) -> + event.preventDefault() + $scope.$apply -> + $rootscope.$broadcast("assigned-to:add", $model.$modelValue) - $el.on "click", ".icon-delete", (event) -> - event.preventDefault() - title = "Delete assignetion" - message = "" + $el.on "click", ".icon-delete", (event) -> + event.preventDefault() + title = "Remove assigned to" + subtitle = "" - $confirm.askOnDelete(title, message).then (finish) => - finish() - $model.$modelValue.assigned_to = null - renderAssignedTo($model.$modelValue) + $confirm.ask(title, subtitle).then (finish) => + finish() + $model.$modelValue.assigned_to = null + save($model.$modelValue) - $scope.$on "assigned-to:added", (ctx, userId) -> - $model.$modelValue.assigned_to = userId - renderAssignedTo($model.$modelValue) + $scope.$on "assigned-to:added", (ctx, userId) -> + $model.$modelValue.assigned_to = userId + save($model.$modelValue) return { link:link, @@ -268,7 +278,7 @@ AssignedToDirective = ($rootscope, $confirm) -> } -module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", AssignedToDirective]) +module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", AssignedToDirective]) ############################################################################# diff --git a/app/coffee/modules/related-tasks.coffee b/app/coffee/modules/related-tasks.coffee index 8b853704..da91f7c0 100644 --- a/app/coffee/modules/related-tasks.coffee +++ b/app/coffee/modules/related-tasks.coffee @@ -166,7 +166,7 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading) -> return {link:link, require:"ngModel"} -module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", RelatedTaskRowDirective]) +module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", "$tgAnalytics", RelatedTaskRowDirective]) RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $analytics) -> template = _.template(""" diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 15075c3a..6064630c 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -47,3 +47,6 @@ block content section.us-detail-settings tg-promote-issue-to-us-button(ng-model="issue") + + div.lightbox.lightbox-select-user(tg-lb-assignedto) + div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 1f9727d3..0d365d6e 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -52,3 +52,6 @@ block content section.us-detail-settings span.button.button-gray(href="", ng-class="{'active': task.is_iocaine }", title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") Iocaine + + div.lightbox.lightbox-select-user(tg-lb-assignedto) + div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 2553c7f4..bfe0a984 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -62,3 +62,4 @@ block content ng-class="{'active': us.team_requirement}") Team requirement div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) + div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) From 99365d935b18dbc41dd36fadac2d6da89b2230c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 13 Oct 2014 18:26:26 +0200 Subject: [PATCH 02/89] Refactor of user-story buttons --- app/coffee/modules/common/lightboxes.coffee | 42 +++++-- app/coffee/modules/userstories/detail.coffee | 117 ++++++++++++++----- app/partials/us-detail.jade | 8 +- 3 files changed, 126 insertions(+), 41 deletions(-) diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index 7bfaa917..88c4494e 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -127,16 +127,29 @@ module.directive("lightbox", ["lightboxService", LightboxDirective]) # Issue/Userstory blocking message lightbox directive. -BlockLightboxDirective = (lightboxService) -> +BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService) -> link = ($scope, $el, $attrs, $model) -> $el.find("h2.title").text($attrs.title) $scope.$on "block", -> + $el.find(".reason").val($model.$modelValue.blocked_note) lightboxService.open($el) $scope.$on "unblock", -> - $model.$modelValue.is_blocked = false - $model.$modelValue.blocked_note_html = "" + item = $model.$modelValue.clone() + item.is_blocked = false + item.blocked_note = "" + $model.$setViewValue(item) + + promise = $tgrepo.save($model.$modelValue) + promise.then -> + $confirm.notify("success") + $rootscope.$broadcast("history:reload") + + promise.then null, -> + $confirm.notify("error") + item.revert() + $model.$setViewValue(item) $scope.$on "$destroy", -> $el.off() @@ -144,19 +157,30 @@ BlockLightboxDirective = (lightboxService) -> $el.on "click", ".button-green", (event) -> event.preventDefault() - $scope.$apply -> - $model.$modelValue.is_blocked = true - $model.$modelValue.blocked_note = $el.find(".reason").val() + item = $model.$modelValue.clone() + item.is_blocked = true + item.blocked_note = $el.find(".reason").val() + $model.$setViewValue(item) + + promise = $tgrepo.save($model.$modelValue) + promise.then -> + $confirm.notify("success") + $rootscope.$broadcast("history:reload") + + promise.then null, -> + $confirm.notify("error") + item.revert() + $model.$setViewValue(item) lightboxService.close($el) return { templateUrl: "/partials/views/modules/lightbox-block.html" - link:link, - require:"ngModel" + link: link + require: "ngModel" } -module.directive("tgLbBlock", ["lightboxService", BlockLightboxDirective]) +module.directive("tgLbBlock", ["$rootScope", "$tgRepo", "$tgConfirm", "lightboxService", BlockLightboxDirective]) ############################################################################# diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index f3845362..567e37f5 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -133,32 +133,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) .then(=> @q.all([@.loadUs(), @.loadTasks()])) - block: -> - @rootscope.$broadcast("block", @scope.us) - - unblock: -> - @rootscope.$broadcast("unblock", @scope.us) - - delete: -> - #TODO: i18n - title = "Delete User Story" - message = @scope.us.subject - - @confirm.askOnDelete(title, message).then (finish) => - promise = @.repo.remove(@scope.us) - promise.then => - finish() - - if @scope.us.milestone - @location.path(@navUrls.resolve("project-taskboard", {project: @scope.project.slug, sprint: @scope.sprint.slug})) - else if @scope.project.is_backlog_activated - @location.path(@navUrls.resolve("project-backlog", {project: @scope.project.slug})) - else - @location.path(@navUrls.resolve("project-kanban", {project: @scope.project.slug})) - promise.then null, => - finish(false) - $confirm.notify("error") - module.controller("UserStoryDetailController", UserStoryDetailController) ############################################################################# @@ -507,3 +481,94 @@ UsEstimationDirective = ($log) -> } module.directive("tgUsEstimation", UsEstimationDirective) + +UsButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> + template = _.template(""" +
+ + +
+
+ + +
+ Block + Unblock + <% if (deletePerm) { %> + Delete + <% } %> + """) + + link = ($scope, $el, $attrs, $model) -> + render = _.once (us) -> + deletePerm = $scope.project.my_permissions.indexOf("delete_us") != -1 + html = template({deletePerm: deletePerm}) + $el.html(html) + + refresh = (us) -> + if us?.is_blocked + $el.find('.us-block').hide() + $el.find('.us-unblock').show() + else + $el.find('.us-block').show() + $el.find('.us-unblock').hide() + + if us?.client_requirement + $el.find('.client-requirement').addClass('active') + else + $el.find('.client-requirement').removeClass('active') + + if us?.team_requirement + $el.find('.team-requirement').addClass('active') + else + $el.find('.team-requirement').removeClass('active') + + $scope.$watch $attrs.ngModel, (us) -> + return if not us + render(us) + refresh(us) + + $scope.$on "$destroy", -> + $el.off() + + $el.on "click", ".client-requirement", (event) -> + us = $model.$modelValue.clone() + us.client_requirement = not us.client_requirement + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") + + $el.on "click", ".team-requirement", (event) -> + us = $model.$modelValue.clone() + us.team_requirement = not us.team_requirement + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") + + $el.on "click", ".us-block", (event) -> + $rootscope.$broadcast("block", $model.$modelValue) + + $el.on "click", ".us-unblock", (event) -> + $rootscope.$broadcast("unblock", $model.$modelValue) + + $el.on "click", ".us-delete", (event) -> + #TODO: i18n + title = "Delete User Story" + subtitle = $model.$modelValue.subject + + $confirm.ask(title, subtitle).then (finish) => + promise = $tgrepo.remove($model.$modelValue) + promise.then => + finish() + $location.path($navurls.resolve("project-backlog", {project: $scope.project.slug})) + promise.then null, => + finish(false) + $confirm.notify("error") + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgUsButtons", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", UsButtonsDirective]) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index bfe0a984..d9db1180 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -54,12 +54,8 @@ block content section.us-assigned-to(tg-assigned-to, ng-model="us") section.us-created-by(tg-created-by, ng-model="us") section.watchers(tg-watchers, ng-model="us") + section.us-detail-settings(tg-us-buttons, ng-model="us") - section.us-detail-settings - span.button.button-gray(href="", title="Client requirement", - ng-class="{'active': us.client_requirement}") Client requirement - span.button.button-gray(href="", title="Team requirement", - ng-class="{'active': us.team_requirement}") Team requirement - + div.lightbox.lightbox_block.hidden(tg-lb-block, title="Blocking issue", ng-model="us") div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) From 9a5d87ef194a2d698d9b2c73eff6610c4808e370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 13 Oct 2014 18:40:32 +0200 Subject: [PATCH 03/89] Refactor of task buttons --- app/coffee/modules/tasks/detail.coffee | 94 ++++++++++++++++++++------ app/partials/issues-detail.jade | 2 + app/partials/task-detail.jade | 5 +- app/partials/us-detail.jade | 2 +- 4 files changed, 80 insertions(+), 23 deletions(-) diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index de3b715d..0cd795a5 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -127,25 +127,6 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) unblock: -> @rootscope.$broadcast("unblock", @scope.task) - delete: -> - #TODO: i18n - title = "Delete Task" - message = @scope.task.subject - - @confirm.askOnDelete(title, message).then (finish) => - promise = @.repo.remove(@scope.task) - promise.then => - finish() - - if @scope.task.milestone - @location.path(@navUrls.resolve("project-taskboard", {project: @scope.project.slug, sprint: @scope.sprint.slug})) - else if @scope.us - @location.path(@navUrls.resolve("project-userstories-detail", {project: @scope.project.slug, ref: @scope.us.ref})) - - promise.then null, => - finish(false) - @confirm.notify("error") - module.controller("TaskDetailController", TaskDetailController) @@ -273,3 +254,78 @@ TaskStatusDirective = () -> return {link:link, require:"ngModel"} module.directive("tgTaskStatus", TaskStatusDirective) + +TaskButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> + template = _.template(""" +
+ + +
+ Block + Unblock + <% if (deletePerm) { %> + Delete + <% } %> + """) + + link = ($scope, $el, $attrs, $model) -> + render = _.once (us) -> + deletePerm = $scope.project.my_permissions.indexOf("delete_us") != -1 + html = template({deletePerm: deletePerm}) + $el.html(html) + + refresh = (us) -> + if us?.is_blocked + $el.find('.task-block').hide() + $el.find('.task-unblock').show() + else + $el.find('.task-block').show() + $el.find('.task-unblock').hide() + + if us?.is_iocaine + $el.find('.is-iocaine').addClass('active') + else + $el.find('.is-iocaine').removeClass('active') + + $scope.$watch $attrs.ngModel, (us) -> + return if not us + render(us) + refresh(us) + + $scope.$on "$destroy", -> + $el.off() + + $el.on "click", ".is-iocaine", (event) -> + us = $model.$modelValue.clone() + us.is_iocaine = not us.is_iocaine + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") + + $el.on "click", ".task-block", (event) -> + $rootscope.$broadcast("block", $model.$modelValue) + + $el.on "click", ".task-unblock", (event) -> + $rootscope.$broadcast("unblock", $model.$modelValue) + + $el.on "click", ".task-delete", (event) -> + #TODO: i18n + title = "Delete Task" + subtitle = $model.$modelValue.subject + + $confirm.ask(title, subtitle).then (finish) => + promise = $tgrepo.remove($model.$modelValue) + promise.then => + finish() + $location.path($navurls.resolve("project-backlog", {project: $scope.project.slug})) + promise.then null, => + finish(false) + $confirm.notify("error") + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgTaskButtons", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", TaskButtonsDirective]) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 6064630c..08ad02e3 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -47,6 +47,8 @@ block content section.us-detail-settings tg-promote-issue-to-us-button(ng-model="issue") + a.button.button-gray.clickable(title="Click to block the issue", ng-show="!issue.is_blocked", ng-click="ctrl.block()") Block + a.button.button-red(title="Click to delete the issue", tg-check-permission="delete_issue", ng-click="ctrl.delete()", href="") Delete div.lightbox.lightbox-select-user(tg-lb-assignedto) div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 0d365d6e..375246fd 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -49,9 +49,8 @@ block content section.us-status(tg-task-status, ng-model="task") section.us-assigned-to(tg-assigned-to, ng-model="task") section.watchers(tg-watchers, ng-model="task") + section.us-detail-settings(tg-task-buttons, ng-model="task") - section.us-detail-settings - span.button.button-gray(href="", ng-class="{'active': task.is_iocaine }", title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") Iocaine - + div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking task", ng-model="task") div.lightbox.lightbox-select-user(tg-lb-assignedto) div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index d9db1180..ce05d706 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -56,6 +56,6 @@ block content section.watchers(tg-watchers, ng-model="us") section.us-detail-settings(tg-us-buttons, ng-model="us") - div.lightbox.lightbox_block.hidden(tg-lb-block, title="Blocking issue", ng-model="us") + div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking issue", ng-model="us") div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) From d0e6b296e989b160e5bc5e4482831a7687c60bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 14 Oct 2014 10:46:26 +0200 Subject: [PATCH 04/89] Splited buttons on diferente directives --- app/coffee/modules/common/components.coffee | 85 ++++++++++++++++ app/coffee/modules/issues/detail.coffee | 20 ---- app/coffee/modules/tasks/detail.coffee | 45 +-------- app/coffee/modules/userstories/detail.coffee | 101 ++++++++----------- app/partials/issues-detail.jade | 7 +- app/partials/task-detail.jade | 7 +- app/partials/us-detail.jade | 10 +- 7 files changed, 150 insertions(+), 125 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index d0e6ff13..c0fbfcef 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -280,6 +280,91 @@ AssignedToDirective = ($rootscope, $confirm, $tgrepo) -> module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", AssignedToDirective]) +############################################################################# +## Block Button directive +############################################################################# + +BlockButtonDirective = ($rootscope) -> + template = _.template(""" + Block + Unblock + """) + + link = ($scope, $el, $attrs, $model) -> + render = _.once (item) -> + $el.html(template()) + + refresh = (item) -> + if item?.is_blocked + $el.find('.item-block').hide() + $el.find('.item-unblock').show() + else + $el.find('.item-block').show() + $el.find('.item-unblock').hide() + + $scope.$watch $attrs.ngModel, (item) -> + return if not item + render(item) + refresh(item) + + $scope.$on "$destroy", -> + $el.off() + + $el.on "click", ".item-block", (event) -> + $rootscope.$broadcast("block", $model.$modelValue) + + $el.on "click", ".item-unblock", (event) -> + $rootscope.$broadcast("unblock", $model.$modelValue) + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgBlockButton", ["$rootScope", BlockButtonDirective]) + +############################################################################# +## Delete Button directive +############################################################################# + +DeleteButtonDirective = ($tgrepo, $confirm, $navurls, $location) -> + template = _.template(""" + Delete + """) + + link = ($scope, $el, $attrs, $model) -> + render = _.once (item) -> + $el.html(template()) + + $scope.$watch $attrs.ngModel, (item) -> + return if not item + render(item) + + $scope.$on "$destroy", -> + $el.off() + + $el.on "click", ".button", (event) -> + #TODO: i18n + title = "Delete User Story" + subtitle = $model.$modelValue.subject + + $confirm.ask(title, subtitle).then (finish) => + promise = $tgrepo.remove($model.$modelValue) + promise.then => + finish() + $location.path($navurls.resolve($attrs.onDeleteGoToUrl, {project: $attrs.projectSlug})) + promise.then null, => + finish(false) + $confirm.notify("error") + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgDeleteButton", ["$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", DeleteButtonDirective]) ############################################################################# ## Common list directives diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index b526a337..cc26cec7 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -130,26 +130,6 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) .then(=> @.loadUsersAndRoles()) .then(=> @.loadIssue()) - block: -> - @rootscope.$broadcast("block", @scope.issue) - - unblock: -> - @rootscope.$broadcast("unblock", @scope.issue) - - delete: -> - # TODO: i18n - title = "Delete Issue" - message = @scope.issue.subject - - @confirm.askOnDelete(title, message).then (finish) => - promise = @.repo.remove(@scope.issue) - promise.then => - finish() - @location.path(@navUrls.resolve("project-issues", {project: @scope.project.slug})) - promise.then null, => - finish(false) - @confirm.notify("error") - module.controller("IssueDetailController", IssueDetailController) diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 0cd795a5..9abb3640 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -121,12 +121,6 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) .then(=> @.loadUsersAndRoles()) .then(=> @.loadTask()) - block: -> - @rootscope.$broadcast("block", @scope.task) - - unblock: -> - @rootscope.$broadcast("unblock", @scope.task) - module.controller("TaskDetailController", TaskDetailController) @@ -255,33 +249,19 @@ TaskStatusDirective = () -> module.directive("tgTaskStatus", TaskStatusDirective) -TaskButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> +TaskIsIocaineButtonDirective = ($rootscope, $tgrepo) -> template = _.template("""
- Block - Unblock - <% if (deletePerm) { %> - Delete - <% } %> """) link = ($scope, $el, $attrs, $model) -> render = _.once (us) -> - deletePerm = $scope.project.my_permissions.indexOf("delete_us") != -1 - html = template({deletePerm: deletePerm}) - $el.html(html) + $el.html(template()) refresh = (us) -> - if us?.is_blocked - $el.find('.task-block').hide() - $el.find('.task-unblock').show() - else - $el.find('.task-block').show() - $el.find('.task-unblock').hide() - if us?.is_iocaine $el.find('.is-iocaine').addClass('active') else @@ -302,25 +282,6 @@ TaskButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> $tgrepo.save($model.$modelValue).then -> $rootscope.$broadcast("history:reload") - $el.on "click", ".task-block", (event) -> - $rootscope.$broadcast("block", $model.$modelValue) - - $el.on "click", ".task-unblock", (event) -> - $rootscope.$broadcast("unblock", $model.$modelValue) - - $el.on "click", ".task-delete", (event) -> - #TODO: i18n - title = "Delete Task" - subtitle = $model.$modelValue.subject - - $confirm.ask(title, subtitle).then (finish) => - promise = $tgrepo.remove($model.$modelValue) - promise.then => - finish() - $location.path($navurls.resolve("project-backlog", {project: $scope.project.slug})) - promise.then null, => - finish(false) - $confirm.notify("error") return { link: link @@ -328,4 +289,4 @@ TaskButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> require: "ngModel" } -module.directive("tgTaskButtons", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", TaskButtonsDirective]) +module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", TaskIsIocaineButtonDirective]) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 567e37f5..78bbfa7f 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -482,42 +482,17 @@ UsEstimationDirective = ($log) -> module.directive("tgUsEstimation", UsEstimationDirective) -UsButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> +UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> template = _.template(""" -
- - -
-
- - -
- Block - Unblock - <% if (deletePerm) { %> - Delete - <% } %> + + """) link = ($scope, $el, $attrs, $model) -> render = _.once (us) -> - deletePerm = $scope.project.my_permissions.indexOf("delete_us") != -1 - html = template({deletePerm: deletePerm}) - $el.html(html) + $el.html(template()) refresh = (us) -> - if us?.is_blocked - $el.find('.us-block').hide() - $el.find('.us-unblock').show() - else - $el.find('.us-block').show() - $el.find('.us-unblock').hide() - - if us?.client_requirement - $el.find('.client-requirement').addClass('active') - else - $el.find('.client-requirement').removeClass('active') - if us?.team_requirement $el.find('.team-requirement').addClass('active') else @@ -531,6 +506,44 @@ UsButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> $scope.$on "$destroy", -> $el.off() + $el.on "click", ".team-requirement", (event) -> + us = $model.$modelValue.clone() + us.team_requirement = not us.team_requirement + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") + + return { + link: link + restrict: "EA" + require: "ngModel" + } +module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", UsTeamRequirementButtonDirective]) + +UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> + template = _.template(""" + + + """) + + link = ($scope, $el, $attrs, $model) -> + render = _.once (us) -> + $el.html(template()) + + refresh = (us) -> + if us?.client_requirement + $el.find('.client-requirement').addClass('active') + else + $el.find('.client-requirement').removeClass('active') + + $scope.$watch $attrs.ngModel, (us) -> + return if not us + render(us) + refresh(us) + + $scope.$on "$destroy", -> + $el.off() + $el.on "click", ".client-requirement", (event) -> us = $model.$modelValue.clone() us.client_requirement = not us.client_requirement @@ -538,37 +551,9 @@ UsButtonsDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> $tgrepo.save($model.$modelValue).then -> $rootscope.$broadcast("history:reload") - $el.on "click", ".team-requirement", (event) -> - us = $model.$modelValue.clone() - us.team_requirement = not us.team_requirement - $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> - $rootscope.$broadcast("history:reload") - - $el.on "click", ".us-block", (event) -> - $rootscope.$broadcast("block", $model.$modelValue) - - $el.on "click", ".us-unblock", (event) -> - $rootscope.$broadcast("unblock", $model.$modelValue) - - $el.on "click", ".us-delete", (event) -> - #TODO: i18n - title = "Delete User Story" - subtitle = $model.$modelValue.subject - - $confirm.ask(title, subtitle).then (finish) => - promise = $tgrepo.remove($model.$modelValue) - promise.then => - finish() - $location.path($navurls.resolve("project-backlog", {project: $scope.project.slug})) - promise.then null, => - finish(false) - $confirm.notify("error") - return { link: link restrict: "EA" require: "ngModel" } - -module.directive("tgUsButtons", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", UsButtonsDirective]) +module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", UsClientRequirementButtonDirective]) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 08ad02e3..365623e2 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -47,8 +47,11 @@ block content section.us-detail-settings tg-promote-issue-to-us-button(ng-model="issue") - a.button.button-gray.clickable(title="Click to block the issue", ng-show="!issue.is_blocked", ng-click="ctrl.block()") Block - a.button.button-red(title="Click to delete the issue", tg-check-permission="delete_issue", ng-click="ctrl.delete()", href="") Delete + div(tg-block-button, ng-model="issue") + div(tg-check-permission="delete_issue", tg-delete-button, + on-delete-go-to-url="project-issues", + project-slug="{{ project.slug }}" ng-model="issue") + div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking issue", ng-model="issue") div.lightbox.lightbox-select-user(tg-lb-assignedto) div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 375246fd..d961d7cb 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -49,7 +49,12 @@ block content section.us-status(tg-task-status, ng-model="task") section.us-assigned-to(tg-assigned-to, ng-model="task") section.watchers(tg-watchers, ng-model="task") - section.us-detail-settings(tg-task-buttons, ng-model="task") + section.us-detail-settings + fieldset(tg-task-is-iocaine-button, ng-model="task") + div(tg-block-button, ng-model="task") + div(tg-check-permission="delete_task", tg-delete-button, + on-delete-go-to-url="project-backlog", + project-slug="{{ project.slug }}" ng-model="task") div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking task", ng-model="task") div.lightbox.lightbox-select-user(tg-lb-assignedto) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index ce05d706..36678047 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -54,8 +54,14 @@ block content section.us-assigned-to(tg-assigned-to, ng-model="us") section.us-created-by(tg-created-by, ng-model="us") section.watchers(tg-watchers, ng-model="us") - section.us-detail-settings(tg-us-buttons, ng-model="us") + section.us-detail-settings + fieldset(tg-us-team-requirement-button, ng-model="us") + fieldset(tg-us-client-requirement-button, ng-model="us") + div(tg-block-button, ng-model="us") + div(tg-check-permission="delete_us", tg-delete-button, + on-delete-go-to-url="project-backlog", + project-slug="{{ project.slug }}" ng-model="us") - div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking issue", ng-model="us") + div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking us", ng-model="us") div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) From fff087a2ff9d315d7cd810c4254dbb188b593b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 19:43:42 +0200 Subject: [PATCH 05/89] Refactor tg-us-status directive: Create directives tg-us-status-display and tg-us-tasks-progress-display --- app/coffee/modules/userstories/detail.coffee | 111 +++++++++++++++++++ app/partials/us-detail.jade | 9 +- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 78bbfa7f..4d0a9b35 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -65,6 +65,10 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> + @scope.$on "related-tasks:update", => + @.loadUs() + @scope.tasks = _.clone(@scope.tasks, false) + @scope.$on "attachment:create", => @analytics.trackEvent("attachment", "create", "create attachment on userstory", 1) @rootscope.$broadcast("history:reload") @@ -135,6 +139,7 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) module.controller("UserStoryDetailController", UserStoryDetailController) + ############################################################################# ## User story Main Directive ############################################################################# @@ -175,6 +180,7 @@ UsDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) -> module.directive("tgUsDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls", "$tgLoading", UsDirective]) + ############################################################################# ## User story status directive ############################################################################# @@ -356,6 +362,100 @@ UsStatusDetailDirective = () -> module.directive("tgUsStatusDetail", UsStatusDetailDirective) + + +############################################################################# +## User story status display directive +############################################################################# + +UsStatusDisplayDirective = -> + # Display if a US is open or closed and its kanban status. + # + # Example: + # h1(tg-us-status-display, ng-model="us") + # + # Requirements: + # - US object + # - scope.statusById object + + template = _.template(""" + + <% if (is_closed) { %> + Closed + <% } else { %> + Open + <% } %> + + + <%= status.name %> + + """) # TODO: i18n + + link = ($scope, $el, $attrs) -> + render = (us) -> + html = template({ + is_closed: us.is_closed + status: $scope.statusById[us.status] + }) + $el.html(html) + + $scope.$watch $attrs.ngModel, (us) -> + render(us) if us? + + $scope.$on "$destroy", -> + $el.off() + + return {link:link, require:"ngModel"} + +module.directive("tgUsStatusDisplay", UsStatusDisplayDirective) + + +############################################################################# +## User story related tasts progress splay Directive +############################################################################# + +UsTasksProgressDisplayDirective = -> + # Display a progress bar with the stats of completed tasks. + # + # Example: + # div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") + # + # Requirements: + # - Task object list + # - scope.taskStatusById object + + template = _.template(""" +
+ + <%- totalClosedTasks %>/<%- totalTasks %> tasks completed + + """) # TODO: i18n + + link = ($scope, $el, $attrs) -> + render = (tasks) -> + totalTasks = tasks.length + totalClosedTasks = _.filter(tasks, (task) => $scope.taskStatusById[task.status].is_closed).length + + progress = if totalTasks > 0 then 100 * totalClosedTasks / totalTasks else 0 + + html = template({ + totalTasks: totalTasks + totalClosedTasks: totalClosedTasks + progress: progress + }) + $el.html(html) + + $scope.$watch $attrs.ngModel, (tasks) -> + render(tasks) if tasks? + + $scope.$on "$destroy", -> + $el.off() + + return {link:link, require:"ngModel"} + +module.directive("tgUsTasksProgressDisplay", UsTasksProgressDisplayDirective) + + ############################################################################# ## User story estimation directive ############################################################################# @@ -482,6 +582,11 @@ UsEstimationDirective = ($log) -> module.directive("tgUsEstimation", UsEstimationDirective) + +############################################################################# +## User story team requirements button directive +############################################################################# + UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> template = _.template(""" @@ -518,8 +623,14 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> restrict: "EA" require: "ngModel" } + module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", UsTeamRequirementButtonDirective]) + +############################################################################# +## User story client requirements button directive +############################################################################# + UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> template = _.template(""" diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 36678047..ae9c24e7 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -37,7 +37,7 @@ block content span.block-description(tg-bind-html="us.blocked_note || 'This user story is blocked'") div.issue-nav a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", - title="previous user story") + title="previous user story") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next user story") div(tg-tag-line, ng-model="us.tags", ng-show="us.tags") @@ -50,10 +50,15 @@ block content tg-history(ng-model="us", type="us") sidebar.menu-secondary.sidebar - section.us-status(tg-us-status-detail, ng-model="us") + section.us-status + h1(tg-us-status-display, ng-model="us") + div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") + section.us-assigned-to(tg-assigned-to, ng-model="us") section.us-created-by(tg-created-by, ng-model="us") + section.watchers(tg-watchers, ng-model="us") + section.us-detail-settings fieldset(tg-us-team-requirement-button, ng-model="us") fieldset(tg-us-client-requirement-button, ng-model="us") From d3fbfce06dc371ce975baae3475b49425a2169fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 19:45:09 +0200 Subject: [PATCH 06/89] Refactor tg-us-status directive: Create directive tg-created-by-display --- app/coffee/modules/common/components.coffee | 95 ++++++++++++++++----- app/partials/us-detail.jade | 2 +- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index c0fbfcef..84d5504f 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -46,6 +46,33 @@ DateRangeDirective = -> module.directive("tgDateRange", DateRangeDirective) +############################################################################# +## Date Selector Directive (using pikaday) +############################################################################# + +DateSelectorDirective =-> + link = ($scope, $el, $attrs, $model) -> + selectedDate = null + $el.picker = new Pikaday({ + field: $el[0] + format: "DD MMM YYYY" + onSelect: (date) => + selectedDate = date + onOpen: => + $el.picker.setDate(selectedDate) if selectedDate? + }) + + $scope.$watch $attrs.ngModel, (val) -> + $el.picker.setDate(val) if val? + + return { + link: link + require: "ngModel" + } + +module.directive("tgDateSelector", DateSelectorDirective) + + ############################################################################# ## Sprint Progress Bar Directive ############################################################################# @@ -76,30 +103,48 @@ module.directive("tgSprintProgressbar", SprintProgressBarDirective) ############################################################################# -## Date Selector Directive (using pikaday) +## Created-by display directive ############################################################################# -DateSelectorDirective =-> - link = ($scope, $el, $attrs, $model) -> - selectedDate = null - $el.picker = new Pikaday({ - field: $el[0] - format: "DD MMM YYYY" - onSelect: (date) => - selectedDate = date - onOpen: => - $el.picker.setDate(selectedDate) if selectedDate? - }) +CreatedByDisplayDirective = -> + # Display the owner information (full name and photo) and the date of + # creation of an object (like USs, tasks and issues). + # + # Example: + # div.us-created-by(tg-created-by-display, ng-model="us") + # + # Requirements: + # - model object must have the attributes 'created_date' and 'owner' + # - scope.usersById object is required. - $scope.$watch $attrs.ngModel, (val) -> - $el.picker.setDate(val) if val? + template = _.template(""" +
+ <%- owner.full_name_display %> +
- return { - link: link - require: "ngModel" - } +
+ Created by <%- owner.full_name_display %> + <%- date %> +
+ """) # TODO: i18n -module.directive("tgDateSelector", DateSelectorDirective) + link = ($scope, $el, $attrs) -> + render = (model) -> + html = template({ + owner: $scope.usersById?[model.owner] + date: moment(model.created_date).format("DD MMM YYYY HH:mm") + }) + $el.html(html) + + bindOnce $scope, $attrs.ngModel, (model) -> + render(model) if model? + + $scope.$on "$destroy", -> + $el.off() + + return {link:link, require:"ngModel"} + +module.directive("tgCreatedByDisplay", CreatedByDisplayDirective) ############################################################################# @@ -194,6 +239,9 @@ WatchersDirective = ($rootscope, $confirm, $tgrepo) -> $model.$setViewValue(item) save(item) + $scope.$on "$destroy", -> + $el.off() + return {link:link, require:"ngModel"} module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", WatchersDirective]) @@ -272,6 +320,9 @@ AssignedToDirective = ($rootscope, $confirm, $tgrepo) -> $model.$modelValue.assigned_to = userId save($model.$modelValue) + $scope.$on "$destroy", -> + $el.off() + return { link:link, require:"ngModel" @@ -307,15 +358,15 @@ BlockButtonDirective = ($rootscope) -> render(item) refresh(item) - $scope.$on "$destroy", -> - $el.off() - $el.on "click", ".item-block", (event) -> $rootscope.$broadcast("block", $model.$modelValue) $el.on "click", ".item-unblock", (event) -> $rootscope.$broadcast("unblock", $model.$modelValue) + $scope.$on "$destroy", -> + $el.off() + return { link: link restrict: "EA" diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index ae9c24e7..38a4cbec 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -53,9 +53,9 @@ block content section.us-status h1(tg-us-status-display, ng-model="us") div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") + div.us-created-by(tg-created-by-display, ng-model="us") section.us-assigned-to(tg-assigned-to, ng-model="us") - section.us-created-by(tg-created-by, ng-model="us") section.watchers(tg-watchers, ng-model="us") From 235a38641e3aefdcbe4b4649d3873f8bec76474f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 20:57:14 +0200 Subject: [PATCH 07/89] Refactor tg-us-status directive: Update tg-us-estimations directive to support inline-edition --- app/coffee/modules/userstories/detail.coffee | 54 +++++++++++++++----- app/partials/us-detail.jade | 1 + 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 4d0a9b35..93ae6178 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -460,7 +460,18 @@ module.directive("tgUsTasksProgressDisplay", UsTasksProgressDisplayDirective) ## User story estimation directive ############################################################################# -UsEstimationDirective = ($log) -> +UsEstimationDirective = ($rootScope, $repo, $confirm) -> + # Display the points of a US and you can edit it. + # + # Example: + # tg-us-estimation-progress-bar(ng-model="us") + # + # Requirements: + # - Us object (ng-model) + # - scope.project object + # Optionals: + # - save-after-modify (boolean): save object after modify + mainTemplate = _.template("""
  • @@ -491,8 +502,11 @@ UsEstimationDirective = ($log) ->
""") - link = ($scope, $el, $attrs) -> + link = ($scope, $el, $attrs, $model) -> + saveAfterModify = $attrs.saveAfterModify or false + render = (us) -> + console.log us.points totalPoints = us.total_points or 0 computableRoles = _.filter($scope.project.roles, "computable") @@ -537,19 +551,13 @@ UsEstimationDirective = ($log) -> return "0" return _.reduce(values, (acc, num) -> acc + num) - $scope.$watch $attrs.ngModel, (us) -> - render(us) if us - - $scope.$on "$destroy", -> - $el.off() - $el.on "click", ".total.clickable", (event) -> event.preventDefault() event.stopPropagation() target = angular.element(event.currentTarget) roleId = target.data("role-id") - us = $scope.$eval($attrs.ngModel) + us = $model.$modelValue renderPoints(target, us, roleId) target.siblings().removeClass('active') @@ -559,28 +567,48 @@ UsEstimationDirective = ($log) -> event.preventDefault() event.stopPropagation() - us = $scope.$eval($attrs.ngModel) - target = angular.element(event.currentTarget) roleId = target.data("role-id") pointId = target.data("point-id") $el.find(".popover").popover().close() + us = $model.$modelValue + points = _.clone(us.points, true) points[roleId] = pointId + # NOTE: your past self wants that you make a refactor of this $scope.$apply -> us.points = points us.total_points = calculateTotalPoints(us) - render(us) + + if saveAfterModify + onSuccess = -> + $repo.refresh(us).then -> + render(us) + $rootScope.$broadcast("history:reload") + onError = -> + $repo.refresh(us).then -> + render(us) + $confirm.notify("error") + $repo.save(us).then(onSuccess, onError) + else + render(us) + + $scope.$watch $attrs.ngModel, (us) -> + render(us) if us + + $scope.$on "$destroy", -> + $el.off() return { link: link restrict: "EA" + require: "ngModel" } -module.directive("tgUsEstimation", UsEstimationDirective) +module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", UsEstimationDirective]) ############################################################################# diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 38a4cbec..c673b399 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -54,6 +54,7 @@ block content h1(tg-us-status-display, ng-model="us") div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") div.us-created-by(tg-created-by-display, ng-model="us") + tg-us-estimation(ng-model="us", save-after-modify="true") section.us-assigned-to(tg-assigned-to, ng-model="us") From c08f972d83b15280f472f01a8d40bdc3b1752892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 23:19:14 +0200 Subject: [PATCH 08/89] Refactor tg-us-status directive: Add tg-us-status-burtton directive --- app/coffee/modules/userstories/detail.coffee | 75 ++++++++++++++++++++ app/partials/us-detail.jade | 1 + 2 files changed, 76 insertions(+) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 93ae6178..0ee5fb38 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -611,6 +611,81 @@ UsEstimationDirective = ($rootScope, $repo, $confirm) -> module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", UsEstimationDirective]) +############################################################################# +## User story status button directive +############################################################################# + +UsStatusButtonDirective = ($rootScope, $repo) -> + # Display the status of a US and you can edit it. + # + # Example: + # tg-us-status-button(ng-model="us") + # + # Requirements: + # - Us object (ng-model) + # - scope.statusById object + + template = _.template(""" +
+ + <%= status.name %> + + status + +
    + <% _.each(statuses, function(st) { %> +
  • <%- st.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (us) => + status = $scope.statusById[us.status] + + html = template({ + status: status + statuses: $scope.statusList + }) + $el.html(html) + + $el.on "click", ".status-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-status").popover().open() + + $el.on "click", ".status", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + us = $model.$modelValue.clone() + us.status = target.data("status-id") + + $model.$setViewValue(us) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (us) -> + render(us) if us + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", UsStatusButtonDirective]) + + ############################################################################# ## User story team requirements button directive ############################################################################# diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index c673b399..c8a455cb 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -55,6 +55,7 @@ block content div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") div.us-created-by(tg-created-by-display, ng-model="us") tg-us-estimation(ng-model="us", save-after-modify="true") + tg-us-status-button.issue-data(ng-model="us") section.us-assigned-to(tg-assigned-to, ng-model="us") From d41b259fbdec91abca7d96a8fc85c9435562e94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 23:16:21 +0200 Subject: [PATCH 09/89] Refactor tg-us-status directive: :dancers: refactor of a refactor :dancers: --- app/coffee/modules/common/components.coffee | 9 +++++++-- app/coffee/modules/userstories/detail.coffee | 21 +++++++++++++------- app/partials/us-detail.jade | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 84d5504f..ef71e14a 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -114,7 +114,8 @@ CreatedByDisplayDirective = -> # div.us-created-by(tg-created-by-display, ng-model="us") # # Requirements: - # - model object must have the attributes 'created_date' and 'owner' + # - model object must have the attributes 'created_date' and + # 'owner'(ng-model) # - scope.usersById object is required. template = _.template(""" @@ -142,7 +143,11 @@ CreatedByDisplayDirective = -> $scope.$on "$destroy", -> $el.off() - return {link:link, require:"ngModel"} + return { + link: link + restrict: "EA" + require: "ngModel" + } module.directive("tgCreatedByDisplay", CreatedByDisplayDirective) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 0ee5fb38..ce7e81e8 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -372,10 +372,10 @@ UsStatusDisplayDirective = -> # Display if a US is open or closed and its kanban status. # # Example: - # h1(tg-us-status-display, ng-model="us") + # tg-us-status-display(ng-model="us") # # Requirements: - # - US object + # - US object (ng-model) # - scope.statusById object template = _.template(""" @@ -405,7 +405,11 @@ UsStatusDisplayDirective = -> $scope.$on "$destroy", -> $el.off() - return {link:link, require:"ngModel"} + return { + link: link + restrict: "EA" + require: "ngModel" + } module.directive("tgUsStatusDisplay", UsStatusDisplayDirective) @@ -418,10 +422,10 @@ UsTasksProgressDisplayDirective = -> # Display a progress bar with the stats of completed tasks. # # Example: - # div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") + # tg-us-tasks-progress-display(ng-model="tasks") # # Requirements: - # - Task object list + # - Task object list (ng-model) # - scope.taskStatusById object template = _.template(""" @@ -451,7 +455,11 @@ UsTasksProgressDisplayDirective = -> $scope.$on "$destroy", -> $el.off() - return {link:link, require:"ngModel"} + return { + link: link + restrict: "EA" + require: "ngModel" + } module.directive("tgUsTasksProgressDisplay", UsTasksProgressDisplayDirective) @@ -506,7 +514,6 @@ UsEstimationDirective = ($rootScope, $repo, $confirm) -> saveAfterModify = $attrs.saveAfterModify or false render = (us) -> - console.log us.points totalPoints = us.total_points or 0 computableRoles = _.filter($scope.project.roles, "computable") diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index c8a455cb..d8e51b7a 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -53,7 +53,7 @@ block content section.us-status h1(tg-us-status-display, ng-model="us") div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") - div.us-created-by(tg-created-by-display, ng-model="us") + tg-created-by-display.us-created-by(ng-model="us") tg-us-estimation(ng-model="us", save-after-modify="true") tg-us-status-button.issue-data(ng-model="us") From 94a9e4b7bfc9dc050aa2292f943b6868d875c680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 14 Oct 2014 23:30:13 +0200 Subject: [PATCH 10/89] Remove US edition page and some extra deprecated code --- app/coffee/app.coffee | 2 - app/coffee/modules/base.coffee | 1 - app/coffee/modules/userstories/detail.coffee | 224 ------------------- app/partials/us-detail-edit.jade | 52 ----- app/partials/us-detail.jade | 6 +- 5 files changed, 1 insertion(+), 284 deletions(-) delete mode 100644 app/partials/us-detail-edit.jade diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 3d298fe5..71c5644e 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -52,8 +52,6 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven # User stories $routeProvider.when("/project/:pslug/us/:usref", {templateUrl: "/partials/us-detail.html", resolve: {loader: tgLoaderProvider.add()}}) - $routeProvider.when("/project/:pslug/us/:usref/edit", - {templateUrl: "/partials/us-detail-edit.html"}) # Tasks $routeProvider.when("/project/:pslug/task/:taskref", diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index d3dffe7f..61917a05 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -67,7 +67,6 @@ urls = { "project-search": "/project/:project/search" "project-userstories-detail": "/project/:project/us/:ref" - "project-userstories-detail-edit": "/project/:project/us/:ref/edit" "project-tasks-detail": "/project/:project/task/:ref" "project-tasks-detail-edit": "/project/:project/task/:ref/edit" diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index ce7e81e8..ef455461 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -140,230 +140,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) module.controller("UserStoryDetailController", UserStoryDetailController) -############################################################################# -## User story Main Directive -############################################################################# - -UsDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) -> - linkSidebar = ($scope, $el, $attrs, $ctrl) -> - - link = ($scope, $el, $attrs) -> - $ctrl = $el.controller() - linkSidebar($scope, $el, $attrs, $ctrl) - - if $el.is("form") - form = $el.checksley() - - $el.on "click", ".save-us", (event) -> - if not form.validate() - return - - onSuccess = -> - $loading.finish(target) - $confirm.notify("success") - ctx = { - project: $scope.project.slug - ref: $scope.us.ref - } - $location.path($navUrls.resolve("project-userstories-detail", ctx)) - - onError = -> - $loading.finish(target) - $confirm.notify("error") - - target = angular.element(event.currentTarget) - $loading.start(target) - $tgrepo.save($scope.us).then(onSuccess, onError) - - return {link:link} - -module.directive("tgUsDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", - "$tgNavUrls", "$tgLoading", UsDirective]) - - -############################################################################# -## User story status directive -############################################################################# - -UsStatusDetailDirective = () -> - #TODO: i18n - template = _.template(""" -

- - <% if (is_closed) { %> - Closed - <% } else { %> - Open - <% } %> - <%= status.name %> -

- -
-
- - <%- totalClosedTasks %>/<%- totalTasks %> tasks completed - -
- -
-
- <%- owner.full_name_display %> -
- -
- Created by <%- owner.full_name_display %> - <%- date %> -
-
- -
    -
  • - <%- totalPoints %> - total -
  • - <% _.each(rolePoints, function(rolePoint) { %> -
  • - <%- rolePoint.points %> - <%- rolePoint.name %>
  • - <% }); %> -
- -
-
- - <%= status.name %> - <% if (editable) { %> - - <% } %> - status -
-
- """) - selectionStatusTemplate = _.template(""" - - """) - selectionPointsTemplate = _.template(""" - - """) - - link = ($scope, $el, $attrs, $model) -> - editable = $attrs.editable? - updatingSelectedRoleId = null - $ctrl = $el.controller() - - showSelectPoints = (target) -> - us = $model.$modelValue - $el.find(".pop-points-open").remove() - $el.find(target).append(selectionPointsTemplate({ "points": $scope.project.points })) - target.removeClass('active') - $el.find(".pop-points-open a[data-point-id='#{us.points[updatingSelectedRoleId]}']").addClass("active") - # If not showing role selection let's move to the left - $el.find(".pop-points-open").popover().open() - - calculateTotalPoints = (us)-> - values = _.map(us.points, (v, k) -> $scope.pointsById[v].value) - values = _.filter(values, (num) -> num?) - if values.length == 0 - return "?" - - return _.reduce(values, (acc, num) -> acc + num) - - renderUsstatus = (us) -> - owner = $scope.usersById?[us.owner] - date = moment(us.created_date).format("DD MMM YYYY HH:mm") - status = $scope.statusById[us.status] - rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true) - _.map rolePoints, (v, k) -> - name = $scope.pointsById[us.points[v.id]].name - name = "?" if not name? - v.points = name - - totalTasks = $scope.tasks.length - totalClosedTasks = _.filter($scope.tasks, (task) => $scope.taskStatusById[task.status].is_closed).length - usProgress = 0 - usProgress = 100 * totalClosedTasks / totalTasks if totalTasks > 0 - html = template({ - owner: owner - date: date - editable: editable - is_closed: us.is_closed - status: status - totalPoints: us.total_points - rolePoints: rolePoints - totalTasks: totalTasks - totalClosedTasks: totalClosedTasks - usProgress: usProgress - }) - $el.html(html) - $el.find(".status-data").append(selectionStatusTemplate({statuses:$scope.statusList})) - - bindOnce $scope, "tasks", (tasks) -> - $scope.$watch $attrs.ngModel, (us) -> - if us? - renderUsstatus(us) - - $scope.$on "related-tasks:update", -> - us = $scope.$eval $attrs.ngModel - if us? - # Reload the us because the status could have changed - $ctrl.loadUs() - renderUsstatus(us) - - if editable - $el.on "click", ".status-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-status").popover().open() - - $el.on "click", ".status", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.status = target.data("status-id") - renderUsstatus($model.$modelValue) - $.fn.popover().closeAll() - - $el.on "click", ".total.clickable", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - updatingSelectedRoleId = target.data("role-id") - target.siblings().removeClass('active') - target.addClass('active') - showSelectPoints(target) - - $el.on "click", ".point", (event) -> - event.preventDefault() - event.stopPropagation() - - target = angular.element(event.currentTarget) - $.fn.popover().closeAll() - - $scope.$apply () -> - us = $model.$modelValue - usPoints = _.clone(us.points, true) - usPoints[updatingSelectedRoleId] = target.data("point-id") - us.points = usPoints - us.total_points = calculateTotalPoints(us) - renderUsstatus(us) - - return {link:link, require:"ngModel"} - -module.directive("tgUsStatusDetail", UsStatusDetailDirective) - - - ############################################################################# ## User story status display directive ############################################################################# diff --git a/app/partials/us-detail-edit.jade b/app/partials/us-detail-edit.jade deleted file mode 100644 index 43fe65c2..00000000 --- a/app/partials/us-detail-edit.jade +++ /dev/null @@ -1,52 +0,0 @@ -extends dummy-layout - -block head - title Taiga Your agile, free, and open source project management tool - -block content - form.wrapper(tg-us-detail, ng-controller="UserStoryDetailController as ctrl", - ng-init="section='backlog'") - div.main.us-detail - div.us-detail-header.header-with-actions - include views/components/mainTitle - .action-buttons - a.button.button-green.save-us(href="", title="Save") Save - a.button.button-red.cancel(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", href="", title="Cancel") Cancel - - section.us-story-main-data - div.us-title(ng-class="{blocked: us.is_blocked}") - div.us-edit-name-inner - span.us-number(tg-bo-ref="us.ref") - input(type="text", ng-model="us.subject", data-required="true", data-maxlength="500") - p.block-desc-container(ng-show="us.is_blocked") - span.block-description-title Blocked - span.block-description(tg-bind-html="us.blocked_note || 'This US is blocked'") - a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock US") Unblock - - div(tg-tag-line, editable="true", ng-model="us.tags") - - section.us-content - textarea(placeholder="Write a description of your user story", ng-model="us.description", tg-markitup) - - tg-attachments(ng-model="us", type="us") - tg-history(ng-model="us", type="us", mode="edit") - - sidebar.menu-secondary.sidebar - section.us-status(tg-us-status-detail, ng-model="us", editable="true") - section.us-assigned-to(tg-assigned-to, ng-model="us", editable="true") - section.watchers(tg-watchers, ng-model="us", editable="true") - - section.us-detail-settings - fieldset - label.clickable.button.button-gray(for="client-requirement", ng-class="{'active': us.client_requirement}") Client requirement - input(ng-model="us.client_requirement", type="checkbox", id="client-requirement", name="client-requirement") - fieldset - label.clickable.button.button-gray(for="team-requirement", ng-class="{'active': us.team_requirement}") Team requirement - input(ng-model="us.team_requirement", type="checkbox", id="team-requirement", name="team-requirement") - - a.button.button-gray.clickable(ng-show="!us.is_blocked", ng-click="ctrl.block()") Block - a.button.button-red(tg-check-permission="delete_us", ng-click="ctrl.delete()", href="") Delete - - div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking issue", ng-model="us") - div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) - div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index d8e51b7a..83db67b7 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -4,7 +4,7 @@ block head title Taiga Your agile, free, and open source project management tool block content - div.wrapper(tg-us-detail, ng-controller="UserStoryDetailController as ctrl", + div.wrapper(ng-controller="UserStoryDetailController as ctrl", ng-init="section='backlog'") div.main.us-detail div.us-detail-header.header-with-actions @@ -15,10 +15,6 @@ block content href="", title="Go to taskboard", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", ng-if="sprint && project.is_backlog_activated") Taskboard - a.button.button-green( - tg-check-permission="modify_us", href="", - title="Edit", - tg-nav="project-userstories-detail-edit:project=project.slug,ref=us.ref") Edit section.us-story-main-data div.us-title(ng-class="{blocked: us.is_blocked}") From 060fe067ba89daf247c8a496a69fabf2b9290eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 15 Oct 2014 00:03:42 +0200 Subject: [PATCH 11/89] Refactor tg-task-status directive: Create directives tg-task-status-display and tg-task-status-button --- app/coffee/modules/tasks/detail.coffee | 125 +++++++++++++++++++++++++ app/partials/task-detail.jade | 8 +- 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 9abb3640..ee9e58d6 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -249,6 +249,131 @@ TaskStatusDirective = () -> module.directive("tgTaskStatus", TaskStatusDirective) + +############################################################################# +## Task status display directive +############################################################################# + +TaskStatusDisplayDirective = -> + # Display if a Task is open or closed and its taskboard status. + # + # Example: + # tg-task-status-display(ng-model="task") + # + # Requirements: + # - Task object (ng-model) + # - scope.statusById object + + template = _.template(""" + + <% if (status.is_closed) { %> + Closed + <% } else { %> + Open + <% } %> + + + <%= status.name %> + + """) # TODO: i18n + + link = ($scope, $el, $attrs) -> + render = (task) -> + html = template({ + status: $scope.statusById[task.status] + }) + $el.html(html) + + $scope.$watch $attrs.ngModel, (task) -> + render(task) if task? + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgTaskStatusDisplay", TaskStatusDisplayDirective) + + +############################################################################# +## Task status button directive +############################################################################# + +TaskStatusButtonDirective = ($rootScope, $repo) -> + # Display the status of Task and you can edit it. + # + # Example: + # tg-task-status-button(ng-model="task") + # + # Requirements: + # - Task object (ng-model) + # - scope.statusById object + + template = _.template(""" +
+ + <%= status.name %> + + status + +
    + <% _.each(statuses, function(st) { %> +
  • <%- st.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (task) => + status = $scope.statusById[task.status] + + html = template({ + status: status + statuses: $scope.statusList + }) + $el.html(html) + + $el.on "click", ".status-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-status").popover().open() + + $el.on "click", ".status", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + us = $model.$modelValue.clone() + us.status = target.data("status-id") + + $model.$setViewValue(us) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (task) -> + render(task) if task + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", TaskStatusButtonDirective]) + + TaskIsIocaineButtonDirective = ($rootscope, $tgrepo) -> template = _.template("""
diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index d961d7cb..351d664f 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -46,9 +46,15 @@ block content tg-history(ng-model="task", type="task") sidebar.menu-secondary.sidebar - section.us-status(tg-task-status, ng-model="task") + section.us-status + h1(tg-task-status-display, ng-model="task") + tg-created-by-display.us-created-by(ng-model="task") + tg-task-status-button.issue-data(ng-model="task") + section.us-assigned-to(tg-assigned-to, ng-model="task") + section.watchers(tg-watchers, ng-model="task") + section.us-detail-settings fieldset(tg-task-is-iocaine-button, ng-model="task") div(tg-block-button, ng-model="task") From 85fac7fd72dc060fee11b470804f3b758dee66ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 15 Oct 2014 00:14:11 +0200 Subject: [PATCH 12/89] Remove Task edition page and some extra deprecated code --- app/coffee/app.coffee | 2 - app/coffee/modules/base.coffee | 1 - app/coffee/modules/tasks/detail.coffee | 132 +------------------------ app/partials/task-detail-edit.jade | 49 --------- app/partials/task-detail.jade | 6 +- 5 files changed, 4 insertions(+), 186 deletions(-) delete mode 100644 app/partials/task-detail-edit.jade diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 71c5644e..c38c8352 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -56,8 +56,6 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven # Tasks $routeProvider.when("/project/:pslug/task/:taskref", {templateUrl: "/partials/task-detail.html", resolve: {loader: tgLoaderProvider.add()}}) - $routeProvider.when("/project/:pslug/task/:taskref/edit", - {templateUrl: "/partials/task-detail-edit.html"}) # Wiki $routeProvider.when("/project/:pslug/wiki", diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index 61917a05..93728f50 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -69,7 +69,6 @@ urls = { "project-userstories-detail": "/project/:project/us/:ref" "project-tasks-detail": "/project/:project/task/:ref" - "project-tasks-detail-edit": "/project/:project/task/:ref/edit" "project-issues-detail": "/project/:project/issue/:ref" "project-issues-detail-edit": "/project/:project/issue/:ref/edit" diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index ee9e58d6..30360fe3 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -124,132 +124,6 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) module.controller("TaskDetailController", TaskDetailController) -############################################################################# -## Task Main Directive -############################################################################# - -TaskDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) -> - linkSidebar = ($scope, $el, $attrs, $ctrl) -> - - link = ($scope, $el, $attrs) -> - $ctrl = $el.controller() - linkSidebar($scope, $el, $attrs, $ctrl) - - if $el.is("form") - form = $el.checksley() - - $el.on "click", ".save-task", (event) -> - if not form.validate() - return - - onSuccess = -> - $loading.finish(target) - $confirm.notify("success") - ctx = { - project: $scope.project.slug - ref: $scope.task.ref - } - $location.path($navUrls.resolve("project-tasks-detail", ctx)) - - onError = -> - $loading.finish(target) - $confirm.notify("error") - - target = angular.element(event.currentTarget) - $loading.start(target) - $tgrepo.save($scope.task).then(onSuccess, onError) - - return {link:link} - -module.directive("tgTaskDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls", - "$tgLoading", TaskDirective]) - - -############################################################################# -## Task status directive -############################################################################# - -TaskStatusDirective = () -> - #TODO: i18n - template = _.template(""" -

- - <% if (status.is_closed) { %> - Closed - <% } else { %> - Open - <% } %> - <%= status.name %> -

-
-
- <%- owner.full_name_display %> -
- -
- Created by <%- owner.full_name_display %> - <%- date %> -
-
-
-
- - <%= status.name %> - <% if (editable) { %> - - <% } %> - status -
-
- """) - selectionStatusTemplate = _.template(""" - - """) - - link = ($scope, $el, $attrs, $model) -> - editable = $attrs.editable? - - renderTaskstatus = (task) -> - owner = $scope.usersById?[task.owner] - date = moment(task.created_date).format("DD MMM YYYY HH:mm") - status = $scope.statusById[task.status] - html = template({ - owner: owner - date: date - editable: editable - status: status - }) - $el.html(html) - $el.find(".status-data").append(selectionStatusTemplate({statuses:$scope.statusList})) - - $scope.$watch $attrs.ngModel, (task) -> - if task? - renderTaskstatus(task) - - if editable - $el.on "click", ".status-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-status").popover().open() - - $el.on "click", ".status", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.status = target.data("status-id") - renderTaskstatus($model.$modelValue) - $el.find(".popover").popover().close() - - return {link:link, require:"ngModel"} - -module.directive("tgTaskStatus", TaskStatusDirective) - - ############################################################################# ## Task status display directive ############################################################################# @@ -352,10 +226,10 @@ TaskStatusButtonDirective = ($rootScope, $repo) -> $.fn.popover().closeAll() - us = $model.$modelValue.clone() - us.status = target.data("status-id") + task = $model.$modelValue.clone() + task.status = target.data("status-id") - $model.$setViewValue(us) + $model.$setViewValue(task) $repo.save($model.$modelValue).then -> $rootScope.$broadcast("history:reload") diff --git a/app/partials/task-detail-edit.jade b/app/partials/task-detail-edit.jade deleted file mode 100644 index a056b697..00000000 --- a/app/partials/task-detail-edit.jade +++ /dev/null @@ -1,49 +0,0 @@ -extends dummy-layout - -block head - title Taiga Your agile, free, and open source project management tool - -block content - form.wrapper(tg-task-detail, ng-controller="TaskDetailController as ctrl", - ng-init="section='backlog'") - div.main.us-detail - div.us-detail-header.header-with-actions - include views/components/mainTitle - .action-buttons - a.button.button-green.save-task(href="", title="Save") Save - a.button.button-red.cancel(tg-nav="project-tasks-detail:project=project.slug,ref=task.ref", href="", title="Cancel") Cancel - - section.us-story-main-data - div.us-title(ng-class="{blocked: task.is_blocked}") - div.us-edit-name-inner - span.us-number(tg-bo-ref="task.ref") - input(type="text", ng-model="task.subject", data-required="true", data-maxlength="500") - p.block-desc-container(ng-show="task.is_blocked") - span.block-description-title Blocked - span.block-description(tg-bind-html="task.blocked_note || 'This task is blocked'") - a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock task") Unblock - - div(tg-tag-line, editable="true", ng-model="task.tags") - - section.us-content - textarea(placeholder="Write a description of your task", ng-model="task.description", tg-markitup) - - tg-attachments(ng-model="task", type="task") - tg-history(ng-model="task", type="task", mode="edit") - - sidebar.menu-secondary.sidebar - section.us-status(tg-task-status, ng-model="task", editable="true") - section.us-assigned-to(tg-assigned-to, ng-model="task", editable="true") - section.watchers(tg-watchers, ng-model="task", editable="true") - - section.us-detail-settings - fieldset(title="Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!") - label.clickable.button.button-gray(for="is-iocaine", ng-class="{'active': task.is_iocaine}") Iocaine - input(ng-model="task.is_iocaine", type="checkbox", id="is-iocaine", name="is-iocaine") - - a.button.button-gray.clickable(ng-show="!task.is_blocked", ng-click="ctrl.block()") Block - a.button.button-red(tg-check-permission="delete_task", ng-click="ctrl.delete()", href="") Delete - - div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking task", ng-model="task") - div.lightbox.lightbox-select-user.hidden(tg-lb-assignedto) - div.lightbox.lightbox-select-user.hidden(tg-lb-watchers) diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 351d664f..63514b75 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -4,7 +4,7 @@ block head title Taiga Your agile, free, and open source project management tool block content - div.wrapper(tg-task-detail, ng-controller="TaskDetailController as ctrl", + div.wrapper(ng-controller="TaskDetailController as ctrl", ng-init="section='backlog'") div.main.us-detail div.us-detail-header.header-with-actions @@ -15,10 +15,6 @@ block content href="", title="Go to taskboard", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", ng-if="sprint && project.is_backlog_activated") Taskboard - a.button.button-green( - tg-check-permission="modify_task", href="", - title="Edit", - tg-nav="project-tasks-detail-edit:project=project.slug,ref=task.ref") Edit section.us-story-main-data div.us-title(ng-class="{blocked: task.is_blocked}") From c2ee8829be5a91f37e64f28c239eee0de8002889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 15 Oct 2014 00:41:59 +0200 Subject: [PATCH 13/89] Refactor tg-issue-status directive: Create directives tg-issue-status-display, tg-issue-status-button, tg-issue-type-button, tg-issue-severity-button and tg-issue-priority-button --- app/coffee/modules/issues/detail.coffee | 349 ++++++++++++++++++++++++ app/partials/issues-detail.jade | 10 +- 2 files changed, 358 insertions(+), 1 deletion(-) diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index cc26cec7..eaca6e4f 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -356,6 +356,355 @@ IssueStatusDirective = () -> module.directive("tgIssueStatus", IssueStatusDirective) + +############################################################################# +## Issue status display directive +############################################################################# + +IssueStatusDisplayDirective = -> + # Display if a Issue is open or closed and its issueboard status. + # + # Example: + # tg-issue-status-display(ng-model="issue") + # + # Requirements: + # - Issue object (ng-model) + # - scope.statusById object + + template = _.template(""" + + <% if (status.is_closed) { %> + Closed + <% } else { %> + Open + <% } %> + + + <%= status.name %> + + """) # TODO: i18n + + link = ($scope, $el, $attrs) -> + render = (issue) -> + html = template({ + status: $scope.statusById[issue.status] + }) + $el.html(html) + + $scope.$watch $attrs.ngModel, (issue) -> + render(issue) if issue? + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgIssueStatusDisplay", IssueStatusDisplayDirective) + + +############################################################################# +## Issue status button directive +############################################################################# + +IssueStatusButtonDirective = ($rootScope, $repo) -> + # Display the status of Issue and you can edit it. + # + # Example: + # tg-issue-status-button(ng-model="issue") + # + # Requirements: + # - Issue object (ng-model) + # - scope.statusById object + + template = _.template(""" +
+ + <%= status.name %> + + status + +
    + <% _.each(statuses, function(st) { %> +
  • <%- st.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (issue) => + status = $scope.statusById[issue.status] + + html = template({ + status: status + statuses: $scope.statusList + }) + $el.html(html) + + $el.on "click", ".status-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-status").popover().open() + + $el.on "click", ".status", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + issue = $model.$modelValue.clone() + issue.status = target.data("status-id") + + $model.$setViewValue(issue) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (issue) -> + render(issue) if issue + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgIssueStatusButton", ["$rootScope", "$tgRepo", IssueStatusButtonDirective]) + +############################################################################# +## Issue type button directive +############################################################################# + +IssueTypeButtonDirective = ($rootScope, $repo) -> + # Display the type of Issue and you can edit it. + # + # Example: + # tg-issue-type-button(ng-model="issue") + # + # Requirements: + # - Issue object (ng-model) + # - scope.typeById object + + template = _.template(""" +
+ + <%= type.name %> + + type + +
    + <% _.each(typees, function(tp) { %> +
  • <%- tp.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (issue) => + type = $scope.typeById[issue.type] + + html = template({ + type: type + typees: $scope.typeList + }) + $el.html(html) + + $el.on "click", ".type-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-type").popover().open() + + $el.on "click", ".type", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + issue = $model.$modelValue.clone() + issue.type = target.data("type-id") + + $model.$setViewValue(issue) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (issue) -> + render(issue) if issue + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgIssueTypeButton", ["$rootScope", "$tgRepo", IssueTypeButtonDirective]) + + +############################################################################# +## Issue severity button directive +############################################################################# + +IssueSeverityButtonDirective = ($rootScope, $repo) -> + # Display the severity of Issue and you can edit it. + # + # Example: + # tg-issue-severity-button(ng-model="issue") + # + # Requirements: + # - Issue object (ng-model) + # - scope.severityById object + + template = _.template(""" +
+ + <%= severity.name %> + + severity + +
    + <% _.each(severityes, function(sv) { %> +
  • <%- sv.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (issue) => + severity = $scope.severityById[issue.severity] + + html = template({ + severity: severity + severityes: $scope.severityList + }) + $el.html(html) + + $el.on "click", ".severity-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-severity").popover().open() + + $el.on "click", ".severity", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + issue = $model.$modelValue.clone() + issue.severity = target.data("severity-id") + + $model.$setViewValue(issue) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (issue) -> + render(issue) if issue + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgIssueSeverityButton", ["$rootScope", "$tgRepo", IssueSeverityButtonDirective]) + + +############################################################################# +## Issue priority button directive +############################################################################# + +IssuePriorityButtonDirective = ($rootScope, $repo) -> + # Display the priority of Issue and you can edit it. + # + # Example: + # tg-issue-priority-button(ng-model="issue") + # + # Requirements: + # - Issue object (ng-model) + # - scope.priorityById object + + template = _.template(""" +
+ + <%= priority.name %> + + priority + +
    + <% _.each(priorityes, function(pr) { %> +
  • <%- pr.name %>
  • + <% }); %> +
+
+ """) #TODO: i18n + + link = ($scope, $el, $attrs, $model) -> + render = (issue) => + priority = $scope.priorityById[issue.priority] + + html = template({ + priority: priority + priorityes: $scope.priorityList + }) + $el.html(html) + + $el.on "click", ".priority-data", (event) -> + event.preventDefault() + event.stopPropagation() + + $el.find(".pop-priority").popover().open() + + $el.on "click", ".priority", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + $.fn.popover().closeAll() + + issue = $model.$modelValue.clone() + issue.priority = target.data("priority-id") + + $model.$setViewValue(issue) + $repo.save($model.$modelValue).then -> + $rootScope.$broadcast("history:reload") + + $scope.$watch $attrs.ngModel, (issue) -> + render(issue) if issue + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", IssuePriorityButtonDirective]) + + ############################################################################# ## Promote Issue to US button directive ############################################################################# diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 365623e2..3feb9052 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -41,8 +41,16 @@ block content tg-history(ng-model="issue", type="issue") sidebar.menu-secondary.sidebar - section.us-status(tg-issue-status, ng-model="issue") + section.us-status + h1(tg-issue-status-display, ng-model="issue") + tg-created-by-display.us-created-by(ng-model="issue") + tg-issue-type-button.issue-data(ng-model="issue") + tg-issue-severity-button.issue-data(ng-model="issue") + tg-issue-priority-button.issue-data(ng-model="issue") + tg-issue-status-button.issue-data(ng-model="issue") + section.us-assigned-to(tg-assigned-to, ng-model="issue") + section.watchers(tg-watchers, ng-model="issue") section.us-detail-settings From 9cf3ca18f2e6d5fb4bd7365b84c11ead758bfd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 15 Oct 2014 00:44:49 +0200 Subject: [PATCH 14/89] Remove Issue edition page and some extra deprecated code --- app/coffee/app.coffee | 2 - app/coffee/modules/base.coffee | 1 - app/coffee/modules/issues/detail.coffee | 224 ------------------------ app/partials/issues-detail-edit.jade | 47 ----- app/partials/issues-detail.jade | 4 +- 5 files changed, 1 insertion(+), 277 deletions(-) delete mode 100644 app/partials/issues-detail-edit.jade diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index c38c8352..5ed21f82 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -70,8 +70,6 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven {templateUrl: "/partials/issues.html", resolve: {loader: tgLoaderProvider.add()}}) $routeProvider.when("/project/:pslug/issue/:issueref", {templateUrl: "/partials/issues-detail.html"}) - $routeProvider.when("/project/:pslug/issue/:issueref/edit", - {templateUrl: "/partials/issues-detail-edit.html"}) # Admin $routeProvider.when("/project/:pslug/admin/project-profile/details", diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index 93728f50..4719e659 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -71,7 +71,6 @@ urls = { "project-tasks-detail": "/project/:project/task/:ref" "project-issues-detail": "/project/:project/issue/:ref" - "project-issues-detail-edit": "/project/:project/issue/:ref/edit" "project-wiki": "/project/:project/wiki", "project-wiki-page": "/project/:project/wiki/:slug", diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index eaca6e4f..3712034f 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -133,230 +133,6 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) module.controller("IssueDetailController", IssueDetailController) -############################################################################# -## Issue Main Directive -############################################################################# - -IssueDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) -> - linkSidebar = ($scope, $el, $attrs, $ctrl) -> - - link = ($scope, $el, $attrs) -> - $ctrl = $el.controller() - linkSidebar($scope, $el, $attrs, $ctrl) - - if $el.is("form") - form = $el.checksley() - - $el.on "click", ".save-issue", (event) -> - if not form.validate() - return - - onSuccess = -> - $loading.finish(target) - $confirm.notify("success") - ctx = { - project: $scope.project.slug - ref: $scope.issue.ref - } - $location.path($navUrls.resolve("project-issues-detail", ctx)) - - onError = -> - $loading.finish(target) - $confirm.notify("error") - - target = angular.element(event.currentTarget) - $loading.start(target) - $tgrepo.save($scope.issue).then(onSuccess, onError) - - return {link:link} - -module.directive("tgIssueDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls", - "$tgLoading", IssueDirective]) - - -############################################################################# -## Issue status directive -############################################################################# - -IssueStatusDirective = () -> - # TODO: i18n - template = _.template(""" -

- - <% if (status.is_closed) { %> - Closed - <% } else { %> - Open - <% } %> - - <%= status.name %> -

-
-
- <%- owner.full_name_display %> -
- -
- Created by <%- owner.full_name_display %> - <%- date %> -
-
-
-
- - <%= type.name %> - <% if (editable) { %> - - <% } %> - type -
-
- - <%= severity.name %> - <% if (editable) { %> - - <% } %> - severity -
-
- - <%= priority.name %> - <% if (editable) { %> - - <% } %> - priority -
-
- - <%= status.name %> - <% if (editable) { %> - - <% } %> - status -
-
- """) - selectionTypeTemplate = _.template(""" - - """) - selectionSeverityTemplate = _.template(""" - - """) - selectionPriorityTemplate = _.template(""" - - """) - selectionStatusTemplate = _.template(""" - - """) - - link = ($scope, $el, $attrs, $model) -> - editable = $attrs.editable? - - renderIssuestatus = (issue) -> - owner = $scope.usersById?[issue.owner] - date = moment(issue.created_date).format("DD MMM YYYY HH:mm") - type = $scope.typeById[issue.type] - status = $scope.statusById[issue.status] - severity = $scope.severityById[issue.severity] - priority = $scope.priorityById[issue.priority] - html = template({ - owner: owner - date: date - editable: editable - status: status - severity: severity - priority: priority - type: type - }) - $el.html(html) - $el.find(".type-data").append(selectionTypeTemplate({types:$scope.typeList})) - $el.find(".severity-data").append(selectionSeverityTemplate({severities:$scope.severityList})) - $el.find(".priority-data").append(selectionPriorityTemplate({priorities:$scope.priorityList})) - $el.find(".status-data").append(selectionStatusTemplate({statuses:$scope.statusList})) - - $scope.$watch $attrs.ngModel, (issue) -> - if issue? - renderIssuestatus(issue) - - if editable - $el.on "click", ".type-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-type").popover().open() - - $el.on "click", ".type", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.type = target.data("type-id") - renderIssuestatus($model.$modelValue) - $.fn.popover().closeAll() - - $el.on "click", ".severity-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-severity").popover().open() - - $el.on "click", ".severity", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.severity = target.data("severity-id") - renderIssuestatus($model.$modelValue) - $.fn.popover().closeAll() - - $el.on "click", ".priority-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-priority").popover().open() - - $el.on "click", ".priority", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.priority = target.data("priority-id") - renderIssuestatus($model.$modelValue) - $.fn.popover().closeAll() - - $el.on "click", ".status-data", (event) -> - event.preventDefault() - event.stopPropagation() - $el.find(".pop-status").popover().open() - - $el.on "click", ".status", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - $model.$modelValue.status = target.data("status-id") - renderIssuestatus($model.$modelValue) - $.fn.popover().closeAll() - - return {link:link, require:"ngModel"} - -module.directive("tgIssueStatus", IssueStatusDirective) - - - ############################################################################# ## Issue status display directive ############################################################################# diff --git a/app/partials/issues-detail-edit.jade b/app/partials/issues-detail-edit.jade deleted file mode 100644 index 4cc4db4c..00000000 --- a/app/partials/issues-detail-edit.jade +++ /dev/null @@ -1,47 +0,0 @@ -extends dummy-layout - -block head - title Taiga Your agile, free, and open source project management tool - -block content - form.wrapper(tg-issue-detail, ng-controller="IssueDetailController as ctrl", - ng-init="section='issues'") - div.main.us-detail - div.us-detail-header.header-with-actions - include views/components/mainTitle - .action-buttons - a.button.button-green.save-issue(href="", title="Save") Save - a.button.button-red.cancel(tg-nav="project-issues-detail:project=project.slug, ref=issue.ref", href="", title="Cancel") Cancel - - section.us-story-main-data - div.us-title(ng-class="{blocked: issue.is_blocked}") - div.us-edit-name-inner - span.us-number(tg-bo-ref="issue.ref") - input(type="text", ng-model="issue.subject", data-required="true", data-maxlength="500") - p.block-desc-container(ng-show="issue.is_blocked") - span.block-description-title Blocked - span.block-description(tg-bind-html="issue.blocked_note || 'This issue is blocked'") - a.unblock(ng-click="ctrl.unblock()", href="", title="Unblock issue") Unblock - - div(tg-tag-line, editable="true", ng-model="issue.tags") - - section.us-content - textarea(placeholder="Write a description of your issue", ng-model="issue.description", tg-markitup) - - tg-attachments(ng-model="issue", type="issue") - tg-history(ng-model="issue", type="issue", mode="edit") - - sidebar.menu-secondary.sidebar - section.us-status(tg-issue-status, ng-model="issue", editable="true") - section.us-assigned-to(tg-assigned-to, ng-model="issue", editable="true") - section.watchers(tg-watchers, ng-model="issue", editable="true") - - section.us-detail-settings - a.button.button-gray.clickable(title="Click to block the issue", ng-show="!issue.is_blocked", ng-click="ctrl.block()") Block - a.button.button-red(title="Click to delete the issue", tg-check-permission="delete_issue", ng-click="ctrl.delete()", href="") Delete - - div.lightbox.lightbox-block.hidden(tg-lb-block, title="Blocking issue", ng-model="issue") - - div.lightbox.lightbox-select-user(tg-lb-assignedto) - - div.lightbox.lightbox-select-user(tg-lb-watchers) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 3feb9052..e7aab596 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -4,13 +4,11 @@ block head title Taiga Your agile, free, and open source project management tool block content - div.wrapper(tg-issue-detail, ng-controller="IssueDetailController as ctrl", + div.wrapper(ng-controller="IssueDetailController as ctrl", ng-init="section='issues'") div.main.us-detail div.us-detail-header.header-with-actions include views/components/mainTitle - .action-buttons - a.button.button-green(tg-check-permission="modify_issue", href="", title="Edit", tg-nav="project-issues-detail-edit:project=project.slug,ref=issue.ref") Edit section.us-story-main-data div.us-title(ng-class="{blocked: issue.is_blocked}") From 242216bdd9a45c8288a0978c89890bb114b59590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 15 Oct 2014 11:35:04 +0200 Subject: [PATCH 15/89] Editable tags on details --- app/coffee/modules/common/tags.coffee | 11 +++++++++-- app/partials/issues-detail.jade | 2 +- app/partials/task-detail.jade | 2 +- app/partials/us-detail.jade | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/coffee/modules/common/tags.coffee b/app/coffee/modules/common/tags.coffee index 87a6fef9..1d221d98 100644 --- a/app/coffee/modules/common/tags.coffee +++ b/app/coffee/modules/common/tags.coffee @@ -91,7 +91,7 @@ module.directive("tgColorizeTags", ColorizeTagsDirective) ## TagLine (possible should be moved as generic directive) ############################################################################# -TagLineDirective = ($log, $rs) -> +TagLineDirective = ($log, $rs, $tgrepo) -> # Main directive template (rendered by angular) template = """
@@ -125,6 +125,7 @@ TagLineDirective = ($log, $rs) -> link = ($scope, $el, $attrs, $model) -> editable = if $attrs.editable == "true" then true else false + $el.addClass("tags-block") addValue = (value) -> @@ -137,6 +138,9 @@ TagLineDirective = ($log, $rs) -> $scope.$apply -> $model.$setViewValue(normalizeTags(tags)) + autosaveModel = $scope.$eval($attrs.autosaveModel) + if autosaveModel + $tgrepo.save(autosaveModel) saveInputTag = () -> input = $el.find('input') @@ -204,6 +208,9 @@ TagLineDirective = ($log, $rs) -> $scope.$apply -> $model.$setViewValue(normalizeTags(tags)) + autosaveModel = $scope.$eval($attrs.autosaveModel) + if autosaveModel + $tgrepo.save(autosaveModel) return { link:link, @@ -211,4 +218,4 @@ TagLineDirective = ($log, $rs) -> template: template } -module.directive("tgTagLine", ["$log", "$tgResources", TagLineDirective]) +module.directive("tgTagLine", ["$log", "$tgResources", "$tgRepo", TagLineDirective]) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index e7aab596..4e72b2fb 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -31,7 +31,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous issue") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next issue") - div(tg-tag-line, ng-model="issue.tags", ng-show="issue.tags") + div(tg-tag-line, editable="true", autosave-model="issue", ng-model="issue.tags") section.us-content.wysiwyg(tg-bind-html="issue.description_html") diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 63514b75..e1d6796c 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -34,7 +34,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous task") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next task") - div(tg-tag-line, ng-model="task.tags", ng-show="task.tags") + div(tg-tag-line, editable="true", autosave-model="task", ng-model="task.tags") section.us-content.wysiwyg(tg-bind-html="task.description_html") diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 83db67b7..0350c8c5 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -36,7 +36,7 @@ block content title="previous user story") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next user story") - div(tg-tag-line, ng-model="us.tags", ng-show="us.tags") + div(tg-tag-line, editable="true", autosave-model="us", ng-model="us.tags") section.us-content.wysiwyg(tg-bind-html="us.description_html") From d371bf6c36e6debf577458fb915afa12340108f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 15 Oct 2014 13:49:12 +0200 Subject: [PATCH 16/89] Adding editable subject/description --- app/coffee/modules/common/components.coffee | 128 ++++++++++++++++++++ app/partials/issues-detail.jade | 4 +- app/partials/task-detail.jade | 4 +- app/partials/us-detail.jade | 4 +- 4 files changed, 134 insertions(+), 6 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index ef71e14a..52ddac9e 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -422,6 +422,134 @@ DeleteButtonDirective = ($tgrepo, $confirm, $navurls, $location) -> module.directive("tgDeleteButton", ["$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", DeleteButtonDirective]) +############################################################################# +## Editable subject directive +############################################################################# + +EditableSubjectDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) -> + viewTemplate = _.template(""" + <%- item.subject %> + <% if (canEdit) { %> + + <% } %> + """) + + editTemplate = _.template(""" + + """) + + link = ($scope, $el, $attrs, $model) -> + editing = false + scope = $scope.$new() + + render = -> + if editing + $el.html(editTemplate({item: scope.item})) + else + canEdit = $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 + $el.html(viewTemplate({item: scope.item, canEdit: canEdit})) + + $scope.$watch $attrs.ngModel, (item) -> + return if not item + scope.item = item.clone() + scope.item.revert() + render() + + $scope.$on "$destroy", -> + $el.off() + + $el.click -> + if not editing and $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 + editing = true + render() + $el.find('input').focus() + + $el.on "keyup", "input", -> + if event.keyCode == 13 + scope.item.subject = $el.find('input').val() + $tgrepo.save(scope.item).then -> + $rootscope.$broadcast("history:reload") + editing = false + render() + else if event.keyCode == 27 + editing = false + scope.item.revert() + render() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", EditableSubjectDirective]) + +############################################################################# +## Editable subject directive +############################################################################# + +EditableDescriptionDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location, $compile) -> + viewTemplate = _.template(""" +
<%= descriptionHtml %>
+ <% if (canEdit) { %> + + <% } %> + """) + + editTemplate = _.template(""" + +
+ """) + + link = ($scope, $el, $attrs, $model) -> + editing = false + scope = $scope.$new() + + render = -> + if editing + $el.html($compile(editTemplate({item: scope.item}))(scope)) + else + canEdit = $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 + $el.html(viewTemplate({descriptionHtml: scope.item.description_html, canEdit: canEdit})) + + $scope.$watch $attrs.ngModel, (item) -> + return if not item + scope.item = item.clone() + scope.item.revert() + render() + + $scope.$on "$destroy", -> + $el.off() + + $el.click -> + if not editing and $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 + editing = true + render() + $el.find('textarea').focus() + + $el.on "click", ".save", -> + $tgrepo.save(scope.item).then -> + $rootscope.$broadcast("history:reload") + editing = false + render() + + $el.on "keyup", "textarea", -> + if event.keyCode == 27 + editing = false + scope.item.revert() + render() + + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgNavUrls", "$tgLocation", "$compile", EditableDescriptionDirective]) + ############################################################################# ## Common list directives ############################################################################# diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index 4e72b2fb..d1d348e8 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -14,7 +14,7 @@ block content div.us-title(ng-class="{blocked: issue.is_blocked}") h2.us-title-text span.us-number(tg-bo-ref="issue.ref") - span.us-name(ng-bind="issue.subject") + span.us-name(tg-editable-subject, ng-model="issue", required-perm="modify_issue") p.us-related-task(ng-if="issue.generated_user_stories") This issue has been promoted to US: a(ng-repeat="us in issue.generated_user_stories", @@ -33,7 +33,7 @@ block content div(tg-tag-line, editable="true", autosave-model="issue", ng-model="issue.tags") - section.us-content.wysiwyg(tg-bind-html="issue.description_html") + section.us-content.wysiwyg(tg-editable-description, ng-model="issue", required-perm="modify_issue") tg-attachments(ng-model="issue", type="issue") tg-history(ng-model="issue", type="issue") diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index e1d6796c..59786dae 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -20,7 +20,7 @@ block content div.us-title(ng-class="{blocked: task.is_blocked}") h2.us-title-text span.us-number(tg-bo-ref="task.ref") - span.us-name(ng-bind="task.subject") + span.us-name(tg-editable-subject, ng-model="task", required-perm="modify_task") h3.us-related-task This task belongs to a(tg-check-permission="view_us", href="", title="Go to user story", tg-nav="project-userstories-detail:project=project.slug, ref=us.ref", @@ -36,7 +36,7 @@ block content div(tg-tag-line, editable="true", autosave-model="task", ng-model="task.tags") - section.us-content.wysiwyg(tg-bind-html="task.description_html") + section.us-content.wysiwyg(tg-editable-description, ng-model="task", required-perm="modify_task") tg-attachments(ng-model="task", type="task") tg-history(ng-model="task", type="task") diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 0350c8c5..4c6393c9 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -20,7 +20,7 @@ block content div.us-title(ng-class="{blocked: us.is_blocked}") h2.us-title-text span.us-number(tg-bo-ref="us.ref") - span.us-name(ng-bind="us.subject") + span.us-name(tg-editable-subject, ng-model="us", required-perm="modify_us") p.us-related-task(ng-if="us.origin_issue") This US has been promoted from Issue a(tg-check-permission="view_us", href="", title="Go to issue", @@ -38,7 +38,7 @@ block content div(tg-tag-line, editable="true", autosave-model="us", ng-model="us.tags") - section.us-content.wysiwyg(tg-bind-html="us.description_html") + section.us-content.wysiwyg(tg-editable-description, ng-model="us", required-perm="modify_us") include views/modules/related-tasks From ba1ba9065d38b88da17858f53a2624f21d479d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 15 Oct 2014 14:59:11 +0200 Subject: [PATCH 17/89] The editable tags are aware about permissions --- app/coffee/modules/common/tags.coffee | 26 +++++++++++++++----------- app/partials/issues-detail.jade | 2 +- app/partials/task-detail.jade | 2 +- app/partials/us-detail.jade | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/coffee/modules/common/tags.coffee b/app/coffee/modules/common/tags.coffee index 1d221d98..2ec1671a 100644 --- a/app/coffee/modules/common/tags.coffee +++ b/app/coffee/modules/common/tags.coffee @@ -91,7 +91,7 @@ module.directive("tgColorizeTags", ColorizeTagsDirective) ## TagLine (possible should be moved as generic directive) ############################################################################# -TagLineDirective = ($log, $rs, $tgrepo) -> +TagLineDirective = ($rootscope, $log, $rs, $tgrepo) -> # Main directive template (rendered by angular) template = """
@@ -124,7 +124,7 @@ TagLineDirective = ($log, $rs, $tgrepo) -> return _.uniq(tags) link = ($scope, $el, $attrs, $model) -> - editable = if $attrs.editable == "true" then true else false + editable = false $el.addClass("tags-block") @@ -140,7 +140,8 @@ TagLineDirective = ($log, $rs, $tgrepo) -> $model.$setViewValue(normalizeTags(tags)) autosaveModel = $scope.$eval($attrs.autosaveModel) if autosaveModel - $tgrepo.save(autosaveModel) + $tgrepo.save(autosaveModel).then -> + $rootscope.$broadcast("history:reload") saveInputTag = () -> input = $el.find('input') @@ -154,9 +155,14 @@ TagLineDirective = ($log, $rs, $tgrepo) -> tags_colors = if $scope.project?.tags_colors? then $scope.project.tags_colors else [] renderTags($el, val, editable, tags_colors) - bindOnce $scope, "projectId", (projectId) -> + bindOnce $scope, "project", (project) -> # If not editable, no tags preloading is needed. - return if not editable + editable = if $attrs.editable == "true" then true else false + editable = editable and project.my_permissions.indexOf($attrs.requiredPerm) != -1 + + if not editable + $el.find("input").remove() + return positioningFunction = (position, elements) -> menu = elements.element.element @@ -164,7 +170,7 @@ TagLineDirective = ($log, $rs, $tgrepo) -> menu.css("top", position.top) menu.css("left", position.left) - $rs.projects.tags(projectId).then (data) -> + $rs.projects.tags(project.id).then (data) -> $el.find("input").autocomplete({ source: data position: { @@ -176,9 +182,6 @@ TagLineDirective = ($log, $rs, $tgrepo) -> ui.item.value = "" }) - if not editable - $el.find("input").remove() - $el.on "keypress", "input", (event) -> return if event.keyCode != 13 event.preventDefault() @@ -210,7 +213,8 @@ TagLineDirective = ($log, $rs, $tgrepo) -> $model.$setViewValue(normalizeTags(tags)) autosaveModel = $scope.$eval($attrs.autosaveModel) if autosaveModel - $tgrepo.save(autosaveModel) + $tgrepo.save(autosaveModel).then -> + $rootscope.$broadcast("history:reload") return { link:link, @@ -218,4 +222,4 @@ TagLineDirective = ($log, $rs, $tgrepo) -> template: template } -module.directive("tgTagLine", ["$log", "$tgResources", "$tgRepo", TagLineDirective]) +module.directive("tgTagLine", ["$rootScope", "$log", "$tgResources", "$tgRepo", TagLineDirective]) diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index d1d348e8..f1e8a3da 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -31,7 +31,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous issue") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next issue") - div(tg-tag-line, editable="true", autosave-model="issue", ng-model="issue.tags") + div(tg-tag-line, editable="true", autosave-model="issue", ng-model="issue.tags", required-perm="modify_issue") section.us-content.wysiwyg(tg-editable-description, ng-model="issue", required-perm="modify_issue") diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 59786dae..8ff59553 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -34,7 +34,7 @@ block content a.icon.icon-arrow-left(ng-show="previousUrl",href="{{ previousUrl }}", title="previous task") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next task") - div(tg-tag-line, editable="true", autosave-model="task", ng-model="task.tags") + div(tg-tag-line, editable="true", autosave-model="task", ng-model="task.tags", required-perm="modify_task") section.us-content.wysiwyg(tg-editable-description, ng-model="task", required-perm="modify_task") diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 4c6393c9..2993eb66 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -36,7 +36,7 @@ block content title="previous user story") a.icon.icon-arrow-right(ng-show="nextUrl", href="{{ nextUrl }}", title="next user story") - div(tg-tag-line, editable="true", autosave-model="us", ng-model="us.tags") + div(tg-tag-line, editable="true", autosave-model="us", ng-model="us.tags", required-perm="modify_us") section.us-content.wysiwyg(tg-editable-description, ng-model="us", required-perm="modify_us") From 20bd7a2d6b2674b920042c4bb088868f1b84c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 15 Oct 2014 16:02:43 +0200 Subject: [PATCH 18/89] Permission control in the buttoms --- app/coffee/modules/common/components.coffee | 7 ++---- app/coffee/modules/userstories/detail.coffee | 26 ++++++++++++-------- app/partials/issues-detail.jade | 4 +-- app/partials/task-detail.jade | 4 +-- app/partials/us-detail.jade | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 52ddac9e..16bd4b15 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -240,7 +240,6 @@ WatchersDirective = ($rootscope, $confirm, $tgrepo) -> item = $model.$modelValue.clone() item.watchers = watchers - $model.$setViewValue(item) save(item) @@ -451,8 +450,7 @@ EditableSubjectDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) $scope.$watch $attrs.ngModel, (item) -> return if not item - scope.item = item.clone() - scope.item.revert() + scope.item = item render() $scope.$on "$destroy", -> @@ -516,8 +514,7 @@ EditableDescriptionDirective = ($rootscope, $tgrepo, $confirm, $navurls, $locati $scope.$watch $attrs.ngModel, (item) -> return if not item - scope.item = item.clone() - scope.item.revert() + scope.item = item render() $scope.$on "$destroy", -> diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index ef455461..33e2912d 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -482,6 +482,8 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> link = ($scope, $el, $attrs, $model) -> render = _.once (us) -> $el.html(template()) + if $scope.project.my_permissions.indexOf("modify_us") == -1 + $el.find('label').css("cursor", "auto") refresh = (us) -> if us?.team_requirement @@ -498,11 +500,12 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> $el.off() $el.on "click", ".team-requirement", (event) -> - us = $model.$modelValue.clone() - us.team_requirement = not us.team_requirement - $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> - $rootscope.$broadcast("history:reload") + if $scope.project.my_permissions.indexOf("modify_us") != -1 + us = $model.$modelValue.clone() + us.team_requirement = not us.team_requirement + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") return { link: link @@ -526,6 +529,8 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> link = ($scope, $el, $attrs, $model) -> render = _.once (us) -> $el.html(template()) + if $scope.project.my_permissions.indexOf("modify_us") == -1 + $el.find('label').css("cursor", "auto") refresh = (us) -> if us?.client_requirement @@ -542,11 +547,12 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> $el.off() $el.on "click", ".client-requirement", (event) -> - us = $model.$modelValue.clone() - us.client_requirement = not us.client_requirement - $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> - $rootscope.$broadcast("history:reload") + if $scope.project.my_permissions.indexOf("modify_us") != -1 + us = $model.$modelValue.clone() + us.client_requirement = not us.client_requirement + $model.$setViewValue(us) + $tgrepo.save($model.$modelValue).then -> + $rootscope.$broadcast("history:reload") return { link: link diff --git a/app/partials/issues-detail.jade b/app/partials/issues-detail.jade index f1e8a3da..85ffd2b5 100644 --- a/app/partials/issues-detail.jade +++ b/app/partials/issues-detail.jade @@ -52,8 +52,8 @@ block content section.watchers(tg-watchers, ng-model="issue") section.us-detail-settings - tg-promote-issue-to-us-button(ng-model="issue") - div(tg-block-button, ng-model="issue") + tg-promote-issue-to-us-button(tg-check-permission="add_us", ng-model="issue") + div(tg-check-permission="modify_issue", tg-block-button, ng-model="issue") div(tg-check-permission="delete_issue", tg-delete-button, on-delete-go-to-url="project-issues", project-slug="{{ project.slug }}" ng-model="issue") diff --git a/app/partials/task-detail.jade b/app/partials/task-detail.jade index 8ff59553..8d6a1aa6 100644 --- a/app/partials/task-detail.jade +++ b/app/partials/task-detail.jade @@ -52,8 +52,8 @@ block content section.watchers(tg-watchers, ng-model="task") section.us-detail-settings - fieldset(tg-task-is-iocaine-button, ng-model="task") - div(tg-block-button, ng-model="task") + fieldset(tg-task-is-iocaine-button, tg-check-permission="modify_task", ng-model="task") + div(tg-check-permission="modify_task", tg-block-button, ng-model="task") div(tg-check-permission="delete_task", tg-delete-button, on-delete-go-to-url="project-backlog", project-slug="{{ project.slug }}" ng-model="task") diff --git a/app/partials/us-detail.jade b/app/partials/us-detail.jade index 2993eb66..74b83afd 100644 --- a/app/partials/us-detail.jade +++ b/app/partials/us-detail.jade @@ -60,7 +60,7 @@ block content section.us-detail-settings fieldset(tg-us-team-requirement-button, ng-model="us") fieldset(tg-us-client-requirement-button, ng-model="us") - div(tg-block-button, ng-model="us") + div(tg-check-permission="modify_us", tg-block-button, ng-model="us") div(tg-check-permission="delete_us", tg-delete-button, on-delete-go-to-url="project-backlog", project-slug="{{ project.slug }}" ng-model="us") From 4ab0512e22fd4361101dc1f88580df40a1565ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 15 Oct 2014 16:23:22 +0200 Subject: [PATCH 19/89] Some error handling --- app/coffee/modules/common/components.coffee | 26 ++++++++++++-------- app/coffee/modules/common/tags.coffee | 14 ++++++++--- app/coffee/modules/tasks/detail.coffee | 10 +++++--- app/coffee/modules/userstories/detail.coffee | 22 ++++++++++++----- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 16bd4b15..c0803d2c 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -446,11 +446,10 @@ EditableSubjectDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) $el.html(editTemplate({item: scope.item})) else canEdit = $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 - $el.html(viewTemplate({item: scope.item, canEdit: canEdit})) + $el.html(viewTemplate({item: $model.$modelValue, canEdit: canEdit})) $scope.$watch $attrs.ngModel, (item) -> return if not item - scope.item = item render() $scope.$on "$destroy", -> @@ -459,19 +458,23 @@ EditableSubjectDirective = ($rootscope, $tgrepo, $confirm, $navurls, $location) $el.click -> if not editing and $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 editing = true + scope.item = {subject: $model.$modelValue.subject} render() $el.find('input').focus() $el.on "keyup", "input", -> if event.keyCode == 13 - scope.item.subject = $el.find('input').val() - $tgrepo.save(scope.item).then -> + $model.$modelValue.subject = $el.find('input').val() + promise = $tgrepo.save($model.$modelValue) + promise.then -> $rootscope.$broadcast("history:reload") editing = false render() + promise.then null, -> + $confirm.notify("error") else if event.keyCode == 27 editing = false - scope.item.revert() + $model.$modelValue.revert() render() return { @@ -510,11 +513,10 @@ EditableDescriptionDirective = ($rootscope, $tgrepo, $confirm, $navurls, $locati $el.html($compile(editTemplate({item: scope.item}))(scope)) else canEdit = $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 - $el.html(viewTemplate({descriptionHtml: scope.item.description_html, canEdit: canEdit})) + $el.html(viewTemplate({descriptionHtml: $model.$modelValue.description_html, canEdit: canEdit})) $scope.$watch $attrs.ngModel, (item) -> return if not item - scope.item = item render() $scope.$on "$destroy", -> @@ -523,22 +525,26 @@ EditableDescriptionDirective = ($rootscope, $tgrepo, $confirm, $navurls, $locati $el.click -> if not editing and $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 editing = true + scope.item = {description: $model.$modelValue.description} render() $el.find('textarea').focus() $el.on "click", ".save", -> - $tgrepo.save(scope.item).then -> + $model.$modelValue.description = scope.item.description + promise = $tgrepo.save($model.$modelValue) + promise.then -> $rootscope.$broadcast("history:reload") editing = false render() + promise.then null, -> + $confirm.notify("error") $el.on "keyup", "textarea", -> if event.keyCode == 27 editing = false - scope.item.revert() + $model.$modelValue.revert() render() - return { link: link restrict: "EA" diff --git a/app/coffee/modules/common/tags.coffee b/app/coffee/modules/common/tags.coffee index 2ec1671a..5ba46b6b 100644 --- a/app/coffee/modules/common/tags.coffee +++ b/app/coffee/modules/common/tags.coffee @@ -91,7 +91,7 @@ module.directive("tgColorizeTags", ColorizeTagsDirective) ## TagLine (possible should be moved as generic directive) ############################################################################# -TagLineDirective = ($rootscope, $log, $rs, $tgrepo) -> +TagLineDirective = ($rootscope, $log, $rs, $tgrepo, $confirm) -> # Main directive template (rendered by angular) template = """
@@ -140,8 +140,11 @@ TagLineDirective = ($rootscope, $log, $rs, $tgrepo) -> $model.$setViewValue(normalizeTags(tags)) autosaveModel = $scope.$eval($attrs.autosaveModel) if autosaveModel - $tgrepo.save(autosaveModel).then -> + promise = $tgrepo.save(autosaveModel) + promise.then -> $rootscope.$broadcast("history:reload") + promise.then null, -> + $confirm.notify("error") saveInputTag = () -> input = $el.find('input') @@ -213,8 +216,11 @@ TagLineDirective = ($rootscope, $log, $rs, $tgrepo) -> $model.$setViewValue(normalizeTags(tags)) autosaveModel = $scope.$eval($attrs.autosaveModel) if autosaveModel - $tgrepo.save(autosaveModel).then -> + promise = $tgrepo.save(autosaveModel) + promise.then -> $rootscope.$broadcast("history:reload") + promise.then null, -> + $confirm.notify("error") return { link:link, @@ -222,4 +228,4 @@ TagLineDirective = ($rootscope, $log, $rs, $tgrepo) -> template: template } -module.directive("tgTagLine", ["$rootScope", "$log", "$tgResources", "$tgRepo", TagLineDirective]) +module.directive("tgTagLine", ["$rootScope", "$log", "$tgResources", "$tgRepo", "$tgConfirm", TagLineDirective]) diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 30360fe3..acfd6dca 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -248,7 +248,7 @@ TaskStatusButtonDirective = ($rootScope, $repo) -> module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", TaskStatusButtonDirective]) -TaskIsIocaineButtonDirective = ($rootscope, $tgrepo) -> +TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm) -> template = _.template("""
@@ -278,9 +278,11 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo) -> us = $model.$modelValue.clone() us.is_iocaine = not us.is_iocaine $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> + promise = $tgrepo.save($model.$modelValue) + promise.then -> $rootscope.$broadcast("history:reload") - + promise.then null, -> + $confirm.notify("error") return { link: link @@ -288,4 +290,4 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo) -> require: "ngModel" } -module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", TaskIsIocaineButtonDirective]) +module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", TaskIsIocaineButtonDirective]) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 33e2912d..3a061603 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -473,7 +473,7 @@ module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", UsStatusButtonDir ## User story team requirements button directive ############################################################################# -UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> +UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm) -> template = _.template(""" @@ -504,8 +504,13 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> us = $model.$modelValue.clone() us.team_requirement = not us.team_requirement $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> + promise = $tgrepo.save($model.$modelValue) + promise.then -> $rootscope.$broadcast("history:reload") + promise.then null, -> + $confirm.notify("error") + us.revert() + $model.$setViewValue(us) return { link: link @@ -513,14 +518,14 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo) -> require: "ngModel" } -module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", UsTeamRequirementButtonDirective]) +module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", UsTeamRequirementButtonDirective]) ############################################################################# ## User story client requirements button directive ############################################################################# -UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> +UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm) -> template = _.template(""" @@ -551,12 +556,17 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo) -> us = $model.$modelValue.clone() us.client_requirement = not us.client_requirement $model.$setViewValue(us) - $tgrepo.save($model.$modelValue).then -> + promise = $tgrepo.save($model.$modelValue) + promise.then -> $rootscope.$broadcast("history:reload") + promise.then null, -> + $confirm.notify("error") + us.revert() + $model.$setViewValue(us) return { link: link restrict: "EA" require: "ngModel" } -module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", UsClientRequirementButtonDirective]) +module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", UsClientRequirementButtonDirective]) From 3552f305f9d160d1392d1e4fd6ed5dffb6b157bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 16 Oct 2014 13:41:35 +0200 Subject: [PATCH 20/89] Check permissions in tg-us-status-button directive --- app/coffee/modules/userstories/detail.coffee | 27 +++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 3a061603..556602e6 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -398,7 +398,7 @@ module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", UsEst ## User story status button directive ############################################################################# -UsStatusButtonDirective = ($rootScope, $repo) -> +UsStatusButtonDirective = ($rootScope, $repo, $confirm) -> # Display the status of a US and you can edit it. # # Example: @@ -407,12 +407,13 @@ UsStatusButtonDirective = ($rootScope, $repo) -> # Requirements: # - Us object (ng-model) # - scope.statusById object + # - $scope.project.my_permissions template = _.template(""" -
+
<%= status.name %> - + <% if(editable){ %><% }%> status