From 7585b8bc8899e1b2a9ca33a37d42db7b83e7637d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 20 Oct 2014 13:49:35 +0200 Subject: [PATCH 001/164] Updating url when editing project slug --- app/coffee/modules/admin/project-profile.coffee | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index 14b3087d..bfb6722d 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -96,7 +96,7 @@ module.controller("ProjectProfileController", ProjectProfileController) ## Project Profile Directive ############################################################################# -ProjectProfileDirective = ($repo, $confirm, $loading) -> +ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) -> link = ($scope, $el, $attrs) -> form = $el.find("form").checksley({"onlyOneErrorElement": true}) submit = (target) => @@ -108,6 +108,8 @@ ProjectProfileDirective = ($repo, $confirm, $loading) -> promise.then -> $loading.finish(target) $confirm.notify("success") + newUrl = $navurls.resolve("project-admin-project-profile-details", {project: $scope.project.slug}) + $location.path(newUrl) $scope.$emit("project:loaded", $scope.project) promise.then null, (data) -> @@ -132,7 +134,7 @@ ProjectProfileDirective = ($repo, $confirm, $loading) -> return {link:link} -module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", ProjectProfileDirective]) +module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation", ProjectProfileDirective]) ############################################################################# From f4dbffad88893d17aedefe04b76119296e75a64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Mon, 20 Oct 2014 14:09:49 +0200 Subject: [PATCH 002/164] Add file upload date on attachment date --- app/coffee/modules/common/attachments.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/coffee/modules/common/attachments.coffee b/app/coffee/modules/common/attachments.coffee index 1f928845..9832c6d8 100644 --- a/app/coffee/modules/common/attachments.coffee +++ b/app/coffee/modules/common/attachments.coffee @@ -267,7 +267,7 @@ module.directive("tgAttachments", ["$tgConfirm", AttachmentsDirective]) AttachmentDirective = -> template = _.template("""
- + <%- name %> @@ -291,7 +291,7 @@ AttachmentDirective = -> templateEdit = _.template("""
<%- size %> @@ -320,6 +320,7 @@ AttachmentDirective = -> ctx = { id: attachment.id name: attachment.name + created_date: moment(attachment.created_date).format("DD MMM YYYY [at] hh:mm") #TODO: i18n url: attachment.url size: sizeFormat(attachment.size) description: attachment.description From d13f9c7da9883fa51a15bd447f25884bd20c1fcc Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 20 Oct 2014 17:14:43 +0200 Subject: [PATCH 003/164] Taskboard link in sprint's title --- app/partials/views/modules/sprints.jade | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/partials/views/modules/sprints.jade b/app/partials/views/modules/sprints.jade index 4f4ab30e..2725c6ac 100644 --- a/app/partials/views/modules/sprints.jade +++ b/app/partials/views/modules/sprints.jade @@ -17,7 +17,11 @@ section.sprints header div.sprint-name a.icon.icon-arrow-up(href="", title="Compact Sprint") - span {{ sprint.name }} + a(href="", tg-bo-title="'Go to Taskboard of ' + sprint.name", + tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", + tg-check-permission="view_milestones") + span {{ sprint.name }} + a.icon.icon-edit(tg-check-permission="modify_milestone", href="", title="Edit Sprint") div.sprint-summary div.sprint-date(tg-date-range="sprint.estimated_start,sprint.estimated_finish") From 4866030558393aee18c90f542780d944488c8fb2 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Oct 2014 13:10:51 +0200 Subject: [PATCH 004/164] Adding example configuration for google analytics --- conf/main.example.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/main.example.json b/conf/main.example.json index b71f9e52..79d738a6 100644 --- a/conf/main.example.json +++ b/conf/main.example.json @@ -2,6 +2,10 @@ "api": "http://localhost:8000/api/v1/", "eventsUrl": "ws://localhost:8888/events", "debug": "true", + # Google analytics, uncomment if needed + "analytics": { + "accountId": "XXXXXXXXXXXX" + }, "publicRegisterEnabled": true, "feedbackEnabled": true, "privacyPolicyUrl": null, From 1b53945a4d767b937c738adebb7c73aadad40bf2 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Oct 2014 13:14:54 +0200 Subject: [PATCH 005/164] Removing unnecesary base configuration --- conf/main.example.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/conf/main.example.json b/conf/main.example.json index 79d738a6..b71f9e52 100644 --- a/conf/main.example.json +++ b/conf/main.example.json @@ -2,10 +2,6 @@ "api": "http://localhost:8000/api/v1/", "eventsUrl": "ws://localhost:8888/events", "debug": "true", - # Google analytics, uncomment if needed - "analytics": { - "accountId": "XXXXXXXXXXXX" - }, "publicRegisterEnabled": true, "feedbackEnabled": true, "privacyPolicyUrl": null, From 5a999595f8e7dc3305af1a8970c7a618c5b2e99f Mon Sep 17 00:00:00 2001 From: Juanfran Date: Tue, 21 Oct 2014 13:28:27 +0200 Subject: [PATCH 006/164] fix #1411 --- app/styles/modules/backlog/backlog-table.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/styles/modules/backlog/backlog-table.scss b/app/styles/modules/backlog/backlog-table.scss index 90bc279c..6d4a653e 100644 --- a/app/styles/modules/backlog/backlog-table.scss +++ b/app/styles/modules/backlog/backlog-table.scss @@ -23,7 +23,7 @@ background: transparent; } .user-stories { - @include table-flex-child(20, 365px, 0); + width: 100%; } .status { @include table-flex-child(0, 150px, 0); @@ -149,8 +149,9 @@ background: lighten($green-taiga, 60%); } .user-story-name { - @include table-flex(); + @include table-flex($flex-wrap: nowrap); input { + @include flex-shrink(0); margin-right: 1rem; vertical-align: super; &:checked { From 576d5b31da85eb297f90a5f932ed96ee8d8a4e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Mon, 20 Oct 2014 13:20:37 +0200 Subject: [PATCH 007/164] Add visual difference to closed US in backlog view --- app/partials/views/modules/sprints.jade | 6 +++--- app/styles/modules/backlog/sprints.scss | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/partials/views/modules/sprints.jade b/app/partials/views/modules/sprints.jade index 2725c6ac..b6168715 100644 --- a/app/partials/views/modules/sprints.jade +++ b/app/partials/views/modules/sprints.jade @@ -21,8 +21,8 @@ section.sprints tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", tg-check-permission="view_milestones") span {{ sprint.name }} - a.icon.icon-edit(tg-check-permission="modify_milestone", href="", title="Edit Sprint") + div.sprint-summary div.sprint-date(tg-date-range="sprint.estimated_start,sprint.estimated_finish") ul @@ -39,10 +39,10 @@ section.sprints div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories track by us.id") div.column-us.width-8 a.us-name.clickable(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", - tg-bo-title="'#' + us.ref + ' ' + us.subject") + tg-bo-title="'#' + us.ref + ' ' + us.subject", ng-class="{closed: us.is_closed}") span(tg-bo-ref="us.ref") span(tg-bo-bind="us.subject") - div.column-points.width-1(tg-bo-bind="us.total_points") + div.column-points.width-1(tg-bo-bind="us.total_points", ng-class="{closed: us.is_closed}") a.button.button-gray(href="", tg-bo-title="'Go to Taskboard of ' + sprint.name", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", diff --git a/app/styles/modules/backlog/sprints.scss b/app/styles/modules/backlog/sprints.scss index b36c4a83..979c8f64 100644 --- a/app/styles/modules/backlog/sprints.scss +++ b/app/styles/modules/backlog/sprints.scss @@ -16,7 +16,7 @@ } } .sprint-name { - span { + a { @extend %large; @extend %title; @include ellipsis($width: 90%); @@ -153,10 +153,16 @@ @include table-flex-child(1, 0, 0); padding: 0 4px; text-align: right; + &.closed { + color: lighten($gray-light, 5%); + } } .us-name { @include ellipsis(250px); display: block; + &.closed { + color: lighten($gray-light, 5%); + } } } .button-gray { From ca4711d3f31f9eda0bb6c1dc6f3eda2b5a8ff3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Oct 2014 19:33:08 +0200 Subject: [PATCH 008/164] Fix bug #1353: Update estimated dates in backlog when update an sprint --- app/coffee/modules/backlog/sprints.coffee | 126 ++++++++++++++++------ app/partials/views/modules/sprints.jade | 30 ++---- 2 files changed, 99 insertions(+), 57 deletions(-) diff --git a/app/coffee/modules/backlog/sprints.coffee b/app/coffee/modules/backlog/sprints.coffee index 5dba9b92..fcdcd8b6 100644 --- a/app/coffee/modules/backlog/sprints.coffee +++ b/app/coffee/modules/backlog/sprints.coffee @@ -21,44 +21,25 @@ taiga = @.taiga -mixOf = @.taiga.mixOf -toggleText = @.taiga.toggleText -scopeDefer = @.taiga.scopeDefer -bindOnce = @.taiga.bindOnce -groupBy = @.taiga.groupBy - module = angular.module("taigaBacklog") + ############################################################################# -## Sprint Directive +## Sprint Actions Directive ############################################################################# BacklogSprintDirective = ($repo, $rootscope) -> - ## Common parts - linkCommon = ($scope, $el, $attrs, $ctrl) -> - sprint = $scope.$eval($attrs.tgBacklogSprint) - if $scope.$first - $el.addClass("sprint-current") - $el.find(".sprint-table").addClass('open') - - else if sprint.closed - $el.addClass("sprint-closed") - - else if not $scope.$first and not sprint.closed - $el.addClass("sprint-old-open") - - # Update progress bars - $scope.$watch $attrs.tgBacklogSprint, (value) -> + link = ($scope, $el, $attrs) -> + $scope.$watch $attrs.tgBacklogSprint, (sprint) -> sprint = $scope.$eval($attrs.tgBacklogSprint) - if sprint.total_points - progressPercentage = Math.round(100 * (sprint.closed_points / sprint.total_points)) - else - progressPercentage = 0 - - $el.find(".current-progress").css("width", "#{progressPercentage}%") - - $el.find(".sprint-table").disableSelection() + if $scope.$first + $el.addClass("sprint-current") + $el.find(".sprint-table").addClass('open') + else if sprint.closed + $el.addClass("sprint-closed") + else if not $scope.$first and not sprint.closed + $el.addClass("sprint-old-open") # Event Handlers $el.on "click", ".sprint-name > .icon-arrow-up", (event) -> @@ -67,15 +48,92 @@ BacklogSprintDirective = ($repo, $rootscope) -> $el.find(".sprint-table").toggleClass('open') $el.on "click", ".sprint-name > .icon-edit", (event) -> + sprint = $scope.$eval($attrs.tgBacklogSprint) $rootscope.$broadcast("sprintform:edit", sprint) - link = ($scope, $el, $attrs) -> - $ctrl = $el.closest("div.wrapper").controller() - linkCommon($scope, $el, $attrs, $ctrl) - $scope.$on "$destroy", -> $el.off() return {link: link} module.directive("tgBacklogSprint", ["$tgRepo", "$rootScope", BacklogSprintDirective]) + + +############################################################################# +## Sprint Header Directive +############################################################################# + +BacklogSprintHeaderDirective = ($navUrls) -> + template = _.template(""" +
+ + + <% if(isVisible){ %> + + <%- name %> + + <% } %> + + <% if(isEditable){ %> + + <% } %> +
+ +
+
<%- estimatedDateRange %>
+
    +
  • + <%- closedPoints %> + closed +
  • +
  • + <%- totalPoints %> + total +
  • +
+
+ """) + + link = ($scope, $el, $attrs, $model) -> + isEditable = -> + return $scope.project.my_permissions.indexOf("modify_milestone") != -1 + + isVisible = -> + return $scope.project.my_permissions.indexOf("view_milestones") != -1 + + render = (sprint) -> + taskboardUrl = $navUrls.resolve("project-taskboard", + {project: $scope.project.slug, sprint: sprint.slug}) + + start = moment(sprint.estimated_start).format("DD MMM YYYY") + finish = moment(sprint.estimated_finish).format("DD MMM YYYY") + estimatedDateRange = "#{start}-#{finish}" + + ctx = { + name: sprint.name + taskboardUrl: taskboardUrl + estimatedDateRange: estimatedDateRange + closedPoints: sprint.closed_points or 0 + totalPoints: sprint.total_points or 0 + isVisible: isVisible() + isEditable: isEditable() + } + $el.html(template(ctx)) + + + $scope.$watch $attrs.ngModel, (sprint) -> + render(sprint) + + $scope.$on "sprintform:edit:success", -> + render($model.$modelValue) + + $scope.$on "$destroy", -> + $el.off() + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgBacklogSprintHeader", ["$tgNavUrls", "$tgRepo", "$rootScope", BacklogSprintHeaderDirective]) diff --git a/app/partials/views/modules/sprints.jade b/app/partials/views/modules/sprints.jade index b6168715..4a39c413 100644 --- a/app/partials/views/modules/sprints.jade +++ b/app/partials/views/modules/sprints.jade @@ -12,39 +12,23 @@ section.sprints tg-check-permission="add_milestone") span.text + New sprint - section.sprint(ng-repeat="sprint in sprints track by sprint.id" - tg-backlog-sprint="sprint") - header - div.sprint-name - a.icon.icon-arrow-up(href="", title="Compact Sprint") - a(href="", tg-bo-title="'Go to Taskboard of ' + sprint.name", - tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", - tg-check-permission="view_milestones") - span {{ sprint.name }} - a.icon.icon-edit(tg-check-permission="modify_milestone", href="", title="Edit Sprint") + section.sprint(ng-repeat="sprint in sprints track by sprint.id" tg-backlog-sprint="sprint") + header(tg-backlog-sprint-header, ng-model="sprint") - div.sprint-summary - div.sprint-date(tg-date-range="sprint.estimated_start,sprint.estimated_finish") - ul - li - span.number(ng-bind="sprint.closed_points|default:''") -- - span.description closed - li - span.number(ng-bind="sprint.total_points|default:''") - span.description total - - div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / total_points") + div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points") div.sprint-table(tg-sprint-sortable) div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories track by us.id") div.column-us.width-8 a.us-name.clickable(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", - tg-bo-title="'#' + us.ref + ' ' + us.subject", ng-class="{closed: us.is_closed}") + tg-bo-title="'#' + us.ref + ' ' + us.subject", + ng-class="{closed: us.is_closed}") span(tg-bo-ref="us.ref") span(tg-bo-bind="us.subject") div.column-points.width-1(tg-bo-bind="us.total_points", ng-class="{closed: us.is_closed}") - a.button.button-gray(href="", tg-bo-title="'Go to Taskboard of ' + sprint.name", + a.button.button-gray(tg-bo-title="'Go to Taskboard of ' + sprint.name", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", tg-check-permission="view_milestones") + span Sprint Taskboard From 63da9b293327d6510671802c3d613c441a810ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Oct 2014 19:40:35 +0200 Subject: [PATCH 009/164] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285860e1..acad2b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features - Multiple User stories Drag & Drop in the backlog. +- Add visual difference to closed USs in backlog panel. - Added beta ribbon. ### Misc From 2bc42bd5b9c70f52205831eff6e4ba7b37a48639 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 11:18:00 +0200 Subject: [PATCH 010/164] fix #1178 - 403 error --- app/coffee/app.coffee | 2 ++ app/coffee/classes.coffee | 10 ++++++++++ app/coffee/modules/admin/memberships.coffee | 6 +----- app/coffee/modules/admin/project-profile.coffee | 6 +----- app/coffee/modules/admin/project-values.coffee | 6 +----- app/coffee/modules/admin/roles.coffee | 6 +----- app/coffee/modules/backlog/main.coffee | 6 +----- app/coffee/modules/base.coffee | 1 + app/coffee/modules/issues/detail.coffee | 6 +----- app/coffee/modules/issues/list.coffee | 6 +----- app/coffee/modules/kanban/main.coffee | 7 +------ app/coffee/modules/projects/main.coffee | 12 ++---------- app/coffee/modules/search.coffee | 6 +----- app/coffee/modules/taskboard/main.coffee | 6 +----- app/coffee/modules/tasks/detail.coffee | 6 +----- .../modules/user-settings/change-password.coffee | 6 +----- app/coffee/modules/user-settings/main.coffee | 6 +----- .../modules/user-settings/notifications.coffee | 6 +----- app/coffee/modules/userstories/detail.coffee | 6 +----- app/coffee/modules/wiki/main.coffee | 6 +----- app/partials/permission-denied.jade | 7 +++++++ 21 files changed, 38 insertions(+), 91 deletions(-) create mode 100644 app/partials/permission-denied.jade diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index e007c11d..3d298fe5 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -136,6 +136,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven {templateUrl: "/partials/error.html"}) $routeProvider.when("/not-found", {templateUrl: "/partials/not-found.html"}) + $routeProvider.when("/permission-denied", + {templateUrl: "/partials/permission-denied.html"}) $routeProvider.otherwise({redirectTo: '/not-found'}) $locationProvider.html5Mode(true) diff --git a/app/coffee/classes.coffee b/app/coffee/classes.coffee index 14ffc5ec..7e995c09 100644 --- a/app/coffee/classes.coffee +++ b/app/coffee/classes.coffee @@ -22,6 +22,16 @@ class TaigaBase class TaigaService extends TaigaBase class TaigaController extends TaigaBase + onInitialDataError: (xhr) => + if xhr + if xhr.status == 404 + @location.path(@navUrls.resolve("not-found")) + @location.replace() + else if xhr.status == 403 + @location.path(@navUrls.resolve("permission-denied")) + @location.replace() + + return @q.reject(xhr) @.taiga.Base = TaigaBase @.taiga.Service = TaigaService diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee index a7435ae9..300ec412 100644 --- a/app/coffee/modules/admin/memberships.coffee +++ b/app/coffee/modules/admin/memberships.coffee @@ -58,11 +58,7 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai promise.then => @appTitle.set("Membership - " + @scope.project.name) - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) @scope.$on "membersform:new:success", => @.loadMembers() diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index bfb6722d..3c1b3505 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -57,11 +57,7 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin) promise.then => @appTitle.set("Project profile - " + @scope.sectionName + " - " + @scope.project.name) - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) @scope.$on "project:loaded", => @appTitle.set("Project profile - " + @scope.sectionName + " - " + @scope.project.name) diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index e86ee0b1..72351515 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -57,11 +57,7 @@ class ProjectValuesController extends mixOf(taiga.Controller, taiga.PageMixin) promise.then () => @appTitle.set("Project values - " + @scope.sectionName + " - " + @scope.project.name) - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) @scope.$on("admin:project-values:move", @.moveValue) diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index b3c64464..423a0ecb 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -58,11 +58,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil promise.then () => @appTitle.set("Roles - " + @scope.project.name) - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index 8201b4f8..7050280d 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -74,11 +74,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> @scope.$on "usform:bulk:success", => diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index 9a4a309f..d3dffe7f 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -46,6 +46,7 @@ urls = { "home": "/" "error": "/error" "not-found": "/not-found" + "permission-denied": "/permission-denied" "login": "/login" "forgot-password": "/forgot-password" diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index ac36f08e..7bdbf59f 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -62,11 +62,7 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @appTitle.set(@scope.issue.subject + " - " + @scope.project.name) # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> diff --git a/app/coffee/modules/issues/list.coffee b/app/coffee/modules/issues/list.coffee index b2aab722..98f76393 100644 --- a/app/coffee/modules/issues/list.coffee +++ b/app/coffee/modules/issues/list.coffee @@ -74,11 +74,7 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) @scope.$on "issueform:new:success", => @analytics.trackEvent("issue", "create", "create issue on issues list", 1) diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index 1446fb39..dda5460d 100644 --- a/app/coffee/modules/kanban/main.coffee +++ b/app/coffee/modules/kanban/main.coffee @@ -78,12 +78,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) - + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> @scope.$on "usform:new:success", => diff --git a/app/coffee/modules/projects/main.coffee b/app/coffee/modules/projects/main.coffee index 86b438f9..db64d8d2 100644 --- a/app/coffee/modules/projects/main.coffee +++ b/app/coffee/modules/projects/main.coffee @@ -53,11 +53,7 @@ class ProjectsController extends taiga.Controller @scope.$emit("projects:loaded") @tgLoader.pageLoaded() - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadInitialData: -> return @rs.projects.list().then (projects) => @@ -96,11 +92,7 @@ class ProjectController extends taiga.Controller promise.then () => @appTitle.set(@scope.project.name) - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadInitialData: -> # Resolve project slug diff --git a/app/coffee/modules/search.coffee b/app/coffee/modules/search.coffee index fa5b390e..e22c787d 100644 --- a/app/coffee/modules/search.coffee +++ b/app/coffee/modules/search.coffee @@ -55,11 +55,7 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin) promise.then () => @appTitle.set("Search") - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) # Search input watcher @scope.searchTerm = "" diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index 8cd2dc37..bed2c3ba 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -66,11 +66,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> # TODO: Reload entire taskboard after create/edit tasks seems diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index ea532357..2829c1ce 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -59,11 +59,7 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @appTitle.set(@scope.task.subject + " - " + @scope.project.name) tgLoader.pageLoaded() - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> @scope.$on "attachment:create", => diff --git a/app/coffee/modules/user-settings/change-password.coffee b/app/coffee/modules/user-settings/change-password.coffee index 3db6c237..54aeb0de 100644 --- a/app/coffee/modules/user-settings/change-password.coffee +++ b/app/coffee/modules/user-settings/change-password.coffee @@ -51,11 +51,7 @@ class UserChangePasswordController extends mixOf(taiga.Controller, taiga.PageMix promise = @.loadInitialData() - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => diff --git a/app/coffee/modules/user-settings/main.coffee b/app/coffee/modules/user-settings/main.coffee index 6137264f..dddf70f0 100644 --- a/app/coffee/modules/user-settings/main.coffee +++ b/app/coffee/modules/user-settings/main.coffee @@ -49,11 +49,7 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.loadInitialData() - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => diff --git a/app/coffee/modules/user-settings/notifications.coffee b/app/coffee/modules/user-settings/notifications.coffee index b6e392f0..6af3c428 100644 --- a/app/coffee/modules/user-settings/notifications.coffee +++ b/app/coffee/modules/user-settings/notifications.coffee @@ -51,11 +51,7 @@ class UserNotificationsController extends mixOf(taiga.Controller, taiga.PageMixi promise = @.loadInitialData() - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index e81f4481..44f79fb5 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -62,11 +62,7 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) initializeEventHandlers: -> @scope.$on "attachment:create", => diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 7a45338e..0a4375db 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -65,11 +65,7 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) tgLoader.pageLoaded() # On Error - promise.then null, (xhr) => - if xhr and xhr.status == 404 - @location.path(@navUrls.resolve("not-found")) - @location.replace() - return @q.reject(xhr) + promise.then null, @.onInitialDataError.bind(@) loadProject: -> return @rs.projects.get(@scope.projectId).then (project) => diff --git a/app/partials/permission-denied.jade b/app/partials/permission-denied.jade new file mode 100644 index 00000000..a855c2af --- /dev/null +++ b/app/partials/permission-denied.jade @@ -0,0 +1,7 @@ +div.error-main + div.error-container + object.logo-svg(type="image/svg+xml", data="/svg/logo.svg") + img(src="/images/logo.png", alt="TAIGA") + h1.logo Permission denied + p.error-text Error 403. + a(href="/", title="") Take me home From ead27ba7b409dfc56ca52c10b2d5322c2320cdd6 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 12:15:01 +0200 Subject: [PATCH 011/164] fix #1410 --- app/styles/layout/us-detail.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/styles/layout/us-detail.scss b/app/styles/layout/us-detail.scss index 98bebef4..4cf822e6 100644 --- a/app/styles/layout/us-detail.scss +++ b/app/styles/layout/us-detail.scss @@ -95,6 +95,7 @@ .block-description-title { @extend %bold; color: $white; + margin-right: .5rem; } .block-description { color: $white; From fb8756e30ba948dfd5dd3ebb28e4ca3e24a55061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 22 Oct 2014 12:30:59 +0200 Subject: [PATCH 012/164] Fix bug #1445: Refactor: $confirm.ask service --- app/coffee/modules/admin/memberships.coffee | 8 +++---- app/coffee/modules/backlog/lightboxes.coffee | 4 ++-- app/coffee/modules/backlog/main.coffee | 4 ++-- app/coffee/modules/common/attachments.coffee | 6 ++--- app/coffee/modules/common/components.coffee | 12 +++++----- app/coffee/modules/common/confirm.coffee | 8 ++++--- app/coffee/modules/issues/detail.coffee | 4 ++-- app/coffee/modules/issues/list.coffee | 4 ++-- app/coffee/modules/related-tasks.coffee | 4 ++-- app/coffee/modules/tasks/detail.coffee | 4 ++-- app/coffee/modules/userstories/detail.coffee | 4 ++-- app/coffee/modules/wiki/main.coffee | 4 ++-- app/coffee/modules/wiki/nav.coffee | 4 ++-- app/index.jade | 4 ++-- .../help-notions/lightbox-generic-notion.jade | 2 +- .../views/modules/lightbox-ask-choice.jade | 4 ++-- .../modules/lightbox-confirm-delete.jade | 12 ---------- .../modules/lightbox-delete-account.jade | 4 ++-- .../modules/lightbox-delete-project.jade | 4 ++-- .../views/modules/lightbox-generic-ask.jade | 12 ++++++++++ .../views/modules/lightbox-generic-error.jade | 2 +- .../modules/lightbox-generic-success.jade | 2 +- app/styles/modules/common/lightbox.scss | 24 +++++++++---------- 23 files changed, 71 insertions(+), 69 deletions(-) delete mode 100644 app/partials/views/modules/lightbox-confirm-delete.jade create mode 100644 app/partials/views/modules/lightbox-generic-ask.jade diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee index a7435ae9..849df141 100644 --- a/app/coffee/modules/admin/memberships.coffee +++ b/app/coffee/modules/admin/memberships.coffee @@ -432,18 +432,18 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm) -> event.preventDefault() title = "Delete member" # TODO: i18n - subtitle = if member.user then member.full_name else "the invitation to #{member.email}" # TODO: i18n + message = if member.user then member.full_name else "the invitation to #{member.email}" # TODO: i18n - $confirm.ask(title, subtitle).then (finish) -> + $confirm.askOnDelete(title, message).then (finish) -> onSuccess = -> finish() $ctrl.loadMembers() - $confirm.notify("success", null, "We've deleted #{subtitle}.") # TODO: i18n + $confirm.notify("success", null, "We've deleted #{message}.") # TODO: i18n onError = -> finish(false) # TODO: i18in - $confirm.notify("error", null, "We have not been able to delete #{subtitle}.") + $confirm.notify("error", null, "We have not been able to delete #{message}.") $repo.remove(member).then(onSuccess, onError) diff --git a/app/coffee/modules/backlog/lightboxes.coffee b/app/coffee/modules/backlog/lightboxes.coffee index 00ba58ba..d0a31b46 100644 --- a/app/coffee/modules/backlog/lightboxes.coffee +++ b/app/coffee/modules/backlog/lightboxes.coffee @@ -86,9 +86,9 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading) remove = -> #TODO: i18n title = "Delete sprint" - subtitle = $scope.sprint.name + message = $scope.sprint.name - $confirm.ask(title, subtitle).then (finish) => + $confirm.askOnDelete(title, message).then (finish) => onSuccess = -> finish() $scope.milestonesCounter -= 1 diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index 8201b4f8..8ac10ce4 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -469,9 +469,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F deleteUserStory: (us) -> #TODO: i18n title = "Delete User Story" - subtitle = us.subject + message = us.subject - @confirm.ask(title, subtitle).then (finish) => + @confirm.askOnDelete(title, message).then (finish) => # We modify the userstories in scope so the user doesn't see the removed US for a while @scope.userstories = _.without(@scope.userstories, us) @filterVisibleUserstories() diff --git a/app/coffee/modules/common/attachments.coffee b/app/coffee/modules/common/attachments.coffee index 9832c6d8..3402a392 100644 --- a/app/coffee/modules/common/attachments.coffee +++ b/app/coffee/modules/common/attachments.coffee @@ -127,9 +127,9 @@ class AttachmentsController extends taiga.Controller # Remove one concrete attachment. removeAttachment: (attachment) -> title = "Delete attachment" #TODO: i18in - subtitle = "the attachment '#{attachment.name}'" #TODO: i18in + message = "the attachment '#{attachment.name}'" #TODO: i18in - return @confirm.ask(title, subtitle).then (finish) => + return @confirm.askOnDelete(title, message).then (finish) => onSuccess = => finish() index = @.attachments.indexOf(attachment) @@ -139,7 +139,7 @@ class AttachmentsController extends taiga.Controller onError = => finish(false) - @confirm.notify("error", null, "We have not been able to delete #{subtitle}.") + @confirm.notify("error", null, "We have not been able to delete #{message}.") return @q.reject() return @repo.remove(attachment).then(onSuccess, onError) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 4c429a87..30d3a35c 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -165,10 +165,10 @@ WatchersDirective = ($rootscope, $confirm) -> target = angular.element(event.currentTarget) watcherId = target.data("watcher-id") - title = "Remove watcher" - subtitle = $scope.usersById[watcherId].full_name_display + title = "Delete watcher" + message = $scope.usersById[watcherId].full_name_display - $confirm.ask(title, subtitle).then (finish) => + $confirm.askOnDelete(title, message).then (finish) => finish() watcherIds = _.clone($model.$modelValue.watchers, false) watcherIds = _.pull(watcherIds, watcherId) @@ -250,10 +250,10 @@ AssignedToDirective = ($rootscope, $confirm) -> $el.on "click", ".icon-delete", (event) -> event.preventDefault() - title = "Remove assigned to" - subtitle = "" + title = "Delete assignetion" + message = "" - $confirm.ask(title, subtitle).then (finish) => + $confirm.askOnDelete(title, message).then (finish) => finish() $model.$modelValue.assigned_to = null renderAssignedTo($model.$modelValue) diff --git a/app/coffee/modules/common/confirm.coffee b/app/coffee/modules/common/confirm.coffee index dbae0425..d52ce5d4 100644 --- a/app/coffee/modules/common/confirm.coffee +++ b/app/coffee/modules/common/confirm.coffee @@ -50,14 +50,13 @@ class ConfirmService extends taiga.Service el.off(".confirm-dialog") - ask: (title, subtitle, message=null, lightboxSelector=".lightbox-confirm-delete") -> + ask: (title, subtitle, message, lightboxSelector=".lightbox-generic-ask") -> el = angular.element(lightboxSelector) # Render content el.find("h2.title").html(title) el.find("span.subtitle").html(subtitle) - if message - el.find("span.delete-question").html(message) + el.find("span.message").html(message) defered = @q.defer() @@ -80,6 +79,9 @@ class ConfirmService extends taiga.Service return defered.promise + askOnDelete: (title, message) -> + return @.ask(title, "Are you sure you want to delete?", message) #TODO: i18n + askChoice: (title, subtitle, choices, lightboxSelector=".lightbox-ask-choice") -> el = angular.element(lightboxSelector) diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index ac36f08e..7db92857 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -143,9 +143,9 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) delete: -> # TODO: i18n title = "Delete Issue" - subtitle = @scope.issue.subject + message = @scope.issue.subject - @confirm.ask(title, subtitle).then (finish) => + @confirm.askOnDelete(title, message).then (finish) => promise = @.repo.remove(@scope.issue) promise.then => finish() diff --git a/app/coffee/modules/issues/list.coffee b/app/coffee/modules/issues/list.coffee index b2aab722..4ddd3a41 100644 --- a/app/coffee/modules/issues/list.coffee +++ b/app/coffee/modules/issues/list.coffee @@ -642,9 +642,9 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading) -> target = angular.element(event.currentTarget) customFilterName = target.parent().data('id') title = "Delete custom filter" # TODO: i18n - subtitle = "the custom filter '#{customFilterName}'" # TODO: i18n + message = "the custom filter '#{customFilterName}'" # TODO: i18n - $confirm.ask(title, subtitle).then (finish) -> + $confirm.askOnDelete(title, message).then (finish) -> promise = $ctrl.deleteMyFilter(customFilterName) promise.then -> promise = $ctrl.loadMyFilters() diff --git a/app/coffee/modules/related-tasks.coffee b/app/coffee/modules/related-tasks.coffee index 1e142421..8b853704 100644 --- a/app/coffee/modules/related-tasks.coffee +++ b/app/coffee/modules/related-tasks.coffee @@ -139,9 +139,9 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading) -> #TODO: i18n task = $model.$modelValue title = "Delete Task" - subtitle = task.subject + message = task.subject - $confirm.ask(title, subtitle).then (finish) -> + $confirm.askOnDelete(title, message).then (finish) -> promise = $repo.remove(task) promise.then -> finish() diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index ea532357..7778b193 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -134,9 +134,9 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) delete: -> #TODO: i18n title = "Delete Task" - subtitle = @scope.task.subject + message = @scope.task.subject - @confirm.ask(title, subtitle).then (finish) => + @confirm.askOnDelete(title, message).then (finish) => promise = @.repo.remove(@scope.task) promise.then => finish() diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index e81f4481..aac5f273 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -146,9 +146,9 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) delete: -> #TODO: i18n title = "Delete User Story" - subtitle = @scope.us.subject + message = @scope.us.subject - @confirm.ask(title, subtitle).then (finish) => + @confirm.askOnDelete(title, message).then (finish) => promise = @.repo.remove(@scope.us) promise.then => finish() diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 7a45338e..fb7a52ee 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -138,9 +138,9 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) delete: -> # TODO: i18n title = "Delete Wiki Page" - subtitle = unslugify(@scope.wiki.slug) + message = unslugify(@scope.wiki.slug) - @confirm.ask(title, subtitle).then (finish) => + @confirm.askOnDelete(title, message).then (finish) => onSuccess = => finish() ctx = {project: @scope.projectSlug} diff --git a/app/coffee/modules/wiki/nav.coffee b/app/coffee/modules/wiki/nav.coffee index 2f4530ee..4daf00bd 100644 --- a/app/coffee/modules/wiki/nav.coffee +++ b/app/coffee/modules/wiki/nav.coffee @@ -107,9 +107,9 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $l # TODO: i18n title = "Delete Wiki Link" - subtitle = $scope.wikiLinks[linkId].title + message = $scope.wikiLinks[linkId].title - $confirm.ask(title, subtitle).then (finish) => + $confirm.askOnDelete(title, message).then (finish) => promise = $tgrepo.remove($scope.wikiLinks[linkId]) promise.then -> promise = $ctrl.loadWikiLinks() diff --git a/app/index.jade b/app/index.jade index bd7e4d4f..87f42db2 100644 --- a/app/index.jade +++ b/app/index.jade @@ -21,8 +21,8 @@ html(lang="en", ng-app="taiga") div.master(ng-view) - div.hidden.lightbox.lightbox-confirm-delete - include partials/views/modules/lightbox-confirm-delete + div.hidden.lightbox.lightbox-generic-ask + include partials/views/modules/lightbox-generic-ask div.hidden.lightbox.lightbox-ask-choice include partials/views/modules/lightbox-ask-choice div.hidden.lightbox.lightbox-generic-success diff --git a/app/partials/views/modules/help-notions/lightbox-generic-notion.jade b/app/partials/views/modules/help-notions/lightbox-generic-notion.jade index 6e4222e7..00ce3a93 100644 --- a/app/partials/views/modules/help-notions/lightbox-generic-notion.jade +++ b/app/partials/views/modules/help-notions/lightbox-generic-notion.jade @@ -7,6 +7,6 @@ section block content - div.delete-options + div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/partials/views/modules/lightbox-ask-choice.jade b/app/partials/views/modules/lightbox-ask-choice.jade index f248c246..0dcf1406 100644 --- a/app/partials/views/modules/lightbox-ask-choice.jade +++ b/app/partials/views/modules/lightbox-ask-choice.jade @@ -3,11 +3,11 @@ a.close(href="", title="close") form h2.title Delete User Story p - span.delete-question Are you sure you want to delete? + span.question Are you sure you want to delete? span.subtitle #125 Crear el perfil de usuario senior en el admin span.replacement What value do you want to use as replacement? select.choices - div.delete-options + div.options a.button.button-green(href="", title="Accept") span Accept a.button.button-red(href="", title="Delete") diff --git a/app/partials/views/modules/lightbox-confirm-delete.jade b/app/partials/views/modules/lightbox-confirm-delete.jade deleted file mode 100644 index 388a7160..00000000 --- a/app/partials/views/modules/lightbox-confirm-delete.jade +++ /dev/null @@ -1,12 +0,0 @@ -a.close(href="", title="close") - span.icon.icon-delete -form - h2.title Delete User Story - p - span.delete-question Are you sure you want to delete? - span.subtitle #125 Crear el perfil de usuario senior en el admin - div.delete-options - a.button.button-green(href="", title="Accept") - span Accept - a.button.button-red(href="", title="Delete") - span Cancel \ No newline at end of file diff --git a/app/partials/views/modules/lightbox-delete-account.jade b/app/partials/views/modules/lightbox-delete-account.jade index 0fb4ab2b..bd7c15af 100644 --- a/app/partials/views/modules/lightbox-delete-account.jade +++ b/app/partials/views/modules/lightbox-delete-account.jade @@ -3,9 +3,9 @@ a.close(href="", title="close") form h2.title Delete Taiga Account p - span.delete-question Are you sure you want to delete your Taiga account? + span.question Are you sure you want to delete your Taiga account? span.subtitle We're going to miss you! :-( - div.delete-options + div.options a.button.button-green(href="", title="Accept") span Accept a.button.button-red(href="", title="Cancel") diff --git a/app/partials/views/modules/lightbox-delete-project.jade b/app/partials/views/modules/lightbox-delete-project.jade index 63749499..b05fb346 100644 --- a/app/partials/views/modules/lightbox-delete-project.jade +++ b/app/partials/views/modules/lightbox-delete-project.jade @@ -3,9 +3,9 @@ a.close(href="", title="close") form h2.title Delete project p - span.delete-question Are you sure you want to delete this project? + span.question Are you sure you want to delete this project? span.subtitle All project data US/Tasks/Issues/Sprints/WikiPages will be lost! :-( - div.delete-options + div.options a.button.button-green(href="", title="Yes, I'm really sure") span Yes, I'm really sure a.button.button-red(href="", title="Cancel") diff --git a/app/partials/views/modules/lightbox-generic-ask.jade b/app/partials/views/modules/lightbox-generic-ask.jade new file mode 100644 index 00000000..af346d15 --- /dev/null +++ b/app/partials/views/modules/lightbox-generic-ask.jade @@ -0,0 +1,12 @@ +a.close(href="", title="close") + span.icon.icon-delete +form + h2.title + p + span.subtitle + span.message + div.options + a.button.button-green(href="", title="Accept") + span Accept + a.button.button-red(href="", title="Delete") + span Cancel diff --git a/app/partials/views/modules/lightbox-generic-error.jade b/app/partials/views/modules/lightbox-generic-error.jade index db878226..e8277d96 100644 --- a/app/partials/views/modules/lightbox-generic-error.jade +++ b/app/partials/views/modules/lightbox-generic-error.jade @@ -2,6 +2,6 @@ a.close(href="", title="close") span.icon.icon-delete section h2.title - div.delete-options + div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/partials/views/modules/lightbox-generic-success.jade b/app/partials/views/modules/lightbox-generic-success.jade index db878226..e8277d96 100644 --- a/app/partials/views/modules/lightbox-generic-success.jade +++ b/app/partials/views/modules/lightbox-generic-success.jade @@ -2,6 +2,6 @@ a.close(href="", title="close") span.icon.icon-delete section h2.title - div.delete-options + div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index d3e57bbb..46d83ba5 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -283,12 +283,12 @@ } } -.lightbox-confirm-delete { +.lightbox-generic-ask { form { @include table-flex-child(0, 420px, 0, 420px); } - .delete-question, - .subtitle { + .subtitle, + .message { display: block; line-height: 2rem; text-align: center; @@ -297,7 +297,7 @@ @extend %large; @extend %title; } - .delete-options { + .options { @include table-flex(); a { @include table-flex-child(1, 0, 0); @@ -314,7 +314,7 @@ form { @include table-flex-child(0, 420px, 0, 420px); } - .delete-question, + .question, .subtitle { display: block; line-height: 2rem; @@ -328,7 +328,7 @@ display: block; text-align: center; } - .delete-options { + .options { @include table-flex(); a { @include table-flex-child(1, 0, 0); @@ -345,7 +345,7 @@ form { @include table-flex-child(0, 420px, 0, 420px); } - .delete-question, + .question, .subtitle { display: block; line-height: 2rem; @@ -372,7 +372,7 @@ form { @include table-flex-child(0, 420px, 0, 420px); } - .delete-question, + .question, .subtitle { display: block; line-height: 2rem; @@ -382,7 +382,7 @@ @extend %large; @extend %title; } - .newsletter-delete { + .newsletter { margin-top: 1rem; text-align: center; input { @@ -393,7 +393,7 @@ } } } - .delete-options { + .options { @include table-flex(); a { @include table-flex-child(1, 0, 0); @@ -410,7 +410,7 @@ form { @include table-flex-child(0, 420px, 0, 420px); } - .delete-question, + .question, .subtitle { display: block; line-height: 2rem; @@ -420,7 +420,7 @@ @extend %large; @extend %title; } - .delete-options { + .options { @include table-flex(); a { @include table-flex-child(1, 0, 0); From d81b0d445b7da9b2547b4a765250b2d3ed9dda5b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Oct 2014 10:32:50 +0200 Subject: [PATCH 013/164] Adding raven requirement --- bower.json | 3 ++- gulpfile.coffee | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index d01e3285..10a70219 100644 --- a/bower.json +++ b/bower.json @@ -72,7 +72,8 @@ "favico.js": "0.3.4", "Sortable": "~0.1.8", "pikaday": "~1.2.0", - "malihu-custom-scrollbar-plugin": "~3.0.4" + "malihu-custom-scrollbar-plugin": "~3.0.4", + "raven-js": "~1.1.16" }, "resolutions": { "lodash": "~2.4.1", diff --git a/gulpfile.coffee b/gulpfile.coffee index 56127693..4a495b0c 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -85,6 +85,7 @@ paths.js = [ paths.app + "vendor/jquery-textcomplete/jquery.textcomplete.js", paths.app + "vendor/markitup/markitup/jquery.markitup.js", paths.app + "vendor/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js", + paths.app + "vendor/raven-js/dist/raven.js", paths.app + "js/jquery.ui.git-custom.js", paths.app + "js/jquery-ui.drag-multiple-custom.js", paths.app + "js/sha1-custom.js", From 7b267e0a7bccb6e20dd25128befff339fbde7e8d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Oct 2014 10:51:37 +0200 Subject: [PATCH 014/164] Adding raven logger --- app/coffee/modules/common/raven-logger.coffee | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 app/coffee/modules/common/raven-logger.coffee diff --git a/app/coffee/modules/common/raven-logger.coffee b/app/coffee/modules/common/raven-logger.coffee new file mode 100644 index 00000000..f0f9d415 --- /dev/null +++ b/app/coffee/modules/common/raven-logger.coffee @@ -0,0 +1,41 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# File: modules/common/raven-logger.coffee +### + + +taiga = @.taiga + +module = angular.module("taigaCommon") + +ExceptionHandlerFactory = ($log, @config) -> + ravenConfig = @config.get("ravenConfig", null) + if ravenConfig + $log.debug "Using the RavenJS exception handler." + Raven.config(ravenConfig).install() + return (exception, cause) -> + $log.error.apply($log, arguments) + Raven.captureException(exception) + + else + $log.debug "Using the default logging exception handler." + return (exception, cause) -> + $log.error.apply($log, arguments) + +module.factory("$exceptionHandler", ["$log", "$tgConfig", ExceptionHandlerFactory]) From 7a00157aa71e09c3559c991f846567a5477c777e Mon Sep 17 00:00:00 2001 From: Juanfran Date: Tue, 21 Oct 2014 11:56:34 +0200 Subject: [PATCH 015/164] generate js sourcemaps in the app-deploy --- gulpfile.coffee | 11 ++++++++--- package.json | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/gulpfile.coffee b/gulpfile.coffee index 4a495b0c..74d86bda 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -1,6 +1,6 @@ gulp = require("gulp") jade = require("gulp-jade") - +gutil = require("gulp-util") coffee = require("gulp-coffee") concat = require("gulp-concat") uglify = require("gulp-uglify") @@ -19,6 +19,7 @@ scsslint = require("gulp-scss-lint") newer = require("gulp-newer") cache = require("gulp-cached") jadeInheritance = require('gulp-jade-inheritance') +sourcemaps = require('gulp-sourcemaps') paths = {} paths.app = "app/" @@ -201,8 +202,10 @@ gulp.task "jslibs-watch", -> gulp.task "jslibs-deploy", -> gulp.src(paths.js) .pipe(plumber()) + .pipe(sourcemaps.init()) .pipe(concat("libs.js")) .pipe(uglify({mangle:false, preserveComments: false})) + .pipe(sourcemaps.write('./')) .pipe(gulp.dest("dist/js/")) gulp.task "app-watch", ["coffee", "conf", "locales"], -> @@ -224,8 +227,10 @@ gulp.task "app-deploy", ["coffee", "conf", "locales"], -> ] gulp.src(_paths) - .pipe(concat("app.js")) - .pipe(uglify({mangle:false, preserveComments: false})) + .pipe(sourcemaps.init()) + .pipe(concat("app.js")) + .pipe(uglify({mangle:false, preserveComments: false})) + .pipe(sourcemaps.write('./')) .pipe(gulp.dest(paths.dist + "js/")) ############################################################################## diff --git a/package.json b/package.json index 47a2c2ee..5deac6ac 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "gulp-rename": "^1.2.0", "gulp-ruby-sass": "^0.4.3", "gulp-scss-lint": "0.1.1", + "gulp-sourcemaps": "^1.2.4", "gulp-styledocco": "0.0.1", "gulp-template": "^0.1.1", "gulp-uglify": "~0.2.0", From 6c0423da807cf2a20e6d8bcd88a7dbd9529982a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 22 Oct 2014 13:15:34 +0200 Subject: [PATCH 016/164] Add US #1054 to the changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acad2b7a..f073389a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ ### Features - Multiple User stories Drag & Drop in the backlog. - Add visual difference to closed USs in backlog panel. -- Added beta ribbon. +- Show crerated date of attachments in the hover of the filename. +- Add beta ribbon. ### Misc - Lots of small and not so small bugfixes From 2f7430117d8360046508b6d175bba38f269b8f36 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 16:24:01 +0200 Subject: [PATCH 017/164] redirect after delete an us or task --- app/coffee/modules/tasks/detail.coffee | 7 ++++++- app/coffee/modules/userstories/detail.coffee | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 7778b193..7592c34b 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -140,7 +140,12 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.repo.remove(@scope.task) promise.then => finish() - @location.path(@navUrls.resolve("project-backlog", {project: @scope.project.slug})) + + 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") diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index aac5f273..a944ec43 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -152,7 +152,13 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.repo.remove(@scope.us) promise.then => finish() - @location.path(@navUrls.resolve("project-backlog", {project: @scope.project.slug})) + + 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") From 4035766a7061d36088e4d60382a5f70ee99d75e8 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 23 Oct 2014 09:45:02 +0200 Subject: [PATCH 018/164] fix #1454 --- app/coffee/modules/auth.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/coffee/modules/auth.coffee b/app/coffee/modules/auth.coffee index 939d290e..294a9e83 100644 --- a/app/coffee/modules/auth.coffee +++ b/app/coffee/modules/auth.coffee @@ -225,7 +225,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics) $location.replace() $scope.data = {} - form = $el.find("form").checksley() + form = $el.find("form").checksley({onlyOneErrorElement: true}) onSuccessSubmit = (response) -> $analytics.trackEvent("auth", "register", "user registration", 1) From b1341311b4956f03b29f279ddd5b5fa0eaedd77b Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 23 Oct 2014 10:51:06 +0200 Subject: [PATCH 019/164] fix #1453 - fix double click on auth forms --- app/coffee/modules/auth.coffee | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/coffee/modules/auth.coffee b/app/coffee/modules/auth.coffee index 294a9e83..652b8aa3 100644 --- a/app/coffee/modules/auth.coffee +++ b/app/coffee/modules/auth.coffee @@ -20,6 +20,7 @@ ### taiga = @.taiga +debounce = @.taiga.debounce module = angular.module("taigaAuth", ["taigaResources"]) @@ -238,7 +239,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics) form.setErrors(response.data) - submit = -> + submit = debounce 2000, => if not form.validate() return @@ -278,7 +279,7 @@ ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls) -> $confirm.notify("light-error", "According to our Oompa Loompas, your are not registered yet.") #TODO: i18n - submit = -> + submit = debounce 2000, => if not form.validate() return @@ -323,7 +324,7 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav $confirm.notify("light-error", "One of our Oompa Loompas say '#{response.data._error_message}'.") #TODO: i18n - submit = -> + submit = debounce 2000, => if not form.validate() return @@ -362,7 +363,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics # Login form $scope.dataLogin = {token: token} - loginForm = $el.find("form.login-form").checksley() + loginForm = $el.find("form.login-form").checksley({onlyOneErrorElement: true}) onSuccessSubmitLogin = (response) -> $analytics.trackEvent("auth", "invitationAccept", "invitation accept with existing user", 1) @@ -374,7 +375,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics $confirm.notify("light-error", "According to our Oompa Loompas, your are not registered yet or typed an invalid password.") #TODO: i18n - submitLogin = -> + submitLogin = debounce 2000, => if not loginForm.validate() return @@ -403,7 +404,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics $confirm.notify("light-error", "According to our Oompa Loompas, that username or email is already in use.") #TODO: i18n - submitRegister = -> + submitRegister = debounce 2000, => if not registerForm.validate() return From a3d1c4bc65c200cf1f7ac1afebe573de79bbeb96 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 08:54:38 +0200 Subject: [PATCH 020/164] fix #1397 - rename role --- app/coffee/modules/admin/roles.coffee | 37 +++++++++++++++++++++++ app/partials/admin-roles.jade | 13 ++++++-- app/styles/modules/admin/admin-roles.scss | 14 +++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index 423a0ecb..72fbf60f 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -123,6 +123,43 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil module.controller("RolesController", RolesController) +EditRoleDirective = ($repo, $confirm) -> + link = ($scope, $el, $attrs) -> + toggleView = -> + $el.find('.total').toggle() + $el.find('.edit-role').toggle() + + submit = () -> + $scope.role.name = $el.find("input").val() + + promise = $repo.save($scope.role) + + promise.then -> + $confirm.notify("success") + + promise.then null, (data) -> + $confirm.notify("error") + + toggleView() + + $el.on "click", "a.icon-edit", -> + toggleView() + $el.find("input").focus() + + $el.on "click", "a.save", submit + + $el.on "keyup", "input", -> + if event.keyCode == 13 # Enter key + submit() + else if event.keyCode == 27 # ESC key + toggleView() + + $scope.$on "$destroy", -> + $el.off() + + return {link:link} + +module.directive("tgEditRole", ["$tgRepo", "$tgConfirm", EditRoleDirective]) RolesDirective = -> link = ($scope, $el, $attrs) -> diff --git a/app/partials/admin-roles.jade b/app/partials/admin-roles.jade index 900332e5..acdb715c 100644 --- a/app/partials/admin-roles.jade +++ b/app/partials/admin-roles.jade @@ -17,9 +17,16 @@ block content .action-buttons a.button.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()") Delete - p.total - | {{ role.name }} - span ({{ role.members_count }} members with this role) + + div(tg-edit-role) + .edit-role.hidden + input(type="text", value="{{ role.name }}") + a.save.icon.icon-floppy(href="", title="Save") + + p.total + | {{ role.name }} + a.edit-value.icon.icon-edit + span ({{ role.members_count }} members with this role) div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 1ae92901..4bd3839b 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -1,4 +1,18 @@ .admin-roles { + .edit-value { + cursor: pointer; + margin-left: .5rem; + } + .edit-role { + @include table-flex(stretch, left, center, row, wrap); + margin-bottom: 1rem; + input { + width: auto; + } + .icon-floppy { + margin-left: .5rem; + } + } .total { @extend %large; @extend %title; From f2eeb11a606df3d8647d09d0a75b4b5275e47ba4 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 08:54:38 +0200 Subject: [PATCH 021/164] fix #1397 - rename role --- app/partials/views/modules/lightbox-ask-choice.jade | 1 + app/styles/modules/admin/admin-roles.scss | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/partials/views/modules/lightbox-ask-choice.jade b/app/partials/views/modules/lightbox-ask-choice.jade index 0dcf1406..1fd43298 100644 --- a/app/partials/views/modules/lightbox-ask-choice.jade +++ b/app/partials/views/modules/lightbox-ask-choice.jade @@ -7,6 +7,7 @@ form span.subtitle #125 Crear el perfil de usuario senior en el admin span.replacement What value do you want to use as replacement? select.choices + p if the role is deleted all role estimates will be deleted div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 4bd3839b..9bcbcb46 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -5,8 +5,16 @@ } .edit-role { @include table-flex(stretch, left, center, row, wrap); +<<<<<<< HEAD margin-bottom: 1rem; input { +======= + background-color: $whitish; + margin-bottom: 1rem; + padding: .5rem; + input { + background-color: $white; +>>>>>>> fix #1397 - rename role width: auto; } .icon-floppy { @@ -18,7 +26,7 @@ @extend %title; background-color: $whitish; color: $grayer; - padding: .5rem 1rem; + padding: 1rem; span { @extend %medium; @extend %text; From ebe5f4a4d6e155fbf16f0f013fae1f688b6aed77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Thu, 23 Oct 2014 10:01:40 +0200 Subject: [PATCH 022/164] Admin roles design and UX --- app/partials/admin-roles.jade | 3 +- app/styles/layout/base.scss | 12 ++++++- app/styles/layout/wiki.scss | 10 ------ app/styles/modules/admin/admin-roles.scss | 44 ++++++++++++++++------- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/app/partials/admin-roles.jade b/app/partials/admin-roles.jade index acdb715c..2e7e8091 100644 --- a/app/partials/admin-roles.jade +++ b/app/partials/admin-roles.jade @@ -24,9 +24,8 @@ block content a.save.icon.icon-floppy(href="", title="Save") p.total - | {{ role.name }} + span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }} a.edit-value.icon.icon-edit - span ({{ role.members_count }} members with this role) div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories diff --git a/app/styles/layout/base.scss b/app/styles/layout/base.scss index 9dc54351..f9c50732 100644 --- a/app/styles/layout/base.scss +++ b/app/styles/layout/base.scss @@ -129,7 +129,17 @@ body { margin-bottom: 1rem; .action-buttons { @include flex-shrink(0); - padding-left: 1rem; + } + .button { + color: $white; + float: right; + margin-left: 10px; + &:first-child { + margin-left: 0; + } + &:hover { + color: $white; + } } h1 { margin-bottom: 0; diff --git a/app/styles/layout/wiki.scss b/app/styles/layout/wiki.scss index 743f82da..d8cfd8fb 100644 --- a/app/styles/layout/wiki.scss +++ b/app/styles/layout/wiki.scss @@ -1,13 +1,3 @@ .wiki-content { margin-bottom: 2rem; } -.action-buttons { - .button { - color: $white; - float: right; - margin-left: 10px; - &:hover { - color: $white; - } - } -} diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 9bcbcb46..2907d0e7 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -1,36 +1,56 @@ .admin-roles { + .total { + background-color: $whitish; + padding: 1rem; + &:hover { + .edit-value { + @include transition(opacity .3s linear); + opacity: 1; + } + } + } + .role-name { + @extend %xlarge; + @extend %title; + color: $grayer; + } .edit-value { + @include transition(opacity .3s linear); + @extend %medium; + color: $gray-light; cursor: pointer; margin-left: .5rem; + opacity: 0; } .edit-role { @include table-flex(stretch, left, center, row, wrap); +<<<<<<< HEAD <<<<<<< HEAD margin-bottom: 1rem; input { ======= +======= +>>>>>>> Admin roles design and UX background-color: $whitish; margin-bottom: 1rem; padding: .5rem; input { background-color: $white; +<<<<<<< HEAD >>>>>>> fix #1397 - rename role width: auto; +======= + width: 50%; +>>>>>>> Admin roles design and UX } .icon-floppy { + @include transition(color.3s linear); + color: $gray-light; margin-left: .5rem; - } - } - .total { - @extend %large; - @extend %title; - background-color: $whitish; - color: $grayer; - padding: 1rem; - span { - @extend %medium; - @extend %text; - padding-left: .5rem; + &:hover { + @include transition(color.3s linear); + color: $green-taiga; + } } } .any-computable-role { From b4bc65a00de24d2969bef7d7ea37d50a43096f24 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 23 Oct 2014 11:04:15 +0200 Subject: [PATCH 023/164] close edit view when role changes --- app/coffee/modules/admin/roles.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index 72fbf60f..aa452b01 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -154,6 +154,10 @@ EditRoleDirective = ($repo, $confirm) -> else if event.keyCode == 27 # ESC key toggleView() + $scope.$on "role:changed", -> + if $el.find('.edit-role').is(":visible") + toggleView() + $scope.$on "$destroy", -> $el.off() From ebb000ea4b13e5e56a31329378364cd67acfcc23 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 08:54:38 +0200 Subject: [PATCH 024/164] fix #1397 - rename role --- app/partials/views/modules/lightbox-ask-choice.jade | 11 +++++------ app/styles/modules/admin/admin-roles.scss | 12 ++++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/partials/views/modules/lightbox-ask-choice.jade b/app/partials/views/modules/lightbox-ask-choice.jade index 1fd43298..f539263c 100644 --- a/app/partials/views/modules/lightbox-ask-choice.jade +++ b/app/partials/views/modules/lightbox-ask-choice.jade @@ -2,12 +2,11 @@ a.close(href="", title="close") span.icon.icon-delete form h2.title Delete User Story - p - span.question Are you sure you want to delete? - span.subtitle #125 Crear el perfil de usuario senior en el admin - span.replacement What value do you want to use as replacement? - select.choices - p if the role is deleted all role estimates will be deleted + p.question Are you sure you want to delete? + p.subtitle #125 Crear el perfil de usuario senior en el admin + p.replacement What value do you want to use as replacement? + select.choices + p if the role is deleted all role estimates will be deleted div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 2907d0e7..cd3e3593 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -53,6 +53,18 @@ } } } + .total { + @extend %large; + @extend %title; + background-color: $whitish; + color: $grayer; + padding: 1rem; + span { + @extend %medium; + @extend %text; + padding-left: .5rem; + } + } .any-computable-role { background: $red; color: $white; From 5614f763a72a11c356a30bca155506eb3695677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Thu, 23 Oct 2014 12:39:02 +0200 Subject: [PATCH 025/164] Project values lightbox refactor --- .../modules/admin/project-values.coffee | 5 ++-- app/coffee/modules/admin/roles.coffee | 8 ++++--- app/coffee/modules/common/confirm.coffee | 23 ++++++++++++++----- .../views/modules/lightbox-ask-choice.jade | 11 +++++---- app/styles/modules/common/lightbox.scss | 9 +++++--- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index 72351515..9399e392 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -260,12 +260,13 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) -> choices[option.id] = option.name #TODO: i18n - title = "Delete" + title = "Delete value" subtitle = value.name + replacement = "All items with this value will be changed to" if _.keys(choices).length == 0 return $confirm.error("You can't delete all values.") - return $confirm.askChoice(title, subtitle, choices).then (response) -> + return $confirm.askChoice(title, subtitle, choices, replacement).then (response) -> onSucces = -> $ctrl.loadValues().finally -> response.finish() diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index aa452b01..cf2d6bd7 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -89,8 +89,10 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil delete: -> # TODO: i18n - title = "Delete Role" + title = "Delete Role" # TODO: i18n subtitle = @scope.role.name + replacement = "All the users with this role will be moved to" # TODO: i18n + warning = "Be careful, all role estimations will be removed" # TODO: i18n choices = {} for role in @scope.roles @@ -98,9 +100,9 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil choices[role.id] = role.name if _.keys(choices).length == 0 - return @confirm.error("You can't delete all values.") + return @confirm.error("You can't delete all values.") # TODO: i18n - return @confirm.askChoice(title, subtitle, choices).then (response) => + return @confirm.askChoice(title, subtitle, choices, replacement, warning).then (response) => promise = @repo.remove(@scope.role, {moveTo: response.selected}) promise.then => @.loadProject() diff --git a/app/coffee/modules/common/confirm.coffee b/app/coffee/modules/common/confirm.coffee index d52ce5d4..81679f42 100644 --- a/app/coffee/modules/common/confirm.coffee +++ b/app/coffee/modules/common/confirm.coffee @@ -82,13 +82,24 @@ class ConfirmService extends taiga.Service askOnDelete: (title, message) -> return @.ask(title, "Are you sure you want to delete?", message) #TODO: i18n - askChoice: (title, subtitle, choices, lightboxSelector=".lightbox-ask-choice") -> + askChoice: (title, subtitle, choices, replacement, warning, lightboxSelector=".lightbox-ask-choice") -> el = angular.element(lightboxSelector) # Render content - el.find("h2.title").html(title) - el.find("span.subtitle").html(subtitle) - choicesField = el.find("select.choices") + el.find(".title").html(title) + el.find(".subtitle").html(subtitle) + + if replacement + el.find(".replacement").html(replacement) + else + el.find(".replacement").remove() + + if warning + el.find(".warning").html(warning) + else + el.find(".warning").remove() + + choicesField = el.find(".choices") choicesField.html('') _.each choices, (value, key) -> choicesField.append(angular.element("")) @@ -168,9 +179,9 @@ class ConfirmService extends taiga.Service el = angular.element(selector) if title - el.find("h4").html(title) + el.find("h4").html(title) else - el.find("h4").html(NOTIFICATION_MSG[type].title) + el.find("h4").html(NOTIFICATION_MSG[type].title) if message el.find("p").html(message) diff --git a/app/partials/views/modules/lightbox-ask-choice.jade b/app/partials/views/modules/lightbox-ask-choice.jade index f539263c..4e69be3b 100644 --- a/app/partials/views/modules/lightbox-ask-choice.jade +++ b/app/partials/views/modules/lightbox-ask-choice.jade @@ -1,12 +1,13 @@ a.close(href="", title="close") span.icon.icon-delete form - h2.title Delete User Story - p.question Are you sure you want to delete? - p.subtitle #125 Crear el perfil de usuario senior en el admin - p.replacement What value do you want to use as replacement? + h2.title + p.question + p.subtitle + p.replacement select.choices - p if the role is deleted all role estimates will be deleted + p.warning + div.options a.button.button-green(href="", title="Accept") span Accept diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 46d83ba5..a771c4f3 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -311,13 +311,14 @@ } .lightbox-ask-choice { + text-align: center; form { - @include table-flex-child(0, 420px, 0, 420px); + @include table-flex-child(0, 420px, 0); } .question, .subtitle { display: block; - line-height: 2rem; + line-height: 1.5rem; text-align: center; } .subtitle { @@ -326,7 +327,9 @@ } .replacement { display: block; - text-align: center; + span { + display: block; + } } .options { @include table-flex(); From f9b102527da61b63217cfbed62d21ff0b11fc59e Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 23 Oct 2014 11:04:15 +0200 Subject: [PATCH 026/164] Fix total header in admin roles --- app/styles/modules/admin/admin-roles.scss | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index cd3e3593..0a713da8 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -1,6 +1,9 @@ .admin-roles { .total { + @extend %large; + @extend %title; background-color: $whitish; + color: $grayer; padding: 1rem; &:hover { .edit-value { @@ -53,18 +56,6 @@ } } } - .total { - @extend %large; - @extend %title; - background-color: $whitish; - color: $grayer; - padding: 1rem; - span { - @extend %medium; - @extend %text; - padding-left: .5rem; - } - } .any-computable-role { background: $red; color: $white; From 3eb76b5566dec2d1c6cba3426bfceac91e39e34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Thu, 23 Oct 2014 13:21:13 +0200 Subject: [PATCH 027/164] =?UTF-8?q?Rrebohluci=C3=B3nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/styles/modules/admin/admin-roles.scss | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/styles/modules/admin/admin-roles.scss b/app/styles/modules/admin/admin-roles.scss index 0a713da8..fb5b000f 100644 --- a/app/styles/modules/admin/admin-roles.scss +++ b/app/styles/modules/admin/admin-roles.scss @@ -27,24 +27,12 @@ } .edit-role { @include table-flex(stretch, left, center, row, wrap); -<<<<<<< HEAD -<<<<<<< HEAD - margin-bottom: 1rem; - input { -======= -======= ->>>>>>> Admin roles design and UX background-color: $whitish; margin-bottom: 1rem; padding: .5rem; input { background-color: $white; -<<<<<<< HEAD ->>>>>>> fix #1397 - rename role - width: auto; -======= width: 50%; ->>>>>>> Admin roles design and UX } .icon-floppy { @include transition(color.3s linear); From 016786709e79bdc748422da5daed4e0cc622653f Mon Sep 17 00:00:00 2001 From: Juanfran Date: Mon, 27 Oct 2014 16:10:35 +0100 Subject: [PATCH 028/164] improve scss compile performance Now only scss-compile and css-lint the modified file. --- app/styles/components/buttons.scss | 20 +- app/styles/components/popover.scss | 41 ---- app/styles/components/spinner.scss | 1 - app/styles/dependencies/helpers.scss | 213 +++++++----------- app/styles/dependencies/mixins.scss | 46 +++- app/styles/extras/dependencies.scss | 16 ++ .../{dependencies => layout}/animation.scss | 0 app/styles/layout/base.scss | 15 ++ .../{dependencies => layout}/elements.scss | 12 - .../{dependencies => layout}/forms.scss | 0 app/styles/layout/mail-notifications.scss | 0 app/styles/layout/reset.scss | 135 +++++++++++ .../{dependencies => layout}/typography.scss | 16 -- app/styles/main.scss | 149 ------------ app/styles/modules/backlog/backlog-table.scss | 2 +- app/styles/modules/common/lightbox.scss | 45 +--- app/styles/modules/filters/filters.scss | 20 -- ...notion-admin-project-values-us-points.scss | 0 gulpfile.coffee | 88 +++++--- main-sass.js | 144 ++++++++++++ package.json | 8 +- 21 files changed, 504 insertions(+), 467 deletions(-) delete mode 100644 app/styles/components/popover.scss delete mode 100644 app/styles/components/spinner.scss create mode 100644 app/styles/extras/dependencies.scss rename app/styles/{dependencies => layout}/animation.scss (100%) rename app/styles/{dependencies => layout}/elements.scss (88%) rename app/styles/{dependencies => layout}/forms.scss (100%) delete mode 100644 app/styles/layout/mail-notifications.scss create mode 100644 app/styles/layout/reset.scss rename app/styles/{dependencies => layout}/typography.scss (90%) delete mode 100755 app/styles/main.scss delete mode 100644 app/styles/modules/help/notion-admin-project-values-us-points.scss create mode 100644 main-sass.js diff --git a/app/styles/components/buttons.scss b/app/styles/components/buttons.scss index 957ebaa0..f5a060e1 100755 --- a/app/styles/components/buttons.scss +++ b/app/styles/components/buttons.scss @@ -13,26 +13,8 @@ } } -%button, .button { - @extend %medium; - @extend %title; - @include transition (background .3s linear); - display: inline-block; - padding: 7px 40px 6px; - text-transform: uppercase; - &:hover { - @include transition (background .3s linear); - } - &.loading { - span { - @include animation (loading .5s linear); - @include animation (spin 1s linear infinite); - } - } - .icon { - margin-right: .3rem; - } + @extend %button; } a.button-green { diff --git a/app/styles/components/popover.scss b/app/styles/components/popover.scss deleted file mode 100644 index 88175da3..00000000 --- a/app/styles/components/popover.scss +++ /dev/null @@ -1,41 +0,0 @@ -@mixin popover($width, $top: '', $left: '', $bottom: '', $right: '', $arrow-width: 0, $arrow-top: '', $arrow-left: '', $arrow-bottom: '') { - @extend %text; - background: $blackish; - bottom: #{$bottom}; - color: $white; - display: none; - left: #{$left}; - list-style-type: none; - margin: 0; - padding: 10px; - position: absolute; - right: #{$right}; - top: #{$top}; - width: $width; - z-index: 99; - a { - @extend %small; - border-bottom: 1px solid $grayer; - color: $white; - display: block; - padding: 10px 2px; - &:last-child { - border: 0; - } - &:hover { - color: $fresh-taiga; - @include transition (color .3s linear); - } - } - &:after { - @include transform(rotate(45deg)); - background: $blackish; - bottom: #{$arrow-bottom}; - content: ''; - height: 15px; - left: #{$arrow-left}; - position: absolute; - top: #{$arrow-top}; - width: #{$arrow-width}; - } -} diff --git a/app/styles/components/spinner.scss b/app/styles/components/spinner.scss deleted file mode 100644 index 8b137891..00000000 --- a/app/styles/components/spinner.scss +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/styles/dependencies/helpers.scss b/app/styles/dependencies/helpers.scss index 667f4ab3..300e1a70 100644 --- a/app/styles/dependencies/helpers.scss +++ b/app/styles/dependencies/helpers.scss @@ -1,8 +1,3 @@ -// FTW -* { - box-sizing: border-box; -} - //Flexbox FTW %table-flex { align-content: stretch; @@ -20,132 +15,94 @@ width: 300px; } -// #Reset & Basics (Inspired by E. Meyers) -//================================================== -a, -abbr, -acronym, -address, -applet, -article, -aside, -audio, -b, -big, -blockquote, -body, -canvas, -caption, -center, -cite, -code, -dd, -del, -details, -dfn, -div, -dl, -dt, -em, -embed, -fieldset, -figcaption, -figure, -footer, -form, -h1, -h2, -h3, -h4, -h5, -h6, -header, -hgroup, -html, -i, -iframe, -img, -ins, -kbd, -label, -legend, -li, -mark, -menu, -nav, -object, -ol, -output, -p, -pre, -q, -ruby, -s, -samp, -section, -small, -span, -strike, -strong, -sub, -summary, -sup, -table, -tbody, -td, -tfoot, -th, -thead, -time, -tr, -tt, -u, -ul, -var, -video { - border: 0; - font: inherit; - font-size: 100%; - margin: 0; - padding: 0; - vertical-align: baseline; + +// __Font Sizes__ // +%xsmall {font-size: .5rem;} +%small {font-size: .8rem;} +%medium {font-size: 1rem;} +%large {font-size: 1.2rem;} +%larger {font-size: 1.6rem;} +%xlarge {font-size: 2rem;} +%xxlarge {font-size: 3rem;} + +// __Font Types__ // +%title {font-family: 'OpenSans-CondLight';} +%text {font-family: 'opensans-regular'; line-height: 1.3rem;} +%bold {font-family: 'opensans-semibold';} +%taiga {font-family: 'taiga';} + +%lightbox { + background: rgba($white, .95); + bottom: 0; + display: none; + left: 0; + opacity: 0; + position: fixed; + right: 0; + top: 0; + z-index: 99910; + .close { + @extend %large; + position: absolute; + right: 2rem; + top: 2rem; + } + &.open { + @include table-flex(center, center, flex, row, wrap, center); + @include transition (opacity .3s ease); + opacity: 1; + } + &.close { + @include transition (opacity .3s ease); + opacity: 0; + } + .title { + text-align: center; + } + input, + textarea, + select { + margin-bottom: 1rem; + } + textarea { + resize: vertical; + } + .button-green, + .button-gray { + display: block; + padding: 12px; + text-align: center; + } } -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -menu, -nav, -section { - display: block; -} -body { - line-height: 1; +%button { + @extend %medium; + @extend %title; + @include transition (background .3s linear); + display: inline-block; + padding: 7px 40px 6px; + text-transform: uppercase; + &:hover { + @include transition (background .3s linear); + } + &.loading { + span { + @include animation (loading .5s linear); + @include animation (spin 1s linear infinite); + } + } + .icon { + margin-right: .3rem; + } } -ol, -ul { - list-style: none; +// Background +%triangled-bg { + background: url('/images/bg.png') no-repeat center center; + background-size: cover; } -blockquote, -q { - quotes: none; -} - -blockquote:before, -blockquote:after, -q:before, -q:after { - content: ''; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} +%background-taiga { + background: url('/images/invitation_bg.jpg') no-repeat center center; + background-size: cover; +} \ No newline at end of file diff --git a/app/styles/dependencies/mixins.scss b/app/styles/dependencies/mixins.scss index b58588c9..9e92e00a 100644 --- a/app/styles/dependencies/mixins.scss +++ b/app/styles/dependencies/mixins.scss @@ -5,10 +5,6 @@ text-overflow: ellipsis; } -@mixin background-opacity($color: $white, $opacity: .3) { - background: rgba($color, $opacity); -} - // Table Flex - http://devbryce.com/site/flexbox/ @mixin table-flex($align-content: stretch, $align-items: stretch, $display: flex, $flex-direction: row, $flex-wrap: wrap, $justify-content: flex-start) { @include display($display); @@ -54,3 +50,45 @@ @mixin background($red: 255, $green: 255, $blue: 255, $opacity: 1) { background: rgba($red, $green, $blue, $opacity); } + +@mixin popover($width, $top: '', $left: '', $bottom: '', $right: '', $arrow-width: 0, $arrow-top: '', $arrow-left: '', $arrow-bottom: '') { + @extend %text; + background: $blackish; + bottom: #{$bottom}; + color: $white; + display: none; + left: #{$left}; + list-style-type: none; + margin: 0; + padding: 10px; + position: absolute; + right: #{$right}; + top: #{$top}; + width: $width; + z-index: 99; + a { + @extend %small; + border-bottom: 1px solid $grayer; + color: $white; + display: block; + padding: 10px 2px; + &:last-child { + border: 0; + } + &:hover { + color: $fresh-taiga; + @include transition (color .3s linear); + } + } + &:after { + @include transform(rotate(45deg)); + background: $blackish; + bottom: #{$arrow-bottom}; + content: ''; + height: 15px; + left: #{$arrow-left}; + position: absolute; + top: #{$arrow-top}; + width: #{$arrow-width}; + } +} diff --git a/app/styles/extras/dependencies.scss b/app/styles/extras/dependencies.scss new file mode 100644 index 00000000..89a7026d --- /dev/null +++ b/app/styles/extras/dependencies.scss @@ -0,0 +1,16 @@ +// Bourbon +$prefix-for-webkit: true; +$prefix-for-mozilla: true; +$prefix-for-microsoft: true; +$prefix-for-opera: true; +$prefix-for-spec: true; +@import '../bourbon/bourbon'; + +//################################################# +// dependencies +//################################################# + +@import '../dependencies/colors'; +@import '../dependencies/mixins'; +@import '../dependencies/helpers'; +@import '../dependencies/responsive'; diff --git a/app/styles/dependencies/animation.scss b/app/styles/layout/animation.scss similarity index 100% rename from app/styles/dependencies/animation.scss rename to app/styles/layout/animation.scss diff --git a/app/styles/layout/base.scss b/app/styles/layout/base.scss index f9c50732..2377a961 100644 --- a/app/styles/layout/base.scss +++ b/app/styles/layout/base.scss @@ -98,6 +98,21 @@ body { @include table-flex-child(1, 260px, 0, 260px); background: $whitish; padding: 2rem 1rem; + &.filters-bar { + @include table-flex-child(0, 1px, 0, 1px); + @include transition(all .2s linear); + padding: 0; + &.active { + @include table-flex-child(1, 260px, 0, 260px); + @include transition(all .2s linear); + padding: 2em 1em; + + .filters-inner { + @include transition (all .4s ease-in); + opacity: 1; + } + } + } } .menu-tertiary { diff --git a/app/styles/dependencies/elements.scss b/app/styles/layout/elements.scss similarity index 88% rename from app/styles/dependencies/elements.scss rename to app/styles/layout/elements.scss index 77fd94f7..44f9c1da 100644 --- a/app/styles/dependencies/elements.scss +++ b/app/styles/layout/elements.scss @@ -51,18 +51,6 @@ sup { cursor: move; } -// Background -%triangled-bg { - background: url('/images/bg.png') no-repeat center center; - background-size: cover; -} - -%background-taiga { - background: url('/images/invitation_bg.jpg') no-repeat center center; - background-size: cover; -} - - //Datepicker .pika-single { z-index: 999999; diff --git a/app/styles/dependencies/forms.scss b/app/styles/layout/forms.scss similarity index 100% rename from app/styles/dependencies/forms.scss rename to app/styles/layout/forms.scss diff --git a/app/styles/layout/mail-notifications.scss b/app/styles/layout/mail-notifications.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/app/styles/layout/reset.scss b/app/styles/layout/reset.scss new file mode 100644 index 00000000..99deddc2 --- /dev/null +++ b/app/styles/layout/reset.scss @@ -0,0 +1,135 @@ +// FTW +* { + box-sizing: border-box; +} + + +// #Reset & Basics (Inspired by E. Meyers) +//================================================== +a, +abbr, +acronym, +address, +applet, +article, +aside, +audio, +b, +big, +blockquote, +body, +canvas, +caption, +center, +cite, +code, +dd, +del, +details, +dfn, +div, +dl, +dt, +em, +embed, +fieldset, +figcaption, +figure, +footer, +form, +h1, +h2, +h3, +h4, +h5, +h6, +header, +hgroup, +html, +i, +iframe, +img, +ins, +kbd, +label, +legend, +li, +mark, +menu, +nav, +object, +ol, +output, +p, +pre, +q, +ruby, +s, +samp, +section, +small, +span, +strike, +strong, +sub, +summary, +sup, +table, +tbody, +td, +tfoot, +th, +thead, +time, +tr, +tt, +u, +ul, +var, +video { + border: 0; + font: inherit; + font-size: 100%; + margin: 0; + padding: 0; + vertical-align: baseline; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + line-height: 1; +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/app/styles/dependencies/typography.scss b/app/styles/layout/typography.scss similarity index 90% rename from app/styles/dependencies/typography.scss rename to app/styles/layout/typography.scss index 92dc71b8..bc1edf8c 100755 --- a/app/styles/dependencies/typography.scss +++ b/app/styles/layout/typography.scss @@ -25,21 +25,6 @@ h6 { } } -// __Font Sizes__ // -%xsmall {font-size: .5rem;} -%small {font-size: .8rem;} -%medium {font-size: 1rem;} -%large {font-size: 1.2rem;} -%larger {font-size: 1.6rem;} -%xlarge {font-size: 2rem;} -%xxlarge {font-size: 3rem;} - -// __Font Types__ // -%title {font-family: 'OpenSans-CondLight';} -%text {font-family: 'opensans-regular'; line-height: 1.3rem;} -%bold {font-family: 'opensans-semibold';} -%taiga {font-family: 'taiga';} - h1 { @extend %xxlarge; @extend %title; @@ -255,4 +240,3 @@ a:visited { .icon-stats:before { content: 'L'; } - diff --git a/app/styles/main.scss b/app/styles/main.scss deleted file mode 100755 index 43c1aee0..00000000 --- a/app/styles/main.scss +++ /dev/null @@ -1,149 +0,0 @@ -// THIS IS THE MAIN INCLUDES SASS FILE - -// Bourbon -$prefix-for-webkit: true; -$prefix-for-mozilla: true; -$prefix-for-microsoft: true; -$prefix-for-opera: true; -$prefix-for-spec: true; -@import 'bourbon/bourbon'; - -// Codehilite -@import 'vendor/codehilite.github'; - -//################################################# -// dependencies -//################################################# - -@import 'dependencies/helpers'; -@import 'dependencies/colors'; -@import 'dependencies/typography'; -@import 'dependencies/elements'; -@import 'dependencies/mixins'; -@import 'dependencies/responsive'; -@import 'dependencies/forms'; -@import 'dependencies/animation'; - -//################################################# -// components -//################################################# - -@import 'components/buttons'; -@import 'components/avatar'; -@import 'components/summary'; -@import 'components/popover'; -@import 'components/tag'; -@import 'components/filter'; -@import 'components/taskboard-task'; -@import 'components/kanban-task'; -@import 'components/notification-message'; -@import 'components/basic-table'; -@import 'components/paginator'; -@import 'components/watchers'; -@import 'components/level'; -@import 'components/created-by'; -@import 'components/wysiwyg'; -@import 'components/select-color'; -@import 'components/loader'; -@import 'components/spinner'; -@import 'components/help-notion-button'; -@import 'components/beta'; - -//################################################# -// Modules -//################################################# - -//Common modules -@import 'modules/common/assigned-to'; -@import 'modules/common/nav'; -@import 'modules/common/projects-nav'; -@import 'modules/common/lightbox'; -@import 'modules/common/colors-table'; -@import 'modules/common/category-config'; -@import 'modules/common/attachments'; -@import 'modules/common/related-tasks'; -@import 'modules/common/history'; -@import 'modules/common/wizard'; - -//Project modules -@import 'modules/home-projects-list'; -@import 'modules/home-project'; -@import 'modules/create-project'; - -//Issues modules -@import 'modules/issues/issues-table'; - -//Kanban modules -@import 'modules/kanban/kanban-table'; - -//Search modules -@import 'modules/search/search-filter'; -@import 'modules/search/search-result-table'; -@import 'modules/search/search-in'; - -//Filters modules -@import 'modules/filters/filters'; -@import 'modules/filters/list-filters'; -@import 'modules/filters/filter-tags'; - -//Backlog modules -@import 'modules/backlog/sprints'; -@import 'modules/backlog/burndown'; -@import 'modules/backlog/backlog-table'; -@import 'modules/backlog/taskboard-table'; - -//Login modules -@import 'modules/auth/login-form'; -@import 'modules/auth/register-form'; -@import 'modules/auth/forgot-form'; -@import 'modules/auth/change-password-from-recovery'; - -//Wiki modules -@import 'modules/wiki/wiki-nav'; -@import 'modules/wiki/wiki-summary'; - -//modules admin -@import 'modules/admin/admin-menu'; -@import 'modules/admin/admin-submenu'; -@import 'modules/admin/admin-submenu-roles'; -@import 'modules/admin/admin-roles'; -@import 'modules/admin/admin-functionalities'; -@import 'modules/admin/admin-membership-table'; -@import 'modules/admin/admin-project-profile'; -@import 'modules/admin/default-values'; -@import 'modules/admin/project-values'; - -//Modules user Settings -@import 'modules/user-settings/user-profile'; -@import 'modules/user-settings/user-change-password'; -@import 'modules/user-settings/mail-notifications-table'; - -//################################################# -// Layout -//################################################# - -@import 'layout/base'; -@import 'layout/login'; -@import 'layout/invitation'; -@import 'layout/not-found'; -@import 'layout/backlog'; -@import 'layout/taskboard'; -@import 'layout/us-detail'; -@import 'layout/admin-memberships'; -@import 'layout/admin-project-values'; -@import 'layout/project-colors'; -@import 'layout/kanban'; -@import 'layout/issues'; -@import 'layout/wiki'; -@import 'layout/wiki-edit'; - -//################################################# -// Help -//################################################# -@import 'modules/help/lightbox-generic-notion'; - -//################################################# -// Shame -//################################################# - -@import 'shame/shame'; diff --git a/app/styles/modules/backlog/backlog-table.scss b/app/styles/modules/backlog/backlog-table.scss index 6d4a653e..3c8e7212 100644 --- a/app/styles/modules/backlog/backlog-table.scss +++ b/app/styles/modules/backlog/backlog-table.scss @@ -14,7 +14,6 @@ padding: .5rem 0 .5rem .5rem; text-align: left; width: 100%; - } .backlog-table-title, .backlog-table-subtitle, @@ -24,6 +23,7 @@ } .user-stories { width: 100%; + } .status { @include table-flex-child(0, 150px, 0); diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index a771c4f3..47097423 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -1,46 +1,5 @@ -.lightbox, -%lightbox { - @include background-opacity($white, .95); - bottom: 0; - display: none; - left: 0; - opacity: 0; - position: fixed; - right: 0; - top: 0; - z-index: 99910; - .close { - @extend %large; - position: absolute; - right: 2rem; - top: 2rem; - } - &.open { - @include table-flex(center, center, flex, row, wrap, center); - @include transition (opacity .3s ease); - opacity: 1; - } - &.close { - @include transition (opacity .3s ease); - opacity: 0; - } - .title { - text-align: center; - } - input, - textarea, - select { - margin-bottom: 1rem; - } - textarea { - resize: vertical; - } - .button-green, - .button-gray { - display: block; - padding: 12px; - text-align: center; - } +.lightbox { + @extend %lightbox; } .markdown-preview { diff --git a/app/styles/modules/filters/filters.scss b/app/styles/modules/filters/filters.scss index 015df762..a98e3218 100644 --- a/app/styles/modules/filters/filters.scss +++ b/app/styles/modules/filters/filters.scss @@ -1,21 +1,3 @@ -.menu-secondary { - &.filters-bar { - @include table-flex-child(0, 1px, 0, 1px); - @include transition(all .2s linear); - padding: 0; - &.active { - @include table-flex-child(1, 260px, 0, 260px); - @include transition(all .2s linear); - padding: 2em 1em; - - .filters-inner { - @include transition (all .4s ease-in); - opacity: 1; - } - } - } -} - .filters { h1 { vertical-align: baseline; @@ -47,13 +29,11 @@ right: .7rem; top: .7rem; } - } .filters-inner { opacity: 0; @include transition (all .1s ease-in); - .loading { background: $grayer; border: 1px solid #b8b8b8; diff --git a/app/styles/modules/help/notion-admin-project-values-us-points.scss b/app/styles/modules/help/notion-admin-project-values-us-points.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/gulpfile.coffee b/gulpfile.coffee index 74d86bda..136346bf 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -8,6 +8,7 @@ plumber = require("gulp-plumber") wrap = require("gulp-wrap") rename = require("gulp-rename") flatten = require('gulp-flatten') +gulpif = require('gulp-if') minifyHTML = require("gulp-minify-html") sass = require("gulp-ruby-sass") @@ -20,11 +21,19 @@ newer = require("gulp-newer") cache = require("gulp-cached") jadeInheritance = require('gulp-jade-inheritance') sourcemaps = require('gulp-sourcemaps') +insert = require("gulp-insert") +runSequence = require('run-sequence') +lazypipe = require('lazypipe') +rimraf = require('rimraf') + +mainSass = require("./main-sass").files paths = {} paths.app = "app/" paths.dist = "dist/" paths.tmp = "tmp/" +paths.tmpStyles = paths.tmp + "styles/" +paths.tmpStylesExtras = "#{paths.tmpStyles}/taiga-front-extras/**/*.css" paths.extras = "extras/" paths.jade = [ @@ -38,9 +47,11 @@ paths.svg = paths.app + "svg/**/*" paths.css = paths.app + "styles/vendor/*.css" paths.locales = paths.app + "locales/**/*.json" paths.sass = [ - paths.app + "styles/**/*.scss" + "#{paths.app}/styles/**/*.scss" + "#{paths.app}/plugins/**/*.scss" "!#{paths.app}/styles/bourbon/**/*.scss" - paths.app + "plugins/**/*.scss" + "!#{paths.app}/styles/dependencies/**/*.scss" + "!#{paths.app}/styles/extras/**/*.scss" ] paths.coffee = [ @@ -93,6 +104,8 @@ paths.js = [ paths.app + "plugins/**/*.js" ] +isDeploy = process.argv[process.argv.length - 1] == 'deploy' + ############################################################################ # Layout/CSS Related tasks ############################################################################## @@ -118,38 +131,54 @@ gulp.task "templates", -> .pipe(jade({pretty: true, locals:{v:(new Date()).getTime()}})) .pipe(gulp.dest(paths.dist)) +############################################################################## +# CSS Related tasks +############################################################################## + gulp.task "sass-lint", -> gulp.src(paths.sass) .pipe(cache("sasslint")) - .pipe(scsslint({config: "scsslint.yml"})) + .pipe(gulpif(!isDeploy, scsslint({config: "scsslint.yml"}))) -gulp.task "sass-watch", ["sass-lint"], -> - gulp.src(["#{paths.app}/styles/main.scss", "#{paths.app}/plugins/**/*.scss"]) +gulp.task "sass-compile", ["sass-lint"], -> + gulp.src(paths.sass) .pipe(plumber()) - .pipe(concat("all.scss")) - .pipe(sass()) - .pipe(rename("app.css")) + .pipe(cache("scss")) + .pipe(insert.prepend('@import "dependencies";')) + .pipe(sass({ + 'sourcemap=none': true, + loadPath: [ + "#{paths.app}styles/extras/" + ] + })) + .pipe(gulp.dest(paths.tmpStyles)) + +csslintChannel = lazypipe() + .pipe(csslint, "csslintrc.json") + .pipe(csslint.reporter) + +gulp.task "css-lint-app", -> + gulp.src(mainSass.concat([paths.tmpStylesExtras])) + .pipe(cache("csslint")) + .pipe(gulpif(!isDeploy, csslintChannel())) + +gulp.task "css-join", ["css-lint-app"], -> + gulp.src(mainSass.concat([paths.tmpStylesExtras])) + .pipe(concat("app.css")) .pipe(gulp.dest(paths.tmp)) -gulp.task "sass-deploy", -> - gulp.src(["#{paths.app}/styles/main.scss", "#{paths.app}/plugins/**/*.scss"]) - .pipe(plumber()) - .pipe(concat("all.scss")) - .pipe(sass()) - .pipe(rename("app.css")) - .pipe(gulp.dest(paths.tmp)) +gulp.task "css-app", -> + runSequence("sass-compile", "css-join") gulp.task "css-vendor", -> gulp.src(paths.css) .pipe(concat("vendor.css")) .pipe(gulp.dest(paths.tmp)) -gulp.task "css-lint-app", ["sass-watch"], -> - gulp.src(paths.tmp + "app.css") - .pipe(csslint("csslintrc.json")) - .pipe(csslint.reporter()) +gulp.task "delete-tmp-styles", (cb) -> + rimraf(paths.tmpStyles, cb) -gulp.task "styles-watch", ["sass-watch", "css-vendor", "css-lint-app"], -> +gulp.task "styles-watch", ["css-app", "css-vendor"], -> _paths = [ paths.tmp + "vendor.css", paths.tmp + "app.css" @@ -157,18 +186,11 @@ gulp.task "styles-watch", ["sass-watch", "css-vendor", "css-lint-app"], -> gulp.src(_paths) .pipe(concat("main.css")) + .pipe(gulpif(isDeploy, minifyCSS())) .pipe(gulp.dest(paths.dist + "styles/")) -gulp.task "styles-deploy", ["sass-deploy", "css-vendor"], -> - _paths = [ - paths.tmp + "vendor.css", - paths.tmp + "app.css" - ] - - gulp.src(_paths) - .pipe(concat("main.css")) - .pipe(minifyCSS()) - .pipe(gulp.dest(paths.dist + "styles/")) +gulp.task "styles", ["delete-tmp-styles"], -> + gulp.start("styles-watch") ############################################################################## # JS Related tasks @@ -297,19 +319,21 @@ gulp.task "watch", -> gulp.task "deploy", [ + "delete-tmp-styles", "templates", "copy", "jade-deploy", "app-deploy", "jslibs-deploy", - "styles-deploy" + "styles" ] # The default task (called when you run gulp from cli) gulp.task "default", [ + "delete-tmp-styles", "copy", "templates", - "styles-watch", + "styles", "app-watch", "jslibs-watch", "jade-deploy", diff --git a/main-sass.js b/main-sass.js new file mode 100644 index 00000000..5f77ff33 --- /dev/null +++ b/main-sass.js @@ -0,0 +1,144 @@ +exports.files = function () { + var base = process.cwd() + "/tmp/styles/"; + + var files = [ + // Codehilite + 'vendor/codehilite.github', + + //################################################# + // Layout + //################################################# + + 'layout/reset', + 'layout/base', + 'layout/animation', + 'layout/typography', + 'layout/login', + 'layout/invitation', + 'layout/elements', + 'layout/forms', + 'layout/not-found', + 'layout/backlog', + 'layout/taskboard', + 'layout/us-detail', + 'layout/admin-memberships', + 'layout/admin-project-values', + 'layout/project-colors', + 'layout/kanban', + 'layout/issues', + 'layout/wiki', + 'layout/wiki-edit', + + //################################################# + // components + //################################################# + + 'components/buttons', + 'components/avatar', + 'components/summary', + 'components/popover', + 'components/tag', + 'components/filter', + 'components/taskboard-task', + 'components/kanban-task', + 'components/notification-message', + 'components/basic-table', + 'components/paginator', + 'components/watchers', + 'components/level', + 'components/created-by', + 'components/wysiwyg', + 'components/select-color', + 'components/loader', + 'components/spinner', + 'components/help-notion-button', + 'components/beta', + + //################################################# + // Modules + //################################################# + + //Common modules + 'modules/common/assigned-to', + 'modules/common/nav', + 'modules/common/projects-nav', + 'modules/common/lightbox', + 'modules/common/colors-table', + 'modules/common/category-config', + 'modules/common/attachments', + 'modules/common/related-tasks', + 'modules/common/history', + 'modules/common/wizard', + + //Project modules + 'modules/home-projects-list', + 'modules/home-project', + 'modules/create-project', + + //Issues modules + 'modules/issues/issues-table', + + //Kanban modules + 'modules/kanban/kanban-table', + + //Search modules + 'modules/search/search-filter', + 'modules/search/search-result-table', + 'modules/search/search-in', + + //Filters modules + 'modules/filters/filters', + 'modules/filters/list-filters', + 'modules/filters/filter-tags', + + //Backlog modules + 'modules/backlog/sprints', + 'modules/backlog/burndown', + 'modules/backlog/backlog-table', + 'modules/backlog/taskboard-table', + + //Login modules + 'modules/auth/login-form', + 'modules/auth/register-form', + 'modules/auth/forgot-form', + 'modules/auth/change-password-from-recovery', + + //Wiki modules + 'modules/wiki/wiki-nav', + 'modules/wiki/wiki-summary', + + //modules admin + 'modules/admin/admin-menu', + 'modules/admin/admin-submenu', + 'modules/admin/admin-submenu-roles', + 'modules/admin/admin-roles', + 'modules/admin/admin-functionalities', + 'modules/admin/admin-membership-table', + 'modules/admin/admin-project-profile', + 'modules/admin/default-values', + 'modules/admin/project-values', + + //Modules user Settings + 'modules/user-settings/user-profile', + 'modules/user-settings/user-change-password', + 'modules/user-settings/mail-notifications-table', + + //################################################# + // Help + //################################################# + + 'modules/help/lightbox-generic-notion', + + //################################################# + // Shame + //################################################# + + 'shame/shame', + ]; + + files = files.map(function (file) { + return base + file + ".css"; + }); + + return files; +}(); diff --git a/package.json b/package.json index 5deac6ac..3f930598 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "gulp-csslint": "^0.1.5", "gulp-flatten": "0.0.4", "gulp-if": "0.0.5", + "gulp-insert": "^0.4.0", + "gulp-intermediate": "^3.0.1", "gulp-jade": "^0.5.0", "gulp-jade-inheritance": "0.0.4", "gulp-minify-css": "^0.3.1", @@ -37,7 +39,7 @@ "gulp-notify": "^1.2.5", "gulp-plumber": "^0.6.2", "gulp-rename": "^1.2.0", - "gulp-ruby-sass": "^0.4.3", + "gulp-ruby-sass": "^0.7.1", "gulp-scss-lint": "0.1.1", "gulp-sourcemaps": "^1.2.4", "gulp-styledocco": "0.0.1", @@ -46,7 +48,11 @@ "gulp-util": "~2.2.14", "gulp-watch": "^0.5.4", "gulp-wrap": "^0.3.0", + "lazypipe": "^0.2.2", "readable-stream": "~1.0.31", + "rimraf": "^2.2.8", + "run-sequence": "^1.0.1", + "gulp-if": "^1.2.5", "through2": "~0.6.1" } } From 9b97454a3e53c7c0d2630d440b46f2139ebf5dda Mon Sep 17 00:00:00 2001 From: Juanfran Date: Tue, 28 Oct 2014 14:48:52 +0100 Subject: [PATCH 029/164] fix #1469 - recreating deleted project --- app/coffee/modules/projects/lightboxes.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/coffee/modules/projects/lightboxes.coffee b/app/coffee/modules/projects/lightboxes.coffee index e88aaa35..9d6cb8c0 100644 --- a/app/coffee/modules/projects/lightboxes.coffee +++ b/app/coffee/modules/projects/lightboxes.coffee @@ -26,7 +26,7 @@ debounce = @.taiga.debounce module = angular.module("taigaProject") -CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, lightboxService) -> +CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, lightboxService, $cacheFactory) -> link = ($scope, $el, attrs) -> $scope.data = {} $scope.templates = [] @@ -34,6 +34,11 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project form = $el.find("form").checksley({"onlyOneErrorElement": true}) onSuccessSubmit = (response) -> + # remove all $http cache + # This is necessary when a project is created with the same name + # than another deleted in the same session + $cacheFactory.get('$http').removeAll() + $rootscope.$broadcast("projects:reload") $confirm.notify("success", "Success") #TODO: i18n $location.url($projectUrl.get(response)) @@ -116,7 +121,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project return {link:link} module.directive("tgLbCreateProject", ["$rootScope", "$tgRepo", "$tgConfirm", "$location", "$tgNavUrls", - "$tgResources", "$projectUrl", "lightboxService", CreateProject]) + "$tgResources", "$projectUrl", "lightboxService", "$cacheFactory", CreateProject]) ############################################################################# From cacaf0e897c0383a36278b9cf2f2587b412d3e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Tue, 28 Oct 2014 11:49:26 +0100 Subject: [PATCH 030/164] Memberlist list fluid layout fix --- app/styles/dependencies/mixins.scss | 10 +-- .../modules/admin/admin-membership-table.scss | 75 ++++++++++--------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/app/styles/dependencies/mixins.scss b/app/styles/dependencies/mixins.scss index b58588c9..22a4af58 100644 --- a/app/styles/dependencies/mixins.scss +++ b/app/styles/dependencies/mixins.scss @@ -12,11 +12,11 @@ // Table Flex - http://devbryce.com/site/flexbox/ @mixin table-flex($align-content: stretch, $align-items: stretch, $display: flex, $flex-direction: row, $flex-wrap: wrap, $justify-content: flex-start) { @include display($display); - @include align-content($align-content); - @include align-items($align-items); - @include flex-direction($flex-direction); - @include flex-wrap($flex-wrap); - @include justify-content($justify-content); + @include align-content($align-content); //flex-start | flex-end | center | space-between | space-around | stretch + @include align-items($align-items); //flex-start | flex-end | center | baseline | stretch + @include flex-direction($flex-direction); //row | row-reverse | column | column-reverse + @include flex-wrap($flex-wrap); //nowrap | wrap | wrap-reverse + @include justify-content($justify-content); //flex-start | flex-end | center | space-between | space-around } @mixin table-flex-child($flex-grow: 1, $flex-basis: 300px, $flex-shrink: 0, $width:'') { diff --git a/app/styles/modules/admin/admin-membership-table.scss b/app/styles/modules/admin/admin-membership-table.scss index 43a1a646..1c354b20 100644 --- a/app/styles/modules/admin/admin-membership-table.scss +++ b/app/styles/modules/admin/admin-membership-table.scss @@ -6,6 +6,13 @@ @include table-flex(stretch, center, flex, row, wrap, flex-start); figcaption { margin-left: 1rem; + width: 75%; + span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; + } } img { @include table-flex-child(1, 35px, 0); @@ -23,29 +30,6 @@ color: $gray-light; } } - .active, - .pending { - padding: 8px; - width: 115px; - .icon { - @extend %large; - } - } - .active { - background-color: $whitish; - } - .pending { - background-color: $red-light; - color: $white; - .icon { - float: right; - } - &:hover { - @include transition (background-color .3s linear); - background-color: $red; - color: $white; - } - } .header-role, .header-status { padding-left: .5rem; @@ -54,16 +38,32 @@ padding-right: 1rem; } .row-status { - @include table-flex(); - .delete { - @extend %large; - @include table-flex(stretch, center, flex, row, wrap, flex-start); - color: $gray-light; - margin-left: 15px; - padding: 0 15px; - &:hover { - color: $red; - } + @include table-flex($justify-content: space-between, $align-items: center); + } + .active, + .pending { + padding: 8px; + } + .active { + background-color: $whitish; + } + .pending { + background-color: $red-light; + color: $white; + .icon { + float: rsdsdfdvsdvight; + } + &:hover { + @include transition (background-color .3s linear); + background-color: $red; + color: $white; + } + } + .delete { + @extend %large; + color: $gray-light; + &:hover { + color: $red; } } .row-admin { @@ -78,14 +78,17 @@ .row-role, .header-member, .header-role { - @include table-flex-child(3, 35px, 0); + @include table-flex-child(4, 0, 0); + } + .row-admin, + .header-admin { + @include table-flex-child(1, 0, 0); } .row-status, - .row-admin, - .header-admin, .header-status { @include table-flex-child(1, 50px, 0); } + .check { background-color: darken($whitish, 10%); border-radius: 2px; From 63fdf3867a3ebb8bd4cbba5b2d730ad9c6443fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 20 Oct 2014 21:38:37 +0200 Subject: [PATCH 031/164] Fixed typo --- extras/humans.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/humans.txt b/extras/humans.txt index 3d621351..82ff42f9 100644 --- a/extras/humans.txt +++ b/extras/humans.txt @@ -49,7 +49,7 @@ Twitter: @elhombretecla Location: Madrid, Spain - Taiga CEO, Community Manager & Chico Almodobar: Ricky Posner + Taiga CEO, Community Manager & Chico Almodóvar: Ricky Posner Twitter: @eposner Location: Madrid, Spain From 8b28062ee3ac3643aa1a403485d4cc8b4fa6ee50 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 29 Oct 2014 09:13:08 +0100 Subject: [PATCH 032/164] fix styles join --- gulpfile.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gulpfile.coffee b/gulpfile.coffee index 136346bf..5f444499 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -167,8 +167,8 @@ gulp.task "css-join", ["css-lint-app"], -> .pipe(concat("app.css")) .pipe(gulp.dest(paths.tmp)) -gulp.task "css-app", -> - runSequence("sass-compile", "css-join") +gulp.task "css-app", (cb) -> + runSequence("sass-compile", "css-join", cb) gulp.task "css-vendor", -> gulp.src(paths.css) From 60317f7d20c0f921f42d976f1d0f7e641ec7a71a Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 29 Oct 2014 09:28:47 +0100 Subject: [PATCH 033/164] fix #1472 --- app/styles/layout/base.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/styles/layout/base.scss b/app/styles/layout/base.scss index 2377a961..1546428f 100644 --- a/app/styles/layout/base.scss +++ b/app/styles/layout/base.scss @@ -149,9 +149,6 @@ body { color: $white; float: right; margin-left: 10px; - &:first-child { - margin-left: 0; - } &:hover { color: $white; } From cd31fc239f38afef80271b3a53d1b43487aa9dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Mon, 27 Oct 2014 09:59:39 +0100 Subject: [PATCH 034/164] Add attachment limit size information on avatar and attachments --- app/coffee/modules/common/attachments.coffee | 3 ++- app/partials/user-profile.jade | 8 ++++---- app/styles/modules/common/attachments.scss | 4 ++++ app/styles/modules/user-settings/user-profile.scss | 6 +++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/coffee/modules/common/attachments.coffee b/app/coffee/modules/common/attachments.coffee index 3402a392..fb36ccde 100644 --- a/app/coffee/modules/common/attachments.coffee +++ b/app/coffee/modules/common/attachments.coffee @@ -159,8 +159,9 @@ AttachmentsDirective = ($confirm) -> attachments -
+
+
diff --git a/app/partials/user-profile.jade b/app/partials/user-profile.jade index 0826e8d8..2b4289e5 100644 --- a/app/partials/user-profile.jade +++ b/app/partials/user-profile.jade @@ -24,9 +24,9 @@ block content span.icon.icon-spinner input(type="file", id="avatar-field", class="hidden", tg-avatar-model="avatarAttachment") - - p The image will be cropped to 80x80 size. - a.button.button-green.change Change + p The image will be cropped to 80x80px.
+ span.hidden Maximum upload size is 700Kb + a.button.button-green.change(title="Maximum upload size is 700Kb") Change a.use-gravatar Use gravatar image div.data @@ -60,7 +60,7 @@ block content a.delete-account(href="", title="Delete Taiga account", ng-click="ctrl.openDeleteLightbox()") Delete Taiga account - div.lightbox.lightbox-delete-account.hidden(tg-lb-delete-user) + div.lightbox.lightbox-delete-account.hidden(tg-lb-delete-user) div.lightbox.lightbox-confirm-use-gravatar.hidden include views/modules/lightbox-use-gravatar diff --git a/app/styles/modules/common/attachments.scss b/app/styles/modules/common/attachments.scss index 56a2fdc9..b49b67e0 100644 --- a/app/styles/modules/common/attachments.scss +++ b/app/styles/modules/common/attachments.scss @@ -169,4 +169,8 @@ input { display: none; } + span { + @extend %small; + color: $gray-light; + } } diff --git a/app/styles/modules/user-settings/user-profile.scss b/app/styles/modules/user-settings/user-profile.scss index a720aa64..21c1a219 100644 --- a/app/styles/modules/user-settings/user-profile.scss +++ b/app/styles/modules/user-settings/user-profile.scss @@ -36,9 +36,13 @@ } p { @extend %xsmall; - margin-bottom: 0; + line-height: .8rem; + margin-bottom: .3rem; text-align: center; } + span { + @extend %bold; + } .use-gravatar { @extend %small; cursor: pointer; From 0b7e20143ca1c3b2ad9bda8202885a5e4bf69d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 28 Oct 2014 09:36:36 +0100 Subject: [PATCH 035/164] Catch errors with heavy files and show info messages about the maximum size allowed. --- app/coffee/modules/common/attachments.coffee | 38 ++++++++++++++----- .../modules/resources/attachments.coffee | 16 ++++++-- .../modules/resources/user-settings.coffee | 22 +++++++++-- app/coffee/modules/user-settings/main.coffee | 12 +++++- app/partials/user-profile.jade | 4 +- conf/main.example.json | 3 +- 6 files changed, 75 insertions(+), 20 deletions(-) diff --git a/app/coffee/modules/common/attachments.coffee b/app/coffee/modules/common/attachments.coffee index fb36ccde..05951831 100644 --- a/app/coffee/modules/common/attachments.coffee +++ b/app/coffee/modules/common/attachments.coffee @@ -72,10 +72,12 @@ class AttachmentsController extends taiga.Controller @.attachments.push(data) @rootscope.$broadcast("attachment:create") - promise = promise.then null, (data) -> + promise = promise.then null, (data) => + @scope.$emit("attachments:size-error") if data.status == 413 index = @.uploadingAttachments.indexOf(attachment) @.uploadingAttachments.splice(index, 1) - @confirm.notify("error", null, "We have not been able to upload '#{attachment.name}'.") + @confirm.notify("error", "We have not been able to upload '#{attachment.name}'. + #{data.data._error_message}") return @q.reject(data) return promise @@ -109,7 +111,8 @@ class AttachmentsController extends taiga.Controller @.updateCounters() @rootscope.$broadcast("attachment:edit") - onError = => + onError = (response) => + $scope.$emit("attachments:size-error") if response.status == 413 @confirm.notify("error") return @q.reject() @@ -151,7 +154,7 @@ class AttachmentsController extends taiga.Controller return not item.is_deprecated -AttachmentsDirective = ($confirm) -> +AttachmentsDirective = ($config, $confirm) -> template = _.template("""
@@ -159,9 +162,12 @@ AttachmentsDirective = ($confirm) -> attachments -
+
+ <% if (maxFileSize){ %> + + <% }; %> -
@@ -196,7 +202,6 @@ AttachmentsDirective = ($confirm) ->
""") - link = ($scope, $el, $attrs, $ctrls) -> $ctrl = $ctrls[0] $model = $ctrls[1] @@ -223,6 +228,12 @@ AttachmentsDirective = ($confirm) -> $ctrl.reorderAttachment(attachment, newIndex) $ctrl.saveAttachments() + showSizeInfo = -> + $el.find(".size-info").removeClass("hidden") + + $scope.$on "attachments:size-error", -> + showSizeInfo() + $el.on "change", ".attachments-header input", (event) -> files = _.toArray(event.target.files) return if files.length < 1 @@ -250,7 +261,16 @@ AttachmentsDirective = ($confirm) -> $el.off() templateFn = ($el, $attrs) -> - return template({type: $attrs.type}) + maxFileSize = $config.get("maxUploadFileSize", null) + maxFileSize = sizeFormat(maxFileSize) if maxFileSize + maxFileSizeMsg = if maxFileSize then "Maximum upload size is #{maxFileSize}" else "" # TODO: i18n + + ctx = { + type: $attrs.type + maxFileSize: maxFileSize + maxFileSizeMsg: maxFileSizeMsg + } + return template(ctx) return { require: ["tgAttachments", "ngModel"] @@ -262,7 +282,7 @@ AttachmentsDirective = ($confirm) -> template: templateFn } -module.directive("tgAttachments", ["$tgConfirm", AttachmentsDirective]) +module.directive("tgAttachments", ["$tgConfig", "$tgConfirm", AttachmentsDirective]) AttachmentDirective = -> diff --git a/app/coffee/modules/resources/attachments.coffee b/app/coffee/modules/resources/attachments.coffee index f56e82fd..f5ab0421 100644 --- a/app/coffee/modules/resources/attachments.coffee +++ b/app/coffee/modules/resources/attachments.coffee @@ -24,7 +24,7 @@ taiga = @.taiga sizeFormat = @.taiga.sizeFormat -resourceProvider = ($rootScope, $urls, $model, $repo, $auth, $q) -> +resourceProvider = ($rootScope, $config, $urls, $model, $repo, $auth, $q) -> service = {} service.list = (urlName, objectId, projectId) -> @@ -38,6 +38,16 @@ resourceProvider = ($rootScope, $urls, $model, $repo, $auth, $q) -> defered.reject(null) return defered.promise + maxFileSize = $config.get("maxUploadFileSize", null) + if maxFileSize and file.size > maxFileSize + response = { + status: 413, + data: _error_message: "'#{file.name}' (#{sizeFormat(file.size)}) is too heavy for our oompa + loompas, try it with a smaller than {#{sizeFormat(maxFileSize)})" + } + defered.reject(response) + return defered.promise + uploadProgress = (evt) => $rootScope.$apply => file.status = "in-progress" @@ -83,5 +93,5 @@ resourceProvider = ($rootScope, $urls, $model, $repo, $auth, $q) -> module = angular.module("taigaResources") -module.factory("$tgAttachmentsResourcesProvider", ["$rootScope", "$tgUrls", "$tgModel", "$tgRepo", "$tgAuth", - "$q", resourceProvider]) +module.factory("$tgAttachmentsResourcesProvider", ["$rootScope", "$tgConfig", "$tgUrls", "$tgModel", "$tgRepo", + "$tgAuth", "$q", resourceProvider]) diff --git a/app/coffee/modules/resources/user-settings.coffee b/app/coffee/modules/resources/user-settings.coffee index c9bfe0b5..aa03b1c2 100644 --- a/app/coffee/modules/resources/user-settings.coffee +++ b/app/coffee/modules/resources/user-settings.coffee @@ -21,13 +21,26 @@ taiga = @.taiga +sizeFormat = @.taiga.sizeFormat -resourceProvider = ($repo, $http, $urls) -> + +resourceProvider = ($config, $repo, $http, $urls, $q) -> service = {} - service.changeAvatar = (attachmentModel) -> + service.changeAvatar = (file) -> + maxFileSize = $config.get("maxUploadFileSize", null) + if maxFileSize and file.size > maxFileSize + response = { + status: 413, + data: _error_message: "'#{file.name}' (#{sizeFormat(file.size)}) is too heavy for our oompa + loompas, try it with a smaller than {#{sizeFormat(maxFileSize)})" + } + defered = $q.defer() + defered.reject(response) + return defered.promise + data = new FormData() - data.append('avatar', attachmentModel) + data.append('avatar', file) options = { transformRequest: angular.identity, headers: {'Content-Type': undefined} @@ -52,4 +65,5 @@ resourceProvider = ($repo, $http, $urls) -> module = angular.module("taigaResources") -module.factory("$tgUserSettingsResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", resourceProvider]) +module.factory("$tgUserSettingsResourcesProvider", ["$tgConfig", "$tgRepo", "$tgHttp", "$tgUrls", "$q", + resourceProvider]) diff --git a/app/coffee/modules/user-settings/main.coffee b/app/coffee/modules/user-settings/main.coffee index dddf70f0..c356df27 100644 --- a/app/coffee/modules/user-settings/main.coffee +++ b/app/coffee/modules/user-settings/main.coffee @@ -21,6 +21,7 @@ taiga = @.taiga mixOf = @.taiga.mixOf +sizeFormat = @.taiga.sizeFormat module = angular.module("taigaUserSettings") @@ -32,6 +33,7 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin) @.$inject = [ "$scope", "$rootScope", + "$tgConfig", "$tgRepo", "$tgConfirm", "$tgResources", @@ -42,11 +44,15 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin) "$tgAuth" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) -> + constructor: (@scope, @rootscope, @config, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) -> @scope.sectionName = "User Profile" #i18n @scope.project = {} @scope.user = @auth.getUser() + maxFileSize = @config.get("maxUploadFileSize", null) + if maxFileSize + @scope.maxFileSizeMsg = "[Max, size: #{sizeFormat(maxFileSize)}" # TODO: i18n + promise = @.loadInitialData() promise.then null, @.onInitialDataError.bind(@) @@ -111,6 +117,9 @@ module.directive("tgUserProfile", ["$tgConfirm", "$tgAuth", "$tgRepo", UserProf UserAvatarDirective = ($auth, $model, $rs, $confirm) -> link = ($scope, $el, $attrs) -> + showSizeInfo = -> + $el.find(".size-info").removeClass("hidden") + onSuccess = (response) -> user = $model.make_model("users", response.data) $auth.setUser(user) @@ -120,6 +129,7 @@ UserAvatarDirective = ($auth, $model, $rs, $confirm) -> $confirm.notify('success') onError = (response) -> + showSizeInfo() if response.status == 413 $el.find('.overlay').hide() $confirm.notify('error', response.data._error_message) diff --git a/app/partials/user-profile.jade b/app/partials/user-profile.jade index 2b4289e5..d675501e 100644 --- a/app/partials/user-profile.jade +++ b/app/partials/user-profile.jade @@ -25,8 +25,8 @@ block content input(type="file", id="avatar-field", class="hidden", tg-avatar-model="avatarAttachment") p The image will be cropped to 80x80px.
- span.hidden Maximum upload size is 700Kb - a.button.button-green.change(title="Maximum upload size is 700Kb") Change + span.size-info.hidden(tg-bo-html="maxFileSizeMsg") + a.button.button-green.change(tg-bo-title="'Change photo. ' + maxFileSizeMsg") Change a.use-gravatar Use gravatar image div.data diff --git a/conf/main.example.json b/conf/main.example.json index b71f9e52..5d382492 100644 --- a/conf/main.example.json +++ b/conf/main.example.json @@ -5,5 +5,6 @@ "publicRegisterEnabled": true, "feedbackEnabled": true, "privacyPolicyUrl": null, - "termsOfServiceUrl": null + "termsOfServiceUrl": null, + "maxUploadFileSize": null } From 957399275f8a290ee8c7c19e7bce419859bd4249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 29 Oct 2014 15:55:15 +0100 Subject: [PATCH 036/164] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f073389a..34ae26bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Multiple User stories Drag & Drop in the backlog. - Add visual difference to closed USs in backlog panel. - Show crerated date of attachments in the hover of the filename. +- Show info about maximun size allowed for avatar and attachments files. - Add beta ribbon. ### Misc From 3087cb3cbe2762cd75cf87fec725b69a266bc73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Wed, 29 Oct 2014 16:22:34 +0100 Subject: [PATCH 037/164] Fix CSS errors in linter --- app/styles/layout/us-detail.scss | 10 +++++----- app/styles/modules/admin/admin-menu.scss | 5 ++++- app/styles/modules/admin/project-values.scss | 2 +- app/styles/modules/filters/list-filters.scss | 6 +++++- app/styles/modules/search/search-filter.scss | 9 ++++++--- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/styles/layout/us-detail.scss b/app/styles/layout/us-detail.scss index 4cf822e6..a9c813d6 100644 --- a/app/styles/layout/us-detail.scss +++ b/app/styles/layout/us-detail.scss @@ -242,11 +242,11 @@ &:last-child { margin: 0; } - &.clickable { - &:hover { - @include transition(background .2s ease-in); - background: darken($whitish, 10%); - } + } + .clickable { + &:hover { + @include transition(background .2s ease-in); + background: darken($whitish, 10%); } } .level { diff --git a/app/styles/modules/admin/admin-menu.scss b/app/styles/modules/admin/admin-menu.scss index bc6bd31a..99ab5cbf 100644 --- a/app/styles/modules/admin/admin-menu.scss +++ b/app/styles/modules/admin/admin-menu.scss @@ -11,7 +11,6 @@ a { display: block; padding: 1rem 0 1rem 1rem; - &.active, &:hover { .icon { @include transition (opacity .3s linear); @@ -19,6 +18,10 @@ } } } + .active { + @include transition (opacity .3s linear); + opacity: 1; + } .icon { color: $blackish; float: right; diff --git a/app/styles/modules/admin/project-values.scss b/app/styles/modules/admin/project-values.scss index 1c1f8571..46fabb7e 100644 --- a/app/styles/modules/admin/project-values.scss +++ b/app/styles/modules/admin/project-values.scss @@ -56,7 +56,7 @@ &:hover { @include transition(color .3s linear); color: $green-taiga; - &.icon-delete { + .icon-delete { color: $red; } } diff --git a/app/styles/modules/filters/list-filters.scss b/app/styles/modules/filters/list-filters.scss index 06e0a234..29753c6c 100644 --- a/app/styles/modules/filters/list-filters.scss +++ b/app/styles/modules/filters/list-filters.scss @@ -15,7 +15,6 @@ @extend %large; @extend %title; opacity: .4; - &.active, &:hover { @include transition (opacity .3s linear); color: $blackish; @@ -23,6 +22,11 @@ } } + .active { + @include transition (opacity .3s linear); + color: $blackish; + opacity: 1; + } .icon { padding-right: .5rem; } diff --git a/app/styles/modules/search/search-filter.scss b/app/styles/modules/search/search-filter.scss index 6ef3ab9c..b9aa3314 100644 --- a/app/styles/modules/search/search-filter.scss +++ b/app/styles/modules/search/search-filter.scss @@ -11,18 +11,21 @@ @extend %large; @extend %title; opacity: .2; - &.active, &:hover { @include transition (opacity .3s linear); color: $blackish; opacity: 1; } } + .active { + @include transition (opacity .3s linear); + color: $blackish; + opacity: 1; + } .icon { margin-right: .4rem; } - - span.name { + .name { padding-left: 5px; } } From 8da88472eeb1d1e0f73db4c866304d9f99c489f1 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 29 Oct 2014 12:43:47 +0100 Subject: [PATCH 038/164] fix #1483 --- app/coffee/modules/common/wisiwyg.coffee | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/coffee/modules/common/wisiwyg.coffee b/app/coffee/modules/common/wisiwyg.coffee index 3abb17b3..2177dab3 100644 --- a/app/coffee/modules/common/wisiwyg.coffee +++ b/app/coffee/modules/common/wisiwyg.coffee @@ -96,18 +96,24 @@ tgMarkitupDirective = ($rootscope, $rs, $tr) -> onEnter: keepDefault: false replaceWith: (data) => - lines = data.textarea.value[0..(data.caretPosition - 1)].split("\n") - lastLine = lines[lines.length - 1] + lines = data.textarea.value.split("\n") + cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length + newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0] + lastLine = lines[cursorLine - 1] # unordered list - match = lastLine.match /^(\s*- ).*/ + if match emptyListItem = lastLine.match /^(\s*)\-\s$/ if emptyListItem markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) else - return "\n#{match[1]}" if match + breakLineAtBeginning = newLineContent.match /^(\s*)\-\s/ + + if !breakLineAtBeginning + return "\n#{match[1]}" if match # unordered list * match = lastLine.match /^(\s*\* ).*/ @@ -118,7 +124,10 @@ tgMarkitupDirective = ($rootscope, $rs, $tr) -> if emptyListItem markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) else - return "\n#{match[1]}" if match + breakLineAtBeginning = newLineContent.match /^(\s*)\*\s/ + + if !breakLineAtBeginning + return "\n#{match[1]}" if match # ordered list match = lastLine.match /^(\s*)(\d+)\.\s/ @@ -129,7 +138,10 @@ tgMarkitupDirective = ($rootscope, $rs, $tr) -> if emptyListItem markdownCaretPositon = removeEmptyLine(data.textarea, lines.length - 1, data.caretPosition) else - return "\n#{match[1] + (parseInt(match[2], 10) + 1)}. " + breakLineAtBeginning = newLineContent.match /^(\s*)(\d+)\.\s/ + + if !breakLineAtBeginning + return "\n#{match[1] + (parseInt(match[2], 10) + 1)}. " return "\n" From 670544a7dc594a9ed4774bde9b60d723a28672d9 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Wed, 22 Oct 2014 14:36:43 +0200 Subject: [PATCH 039/164] US/1396 - extra text for inviting users --- app/coffee/modules/admin/lightboxes.coffee | 23 +++++++++++++------ .../modules/resources/memberships.coffee | 4 ++-- .../views/modules/lightbox-add-member.jade | 1 + app/styles/modules/common/lightbox.scss | 21 +++++++++-------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/app/coffee/modules/admin/lightboxes.coffee b/app/coffee/modules/admin/lightboxes.coffee index 2cf83e74..40026a74 100644 --- a/app/coffee/modules/admin/lightboxes.coffee +++ b/app/coffee/modules/admin/lightboxes.coffee @@ -31,6 +31,12 @@ MAX_MEMBERSHIP_FIELDSETS = 6 ############################################################################# CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> + extraTextTemplate = """ +
+ +
+ """ + template = _.template("""
@@ -53,11 +59,14 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> return template(ctx) resetForm = -> - $el.find("form > .add-member-wrapper").remove() + $el.find("form textarea").remove("") + $el.find("form .add-member-wrapper").remove() + + invitations = $el.find(".add-member-forms") + invitations.html(extraTextTemplate) - title = $el.find("h2") fieldSet = createFieldSet() - title.after(fieldSet) + invitations.prepend(fieldSet) $scope.$on "membersform:new", -> resetForm() @@ -112,12 +121,10 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> #checksley find new fields form.destroy() form.initialize() - if not form.validate() return - memberWrappers = $el.find("form > .add-member-wrapper") - + memberWrappers = $el.find("form .add-member-wrapper") memberWrappers = _.filter memberWrappers, (mw) -> angular.element(mw).find("input").hasClass('checksley-ok') @@ -132,7 +139,9 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> } if invitations.length - $rs.memberships.bulkCreateMemberships($scope.project.id, invitations).then(onSuccess, onError) + invitation_extra_text = $el.find("form textarea").val() + + $rs.memberships.bulkCreateMemberships($scope.project.id, invitations, invitation_extra_text).then(onSuccess, onError) return {link: link} diff --git a/app/coffee/modules/resources/memberships.coffee b/app/coffee/modules/resources/memberships.coffee index de569993..6a2763b7 100644 --- a/app/coffee/modules/resources/memberships.coffee +++ b/app/coffee/modules/resources/memberships.coffee @@ -42,9 +42,9 @@ resourceProvider = ($repo, $http, $urls) -> url = $urls.resolve("memberships") return $http.post("#{url}/#{id}/resend_invitation", {}) - service.bulkCreateMemberships = (projectId, data) -> + service.bulkCreateMemberships = (projectId, data, invitation_extra_text) -> url = $urls.resolve("bulk-create-memberships") - params = {project_id: projectId, bulk_memberships: data} + params = {project_id: projectId, bulk_memberships: data, invitation_extra_text: invitation_extra_text} return $http.post(url, params) return (instance) -> diff --git a/app/partials/views/modules/lightbox-add-member.jade b/app/partials/views/modules/lightbox-add-member.jade index 7540b0a8..8bff3b80 100644 --- a/app/partials/views/modules/lightbox-add-member.jade +++ b/app/partials/views/modules/lightbox-add-member.jade @@ -4,6 +4,7 @@ form h2.title New Member //- Form is set in a directive + .add-member-forms a.button.button-green(href="", title="Save") span Create diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 47097423..71cf8bb0 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -150,16 +150,19 @@ .lightbox-add-member { .add-member-wrapper { @include table-flex(); + fieldset { + position: relative; + &:first-child { + @include table-flex-child(3, 400px); + } + &:last-child { + @include table-flex-child(1, 200px); + margin-left: .5rem; + } + } } - fieldset { - position: relative; - &:first-child { - @include table-flex-child(3, 400px); - } - &:last-child { - @include table-flex-child(1, 200px); - margin-left: .5rem; - } + .extra-text { + margin-top: 1rem; } input[type=email], select { From 2846dc8bf1c1af417684343336359714389a467c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Thu, 23 Oct 2014 15:26:45 +0200 Subject: [PATCH 040/164] Minor UX and style fixes in add members lightbox --- app/coffee/modules/admin/lightboxes.coffee | 6 +++--- app/styles/modules/common/lightbox.scss | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/coffee/modules/admin/lightboxes.coffee b/app/coffee/modules/admin/lightboxes.coffee index 40026a74..cd38aaba 100644 --- a/app/coffee/modules/admin/lightboxes.coffee +++ b/app/coffee/modules/admin/lightboxes.coffee @@ -24,7 +24,7 @@ debounce = @.taiga.debounce module = angular.module("taigaKanban") -MAX_MEMBERSHIP_FIELDSETS = 6 +MAX_MEMBERSHIP_FIELDSETS = 4 ############################################################################# ## Create Members Lightbox Directive @@ -33,7 +33,7 @@ MAX_MEMBERSHIP_FIELDSETS = 6 CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> extraTextTemplate = """
- +
""" @@ -100,7 +100,7 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> fieldSet.after(newFieldSet) if $el.find(".add-member-wrapper").length == MAX_MEMBERSHIP_FIELDSETS - $el.find("fieldset:last > a").removeClass("icon-plus add-fieldset") + $el.find(".add-member-wrapper fieldset:last > a").removeClass("icon-plus add-fieldset") .addClass("icon-delete delete-fieldset") $el.on "click", ".button-green", debounce 2000, (event) -> diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 71cf8bb0..7793c182 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -150,6 +150,10 @@ .lightbox-add-member { .add-member-wrapper { @include table-flex(); + margin-bottom: .5rem; + &:last-child { + margin-bottom: 0; + } fieldset { position: relative; &:first-child { From 98c4928e1b06696921019bef54174b932c5a31ef Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 29 Oct 2014 11:15:07 +0100 Subject: [PATCH 041/164] Updating changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f073389a..1f8644bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add visual difference to closed USs in backlog panel. - Show crerated date of attachments in the hover of the filename. - Add beta ribbon. +- Support for custom text when inviting users. ### Misc - Lots of small and not so small bugfixes From 41831c2bd1c2d67be85bbd286478ffddb7437739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 29 Oct 2014 17:33:34 +0100 Subject: [PATCH 042/164] Fix placeholder --- app/coffee/modules/admin/lightboxes.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/coffee/modules/admin/lightboxes.coffee b/app/coffee/modules/admin/lightboxes.coffee index cd38aaba..bf30904a 100644 --- a/app/coffee/modules/admin/lightboxes.coffee +++ b/app/coffee/modules/admin/lightboxes.coffee @@ -33,7 +33,7 @@ MAX_MEMBERSHIP_FIELDSETS = 4 CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) -> extraTextTemplate = """
- +
""" 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 043/164] 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 044/164] 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 045/164] 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 046/164] 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 047/164] 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 048/164] 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 049/164] 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 050/164] 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 051/164] 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 052/164] 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 053/164] 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 054/164] 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 055/164] 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 056/164] 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 057/164] 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 058/164] 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 059/164] 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 060/164] 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 061/164] 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 062/164] 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