Fixing merge
commit
bfcab13dfb
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,11 +1,28 @@
|
|||
# Changelog #
|
||||
|
||||
## 1.4.0 Unknown (Unreleased)
|
||||
|
||||
### Features
|
||||
- Gitlab integration:
|
||||
+ Create Admin Panel with the Gitlab webhooks settings.
|
||||
- Bitbucket integration:
|
||||
+ Create Admin Panel with the Bitbucket webhooks settings.
|
||||
- Added team members section.
|
||||
- Taskboard enhancements: Collapse of columns (task statuses) and rows (user stories).
|
||||
- Use enter to submit lightboxes forms.
|
||||
|
||||
### Misc
|
||||
- Upgrade to AngularJS 1.3.
|
||||
- Lots of small and not so small bugfixes.
|
||||
|
||||
|
||||
## 1.3.0 Dryas hookeriana (2014-11-18)
|
||||
|
||||
### Features
|
||||
- GitHub integration (Phase I):
|
||||
+ Add button to login/singin with a GitHub account.
|
||||
+ Create Admin Panel with the GitHub webhooks settings.
|
||||
- Show/Hide columns in the Kanban view.
|
||||
- Differentiate blocked user stories on a milestone.
|
||||
|
||||
### Misc
|
||||
|
|
|
@ -63,6 +63,10 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
$routeProvider.when("/project/:pslug/wiki/:slug",
|
||||
{templateUrl: "/partials/wiki.html", resolve: {loader: tgLoaderProvider.add()}})
|
||||
|
||||
# Team
|
||||
$routeProvider.when("/project/:pslug/team",
|
||||
{templateUrl: "/partials/views/team/team.html", resolve: {loader: tgLoaderProvider.add()}})
|
||||
|
||||
# Issues
|
||||
$routeProvider.when("/project/:pslug/issues",
|
||||
{templateUrl: "/partials/issues.html", resolve: {loader: tgLoaderProvider.add()}})
|
||||
|
@ -96,6 +100,10 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
{templateUrl: "/partials/admin-roles.html"})
|
||||
$routeProvider.when("/project/:pslug/admin/third-parties/github",
|
||||
{templateUrl: "/partials/admin-third-parties-github.html"})
|
||||
$routeProvider.when("/project/:pslug/admin/third-parties/gitlab",
|
||||
{templateUrl: "/partials/admin-third-parties-gitlab.html"})
|
||||
$routeProvider.when("/project/:pslug/admin/third-parties/bitbucket",
|
||||
{templateUrl: "/partials/admin-third-parties-bitbucket.html"})
|
||||
|
||||
# User settings
|
||||
$routeProvider.when("/project/:pslug/user-settings/user-profile",
|
||||
|
@ -134,7 +142,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
{templateUrl: "/partials/permission-denied.html"})
|
||||
|
||||
$routeProvider.otherwise({redirectTo: '/not-found'})
|
||||
$locationProvider.html5Mode(true)
|
||||
$locationProvider.html5Mode({enabled: true, requireBase: false})
|
||||
|
||||
defaultHeaders = {
|
||||
"Content-Type": "application/json"
|
||||
|
@ -153,9 +161,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
$tgEventsProvider.setSessionId(taiga.sessionId)
|
||||
|
||||
# Add next param when user try to access to a secction need auth permissions.
|
||||
authHttpIntercept = ($q, $location, $confirm, $navUrls, $lightboxService) ->
|
||||
return (promise) ->
|
||||
return promise.then null, (response) ->
|
||||
authHttpIntercept = ($q, $location, $navUrls, $lightboxService) ->
|
||||
httpResponseError = (response) ->
|
||||
if response.status == 0
|
||||
$lightboxService.closeAll()
|
||||
$location.path($navUrls.resolve("error"))
|
||||
|
@ -163,11 +170,36 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
else if response.status == 401
|
||||
nextPath = $location.path()
|
||||
$location.url($navUrls.resolve("login")).search("next=#{nextPath}")
|
||||
|
||||
return $q.reject(response)
|
||||
|
||||
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgConfirm", "$tgNavUrls",
|
||||
"lightboxService", authHttpIntercept])
|
||||
$httpProvider.responseInterceptors.push('authHttpIntercept')
|
||||
return {
|
||||
responseError: httpResponseError
|
||||
}
|
||||
|
||||
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgNavUrls", "lightboxService", authHttpIntercept])
|
||||
|
||||
$httpProvider.interceptors.push('authHttpIntercept');
|
||||
|
||||
# If there is an error in the version throw a notify error
|
||||
versionCheckHttpIntercept = ($q, $confirm) ->
|
||||
versionErrorMsg = "Someone inside Taiga has changed this before and our Oompa Loompas cannot apply your changes. Please reload and apply your changes again (they will be lost)." #TODO: i18n
|
||||
|
||||
httpResponseError = (response) ->
|
||||
if response.status == 400 && response.data.version
|
||||
$confirm.notify("error", versionErrorMsg, null, 10000)
|
||||
|
||||
return $q.reject(response)
|
||||
|
||||
return $q.reject(response)
|
||||
|
||||
return {
|
||||
responseError: httpResponseError
|
||||
}
|
||||
|
||||
$provide.factory("versionCheckHttpIntercept", ["$q", "$tgConfirm", versionCheckHttpIntercept])
|
||||
|
||||
$httpProvider.interceptors.push('versionCheckHttpIntercept');
|
||||
|
||||
window.checksley.updateValidators({
|
||||
linewidth: (val, width) ->
|
||||
|
@ -210,6 +242,7 @@ modules = [
|
|||
"taigaIssues",
|
||||
"taigaUserStories",
|
||||
"taigaTasks",
|
||||
"taigaTeam",
|
||||
"taigaWiki",
|
||||
"taigaSearch",
|
||||
"taigaAdmin",
|
||||
|
|
|
@ -30,7 +30,7 @@ MAX_MEMBERSHIP_FIELDSETS = 4
|
|||
## Create Members Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) ->
|
||||
CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService) ->
|
||||
extraTextTemplate = """
|
||||
<fieldset class="extra-text">
|
||||
<textarea placeholder="(Optional) Add a personalized text to the invitation. Tell something lovely to your new members ;-)"></textarea>
|
||||
|
@ -103,15 +103,19 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) ->
|
|||
$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) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
onSuccess = (data) ->
|
||||
$loading.finish(submitButton)
|
||||
lightboxService.close($el)
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("membersform:new:success")
|
||||
|
||||
onError = (data) ->
|
||||
$loading.finish(submitButton)
|
||||
lightboxService.close($el)
|
||||
$confirm.notify("error")
|
||||
$rootScope.$broadcast("membersform:new:error")
|
||||
|
@ -143,7 +147,12 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, lightboxService) ->
|
|||
|
||||
$rs.memberships.bulkCreateMemberships($scope.project.id, invitations, invitation_extra_text).then(onSuccess, onError)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgLbCreateMembers", ["$tgResources", "$rootScope", "$tgConfirm", "lightboxService",
|
||||
module.directive("tgLbCreateMembers", ["$tgResources", "$rootScope", "$tgConfirm", "$tgLoading", "lightboxService",
|
||||
CreateMembersDirective])
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
taiga = @.taiga
|
||||
|
||||
mixOf = @.taiga.mixOf
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaAdmin")
|
||||
|
||||
|
@ -47,7 +48,7 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
|
||||
@location, @navUrls, @analytics, @appTitle) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Manage Members" #i18n
|
||||
@scope.project = {}
|
||||
|
@ -301,8 +302,10 @@ MembershipsRowAdminCheckboxDirective = ($log, $repo, $confirm) ->
|
|||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
onError = (data) ->
|
||||
member.revert()
|
||||
$el.find(":checkbox").prop("checked", member.is_owner)
|
||||
$confirm.notify("error", data.is_owner[0])
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
member.is_owner = target.prop("checked")
|
||||
|
|
|
@ -27,6 +27,7 @@ toString = @.taiga.toString
|
|||
joinStr = @.taiga.joinStr
|
||||
groupBy = @.taiga.groupBy
|
||||
bindOnce = @.taiga.bindOnce
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
module = angular.module("taigaAdmin")
|
||||
|
||||
|
@ -95,14 +96,16 @@ module.controller("ProjectProfileController", ProjectProfileController)
|
|||
ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley({"onlyOneErrorElement": true})
|
||||
submit = (target) =>
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
return if not form.validate()
|
||||
|
||||
$loading.start(target)
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.save($scope.project)
|
||||
promise.then ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("success")
|
||||
newUrl = $navurls.resolve("project-admin-project-profile-details", {project: $scope.project.slug})
|
||||
$location.path(newUrl)
|
||||
|
@ -114,24 +117,51 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) ->
|
|||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
$el.on "submit", "form", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
submitButton = $el.find(".submit-button");
|
||||
|
||||
$el.on "click", ".default-values a.button-green", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
submit(target)
|
||||
|
||||
$el.on "click", ".project-details a.button-green", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
submit(target)
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation", ProjectProfileDirective])
|
||||
|
||||
#############################################################################
|
||||
## Project Default Values Directive
|
||||
#############################################################################
|
||||
|
||||
ProjectDefaultValuesDirective = ($repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley({"onlyOneErrorElement": true})
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
return if not form.validate()
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.save($scope.project)
|
||||
promise.then ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("success")
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(target)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgProjectDefaultValues", ["$tgRepo", "$tgConfirm", "$tgLoading", ProjectDefaultValuesDirective])
|
||||
|
||||
#############################################################################
|
||||
## Project Modules Directive
|
||||
|
|
|
@ -208,7 +208,7 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
|
|||
animationFrame.add () ->
|
||||
goToBottomList()
|
||||
|
||||
$el.find(".new-value").hide()
|
||||
$el.find(".new-value").addClass("hidden")
|
||||
initializeNewValue()
|
||||
|
||||
promise.then null, (data) ->
|
||||
|
|
|
@ -24,6 +24,7 @@ taiga = @.taiga
|
|||
mixOf = @.taiga.mixOf
|
||||
bindOnce = @.taiga.bindOnce
|
||||
debounce = @.taiga.debounce
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaAdmin")
|
||||
|
||||
|
@ -47,7 +48,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
|
|||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Permissions" #i18n
|
||||
@scope.project = {}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
taiga = @.taiga
|
||||
|
||||
mixOf = @.taiga.mixOf
|
||||
bindMethods = @.taiga.bindMethods
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
module = angular.module("taigaAdmin")
|
||||
|
||||
|
@ -40,11 +42,10 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
]
|
||||
|
||||
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Github" #i18n
|
||||
@scope.project = {}
|
||||
@scope.anyComputableRole = true
|
||||
|
||||
promise = @.loadInitialData()
|
||||
|
||||
|
@ -61,8 +62,6 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
@scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable))
|
||||
|
||||
return project
|
||||
|
||||
loadInitialData: ->
|
||||
|
@ -76,6 +75,106 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
module.controller("GithubController", GithubController)
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Gitlab Controller
|
||||
#############################################################################
|
||||
|
||||
class GitlabController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"$tgRepo",
|
||||
"$tgResources",
|
||||
"$routeParams",
|
||||
"$appTitle"
|
||||
]
|
||||
|
||||
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Gitlab" #i18n
|
||||
@scope.project = {}
|
||||
promise = @.loadInitialData()
|
||||
|
||||
promise.then () =>
|
||||
@appTitle.set("Gitlab - " + @scope.project.name)
|
||||
|
||||
promise.then null, @.onInitialDataError.bind(@)
|
||||
|
||||
@scope.$on "project:modules:reload", =>
|
||||
@.loadModules()
|
||||
|
||||
loadModules: ->
|
||||
return @rs.modules.list(@scope.projectId, "gitlab").then (gitlab) =>
|
||||
@scope.gitlab = gitlab
|
||||
|
||||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
return project
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
.then(=> @.loadModules())
|
||||
|
||||
|
||||
module.controller("GitlabController", GitlabController)
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Bitbucket Controller
|
||||
#############################################################################
|
||||
|
||||
class BitbucketController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"$tgRepo",
|
||||
"$tgResources",
|
||||
"$routeParams",
|
||||
"$appTitle"
|
||||
]
|
||||
|
||||
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Bitbucket" #i18n
|
||||
@scope.project = {}
|
||||
promise = @.loadInitialData()
|
||||
|
||||
promise.then () =>
|
||||
@appTitle.set("Bitbucket - " + @scope.project.name)
|
||||
|
||||
promise.then null, @.onInitialDataError.bind(@)
|
||||
|
||||
@scope.$on "project:modules:reload", =>
|
||||
@.loadModules()
|
||||
|
||||
loadModules: ->
|
||||
return @rs.modules.list(@scope.projectId, "bitbucket").then (bitbucket) =>
|
||||
@scope.bitbucket = bitbucket
|
||||
|
||||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
return project
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
.then(=> @.loadModules())
|
||||
|
||||
module.controller("BitbucketController", BitbucketController)
|
||||
|
||||
|
||||
SelectInputText = ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$el.on "click", ".select-input-content", () ->
|
||||
|
@ -86,6 +185,7 @@ SelectInputText = ->
|
|||
|
||||
module.directive("tgSelectInputText", SelectInputText)
|
||||
|
||||
|
||||
#############################################################################
|
||||
## GithubWebhooks Directive
|
||||
#############################################################################
|
||||
|
@ -93,31 +193,122 @@ module.directive("tgSelectInputText", SelectInputText)
|
|||
GithubWebhooksDirective = ($repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley({"onlyOneErrorElement": true})
|
||||
submit = (target) =>
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
return if not form.validate()
|
||||
|
||||
$loading.start(target)
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.saveAttribute($scope.github, "github")
|
||||
promise.then ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("success")
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
$el.on "click", "a.button-green", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
submit(target)
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgGithubWebhooks", ["$tgRepo", "$tgConfirm", "$tgLoading", GithubWebhooksDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## GitlabWebhooks Directive
|
||||
#############################################################################
|
||||
|
||||
GitlabWebhooksDirective = ($repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley({"onlyOneErrorElement": true})
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
return if not form.validate()
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.saveAttribute($scope.gitlab, "gitlab")
|
||||
promise.then ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("success")
|
||||
$scope.$emit("project:modules:reload")
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgGitlabWebhooks", ["$tgRepo", "$tgConfirm", "$tgLoading", GitlabWebhooksDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## BitbucketWebhooks Directive
|
||||
#############################################################################
|
||||
|
||||
BitbucketWebhooksDirective = ($repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley({"onlyOneErrorElement": true})
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
return if not form.validate()
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.saveAttribute($scope.bitbucket, "bitbucket")
|
||||
promise.then ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("success")
|
||||
$scope.$emit("project:modules:reload")
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgBitbucketWebhooks", ["$tgRepo", "$tgConfirm", "$tgLoading", BitbucketWebhooksDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Valid Origin IP's Directive
|
||||
#############################################################################
|
||||
ValidOriginIpsDirective = ->
|
||||
link = ($scope, $el, $attrs, $ngModel) ->
|
||||
$ngModel.$parsers.push (value) ->
|
||||
value = $.trim(value)
|
||||
if value == ""
|
||||
return []
|
||||
|
||||
return value.split(",")
|
||||
|
||||
return {
|
||||
link: link
|
||||
restrict: "EA"
|
||||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgValidOriginIps", ValidOriginIpsDirective)
|
||||
|
|
|
@ -189,7 +189,9 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
|
|||
onError = (response) ->
|
||||
$confirm.notify("light-error", "According to our Oompa Loompas, your username/email
|
||||
or password are incorrect.") #TODO: i18n
|
||||
submit = ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = new checksley.Form($el.find("form.login-form"))
|
||||
if not form.validate()
|
||||
return
|
||||
|
@ -202,13 +204,8 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
|
|||
promise = $auth.login(data)
|
||||
return promise.then(onSuccess, onError)
|
||||
|
||||
$el.on "click", "a.button-login", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "submit", "form", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
@ -239,20 +236,17 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics)
|
|||
|
||||
form.setErrors(response.data)
|
||||
|
||||
submit = debounce 2000, =>
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
promise = $auth.register($scope.data)
|
||||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "click", "a.button-register", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
@ -279,20 +273,17 @@ ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls) ->
|
|||
$confirm.notify("light-error", "According to our Oompa Loompas,
|
||||
your are not registered yet.") #TODO: i18n
|
||||
|
||||
submit = debounce 2000, =>
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
promise = $auth.forgotPassword($scope.data)
|
||||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "click", "a.button-forgot", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
@ -324,20 +315,17 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav
|
|||
$confirm.notify("light-error", "One of our Oompa Loompas say
|
||||
'#{response.data._error_message}'.") #TODO: i18n
|
||||
|
||||
submit = debounce 2000, =>
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
promise = $auth.changePasswordFromRecovery($scope.data)
|
||||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "click", "a.button-change-password", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
@ -375,20 +363,17 @@ 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 = debounce 2000, =>
|
||||
submitLogin = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not loginForm.validate()
|
||||
return
|
||||
|
||||
promise = $auth.acceptInvitiationWithExistingUser($scope.dataLogin)
|
||||
promise.then(onSuccessSubmitLogin, onErrorSubmitLogin)
|
||||
|
||||
$el.on "submit", "form.login-form", (event) ->
|
||||
event.preventDefault()
|
||||
submitLogin()
|
||||
|
||||
$el.on "click", "a.button-login", (event) ->
|
||||
event.preventDefault()
|
||||
submitLogin()
|
||||
$el.on "submit", "form.login-form", submitLogin
|
||||
$el.on "click", ".button-login", submitLogin
|
||||
|
||||
# Register form
|
||||
$scope.dataRegister = {token: token}
|
||||
|
@ -404,20 +389,17 @@ 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 = debounce 2000, =>
|
||||
submitRegister = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not registerForm.validate()
|
||||
return
|
||||
|
||||
promise = $auth.acceptInvitiationWithNewUser($scope.dataRegister)
|
||||
promise.then(onSuccessSubmitRegister, onErrorSubmitRegister)
|
||||
|
||||
$el.on "submit", "form.register-form", (event) ->
|
||||
event.preventDefault()
|
||||
submitRegister
|
||||
|
||||
$el.on "click", "a.button-register", (event) ->
|
||||
event.preventDefault()
|
||||
submitRegister()
|
||||
$el.on "submit", "form.register-form", submitRegister
|
||||
$el.on "click", ".button-register", submitRegister
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
@ -483,20 +465,17 @@ CancelAccountDirective = ($repo, $model, $auth, $confirm, $location, $params, $n
|
|||
$confirm.notify("error", "One of our Oompa Loompas says
|
||||
'#{response.data._error_message}'.") #TODO: i18n
|
||||
|
||||
submit = ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
promise = $auth.cancelAccount($scope.data)
|
||||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "click", "a.button-cancel-account", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
|
|||
estimated_finish: null
|
||||
}
|
||||
|
||||
submit = (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
form = $el.find("form").checksley()
|
||||
|
||||
|
@ -65,17 +67,17 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
|
|||
promise = $repo.save(newSprint)
|
||||
broadcastEvent = "sprintform:edit:success"
|
||||
|
||||
$loading.start(target)
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise.then (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
$scope.sprintsCounter += 1 if createSprint
|
||||
$rootscope.$broadcast(broadcastEvent, data)
|
||||
|
||||
lightboxService.close($el)
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
|
@ -152,9 +154,10 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
|
|||
else
|
||||
$el.find(".last-sprint-name").removeClass("disappear")
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
submit(event)
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$el.on "click", ".delete-sprint .icon-delete", (event) ->
|
||||
event.preventDefault()
|
||||
|
|
|
@ -27,6 +27,7 @@ scopeDefer = @.taiga.scopeDefer
|
|||
bindOnce = @.taiga.bindOnce
|
||||
groupBy = @.taiga.groupBy
|
||||
timeout = @.taiga.timeout
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaBacklog")
|
||||
|
||||
|
@ -53,7 +54,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
|
||||
@location, @appTitle, @navUrls, @events, @analytics, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Backlog"
|
||||
@showTags = false
|
||||
|
@ -389,14 +390,14 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
# Rehash userstories order field
|
||||
# and persist in bulk all changes.
|
||||
promise = @q.all.apply(null, promises).then =>
|
||||
promise = @q.all(promises).then =>
|
||||
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
|
||||
data = @.prepareBulkUpdateData(items, "sprint_order")
|
||||
|
||||
return @rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
return @rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
|
||||
for us in usList
|
||||
@rootscope.$broadcast("sprint:us:moved", us, oldSprintId, newSprintId)
|
||||
|
||||
|
@ -444,6 +445,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
return obj
|
||||
|
||||
plainStatuses = _.map(@scope.userstories, "status")
|
||||
|
||||
plainStatuses = _.filter plainStatuses, (status) =>
|
||||
if status
|
||||
return status
|
||||
|
|
|
@ -58,9 +58,7 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm) ->
|
|||
containment: ".wrapper"
|
||||
dropOnEmpty: true
|
||||
placeholder: "row us-item-row us-item-drag sortable-placeholder"
|
||||
# With scroll activated, it has strange behavior
|
||||
# with not full screen browser window.
|
||||
scroll: false
|
||||
scroll: true
|
||||
# A consequence of length of backlog user story item
|
||||
# the default tolerance ("intersection") not works properly.
|
||||
tolerance: "pointer"
|
||||
|
@ -153,6 +151,7 @@ SprintSortableDirective = ($repo, $rs, $rootscope) ->
|
|||
# If the user has not enough permissions we don't enable the sortable
|
||||
if project.my_permissions.indexOf("modify_us") > -1
|
||||
$el.sortable({
|
||||
scroll: true
|
||||
dropOnEmpty: true
|
||||
connectWith: ".sprint-table,.backlog-table-body,.empty-backlog"
|
||||
})
|
||||
|
|
|
@ -29,23 +29,44 @@ module = angular.module("taigaBacklog")
|
|||
#############################################################################
|
||||
|
||||
BacklogSprintDirective = ($repo, $rootscope) ->
|
||||
sprintTableMinHeight = 50
|
||||
slideOptions = {
|
||||
duration: 500,
|
||||
easing: 'linear'
|
||||
}
|
||||
|
||||
refreshSprintTableHeight = (sprintTable) =>
|
||||
if !sprintTable.find(".row").length
|
||||
sprintTable.css("height", sprintTableMinHeight)
|
||||
else
|
||||
sprintTable.css("height", "auto")
|
||||
|
||||
toggleSprint = ($el) =>
|
||||
sprintTable = $el.find(".sprint-table")
|
||||
sprintArrow = $el.find(".icon-arrow-up")
|
||||
|
||||
sprintArrow.toggleClass('active')
|
||||
sprintTable.toggleClass('open')
|
||||
|
||||
refreshSprintTableHeight(sprintTable)
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$scope.$watch $attrs.tgBacklogSprint, (sprint) ->
|
||||
sprint = $scope.$eval($attrs.tgBacklogSprint)
|
||||
|
||||
if $scope.$first
|
||||
$el.addClass("sprint-current")
|
||||
$el.find(".sprint-table").addClass('open')
|
||||
toggleSprint($el)
|
||||
else if sprint.closed
|
||||
$el.addClass("sprint-closed")
|
||||
else if not $scope.$first and not sprint.closed
|
||||
toggleSprint($el)
|
||||
$el.addClass("sprint-old-open")
|
||||
|
||||
# Event Handlers
|
||||
$el.on "click", ".sprint-name > .icon-arrow-up", (event) ->
|
||||
target = $(event.currentTarget)
|
||||
target.toggleClass('active')
|
||||
$el.find(".sprint-table").toggleClass('open')
|
||||
toggleSprint($el)
|
||||
|
||||
$el.find(".sprint-table").slideToggle(slideOptions)
|
||||
|
||||
$el.on "click", ".sprint-name > .icon-edit", (event) ->
|
||||
sprint = $scope.$eval($attrs.tgBacklogSprint)
|
||||
|
|
|
@ -72,8 +72,11 @@ urls = {
|
|||
|
||||
"project-issues-detail": "/project/:project/issue/:ref"
|
||||
|
||||
"project-wiki": "/project/:project/wiki",
|
||||
"project-wiki-page": "/project/:project/wiki/:slug",
|
||||
"project-wiki": "/project/:project/wiki"
|
||||
"project-wiki-page": "/project/:project/wiki/:slug"
|
||||
|
||||
# Team
|
||||
"project-team": "/project/:project/team"
|
||||
|
||||
# Admin
|
||||
"project-admin-home": "/project/:project/admin/project-profile/details"
|
||||
|
@ -90,6 +93,8 @@ urls = {
|
|||
"project-admin-memberships": "/project/:project/admin/memberships"
|
||||
"project-admin-roles": "/project/:project/admin/roles"
|
||||
"project-admin-third-parties-github": "/project/:project/admin/third-parties/github"
|
||||
"project-admin-third-parties-gitlab": "/project/:project/admin/third-parties/gitlab"
|
||||
"project-admin-third-parties-bitbucket": "/project/:project/admin/third-parties/bitbucket"
|
||||
|
||||
# User settings
|
||||
"user-settings-user-profile": "/project/:project/user-settings/user-profile"
|
||||
|
|
|
@ -62,7 +62,7 @@ class RepositoryService extends taiga.Service
|
|||
|
||||
saveAll: (models, patch=true) ->
|
||||
promises = _.map(models, (x) => @.save(x, true))
|
||||
return @q.all.apply(@q, promises)
|
||||
return @q.all(promises)
|
||||
|
||||
save: (model, patch=true) ->
|
||||
defered = @q.defer()
|
||||
|
@ -143,6 +143,7 @@ class RepositoryService extends taiga.Service
|
|||
queryMany: (name, params, options={}) ->
|
||||
url = @urls.resolve(name)
|
||||
httpOptions = {headers: {}}
|
||||
|
||||
if not options.enablePagination
|
||||
httpOptions.headers["x-disable-pagination"] = "1"
|
||||
|
||||
|
|
|
@ -23,6 +23,21 @@ taiga = @.taiga
|
|||
|
||||
module = angular.module("taigaCommon", [])
|
||||
|
||||
#############################################################################
|
||||
## Get the selected text
|
||||
#############################################################################
|
||||
SelectedText = ($window, $document) ->
|
||||
get = () ->
|
||||
if $window.getSelection
|
||||
return $window.getSelection().toString()
|
||||
else if $document.selection
|
||||
return $document.selection.createRange().text
|
||||
return ""
|
||||
|
||||
return {get: get}
|
||||
|
||||
module.factory("$selectedText", ["$window", "$document", SelectedText])
|
||||
|
||||
#############################################################################
|
||||
## Permission directive, hide elements when necessary
|
||||
#############################################################################
|
||||
|
@ -143,3 +158,32 @@ LimitLineLengthDirective = () ->
|
|||
return {link:link}
|
||||
|
||||
module.directive("tgLimitLineLength", LimitLineLengthDirective)
|
||||
|
||||
#############################################################################
|
||||
## Queue Q promises
|
||||
#############################################################################
|
||||
|
||||
Qqueue = ($q) ->
|
||||
deferred = $q.defer()
|
||||
deferred.resolve()
|
||||
|
||||
lastPromise = deferred.promise
|
||||
|
||||
qqueue = {
|
||||
bindAdd: (fn) =>
|
||||
return (args...) =>
|
||||
lastPromise = lastPromise.then () => fn.apply(@, args)
|
||||
|
||||
return qqueue
|
||||
add: (fn) =>
|
||||
if !lastPromise
|
||||
lastPromise = fn()
|
||||
else
|
||||
lastPromise = lastPromise.then(fn)
|
||||
|
||||
return qqueue
|
||||
}
|
||||
|
||||
return qqueue
|
||||
|
||||
module.factory("$tgQqueue", ["$q", Qqueue])
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
taiga = @.taiga
|
||||
sizeFormat = @.taiga.sizeFormat
|
||||
bindOnce = @.taiga.bindOnce
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaCommon")
|
||||
|
||||
|
@ -30,7 +31,7 @@ class AttachmentsController extends taiga.Controller
|
|||
@.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q"]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
@.type = null
|
||||
@.objectId = null
|
||||
@.projectId = null
|
||||
|
@ -85,7 +86,7 @@ class AttachmentsController extends taiga.Controller
|
|||
# Create attachments in bulk
|
||||
createAttachments: (attachments) ->
|
||||
promises = _.map(attachments, (x) => @._createAttachment(x))
|
||||
return @q.all.apply(null, promises).then =>
|
||||
return @q.all(promises).then =>
|
||||
@.updateCounters()
|
||||
|
||||
# Add uploading attachment tracking.
|
||||
|
|
|
@ -161,7 +161,7 @@ module.directive("tgCreatedByDisplay", CreatedByDisplayDirective)
|
|||
## Watchers directive
|
||||
#############################################################################
|
||||
|
||||
WatchersDirective = ($rootscope, $confirm, $repo) ->
|
||||
WatchersDirective = ($rootscope, $confirm, $repo, $qqueue) ->
|
||||
# You have to include a div with the tg-lb-watchers directive in the page
|
||||
# where use this directive
|
||||
#
|
||||
|
@ -204,17 +204,37 @@ WatchersDirective = ($rootscope, $confirm, $repo) ->
|
|||
isEditable = ->
|
||||
return $scope.project?.my_permissions?.indexOf($attrs.requiredPerm) != -1
|
||||
|
||||
save = (model) ->
|
||||
save = $qqueue.bindAdd (watchers) =>
|
||||
item = $model.$modelValue.clone()
|
||||
item.watchers = watchers
|
||||
$model.$setViewValue(item)
|
||||
|
||||
promise = $repo.save($model.$modelValue)
|
||||
promise.then ->
|
||||
$confirm.notify("success")
|
||||
watchers = _.map(model.watchers, (watcherId) -> $scope.usersById[watcherId])
|
||||
watchers = _.map(watchers, (watcherId) -> $scope.usersById[watcherId])
|
||||
renderWatchers(watchers)
|
||||
$rootscope.$broadcast("history:reload")
|
||||
|
||||
promise.then null, ->
|
||||
$model.$modelValue.revert()
|
||||
|
||||
deleteWatcher = $qqueue.bindAdd (watcherIds) =>
|
||||
item = $model.$modelValue.clone()
|
||||
item.watchers = watcherIds
|
||||
$model.$setViewValue(item)
|
||||
|
||||
promise = $repo.save($model.$modelValue)
|
||||
promise.then ->
|
||||
$confirm.notify("success")
|
||||
watchers = _.map(item.watchers, (watcherId) -> $scope.usersById[watcherId])
|
||||
renderWatchers(watchers)
|
||||
$rootscope.$broadcast("history:reload")
|
||||
promise.then null, ->
|
||||
model.revert()
|
||||
item.revert()
|
||||
$confirm.notify("error")
|
||||
|
||||
|
||||
renderWatchers = (watchers) ->
|
||||
ctx = {
|
||||
watchers: watchers
|
||||
|
@ -239,13 +259,11 @@ WatchersDirective = ($rootscope, $confirm, $repo) ->
|
|||
|
||||
$confirm.askOnDelete(title, message).then (finish) =>
|
||||
finish()
|
||||
|
||||
watcherIds = _.clone($model.$modelValue.watchers, false)
|
||||
watcherIds = _.pull(watcherIds, watcherId)
|
||||
|
||||
item = $model.$modelValue.clone()
|
||||
item.watchers = watcherIds
|
||||
$model.$setViewValue(item)
|
||||
save(item)
|
||||
deleteWatcher(watcherIds)
|
||||
|
||||
$el.on "click", ".add-watcher", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -258,10 +276,7 @@ WatchersDirective = ($rootscope, $confirm, $repo) ->
|
|||
watchers.push(watcherId)
|
||||
watchers = _.uniq(watchers)
|
||||
|
||||
item = $model.$modelValue.clone()
|
||||
item.watchers = watchers
|
||||
$model.$setViewValue(item)
|
||||
save(item)
|
||||
save(watchers)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (item) ->
|
||||
return if not item?
|
||||
|
@ -273,14 +288,14 @@ WatchersDirective = ($rootscope, $confirm, $repo) ->
|
|||
|
||||
return {link:link, require:"ngModel"}
|
||||
|
||||
module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", WatchersDirective])
|
||||
module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQqueue", WatchersDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Assigned to directive
|
||||
#############################################################################
|
||||
|
||||
AssignedToDirective = ($rootscope, $confirm, $repo, $loading) ->
|
||||
AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue) ->
|
||||
# You have to include a div with the tg-lb-assignedto directive in the page
|
||||
# where use this directive
|
||||
#
|
||||
|
@ -315,20 +330,24 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading) ->
|
|||
isEditable = ->
|
||||
return $scope.project?.my_permissions?.indexOf($attrs.requiredPerm) != -1
|
||||
|
||||
save = (model) ->
|
||||
save = $qqueue.bindAdd (userId) =>
|
||||
$model.$modelValue.assigned_to = userId
|
||||
|
||||
$loading.start($el)
|
||||
|
||||
promise = $repo.save($model.$modelValue)
|
||||
promise.then ->
|
||||
$loading.finish($el)
|
||||
$confirm.notify("success")
|
||||
renderAssignedTo(model)
|
||||
renderAssignedTo($model.$modelValue)
|
||||
$rootscope.$broadcast("history:reload")
|
||||
promise.then null, ->
|
||||
model.revert()
|
||||
$model.$modelValue.revert()
|
||||
$confirm.notify("error")
|
||||
$loading.finish($el)
|
||||
|
||||
return promise
|
||||
|
||||
renderAssignedTo = (issue) ->
|
||||
assignedToId = issue?.assigned_to
|
||||
assignedTo = if assignedToId? then $scope.usersById[assignedToId] else null
|
||||
|
@ -354,12 +373,12 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading) ->
|
|||
$confirm.ask(title).then (finish) =>
|
||||
finish()
|
||||
$model.$modelValue.assigned_to = null
|
||||
save($model.$modelValue)
|
||||
save(null)
|
||||
|
||||
$scope.$on "assigned-to:added", (ctx, userId, item) ->
|
||||
return if item.id != $model.$modelValue.id
|
||||
$model.$modelValue.assigned_to = userId
|
||||
save($model.$modelValue)
|
||||
|
||||
save(userId)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (instance) ->
|
||||
renderAssignedTo(instance)
|
||||
|
@ -372,7 +391,7 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading) ->
|
|||
require:"ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", AssignedToDirective])
|
||||
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQqueue", AssignedToDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -473,7 +492,7 @@ module.directive("tgDeleteButton", ["$log", "$tgRepo", "$tgConfirm", "$tgLocatio
|
|||
## Editable subject directive
|
||||
#############################################################################
|
||||
|
||||
EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading) ->
|
||||
EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $qqueue) ->
|
||||
template = """
|
||||
<div class="view-subject">
|
||||
{{ item.subject }}
|
||||
|
@ -492,9 +511,11 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading) ->
|
|||
isEditable = ->
|
||||
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
|
||||
|
||||
save = ->
|
||||
$model.$modelValue.subject = $scope.item.subject
|
||||
save = $qqueue.bindAdd (subject) =>
|
||||
$model.$modelValue.subject = subject
|
||||
|
||||
$loading.start($el.find('.save-container'))
|
||||
|
||||
promise = $repo.save($model.$modelValue)
|
||||
promise.then ->
|
||||
$confirm.notify("success")
|
||||
|
@ -506,6 +527,8 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading) ->
|
|||
promise.finally ->
|
||||
$loading.finish($el.find('.save-container'))
|
||||
|
||||
return promise
|
||||
|
||||
$el.click ->
|
||||
return if not isEditable()
|
||||
$el.find('.edit-subject').show()
|
||||
|
@ -513,11 +536,13 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading) ->
|
|||
$el.find('input').focus()
|
||||
|
||||
$el.on "click", ".save", ->
|
||||
save()
|
||||
subject = $scope.item.subject
|
||||
save(subject)
|
||||
|
||||
$el.on "keyup", "input", (event) ->
|
||||
if event.keyCode == 13
|
||||
save()
|
||||
subject = $scope.item.subject
|
||||
save(subject)
|
||||
else if event.keyCode == 27
|
||||
$model.$modelValue.revert()
|
||||
$el.find('div.edit-subject').hide()
|
||||
|
@ -545,7 +570,7 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading) ->
|
|||
template: template
|
||||
}
|
||||
|
||||
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading",
|
||||
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
||||
EditableSubjectDirective])
|
||||
|
||||
|
||||
|
@ -553,7 +578,7 @@ module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$
|
|||
## Editable subject directive
|
||||
#############################################################################
|
||||
|
||||
EditableDescriptionDirective = ($window, $document, $rootscope, $repo, $confirm, $compile, $loading) ->
|
||||
EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading, $selectedText, $qqueue) ->
|
||||
template = """
|
||||
<div class="view-description">
|
||||
<section class="us-content wysiwyg"
|
||||
|
@ -564,6 +589,10 @@ EditableDescriptionDirective = ($window, $document, $rootscope, $repo, $confirm,
|
|||
<textarea placeholder="Empty space is so boring... go on be descriptive... A rose by any other name would smell as sweet..."
|
||||
ng-model="item.description"
|
||||
tg-markitup="tg-markitup"></textarea>
|
||||
<a class="help-markdown" href="https://taiga.io/support/taiga-markdown-syntax/" target="_blank" title="Mardown syntax help">
|
||||
<span class="icon icon-help"></span>
|
||||
<span>Markdown syntax help</span>
|
||||
</a>
|
||||
<span class="save-container">
|
||||
<a class="save icon icon-floppy" href="" title="Save" />
|
||||
</span>
|
||||
|
@ -589,27 +618,8 @@ EditableDescriptionDirective = ($window, $document, $rootscope, $repo, $confirm,
|
|||
isEditable = ->
|
||||
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
|
||||
|
||||
getSelectedText = ->
|
||||
if $window.getSelection
|
||||
return $window.getSelection().toString()
|
||||
else if $document.selection
|
||||
return $document.selection.createRange().text
|
||||
return null
|
||||
|
||||
$el.on "mouseup", ".view-description", (event) ->
|
||||
# We want to dettect the a inside the div so we use the target and
|
||||
# not the currentTarget
|
||||
target = angular.element(event.target)
|
||||
return if not isEditable()
|
||||
return if target.is('a')
|
||||
return if getSelectedText()
|
||||
|
||||
$el.find('.edit-description').show()
|
||||
$el.find('.view-description').hide()
|
||||
$el.find('textarea').focus()
|
||||
|
||||
$el.on "click", ".save", ->
|
||||
$model.$modelValue.description = $scope.item.description
|
||||
save = $qqueue.bindAdd (description) =>
|
||||
$model.$modelValue.description = description
|
||||
|
||||
$loading.start($el.find('.save-container'))
|
||||
promise = $repo.save($model.$modelValue)
|
||||
|
@ -623,6 +633,22 @@ EditableDescriptionDirective = ($window, $document, $rootscope, $repo, $confirm,
|
|||
promise.finally ->
|
||||
$loading.finish($el.find('.save-container'))
|
||||
|
||||
$el.on "mouseup", ".view-description", (event) ->
|
||||
# We want to dettect the a inside the div so we use the target and
|
||||
# not the currentTarget
|
||||
target = angular.element(event.target)
|
||||
return if not isEditable()
|
||||
return if target.is('a')
|
||||
return if $selectedText.get().length
|
||||
|
||||
$el.find('.edit-description').show()
|
||||
$el.find('.view-description').hide()
|
||||
$el.find('textarea').focus()
|
||||
|
||||
$el.on "click", ".save", ->
|
||||
description = $scope.item.description
|
||||
save(description)
|
||||
|
||||
$el.on "keyup", "textarea", (event) ->
|
||||
if event.keyCode == 27
|
||||
$scope.item.revert()
|
||||
|
@ -650,8 +676,8 @@ EditableDescriptionDirective = ($window, $document, $rootscope, $repo, $confirm,
|
|||
template: template
|
||||
}
|
||||
|
||||
module.directive("tgEditableDescription", ["$window", "$document", "$rootScope", "$tgRepo", "$tgConfirm",
|
||||
"$compile", "$tgLoading", EditableDescriptionDirective])
|
||||
module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm",
|
||||
"$compile", "$tgLoading", "$selectedText", "$tgQqueue", EditableDescriptionDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
|
|
@ -23,7 +23,7 @@ taiga = @.taiga
|
|||
timeout = @.taiga.timeout
|
||||
cancelTimeout = @.taiga.cancelTimeout
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
NOTIFICATION_MSG = {
|
||||
"success":
|
||||
|
@ -42,7 +42,7 @@ class ConfirmService extends taiga.Service
|
|||
@.$inject = ["$q", "lightboxService", "$tgLoading"]
|
||||
|
||||
constructor: (@q, @lightboxService, @loading) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
hide: (el)->
|
||||
if el
|
||||
|
@ -170,7 +170,7 @@ class ConfirmService extends taiga.Service
|
|||
|
||||
return defered.promise
|
||||
|
||||
notify: (type, message, title) ->
|
||||
notify: (type, message, title, time) ->
|
||||
# NOTE: Typesi are: error, success, light-error
|
||||
# See partials/components/notification-message.jade)
|
||||
# Add default texts to NOTIFICATION_MSG for new notification types
|
||||
|
@ -178,6 +178,8 @@ class ConfirmService extends taiga.Service
|
|||
selector = ".notification-message-#{type}"
|
||||
el = angular.element(selector)
|
||||
|
||||
return if el.hasClass("active")
|
||||
|
||||
if title
|
||||
el.find("h4").html(title)
|
||||
else
|
||||
|
@ -200,6 +202,7 @@ class ConfirmService extends taiga.Service
|
|||
if @.tsem
|
||||
cancelTimeout(@.tsem)
|
||||
|
||||
if !time
|
||||
time = if type == 'error' or type == 'light-error' then 3500 else 1500
|
||||
|
||||
@.tsem = timeout time, =>
|
||||
|
|
|
@ -165,7 +165,7 @@ module.directive("tgLbUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", LbU
|
|||
## User story estimation directive
|
||||
#############################################################################
|
||||
|
||||
UsEstimationDirective = ($rootScope, $repo, $confirm) ->
|
||||
UsEstimationDirective = ($rootScope, $repo, $confirm, $qqueue) ->
|
||||
# Display the points of a US and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -264,6 +264,29 @@ UsEstimationDirective = ($rootScope, $repo, $confirm) ->
|
|||
|
||||
return _.reduce(notNullValues, (acc, num) -> acc + num)
|
||||
|
||||
save = $qqueue.bindAdd (roleId, pointId) =>
|
||||
$el.find(".popover").popover().close()
|
||||
|
||||
# Hell starts here
|
||||
us = angular.copy($model.$modelValue)
|
||||
points = _.clone($model.$modelValue.points, true)
|
||||
points[roleId] = pointId
|
||||
us.setAttr('points', points)
|
||||
us.points = points
|
||||
us.total_points = calculateTotalPoints(us)
|
||||
$model.$setViewValue(us)
|
||||
# Hell ends here
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
us.revert()
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".total.clickable", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -287,26 +310,7 @@ UsEstimationDirective = ($rootScope, $repo, $confirm) ->
|
|||
roleId = target.data("role-id")
|
||||
pointId = target.data("point-id")
|
||||
|
||||
$el.find(".popover").popover().close()
|
||||
|
||||
# Hell starts here
|
||||
us = angular.copy($model.$modelValue)
|
||||
points = _.clone($model.$modelValue.points, true)
|
||||
points[roleId] = pointId
|
||||
us.setAttr('points', points)
|
||||
us.points = points
|
||||
us.total_points = calculateTotalPoints(us)
|
||||
$model.$setViewValue(us)
|
||||
# Hell ends here
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
us.revert()
|
||||
$model.$setViewValue(us)
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
save(roleId, pointId)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (us) ->
|
||||
render(us) if us
|
||||
|
@ -320,4 +324,4 @@ UsEstimationDirective = ($rootScope, $repo, $confirm) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", UsEstimationDirective])
|
||||
module.directive("tgUsEstimation", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", UsEstimationDirective])
|
||||
|
|
|
@ -61,7 +61,7 @@ class HistoryController extends taiga.Controller
|
|||
return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
|
||||
|
||||
|
||||
HistoryDirective = ($log, $loading) ->
|
||||
HistoryDirective = ($log, $loading, $qqueue) ->
|
||||
templateChangeDiff = _.template("""
|
||||
<div class="change-entry">
|
||||
<div class="activity-changed">
|
||||
|
@ -233,6 +233,10 @@ HistoryDirective = ($log, $loading) ->
|
|||
ng-model="<%- ngmodel %>.comment" tg-markitup="tg-markitup">
|
||||
</textarea>
|
||||
<% if (mode !== "edit") { %>
|
||||
<a class="help-markdown" href="https://taiga.io/support/taiga-markdown-syntax/" target="_blank" title="Mardown syntax help">
|
||||
<span class="icon icon-help"></span>
|
||||
<span>Markdown syntax help</span>
|
||||
</a>
|
||||
<a href="" title="Comment" class="button button-green save-comment">Comment</a>
|
||||
<% } %>
|
||||
</div>
|
||||
|
@ -432,6 +436,24 @@ HistoryDirective = ($log, $loading) ->
|
|||
html = renderHistory(changes, totalChanges)
|
||||
$el.find(".changes-list").html(html)
|
||||
|
||||
save = $qqueue.bindAdd (target) =>
|
||||
$scope.$broadcast("markdown-editor:submit")
|
||||
|
||||
$el.find(".comment-list").addClass("activeanimation")
|
||||
|
||||
onSuccess = ->
|
||||
$ctrl.loadHistory(type, objectId).finally ->
|
||||
$loading.finish(target)
|
||||
|
||||
onError = ->
|
||||
$loading.finish(target)
|
||||
$confirm.notify("error")
|
||||
|
||||
model = $scope.$eval($attrs.ngModel)
|
||||
$loading.start(target)
|
||||
|
||||
$ctrl.repo.save(model).then(onSuccess, onError)
|
||||
|
||||
# Watchers
|
||||
|
||||
$scope.$watch("comments", renderComments)
|
||||
|
@ -443,22 +465,10 @@ HistoryDirective = ($log, $loading) ->
|
|||
|
||||
$el.on "click", ".add-comment a.button-green", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
$scope.$broadcast("markdown-editor:submit")
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
$el.find(".comment-list").addClass("activeanimation")
|
||||
onSuccess = ->
|
||||
$ctrl.loadHistory(type, objectId).finally ->
|
||||
$loading.finish(target)
|
||||
|
||||
onError = ->
|
||||
$loading.finish(target)
|
||||
$confirm.notify("error")
|
||||
|
||||
model = $scope.$eval($attrs.ngModel)
|
||||
$loading.start(target)
|
||||
$ctrl.repo.save(model).then(onSuccess, onError)
|
||||
save(target)
|
||||
|
||||
$el.on "click", ".show-more", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -522,4 +532,4 @@ HistoryDirective = ($log, $loading) ->
|
|||
}
|
||||
|
||||
|
||||
module.directive("tgHistory", ["$log", "$tgLoading", HistoryDirective])
|
||||
module.directive("tgHistory", ["$log", "$tgLoading", "$tgQqueue", HistoryDirective])
|
||||
|
|
|
@ -126,19 +126,11 @@ module.directive("lightbox", ["lightboxService", LightboxDirective])
|
|||
|
||||
# Issue/Userstory blocking message lightbox directive.
|
||||
|
||||
BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loading) ->
|
||||
BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loading, $qqueue) ->
|
||||
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", (event, model, finishCallback) ->
|
||||
item = $model.$modelValue.clone()
|
||||
item.is_blocked = false
|
||||
item.blocked_note = ""
|
||||
|
||||
unblock = $qqueue.bindAdd (item, finishCallback) =>
|
||||
promise = $tgrepo.save(item)
|
||||
promise.then ->
|
||||
$confirm.notify("success")
|
||||
|
@ -154,15 +146,9 @@ BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loadi
|
|||
promise.finally ->
|
||||
finishCallback()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
return promise
|
||||
|
||||
$el.on "click", ".button-green", (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
item = $model.$modelValue.clone()
|
||||
item.is_blocked = true
|
||||
item.blocked_note = $el.find(".reason").val()
|
||||
block = $qqueue.bindAdd (item) =>
|
||||
$model.$setViewValue(item)
|
||||
|
||||
$loading.start($el.find(".button-green"))
|
||||
|
@ -181,13 +167,36 @@ BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loadi
|
|||
$loading.finish($el.find(".button-green"))
|
||||
lightboxService.close($el)
|
||||
|
||||
$scope.$on "block", ->
|
||||
$el.find(".reason").val($model.$modelValue.blocked_note)
|
||||
lightboxService.open($el)
|
||||
|
||||
$scope.$on "unblock", (event, model, finishCallback) =>
|
||||
item = $model.$modelValue.clone()
|
||||
item.is_blocked = false
|
||||
item.blocked_note = ""
|
||||
|
||||
unblock(item, finishCallback)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
$el.on "click", ".button-green", (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
item = $model.$modelValue.clone()
|
||||
item.is_blocked = true
|
||||
item.blocked_note = $el.find(".reason").val()
|
||||
|
||||
block(item)
|
||||
|
||||
return {
|
||||
templateUrl: "/partials/views/modules/lightbox-block.html"
|
||||
link: link
|
||||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgLbBlock", ["$rootScope", "$tgRepo", "$tgConfirm", "lightboxService", "$tgLoading", BlockLightboxDirective])
|
||||
module.directive("tgLbBlock", ["$rootScope", "$tgRepo", "$tgConfirm", "lightboxService", "$tgLoading", "$tgQqueue", BlockLightboxDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
@ -285,15 +294,14 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
|
|||
|
||||
lightboxService.open($el)
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
form = $el.find("form").checksley()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(target)
|
||||
$loading.start(submitButton)
|
||||
|
||||
if $scope.isNew
|
||||
promise = $repo.create("userstories", $scope.us)
|
||||
|
@ -303,16 +311,21 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
|
|||
broadcastEvent = "usform:edit:success"
|
||||
|
||||
promise.then (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
lightboxService.close($el)
|
||||
$rootScope.$broadcast(broadcastEvent, data)
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$el.on "click", ".close", (event) ->
|
||||
event.preventDefault()
|
||||
$scope.$apply ->
|
||||
|
@ -356,28 +369,32 @@ CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $load
|
|||
}
|
||||
lightboxService.open($el)
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
form = $el.find("form").checksley({onlyOneErrorElement: true})
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(target)
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $rs.userstories.bulkCreate($scope.new.projectId, $scope.new.statusId, $scope.new.bulk)
|
||||
promise.then (result) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
$rootscope.$broadcast("usform:bulk:success", result)
|
||||
lightboxService.close($el)
|
||||
|
||||
promise.then null, (data) ->
|
||||
$loading.finish(target)
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ module.directive("tgLbTagLine", ["$tgResources", LbTagLineDirective])
|
|||
## TagLine Directive (for detail pages)
|
||||
#############################################################################
|
||||
|
||||
TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
||||
TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue) ->
|
||||
ENTER_KEY = 13
|
||||
ESC_KEY = 27
|
||||
|
||||
|
@ -288,7 +288,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
|||
$el.find("input").autocomplete("close")
|
||||
|
||||
## Aux methods
|
||||
addValue = (value) ->
|
||||
addValue = $qqueue.bindAdd (value) ->
|
||||
value = trim(value.toLowerCase())
|
||||
return if value.length == 0
|
||||
|
||||
|
@ -308,7 +308,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
|||
$model.$setViewValue(model)
|
||||
$repo.save(model).then(onSuccess, onError)
|
||||
|
||||
deleteValue = (value) ->
|
||||
deleteValue = $qqueue.bindAdd (value) ->
|
||||
value = trim(value.toLowerCase())
|
||||
return if value.length == 0
|
||||
|
||||
|
@ -325,7 +325,8 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
|||
$confirm.notify("error")
|
||||
model.revert()
|
||||
$model.$setViewValue(model)
|
||||
$repo.save(model).then(onSuccess, onError)
|
||||
|
||||
return $repo.save(model).then(onSuccess, onError)
|
||||
|
||||
saveInputTag = () ->
|
||||
value = $el.find("input").val()
|
||||
|
@ -369,6 +370,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
|||
target = angular.element(event.currentTarget)
|
||||
|
||||
value = target.siblings(".tag-name").text()
|
||||
|
||||
deleteValue(value)
|
||||
|
||||
bindOnce $scope, "project", (project) ->
|
||||
|
@ -415,4 +417,4 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm) ->
|
|||
template: template
|
||||
}
|
||||
|
||||
module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", TagLineDirective])
|
||||
module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$tgQqueue", TagLineDirective])
|
||||
|
|
|
@ -28,7 +28,7 @@ module = angular.module("taigaCommon")
|
|||
#############################################################################
|
||||
## WYSIWYG markitup editor directive
|
||||
#############################################################################
|
||||
tgMarkitupDirective = ($rootscope, $rs, $tr) ->
|
||||
tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText) ->
|
||||
previewTemplate = _.template("""
|
||||
<div class="preview">
|
||||
<div class="actions">
|
||||
|
@ -61,10 +61,16 @@ tgMarkitupDirective = ($rootscope, $rs, $tr) ->
|
|||
markdownDomNode.append(previewTemplate({data: data.data}))
|
||||
markItUpDomNode.hide()
|
||||
|
||||
# FIXME: Really `.parents()` is need? seems `.closest`
|
||||
# function is better aproach for it
|
||||
element.parents(".markdown").one "click", ".preview", (event) ->
|
||||
markdown = element.closest(".markdown")
|
||||
|
||||
markdown.on "mouseup.preview", ".preview", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.target)
|
||||
|
||||
if !target.is('a') and $selectedText.get().length
|
||||
return
|
||||
|
||||
markdown.off(".preview")
|
||||
closePreviewMode()
|
||||
|
||||
markdownCaretPositon = false
|
||||
|
@ -277,4 +283,4 @@ tgMarkitupDirective = ($rootscope, $rs, $tr) ->
|
|||
|
||||
return {link:link, require:"ngModel"}
|
||||
|
||||
module.directive("tgMarkitup", ["$rootScope", "$tgResources", "$tgI18n", tgMarkitupDirective])
|
||||
module.directive("tgMarkitup", ["$rootScope", "$tgResources", "$tgI18n", "$selectedText", tgMarkitupDirective])
|
||||
|
|
|
@ -20,13 +20,15 @@
|
|||
###
|
||||
|
||||
taiga = @.taiga
|
||||
startswith = @.taiga.startswith
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaEvents", [])
|
||||
|
||||
|
||||
class EventsService
|
||||
constructor: (@win, @log, @config, @auth) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
initialize: (sessionId) ->
|
||||
@.sessionId = sessionId
|
||||
|
@ -41,7 +43,18 @@ class EventsService
|
|||
setupConnection: ->
|
||||
@.stopExistingConnection()
|
||||
|
||||
url = @config.get("eventsUrl", "ws://localhost:8888/events")
|
||||
url = @config.get("eventsUrl")
|
||||
|
||||
# This allows disable events in case
|
||||
# url is not found on the configuration.
|
||||
return if not url
|
||||
|
||||
# This allows relative urls in configuration.
|
||||
if not startswith(url, "ws:") and not startswith(url, "wss:")
|
||||
loc = @win.location
|
||||
scheme = if loc.protocol == "https:" then "wss:" else "ws:"
|
||||
path = _.str.ltrim(url, "/")
|
||||
url = "#{scheme}//#{loc.host}/#{path}"
|
||||
|
||||
@.ws = new @win.WebSocket(url)
|
||||
@.ws.addEventListener("open", @.onOpen)
|
||||
|
|
|
@ -29,29 +29,33 @@ trim = @.taiga.trim
|
|||
|
||||
module = angular.module("taigaFeedback", [])
|
||||
|
||||
FeedbackDirective = ($lightboxService, $repo, $confirm)->
|
||||
FeedbackDirective = ($lightboxService, $repo, $confirm, $loading)->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley()
|
||||
|
||||
submit = debounce 2000, ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.create("feedback", $scope.feedback)
|
||||
|
||||
promise.then (data) ->
|
||||
$loading.finish(submitButton)
|
||||
$lightboxService.close($el)
|
||||
$confirm.notify("success", "\\o/ we'll be happy to read your")
|
||||
|
||||
promise.then null, ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("error")
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
submit()
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "click", ".button-green", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "feedback:show", ->
|
||||
$scope.$apply ->
|
||||
|
@ -65,4 +69,4 @@ FeedbackDirective = ($lightboxService, $repo, $confirm)->
|
|||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgLbFeedback", ["lightboxService", "$tgRepo", "$tgConfirm", FeedbackDirective])
|
||||
module.directive("tgLbFeedback", ["lightboxService", "$tgRepo", "$tgConfirm", "$tgLoading", FeedbackDirective])
|
||||
|
|
|
@ -195,7 +195,7 @@ module.directive("tgIssueStatusDisplay", IssueStatusDisplayDirective)
|
|||
## Issue status button directive
|
||||
#############################################################################
|
||||
|
||||
IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the status of Issue and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -236,6 +236,21 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
save = $qqueue.bindAdd (value, issue) =>
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
$loading.start($el.find(".level-name"))
|
||||
|
||||
$repo.save(value).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".status-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -256,20 +271,7 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
issue.status = target.data("status-id")
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
save($model.$modelValue, issue)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (issue) ->
|
||||
render(issue) if issue
|
||||
|
@ -283,13 +285,13 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgIssueStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", IssueStatusButtonDirective])
|
||||
module.directive("tgIssueStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", IssueStatusButtonDirective])
|
||||
|
||||
#############################################################################
|
||||
## Issue type button directive
|
||||
#############################################################################
|
||||
|
||||
IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the type of Issue and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -330,6 +332,26 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
save = $qqueue.bindAdd (type) =>
|
||||
$.fn.popover().closeAll()
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.type = type
|
||||
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".type-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -343,26 +365,8 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
return if not isEditable()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.type = target.data("type-id")
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
type = target.data("type-id")
|
||||
save(type)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (issue) ->
|
||||
render(issue) if issue
|
||||
|
@ -376,14 +380,14 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgIssueTypeButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", IssueTypeButtonDirective])
|
||||
module.directive("tgIssueTypeButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", IssueTypeButtonDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Issue severity button directive
|
||||
#############################################################################
|
||||
|
||||
IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the severity of Issue and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -424,6 +428,26 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
save = $qqueue.bindAdd (severity) =>
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.severity = severity
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".severity-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -437,26 +461,9 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
return if not isEditable()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
severity = target.data("severity-id")
|
||||
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.severity = target.data("severity-id")
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
save(severity)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (issue) ->
|
||||
render(issue) if issue
|
||||
|
@ -470,14 +477,14 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgIssueSeverityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", IssueSeverityButtonDirective])
|
||||
module.directive("tgIssueSeverityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", IssueSeverityButtonDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Issue priority button directive
|
||||
#############################################################################
|
||||
|
||||
IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the priority of Issue and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -518,6 +525,26 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
save = $qqueue.bindAdd (priority) =>
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.priority = priority
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".priority-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -531,26 +558,9 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
return if not isEditable()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
priority = target.data("priority-id")
|
||||
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
issue = $model.$modelValue.clone()
|
||||
issue.priority = target.data("priority-id")
|
||||
$model.$setViewValue(issue)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
issue.revert()
|
||||
$model.$setViewValue(issue)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
save(priority)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (issue) ->
|
||||
render(issue) if issue
|
||||
|
@ -564,14 +574,14 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", IssuePriorityButtonDirective])
|
||||
module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", IssuePriorityButtonDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Promote Issue to US button directive
|
||||
#############################################################################
|
||||
|
||||
PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm) ->
|
||||
PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue) ->
|
||||
template = _.template("""
|
||||
<a class="button button-gray editable" tg-check-permission="add_us">
|
||||
Promote to User Story
|
||||
|
@ -579,15 +589,8 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm) ->
|
|||
""") # TODO: i18n
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
$el.on "click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
issue = $model.$modelValue
|
||||
|
||||
title = "Promote this issue to a new user story" # TODO: i18n
|
||||
message = "Are you sure you want to create a new US from this Issue?" # TODO: i18n
|
||||
subtitle = issue.subject
|
||||
|
||||
$confirm.ask(title, subtitle, message).then (finish) =>
|
||||
save = $qqueue.bindAdd (issue, finish) =>
|
||||
data = {
|
||||
generated_from_issue: issue.id
|
||||
project: issue.project,
|
||||
|
@ -609,6 +612,19 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm) ->
|
|||
|
||||
$repo.create("userstories", data).then(onSuccess, onError)
|
||||
|
||||
|
||||
$el.on "click", "a", (event) ->
|
||||
event.preventDefault()
|
||||
issue = $model.$modelValue
|
||||
|
||||
title = "Promote this issue to a new user story" # TODO: i18n
|
||||
message = "Are you sure you want to create a new US from this Issue?" # TODO: i18n
|
||||
subtitle = issue.subject
|
||||
|
||||
$confirm.ask(title, subtitle, message).then (finish) =>
|
||||
save(issue, finish)
|
||||
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
@ -619,5 +635,5 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm) ->
|
|||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgPromoteIssueToUsButton", ["$rootScope", "$tgRepo", "$tgConfirm",
|
||||
module.directive("tgPromoteIssueToUsButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue",
|
||||
PromoteIssueToUsButtonDirective])
|
||||
|
|
|
@ -29,7 +29,7 @@ module = angular.module("taigaIssues")
|
|||
## Issue Create Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService) ->
|
||||
CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley()
|
||||
$scope.issue = {}
|
||||
|
@ -50,31 +50,35 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService) ->
|
|||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
submit = debounce 2000, ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
promise = $repo.create("issues", $scope.issue)
|
||||
|
||||
promise.then (data) ->
|
||||
$loading.finish(submitButton)
|
||||
$rootscope.$broadcast("issueform:new:success", data)
|
||||
lightboxService.close($el)
|
||||
$confirm.notify("success")
|
||||
|
||||
promise.then null, ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("error")
|
||||
|
||||
$el.on "click", ".button-green", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
|
||||
$el.on "submit", "form", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService",
|
||||
module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading",
|
||||
CreateIssueDirective])
|
||||
|
||||
|
||||
|
@ -82,7 +86,7 @@ module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lig
|
|||
## Issue Bulk Create Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateBulkIssuesDirective = ($repo, $rs, $confirm, $rootscope, lightboxService) ->
|
||||
CreateBulkIssuesDirective = ($repo, $rs, $confirm, $rootscope, $loading, lightboxService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
$scope.$on "issueform:bulk", (ctx, projectId, status)->
|
||||
lightboxService.open($el)
|
||||
|
@ -91,29 +95,38 @@ CreateBulkIssuesDirective = ($repo, $rs, $confirm, $rootscope, lightboxService)
|
|||
bulk: ""
|
||||
}
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
data = $scope.new.bulk
|
||||
projectId = $scope.new.projectId
|
||||
|
||||
promise = $rs.issues.bulkCreate(projectId, data)
|
||||
promise.then (result) ->
|
||||
$loading.finish(submitButton)
|
||||
$rootscope.$broadcast("issueform:new:success", result)
|
||||
lightboxService.close($el)
|
||||
$confirm.notify("success")
|
||||
|
||||
promise.then null, ->
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify("error")
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgLbCreateBulkIssues", ["$tgRepo", "$tgResources", "$tgConfirm", "$rootScope",
|
||||
module.directive("tgLbCreateBulkIssues", ["$tgRepo", "$tgResources", "$tgConfirm", "$rootScope", "$tgLoading",
|
||||
"lightboxService", CreateBulkIssuesDirective])
|
||||
|
|
|
@ -27,6 +27,7 @@ scopeDefer = @.taiga.scopeDefer
|
|||
bindOnce = @.taiga.bindOnce
|
||||
groupBy = @.taiga.groupBy
|
||||
timeout = @.taiga.timeout
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaKanban")
|
||||
|
||||
|
@ -66,7 +67,9 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||
@appTitle, @navUrls, @events, @analytics, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Kanban"
|
||||
@scope.statusViewModes = {}
|
||||
@.initializeEventHandlers()
|
||||
|
|
|
@ -121,7 +121,10 @@ ProjectsNavigationDirective = ($rootscope, animationFrame, $timeout, tgLoader, $
|
|||
|
||||
timeout timeoutValue, ->
|
||||
overlay.one 'transitionend', () ->
|
||||
$(document.body).removeClass("loading-project open-projects-nav closed-projects-nav")
|
||||
$(document.body)
|
||||
.removeClass("loading-project open-projects-nav closed-projects-nav")
|
||||
.css("overflow-x", "visible")
|
||||
|
||||
overlay.hide()
|
||||
|
||||
$(document.body).addClass("closed-projects-nav")
|
||||
|
@ -153,11 +156,12 @@ ProjectsNavigationDirective = ($rootscope, animationFrame, $timeout, tgLoader, $
|
|||
|
||||
$scope.$on "nav:projects-list:open", ->
|
||||
if !$(document.body).hasClass("open-projects-nav")
|
||||
animationFrame.add () ->
|
||||
overlay.show()
|
||||
animationFrame.add () => overlay.show()
|
||||
|
||||
animationFrame.add () ->
|
||||
$(document.body).toggleClass("open-projects-nav")
|
||||
animationFrame.add(
|
||||
() => $(document.body).css("overflow-x", "hidden")
|
||||
() => $(document.body).toggleClass("open-projects-nav")
|
||||
)
|
||||
|
||||
$el.on "click", ".projects-list > li > a", (event) ->
|
||||
# HACK: to solve a problem with the loader when the next url
|
||||
|
@ -243,6 +247,12 @@ ProjectMenuDirective = ($log, $compile, $auth, $rootscope, $tgAuth, $location, $
|
|||
</a>
|
||||
</li>
|
||||
<% } %>
|
||||
<li id="nav-team">
|
||||
<a href="" title="Team" tg-nav="project-team:project=project.slug">
|
||||
<span class="icon icon-team"></span>
|
||||
<span class="item">Team</span>
|
||||
</a>
|
||||
</li>
|
||||
<% if (project.videoconferences) { %>
|
||||
<li id="nav-video">
|
||||
<a href="<%- project.videoconferenceUrl %>" target="_blank" title="Meet Up">
|
||||
|
@ -309,6 +319,22 @@ ProjectMenuDirective = ($log, $compile, $auth, $rootscope, $tgAuth, $location, $
|
|||
<div class="menu-container"></div>
|
||||
""")
|
||||
|
||||
# If the last page was kanban or backlog and
|
||||
# the new one is the task detail or the us details
|
||||
# this method preserve the last section name.
|
||||
getSectionName = ($el, sectionName, project) ->
|
||||
oldSectionName = $el.find("a.active").parent().attr("id")?.replace("nav-", "")
|
||||
|
||||
if sectionName == "backlog-kanban"
|
||||
if oldSectionName in ["backlog", "kanban"]
|
||||
sectionName = oldSectionName
|
||||
else if project.is_backlog_activated && !project.is_kanban_activated
|
||||
sectionName = "backlog"
|
||||
else if !project.is_backlog_activated && project.is_kanban_activated
|
||||
sectionName = "kanban"
|
||||
|
||||
return sectionName
|
||||
|
||||
renderMainMenu = ($el) ->
|
||||
html = mainTemplate({})
|
||||
$el.html(html)
|
||||
|
@ -318,7 +344,7 @@ ProjectMenuDirective = ($log, $compile, $auth, $rootscope, $tgAuth, $location, $
|
|||
# content loaded signal is raised using inner scope.
|
||||
renderMenuEntries = ($el, targetScope, project={}) ->
|
||||
container = $el.find(".menu-container")
|
||||
sectionName = targetScope.section
|
||||
sectionName = getSectionName($el, targetScope.section, project)
|
||||
|
||||
ctx = {
|
||||
user: $auth.getUser(),
|
||||
|
|
|
@ -26,7 +26,7 @@ debounce = @.taiga.debounce
|
|||
|
||||
module = angular.module("taigaProject")
|
||||
|
||||
CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, lightboxService, $cacheFactory) ->
|
||||
CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, $loading, lightboxService, $cacheFactory) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
$scope.data = {}
|
||||
$scope.templates = []
|
||||
|
@ -39,12 +39,14 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
# than another deleted in the same session
|
||||
$cacheFactory.get('$http').removeAll()
|
||||
|
||||
$loading.finish(submitButton)
|
||||
$rootscope.$broadcast("projects:reload")
|
||||
$confirm.notify("success", "Success") #TODO: i18n
|
||||
$location.url($projectUrl.get(response))
|
||||
lightboxService.close($el)
|
||||
|
||||
onErrorSubmit = (response) ->
|
||||
$loading.finish(submitButton)
|
||||
form.setErrors(response)
|
||||
selectors = []
|
||||
for error_field in _.keys(response)
|
||||
|
@ -54,10 +56,14 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
error_step.addClass("active")
|
||||
$el.find('.progress-bar').removeClass().addClass('progress-bar').addClass(error_step.data("step"))
|
||||
|
||||
submit = ->
|
||||
submit = (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $repo.create("projects", $scope.data)
|
||||
promise.then(onSuccessSubmit, onErrorSubmit)
|
||||
|
||||
|
@ -109,10 +115,10 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
step = prev.data('step')
|
||||
$el.find('.progress-bar').removeClass().addClass('progress-bar').addClass(step)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "click", ".button-submit", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$el.on "click", ".close", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -121,7 +127,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
|
|||
return {link:link}
|
||||
|
||||
module.directive("tgLbCreateProject", ["$rootScope", "$tgRepo", "$tgConfirm", "$location", "$tgNavUrls",
|
||||
"$tgResources", "$projectUrl", "lightboxService", "$cacheFactory", CreateProject])
|
||||
"$tgResources", "$projectUrl", "$tgLoading", "lightboxService", "$cacheFactory", CreateProject])
|
||||
|
||||
|
||||
#############################################################################
|
||||
|
|
|
@ -42,6 +42,7 @@ urls = {
|
|||
"userstories-restore": "/userstories/%s/restore"
|
||||
"tasks": "/tasks"
|
||||
"bulk-create-tasks": "/tasks/bulk_create"
|
||||
"bulk-update-task-taskboard-order": "/tasks/bulk_update_taskboard_order"
|
||||
"tasks-restore": "/tasks/%s/restore"
|
||||
"issues": "/issues"
|
||||
"bulk-create-issues": "/issues/bulk_create"
|
||||
|
|
|
@ -28,11 +28,14 @@ resourceProvider = ($repo, $http, $urls) ->
|
|||
service.get = (id) ->
|
||||
return $repo.queryOne("memberships", id)
|
||||
|
||||
service.list = (projectId, filters) ->
|
||||
service.list = (projectId, filters, enablePagination=true) ->
|
||||
params = {project: projectId}
|
||||
params = _.extend({}, params, filters or {})
|
||||
if enablePagination
|
||||
return $repo.queryPaginated("memberships", params)
|
||||
|
||||
return $repo.queryMany("memberships", params, options={enablePagination:enablePagination})
|
||||
|
||||
service.listByUser = (userId, filters) ->
|
||||
params = {user: userId}
|
||||
params = _.extend({}, params, filters or {})
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
taiga = @.taiga
|
||||
|
||||
resourceProvider = ($repo) ->
|
||||
resourceProvider = ($repo, $http, $urls) ->
|
||||
service = {}
|
||||
|
||||
service.get = (id) ->
|
||||
|
@ -45,6 +45,13 @@ resourceProvider = ($repo) ->
|
|||
service.stats = (projectId) ->
|
||||
return $repo.queryOneRaw("projects", "#{projectId}/stats")
|
||||
|
||||
service.leave = (projectId) ->
|
||||
url = "#{$urls.resolve("projects")}/#{projectId}/leave"
|
||||
return $http.post(url)
|
||||
|
||||
service.memberStats = (projectId) ->
|
||||
return $repo.queryOneRaw("projects", "#{projectId}/member_stats")
|
||||
|
||||
service.tagsColors = (id) ->
|
||||
return $repo.queryOne("projects", "#{id}/tags_colors")
|
||||
|
||||
|
@ -53,4 +60,4 @@ resourceProvider = ($repo) ->
|
|||
|
||||
|
||||
module = angular.module("taigaResources")
|
||||
module.factory("$tgProjectsResourcesProvider", ["$tgRepo", resourceProvider])
|
||||
module.factory("$tgProjectsResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", resourceProvider])
|
||||
|
|
|
@ -27,6 +27,8 @@ generateHash = taiga.generateHash
|
|||
resourceProvider = ($repo, $http, $urls, $storage) ->
|
||||
service = {}
|
||||
hashSuffix = "tasks-queryparams"
|
||||
hashSuffixStatusColumnModes = "tasks-statuscolumnmodels"
|
||||
hashSuffixUsRowModes = "tasks-usrowmodels"
|
||||
|
||||
service.get = (projectId, taskId) ->
|
||||
params = service.getQueryParams(projectId)
|
||||
|
@ -46,6 +48,11 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
|||
return $http.post(url, params).then (result) ->
|
||||
return result.data
|
||||
|
||||
service.bulkUpdateTaskTaskboardOrder = (projectId, data) ->
|
||||
url = $urls.resolve("bulk-update-task-taskboard-order")
|
||||
params = {project_id: projectId, bulk_tasks: data}
|
||||
return $http.post(url, params)
|
||||
|
||||
service.listValues = (projectId, type) ->
|
||||
params = {"project": projectId}
|
||||
return $repo.queryMany(type, params)
|
||||
|
@ -60,6 +67,28 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
|||
hash = generateHash([projectId, ns])
|
||||
return $storage.get(hash) or {}
|
||||
|
||||
service.storeStatusColumnModes = (projectId, params) ->
|
||||
ns = "#{projectId}:#{hashSuffixStatusColumnModes}"
|
||||
hash = generateHash([projectId, ns])
|
||||
$storage.set(hash, params)
|
||||
|
||||
service.getStatusColumnModes = (projectId) ->
|
||||
ns = "#{projectId}:#{hashSuffixStatusColumnModes}"
|
||||
hash = generateHash([projectId, ns])
|
||||
return $storage.get(hash) or {}
|
||||
|
||||
service.storeUsRowModes = (projectId, sprintId, params) ->
|
||||
ns = "#{projectId}:#{hashSuffixUsRowModes}"
|
||||
hash = generateHash([projectId, sprintId, ns])
|
||||
|
||||
$storage.set(hash, params)
|
||||
|
||||
service.getUsRowModes = (projectId, sprintId) ->
|
||||
ns = "#{projectId}:#{hashSuffixUsRowModes}"
|
||||
hash = generateHash([projectId, sprintId, ns])
|
||||
|
||||
return $storage.get(hash) or {}
|
||||
|
||||
return (instance) ->
|
||||
instance.tasks = service
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ bindOnce = @.taiga.bindOnce
|
|||
mixOf = @.taiga.mixOf
|
||||
debounceLeading = @.taiga.debounceLeading
|
||||
trim = @.taiga.trim
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
module = angular.module("taigaSearch", [])
|
||||
|
||||
|
@ -111,7 +112,9 @@ SearchBoxDirective = ($lightboxService, $navurls, $location, $route)->
|
|||
link = ($scope, $el, $attrs) ->
|
||||
project = null
|
||||
|
||||
submit = ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
@ -131,12 +134,8 @@ SearchBoxDirective = ($lightboxService, $navurls, $location, $route)->
|
|||
$lightboxService.open($el)
|
||||
$el.find("#search-text").val("")
|
||||
|
||||
$el.on "submit", (event) ->
|
||||
submit()
|
||||
|
||||
$el.on "click", ".button-green", (event) ->
|
||||
event.preventDefault()
|
||||
submit()
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
return {link:link}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ taiga = @.taiga
|
|||
bindOnce = @.taiga.bindOnce
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, lightboxService) ->
|
||||
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
$scope.isNew = true
|
||||
|
||||
|
@ -53,7 +53,10 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, lightboxService) ->
|
|||
$el.find(".title").html("Edit task ") #TODO: i18n
|
||||
lightboxService.open($el)
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
|
@ -67,32 +70,36 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, lightboxService) ->
|
|||
promise = $repo.save($scope.task)
|
||||
broadcastEvent = "taskform:edit:success"
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
# FIXME: error handling?
|
||||
promise.then (data) ->
|
||||
$loading.finish(submitButton)
|
||||
lightboxService.close($el)
|
||||
$rootscope.$broadcast(broadcastEvent, data)
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
|
||||
CreateBulkTasksDirective = ($repo, $rs, $rootscope, lightboxService) ->
|
||||
CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
$scope.form = {data: "", usId: null}
|
||||
|
||||
$scope.$on "taskform:bulk", (ctx, sprintId, usId)->
|
||||
lightboxService.open($el)
|
||||
$scope.form = {data: "", sprintId: sprintId, usId: usId}
|
||||
|
||||
$el.on "click", ".button-green", debounce 2000, (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
data = $scope.form.data
|
||||
projectId = $scope.projectId
|
||||
sprintId = $scope.form.sprintId
|
||||
|
@ -100,13 +107,24 @@ CreateBulkTasksDirective = ($repo, $rs, $rootscope, lightboxService) ->
|
|||
|
||||
promise = $rs.tasks.bulkCreate(projectId, sprintId, usId, data)
|
||||
promise.then (result) ->
|
||||
$loading.finish(submitButton)
|
||||
$rootscope.$broadcast("taskform:bulk:success", result)
|
||||
lightboxService.close($el)
|
||||
|
||||
# TODO: error handling
|
||||
promise.then null, ->
|
||||
$loading.finish(submitButton)
|
||||
console.log "FAIL"
|
||||
|
||||
$scope.$on "taskform:bulk", (ctx, sprintId, usId)->
|
||||
lightboxService.open($el)
|
||||
$scope.form = {data: "", sprintId: sprintId, usId: usId}
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
@ -120,6 +138,7 @@ module.directive("tgLbCreateEditTask", [
|
|||
"$tgModel",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
"$tgLoading",
|
||||
"lightboxService",
|
||||
CreateEditTaskDirective
|
||||
])
|
||||
|
@ -128,6 +147,7 @@ module.directive("tgLbCreateBulkTasks", [
|
|||
"$tgRepo",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
"$tgLoading",
|
||||
"lightboxService",
|
||||
CreateBulkTasksDirective
|
||||
])
|
||||
|
|
|
@ -26,6 +26,7 @@ groupBy = @.taiga.groupBy
|
|||
bindOnce = @.taiga.bindOnce
|
||||
scopeDefer = @.taiga.scopeDefer
|
||||
timeout = @.taiga.timeout
|
||||
bindMethods = @.taiga.bindMethods
|
||||
|
||||
module = angular.module("taigaTaskboard")
|
||||
|
||||
|
@ -53,7 +54,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appTitle, @location, @navUrls,
|
||||
@events, @analytics, tgLoader) ->
|
||||
_.bindAll(@)
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Taskboard"
|
||||
@.initializeEventHandlers()
|
||||
|
@ -102,7 +103,6 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
# Not used at this momment
|
||||
@scope.pointsList = _.sortBy(project.points, "order")
|
||||
# @scope.roleList = _.sortBy(project.roles, "order")
|
||||
|
@ -111,6 +111,9 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
@scope.taskStatusList = _.sortBy(project.task_statuses, "order")
|
||||
@scope.usStatusList = _.sortBy(project.us_statuses, "order")
|
||||
@scope.usStatusById = groupBy(project.us_statuses, (e) -> e.id)
|
||||
|
||||
@scope.$emit('project:loaded', project)
|
||||
|
||||
return project
|
||||
|
||||
loadSprintStats: ->
|
||||
|
@ -144,7 +147,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
|
||||
loadTasks: ->
|
||||
return @rs.tasks.list(@scope.projectId, @scope.sprintId).then (tasks) =>
|
||||
@scope.tasks = tasks
|
||||
@scope.tasks = _.sortBy(tasks, 'taskboard_order')
|
||||
@scope.usTasks = {}
|
||||
|
||||
# Iterate over all userstories and
|
||||
|
@ -183,21 +186,46 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
.then(=> @.loadUsersAndRoles())
|
||||
.then(=> @.loadTaskboard())
|
||||
|
||||
refreshTasksOrder: (tasks) ->
|
||||
items = @.resortTasks(tasks)
|
||||
data = @.prepareBulkUpdateData(items)
|
||||
|
||||
return @rs.tasks.bulkUpdateTaskTaskboardOrder(@scope.project.id, data)
|
||||
|
||||
resortTasks: (tasks) ->
|
||||
items = []
|
||||
|
||||
for item, index in tasks
|
||||
item["taskboard_order"] = index
|
||||
if item.isModified()
|
||||
items.push(item)
|
||||
|
||||
return items
|
||||
|
||||
prepareBulkUpdateData: (uses) ->
|
||||
return _.map(uses, (x) -> {"task_id": x.id, "order": x["taskboard_order"]})
|
||||
|
||||
taskMove: (ctx, task, usId, statusId, order) ->
|
||||
# Remove task from old position
|
||||
r = @scope.usTasks[task.user_story][task.status].indexOf(task)
|
||||
@scope.usTasks[task.user_story][task.status].splice(r, 1)
|
||||
|
||||
# Add task to new position
|
||||
@scope.usTasks[usId][statusId].splice(order, 0, task)
|
||||
tasks = @scope.usTasks[usId][statusId]
|
||||
tasks.splice(order, 0, task)
|
||||
|
||||
task.user_story = usId
|
||||
task.status = statusId
|
||||
task.order = order
|
||||
task.taskboard_order = order
|
||||
|
||||
promise = @repo.save(task)
|
||||
|
||||
@rootscope.$broadcast("sprint:task:moved", task)
|
||||
|
||||
promise.then =>
|
||||
@.refreshTasksOrder(tasks)
|
||||
@.loadSprintStats()
|
||||
|
||||
promise.then null, =>
|
||||
console.log "FAIL TASK SAVE"
|
||||
|
||||
|
@ -267,22 +295,6 @@ TaskboardTaskDirective = ($rootscope) ->
|
|||
|
||||
module.directive("tgTaskboardTask", ["$rootScope", TaskboardTaskDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Taskboard Task Row Size Fixer Directive
|
||||
#############################################################################
|
||||
|
||||
TaskboardRowWidthFixerDirective = ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
bindOnce $scope, "taskStatusList", (statuses) ->
|
||||
itemSize = 300 + (10 * statuses.length)
|
||||
size = (1 + statuses.length) * itemSize
|
||||
$el.css("width", "#{size}px")
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgTaskboardRowWidthFixer", TaskboardRowWidthFixerDirective)
|
||||
|
||||
#############################################################################
|
||||
## Taskboard Table Height Fixer Directive
|
||||
#############################################################################
|
||||
|
@ -307,64 +319,156 @@ TaskboardTableHeightFixerDirective = ->
|
|||
|
||||
module.directive("tgTaskboardTableHeightFixer", TaskboardTableHeightFixerDirective)
|
||||
|
||||
#############################################################################
|
||||
## Taskboard Squish Column Directive
|
||||
#############################################################################
|
||||
|
||||
TaskboardSquishColumnDirective = (rs) ->
|
||||
avatarWidth = 40
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$scope.$on "sprint:task:moved", () =>
|
||||
recalculateTaskboardWidth()
|
||||
|
||||
bindOnce $scope, "usTasks", (project) ->
|
||||
$scope.statusesFolded = rs.tasks.getStatusColumnModes($scope.project.id)
|
||||
$scope.usFolded = rs.tasks.getUsRowModes($scope.project.id, $scope.sprintId)
|
||||
|
||||
recalculateTaskboardWidth()
|
||||
|
||||
$scope.foldStatus = (status) ->
|
||||
$scope.statusesFolded[status.id] = !!!$scope.statusesFolded[status.id]
|
||||
rs.tasks.storeStatusColumnModes($scope.projectId, $scope.statusesFolded)
|
||||
|
||||
recalculateTaskboardWidth()
|
||||
|
||||
$scope.foldUs = (us) ->
|
||||
if !us
|
||||
$scope.usFolded["unassigned"] = !!!$scope.usFolded["unassigned"]
|
||||
else
|
||||
$scope.usFolded[us.id] = !!!$scope.usFolded[us.id]
|
||||
|
||||
rs.tasks.storeUsRowModes($scope.projectId, $scope.sprintId, $scope.usFolded)
|
||||
|
||||
recalculateTaskboardWidth()
|
||||
|
||||
getCeilWidth = (usId, statusId) =>
|
||||
tasks = $scope.usTasks[usId][statusId].length
|
||||
|
||||
if $scope.statusesFolded[statusId]
|
||||
if tasks and $scope.usFolded[usId]
|
||||
tasksMatrixSize = Math.round(Math.sqrt(tasks))
|
||||
width = avatarWidth * tasksMatrixSize
|
||||
else
|
||||
width = avatarWidth
|
||||
|
||||
return width
|
||||
|
||||
return 0
|
||||
|
||||
setStatusColumnWidth = (statusId, width) =>
|
||||
column = $el.find(".squish-status-#{statusId}")
|
||||
|
||||
if width
|
||||
column.css('max-width', width)
|
||||
else
|
||||
column.removeAttr("style")
|
||||
|
||||
refreshTaskboardTableWidth = () =>
|
||||
columnWidths = []
|
||||
|
||||
columns = $el.find(".task-colum-name")
|
||||
|
||||
columnWidths = _.map columns, (column) ->
|
||||
return $(column).outerWidth(true)
|
||||
|
||||
totalWidth = _.reduce columnWidths, (total, width) ->
|
||||
return total + width
|
||||
|
||||
$el.find('.taskboard-table-inner').css("width", totalWidth)
|
||||
|
||||
recalculateStatusColumnWidth = (statusId) =>
|
||||
statusFoldedWidth = 0
|
||||
|
||||
_.forEach $scope.userstories, (us) ->
|
||||
width = getCeilWidth(us.id, statusId)
|
||||
|
||||
statusFoldedWidth = width if width > statusFoldedWidth
|
||||
|
||||
setStatusColumnWidth(statusId, statusFoldedWidth)
|
||||
|
||||
recalculateTaskboardWidth = () =>
|
||||
_.forEach $scope.taskStatusList, (status) ->
|
||||
recalculateStatusColumnWidth(status.id)
|
||||
|
||||
refreshTaskboardTableWidth()
|
||||
|
||||
return
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgTaskboardSquishColumn", ["$tgResources", TaskboardSquishColumnDirective])
|
||||
|
||||
#############################################################################
|
||||
## Taskboard User Directive
|
||||
#############################################################################
|
||||
|
||||
TaskboardUserDirective = ($log) ->
|
||||
template = _.template("""
|
||||
<figure class="avatar">
|
||||
<a href="#" title="Assign task" <% if (!clickable) {%>class="not-clickable"<% } %>>
|
||||
<img src="<%- imgurl %>" alt="<%- name %>">
|
||||
template = """
|
||||
<figure class="avatar avatar-assigned-to">
|
||||
<a href="#" title="Assign task" ng-class="{'not-clickable': !clickable}">
|
||||
<img ng-src="{{imgurl}}">
|
||||
</a>
|
||||
</figure>
|
||||
""") # TODO: i18n
|
||||
|
||||
<figure class="avatar avatar-task-link">
|
||||
<a tg-nav="project-tasks-detail:project=project.slug,ref=task.ref" ng-attr-title="{{task.subject}}">
|
||||
<img ng-src="{{imgurl}}">
|
||||
</a>
|
||||
</figure>
|
||||
""" # TODO: i18n
|
||||
|
||||
clickable = false
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
if not $attrs.tgTaskboardUserAvatar?
|
||||
return $log.error "TaskboardUserDirective: no attr is defined"
|
||||
|
||||
wtid = $scope.$watch $attrs.tgTaskboardUserAvatar, (v) ->
|
||||
if not $scope.usersById?
|
||||
$log.error "TaskboardUserDirective requires userById set in scope."
|
||||
wtid()
|
||||
else
|
||||
user = $scope.usersById[v]
|
||||
render(user)
|
||||
|
||||
render = (user) ->
|
||||
if user is undefined
|
||||
ctx = {name: "Unassigned", imgurl: "/images/unnamed.png", clickable: clickable}
|
||||
else
|
||||
ctx = {name: user.full_name_display, imgurl: user.photo, clickable: clickable}
|
||||
|
||||
html = template(ctx)
|
||||
$el.html(html)
|
||||
link = ($scope, $el, $attrs) ->
|
||||
username_label = $el.parent().find("a.task-assigned")
|
||||
username_label.html(ctx.name)
|
||||
username_label.on "click", (event) ->
|
||||
if $el.find('a').hasClass('noclick')
|
||||
return
|
||||
|
||||
us = $model.$modelValue
|
||||
$ctrl = $el.controller()
|
||||
$ctrl.editTaskAssignedTo(us)
|
||||
$ctrl.editTaskAssignedTo($scope.task)
|
||||
|
||||
$scope.$watch 'task.assigned_to', (assigned_to) ->
|
||||
user = $scope.usersById[assigned_to]
|
||||
|
||||
if user is undefined
|
||||
_.assign($scope, {name: "Unassigned", imgurl: "/images/unnamed.png", clickable: clickable})
|
||||
else
|
||||
_.assign($scope, {name: user.full_name_display, imgurl: user.photo, clickable: clickable})
|
||||
|
||||
username_label.text($scope.name)
|
||||
|
||||
|
||||
bindOnce $scope, "project", (project) ->
|
||||
if project.my_permissions.indexOf("modify_task") > -1
|
||||
clickable = true
|
||||
$el.on "click", (event) =>
|
||||
$el.find(".avatar-assigned-to").on "click", (event) =>
|
||||
if $el.find('a').hasClass('noclick')
|
||||
return
|
||||
|
||||
us = $model.$modelValue
|
||||
$ctrl = $el.controller()
|
||||
$ctrl.editTaskAssignedTo(us)
|
||||
$ctrl.editTaskAssignedTo($scope.task)
|
||||
|
||||
return {link: link, require:"ngModel"}
|
||||
return {
|
||||
link: link,
|
||||
template: template,
|
||||
scope: {
|
||||
"usersById": "=users",
|
||||
"project": "=",
|
||||
"task": "=",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.directive("tgTaskboardUserAvatar", ["$log", TaskboardUserDirective])
|
||||
|
|
|
@ -199,7 +199,7 @@ module.directive("tgTaskStatusDisplay", TaskStatusDisplayDirective)
|
|||
## Task status button directive
|
||||
#############################################################################
|
||||
|
||||
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the status of Task and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -240,6 +240,26 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
save = $qqueue.bindAdd (status) =>
|
||||
task = $model.$modelValue.clone()
|
||||
task.status = status
|
||||
|
||||
$model.$setViewValue(task)
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
task.revert()
|
||||
$model.$setViewValue(task)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".status-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -256,25 +276,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
task = $model.$modelValue.clone()
|
||||
task.status = target.data("status-id")
|
||||
$model.$setViewValue(task)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
task.revert()
|
||||
$model.$setViewValue(task)
|
||||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
$loading.start($el.find(".level-name"))
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
save(target.data("status-id"))
|
||||
|
||||
$scope.$watch $attrs.ngModel, (task) ->
|
||||
render(task) if task
|
||||
|
@ -288,11 +290,11 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading",
|
||||
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
||||
TaskStatusButtonDirective])
|
||||
|
||||
|
||||
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
||||
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue) ->
|
||||
template = _.template("""
|
||||
<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 for="is-iocaine"
|
||||
|
@ -319,15 +321,15 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
|||
html = template(ctx)
|
||||
$el.html(html)
|
||||
|
||||
$el.on "click", ".is-iocaine", (event) ->
|
||||
return if not isEditable()
|
||||
|
||||
save = $qqueue.bindAdd (is_iocaine) =>
|
||||
task = $model.$modelValue.clone()
|
||||
task.is_iocaine = not task.is_iocaine
|
||||
task.is_iocaine = is_iocaine
|
||||
|
||||
$model.$setViewValue(task)
|
||||
$loading.start($el.find('label'))
|
||||
|
||||
promise = $tgrepo.save($model.$modelValue)
|
||||
promise = $tgrepo.save(task)
|
||||
|
||||
promise.then ->
|
||||
$confirm.notify("success")
|
||||
$rootscope.$broadcast("history:reload")
|
||||
|
@ -340,6 +342,12 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
|||
promise.finally ->
|
||||
$loading.finish($el.find('label'))
|
||||
|
||||
$el.on "click", ".is-iocaine", (event) ->
|
||||
return if not isEditable()
|
||||
|
||||
is_iocaine = not $model.$modelValue.is_iocaine
|
||||
save(is_iocaine)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (task) ->
|
||||
render(task) if task
|
||||
|
||||
|
@ -352,4 +360,4 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", TaskIsIocaineButtonDirective])
|
||||
module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", TaskIsIocaineButtonDirective])
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: modules/team.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaTeam", [])
|
|
@ -0,0 +1,307 @@
|
|||
###
|
||||
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: modules/team/main.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
mixOf = @.taiga.mixOf
|
||||
|
||||
module = angular.module("taigaTeam")
|
||||
|
||||
#############################################################################
|
||||
## Team Controller
|
||||
#############################################################################
|
||||
|
||||
class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"$rootScope",
|
||||
"$tgRepo",
|
||||
"$tgResources",
|
||||
"$routeParams",
|
||||
"$q",
|
||||
"$location",
|
||||
"$tgNavUrls",
|
||||
"$appTitle",
|
||||
"$tgAuth",
|
||||
"tgLoader"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle, @auth, tgLoader) ->
|
||||
@scope.sectionName = "Team"
|
||||
|
||||
promise = @.loadInitialData()
|
||||
|
||||
# On Success
|
||||
promise.then =>
|
||||
#TODO: i18n
|
||||
@appTitle.set("Team - " + @scope.project.name)
|
||||
tgLoader.pageLoaded()
|
||||
|
||||
# On Error
|
||||
promise.then null, @.onInitialDataError.bind(@)
|
||||
|
||||
setRole: (role) ->
|
||||
if role
|
||||
@scope.filtersRole = role
|
||||
else
|
||||
@scope.filtersRole = ""
|
||||
|
||||
loadMembers: ->
|
||||
return @rs.memberships.list(@scope.projectId, {}, false).then (data) =>
|
||||
currentUser = @auth.getUser()
|
||||
if not currentUser.photo?
|
||||
currentUser.photo = "/images/unnamed.png"
|
||||
|
||||
@scope.currentUser = _.find data, (membership) =>
|
||||
return membership.user == currentUser.id
|
||||
|
||||
@scope.totals = {}
|
||||
_.forEach data, (membership) =>
|
||||
@scope.totals[membership.user] = 0
|
||||
|
||||
@scope.memberships = _.filter data, (membership) =>
|
||||
if membership.user && membership.user != currentUser.id
|
||||
return membership
|
||||
|
||||
for membership in @scope.memberships
|
||||
if not membership.photo?
|
||||
membership.photo = "/images/unnamed.png"
|
||||
|
||||
return data
|
||||
|
||||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
|
||||
@scope.issuesEnabled = project.is_issues_activated
|
||||
@scope.tasksEnabled = project.is_kanban_activated or project.is_backlog_activated
|
||||
@scope.wikiEnabled = project.is_wiki_activated
|
||||
|
||||
return project
|
||||
|
||||
loadMemberStats: ->
|
||||
return @rs.projects.memberStats(@scope.projectId).then (stats) =>
|
||||
totals = {}
|
||||
_.forEach @scope.totals, (total, userId) =>
|
||||
vals = _.map(stats, (memberStats, statsKey) -> memberStats[userId])
|
||||
total = _.reduce(vals, (sum, el) -> sum + el)
|
||||
@scope.totals[userId] = total
|
||||
|
||||
@scope.stats = @.processStats(stats)
|
||||
@scope.stats.totals = @scope.totals
|
||||
|
||||
processStat: (stat) ->
|
||||
max = _.max(stat)
|
||||
min = _.min(stat)
|
||||
singleStat = _.map stat, (value, key) ->
|
||||
if value == min
|
||||
return [key, 0.1]
|
||||
if value == max
|
||||
return [key, 1]
|
||||
return [key, (value * 0.5) / max]
|
||||
singleStat = _.object(singleStat)
|
||||
return singleStat
|
||||
|
||||
processStats: (stats) ->
|
||||
for key,value of stats
|
||||
stats[key] = @.processStat(value)
|
||||
return stats
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
.then(=> @.loadUsersAndRoles())
|
||||
.then(=> @.loadMembers())
|
||||
.then(=> @.loadMemberStats())
|
||||
|
||||
module.controller("TeamController", TeamController)
|
||||
|
||||
#############################################################################
|
||||
## Team Filters Directive
|
||||
#############################################################################
|
||||
|
||||
TeamFiltersDirective = () ->
|
||||
template = """
|
||||
<ul>
|
||||
<li>
|
||||
<a ng-class="{active: !filtersRole.id}" ng-click="ctrl.setRole()" href="">
|
||||
<span class="title">All</span>
|
||||
<span class="icon icon-arrow-right"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-repeat="role in roles">
|
||||
<a ng-class="{active: role.id == filtersRole.id}" ng-click="ctrl.setRole(role)" href="">
|
||||
<span class="title" tg-bo-bind="role.name"></span>
|
||||
<span class="icon icon-arrow-right"></span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
"""
|
||||
|
||||
return {
|
||||
template: template
|
||||
}
|
||||
|
||||
module.directive("tgTeamFilters", [TeamFiltersDirective])
|
||||
|
||||
#############################################################################
|
||||
## Team Member Stats Directive
|
||||
#############################################################################
|
||||
|
||||
TeamMemberStatsDirective = () ->
|
||||
template = """
|
||||
<div class="attribute" ng-if="issuesEnabled">
|
||||
<span class="icon icon-briefcase" ng-style="{'opacity': stats.closed_bugs[userId]}" ng-class="{'top': stats.closed_bugs[userId] == 1}"></span>
|
||||
</div>
|
||||
<div class="attribute" ng-if="tasksEnabled">
|
||||
<span class="icon icon-iocaine" ng-style="{'opacity': stats.iocaine_tasks[userId]}" ng-class="{'top': stats.iocaine_tasks[userId] == 1}"></span>
|
||||
</div>
|
||||
<div class="attribute" ng-if="wikiEnabled">
|
||||
<span class="icon icon-writer" ng-style="{'opacity': stats.wiki_changes[userId]}" ng-class="{'top': stats.wiki_changes[userId] == 1}"></span>
|
||||
</div>
|
||||
<div class="attribute" ng-if="issuesEnabled">
|
||||
<span class="icon icon-bug" ng-style="{'opacity': stats.created_bugs[userId]}" ng-class="{'top': stats.created_bugs[userId] == 1}"></span>
|
||||
</div>
|
||||
<div class="attribute" ng-if="tasksEnabled">
|
||||
<span class="icon icon-tasks" ng-style="{'opacity': stats.closed_tasks[userId]}" ng-class="{'top': stats.closed_tasks[userId] == 1}"></span>
|
||||
</div>
|
||||
<div class="attribute">
|
||||
<span class="points" ng-bind="stats.totals[userId]"></span>
|
||||
</div>
|
||||
"""
|
||||
return {
|
||||
template: template,
|
||||
scope: {
|
||||
stats: "=",
|
||||
userId: "=user"
|
||||
issuesEnabled: "=issuesenabled"
|
||||
tasksEnabled: "=tasksenabled"
|
||||
wikiEnabled: "=wikienabled"
|
||||
}
|
||||
}
|
||||
|
||||
module.directive("tgTeamMemberStats", TeamMemberStatsDirective)
|
||||
|
||||
#############################################################################
|
||||
## Team Current User Directive
|
||||
#############################################################################
|
||||
|
||||
TeamMemberCurrentUserDirective = () ->
|
||||
template = """
|
||||
<div class="row">
|
||||
<div class="username">
|
||||
<figure class="avatar">
|
||||
<img tg-bo-src="currentUser.photo" tg-bo-alt="currentUser.full_name" />
|
||||
<figcaption>
|
||||
<span class="name" tg-bo-bind="currentUser.full_name"></span>
|
||||
<span class="position" tg-bo-bind="currentUser.role_name"></span>
|
||||
<div tg-leave-project projectid="{{projectId}}"></div>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="member-stats" tg-team-member-stats stats="stats" user="currentUser.user" issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled"></div>
|
||||
</div>
|
||||
"""
|
||||
return {
|
||||
template: template
|
||||
scope: {
|
||||
projectId: "=projectid",
|
||||
currentUser: "=currentuser",
|
||||
stats: "="
|
||||
issuesEnabled: "=issuesenabled"
|
||||
tasksEnabled: "=tasksenabled"
|
||||
wikiEnabled: "=wikienabled"
|
||||
}
|
||||
}
|
||||
|
||||
module.directive("tgTeamCurrentUser", TeamMemberCurrentUserDirective)
|
||||
|
||||
#############################################################################
|
||||
## Team Members Directive
|
||||
#############################################################################
|
||||
|
||||
TeamMembersDirective = () ->
|
||||
template = """
|
||||
<div class="row member" ng-repeat="user in memberships | filter:filtersQ | filter:{role: filtersRole.id}">
|
||||
<div class="username">
|
||||
<figure class="avatar">
|
||||
<img tg-bo-src="user.photo" tg-bo-alt="user.full_name" />
|
||||
<figcaption>
|
||||
<span class="name" tg-bo-bind="user.full_name"></span>
|
||||
<span class="position" tg-bo-bind="user.role_name"></span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="member-stats" tg-team-member-stats stats="stats" user="user.user" issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled"></div>
|
||||
</div>
|
||||
"""
|
||||
return {
|
||||
template: template
|
||||
scope: {
|
||||
memberships: "=",
|
||||
filtersQ: "=filtersq",
|
||||
filtersRole: "=filtersrole",
|
||||
stats: "="
|
||||
issuesEnabled: "=issuesenabled"
|
||||
tasksEnabled: "=tasksenabled"
|
||||
wikiEnabled: "=wikienabled"
|
||||
}
|
||||
}
|
||||
|
||||
module.directive("tgTeamMembers", TeamMembersDirective)
|
||||
|
||||
#############################################################################
|
||||
## Leave project Directive
|
||||
#############################################################################
|
||||
|
||||
LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls) ->
|
||||
template= """
|
||||
<a ng-click="leave()" href="" class="leave-project">
|
||||
<span class="icon icon-delete"></span>Leave this project
|
||||
</a>
|
||||
""" #TODO: i18n
|
||||
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$scope.leave = () ->
|
||||
#TODO: i18n
|
||||
$confirm.ask("Leave this project", "Are you sure you want to leave the project?").then (finish) =>
|
||||
promise = $rs.projects.leave($attrs.projectid)
|
||||
|
||||
promise.then =>
|
||||
finish()
|
||||
$confirm.notify("success")
|
||||
$location.path($navurls.resolve("home"))
|
||||
|
||||
promise.then null, (response) ->
|
||||
finish()
|
||||
$confirm.notify('error', response.data._error_message)
|
||||
|
||||
return {
|
||||
scope: {},
|
||||
template: template,
|
||||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgLeaveProject", ["$tgRepo", "$tgConfirm", "$tgLocation", "$tgResources", "$tgNavUrls", LeaveProjectDirective])
|
|
@ -22,6 +22,7 @@
|
|||
taiga = @.taiga
|
||||
|
||||
mixOf = @.taiga.mixOf
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
module = angular.module("taigaUserSettings")
|
||||
|
||||
|
@ -66,18 +67,6 @@ class UserChangePasswordController extends mixOf(taiga.Controller, taiga.PageMix
|
|||
|
||||
return promise.then(=> @.loadProject())
|
||||
|
||||
save: ->
|
||||
if @scope.newPassword1 != @scope.newPassword2
|
||||
@confirm.notify('error', "The passwords dosn't match")
|
||||
return
|
||||
|
||||
promise = @rs.userSettings.changePassword(@scope.currentPassword, @scope.newPassword1)
|
||||
promise.then =>
|
||||
@confirm.notify('success')
|
||||
promise.then null, (response) =>
|
||||
@confirm.notify('error', response.data._error_message)
|
||||
|
||||
|
||||
module.controller("UserChangePasswordController", UserChangePasswordController)
|
||||
|
||||
|
||||
|
@ -85,11 +74,36 @@ module.controller("UserChangePasswordController", UserChangePasswordController)
|
|||
## User ChangePassword Directive
|
||||
#############################################################################
|
||||
|
||||
UserChangePasswordDirective = () ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
UserChangePasswordDirective = ($rs, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs, ctrl) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if $scope.newPassword1 != $scope.newPassword2
|
||||
$confirm.notify('error', "The passwords dosn't match")
|
||||
return
|
||||
|
||||
$loading.start(submitButton)
|
||||
|
||||
promise = $rs.userSettings.changePassword($scope.currentPassword, $scope.newPassword1)
|
||||
promise.then =>
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify('success')
|
||||
|
||||
promise.then null, (response) =>
|
||||
$loading.finish(submitButton)
|
||||
$confirm.notify('error', response.data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {link:link}
|
||||
return {
|
||||
link:link
|
||||
}
|
||||
|
||||
module.directive("tgUserChangePassword", UserChangePasswordDirective)
|
||||
module.directive("tgUserChangePassword", ["$tgResources", "$tgConfirm", "$tgLoading", UserChangePasswordDirective])
|
||||
|
|
|
@ -23,7 +23,7 @@ taiga = @.taiga
|
|||
mixOf = @.taiga.mixOf
|
||||
sizeFormat = @.taiga.sizeFormat
|
||||
module = angular.module("taigaUserSettings")
|
||||
|
||||
debounce = @.taiga.debounce
|
||||
|
||||
#############################################################################
|
||||
## User settings Controller
|
||||
|
@ -82,7 +82,9 @@ module.controller("UserSettingsController", UserSettingsController)
|
|||
|
||||
UserProfileDirective = ($confirm, $auth, $repo) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$el.on "click", ".user-profile form .save-profile", (event) ->
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
return if not form.validate()
|
||||
|
||||
|
@ -103,6 +105,10 @@ UserProfileDirective = ($confirm, $auth, $repo) ->
|
|||
|
||||
$repo.save($scope.user).then(onSuccess, onError)
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
$el.on "click", ".submit-button", submit
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@ module.directive("tgUsTasksProgressDisplay", UsTasksProgressDisplayDirective)
|
|||
## User story status button directive
|
||||
#############################################################################
|
||||
|
||||
UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
||||
UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
|
||||
# Display the status of a US and you can edit it.
|
||||
#
|
||||
# Example:
|
||||
|
@ -300,28 +300,15 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
})
|
||||
$el.html(html)
|
||||
|
||||
$el.on "click", ".status-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return if not isEditable()
|
||||
|
||||
$el.find(".pop-status").popover().open()
|
||||
|
||||
$el.on "click", ".status", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return if not isEditable()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
save = $qqueue.bindAdd (status) =>
|
||||
us = $model.$modelValue.clone()
|
||||
us.status = status
|
||||
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
us = $model.$modelValue.clone()
|
||||
us.status = target.data("status-id")
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$scope.$apply()
|
||||
|
||||
onSuccess = ->
|
||||
$confirm.notify("success")
|
||||
$rootScope.$broadcast("history:reload")
|
||||
|
@ -334,8 +321,26 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
$loading.finish($el.find(".level-name"))
|
||||
|
||||
$loading.start($el.find(".level-name"))
|
||||
|
||||
$repo.save($model.$modelValue).then(onSuccess, onError)
|
||||
|
||||
$el.on "click", ".status-data", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return if not isEditable()
|
||||
|
||||
$el.find(".pop-status").popover().open()
|
||||
|
||||
$el.on "click", ".status", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
return if not isEditable()
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
status = target.data("status-id")
|
||||
|
||||
save(status)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (us) ->
|
||||
render(us) if us
|
||||
|
||||
|
@ -348,7 +353,7 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading",
|
||||
module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading","$tgQqueue",
|
||||
UsStatusButtonDirective])
|
||||
|
||||
|
||||
|
@ -356,7 +361,7 @@ module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$t
|
|||
## User story team requirements button directive
|
||||
#############################################################################
|
||||
|
||||
UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
||||
UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue) ->
|
||||
template = _.template("""
|
||||
<label for="team-requirement"
|
||||
class="button button-gray team-requirement <% if(canEdit){ %>editable<% }; %> <% if(isRequired){ %>active<% }; %>">
|
||||
|
@ -381,24 +386,32 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
|||
html = template(ctx)
|
||||
$el.html(html)
|
||||
|
||||
$el.on "click", ".team-requirement", (event) ->
|
||||
return if not canEdit()
|
||||
|
||||
save = $qqueue.bindAdd (team_requirement) =>
|
||||
us = $model.$modelValue.clone()
|
||||
us.team_requirement = not us.team_requirement
|
||||
us.team_requirement = team_requirement
|
||||
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$loading.start($el.find("label"))
|
||||
|
||||
promise = $tgrepo.save($model.$modelValue)
|
||||
promise.then =>
|
||||
$loading.finish($el.find("label"))
|
||||
$rootscope.$broadcast("history:reload")
|
||||
|
||||
promise.then null, ->
|
||||
$loading.finish($el.find("label"))
|
||||
$confirm.notify("error")
|
||||
us.revert()
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$el.on "click", ".team-requirement", (event) ->
|
||||
return if not canEdit()
|
||||
|
||||
team_requirement = not $model.$modelValue.team_requirement
|
||||
|
||||
save(team_requirement)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (us) ->
|
||||
render(us) if us
|
||||
|
||||
|
@ -411,13 +424,13 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", UsTeamRequirementButtonDirective])
|
||||
module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", UsTeamRequirementButtonDirective])
|
||||
|
||||
#############################################################################
|
||||
## User story client requirements button directive
|
||||
#############################################################################
|
||||
|
||||
UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) ->
|
||||
UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue) ->
|
||||
template = _.template("""
|
||||
<label for="client-requirement"
|
||||
class="button button-gray client-requirement <% if(canEdit){ %>editable<% }; %> <% if(isRequired){ %>active<% }; %>">
|
||||
|
@ -442,11 +455,10 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) -
|
|||
html = template(ctx)
|
||||
$el.html(html)
|
||||
|
||||
$el.on "click", ".client-requirement", (event) ->
|
||||
return if not canEdit()
|
||||
|
||||
save = $qqueue.bindAdd (client_requirement) =>
|
||||
us = $model.$modelValue.clone()
|
||||
us.client_requirement = not us.client_requirement
|
||||
us.client_requirement = client_requirement
|
||||
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$loading.start($el.find("label"))
|
||||
|
@ -460,6 +472,12 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) -
|
|||
us.revert()
|
||||
$model.$setViewValue(us)
|
||||
|
||||
$el.on "click", ".client-requirement", (event) ->
|
||||
return if not canEdit()
|
||||
|
||||
client_requirement = not $model.$modelValue.client_requirement
|
||||
save(client_requirement)
|
||||
|
||||
$scope.$watch $attrs.ngModel, (us) ->
|
||||
render(us) if us
|
||||
|
||||
|
@ -472,5 +490,5 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading) -
|
|||
require: "ngModel"
|
||||
}
|
||||
|
||||
module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading",
|
||||
module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
||||
UsClientRequirementButtonDirective])
|
||||
|
|
|
@ -212,17 +212,20 @@ module.directive("tgWikiSummary", ["$log", WikiSummaryDirective])
|
|||
#############################################################################
|
||||
|
||||
EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $location, $navUrls,
|
||||
$analytics) ->
|
||||
$analytics, $qqueue) ->
|
||||
template = """
|
||||
<div class="view-wiki-content">
|
||||
<section class="wysiwyg"
|
||||
tg-bind-html="wiki.html"></section>
|
||||
<section class="wysiwyg" tg-bind-html="wiki.html"></section>
|
||||
<span class="edit icon icon-edit" title="Edit"></span>
|
||||
</div>
|
||||
<div class="edit-wiki-content" style="display: none;">
|
||||
<textarea placeholder="Write your wiki page here"
|
||||
ng-model="wiki.content"
|
||||
tg-markitup="tg-markitup"></textarea>
|
||||
<a class="help-markdown" href="https://taiga.io/support/taiga-markdown-syntax/" target="_blank" title="Mardown syntax help">
|
||||
<span class="icon icon-help"></span>
|
||||
<span>Markdown syntax help</span>
|
||||
</a>
|
||||
<span class="action-container">
|
||||
<a class="save icon icon-floppy" href="" title="Save" />
|
||||
<a class="cancel icon icon-delete" href="" title="Cancel" />
|
||||
|
@ -262,6 +265,29 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
|
|||
return $document.selection.createRange().text
|
||||
return null
|
||||
|
||||
save = $qqueue.bindAdd (wiki) ->
|
||||
onSuccess = (wikiPage) ->
|
||||
if not wiki.id?
|
||||
$analytics.trackEvent("wikipage", "create", "create wiki page", 1)
|
||||
|
||||
$scope.wiki = wikiPage
|
||||
$model.setModelValue = wiki
|
||||
$confirm.notify("success")
|
||||
switchToReadMode()
|
||||
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
|
||||
$loading.start($el.find('.save-container'))
|
||||
|
||||
if wiki.id?
|
||||
promise = $repo.save(wiki).then(onSuccess, onError)
|
||||
else
|
||||
promise = $repo.create("wiki", wiki).then(onSuccess, onError)
|
||||
|
||||
promise.finally ->
|
||||
$loading.finish($el.find('.save-container'))
|
||||
|
||||
$el.on "mouseup", ".view-wiki-content", (event) ->
|
||||
# We want to dettect the a inside the div so we use the target and
|
||||
# not the currentTarget
|
||||
|
@ -272,25 +298,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
|
|||
switchToEditMode()
|
||||
|
||||
$el.on "click", ".save", debounce 2000, ->
|
||||
onSuccess = (wikiPage) ->
|
||||
if not $scope.wiki.id?
|
||||
$analytics.trackEvent("wikipage", "create", "create wiki page", 1)
|
||||
|
||||
$scope.wiki = wikiPage
|
||||
$model.setModelValue = $scope.wiki
|
||||
$confirm.notify("success")
|
||||
switchToReadMode()
|
||||
|
||||
onError = ->
|
||||
$confirm.notify("error")
|
||||
|
||||
$loading.start($el.find('.save-container'))
|
||||
if $scope.wiki.id?
|
||||
promise = $repo.save($scope.wiki).then(onSuccess, onError)
|
||||
else
|
||||
promise = $repo.create("wiki", $scope.wiki).then(onSuccess, onError)
|
||||
promise.finally ->
|
||||
$loading.finish($el.find('.save-container'))
|
||||
save($scope.wiki)
|
||||
|
||||
$el.on "click", ".cancel", ->
|
||||
cancelEdition()
|
||||
|
@ -321,5 +329,5 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
|
|||
}
|
||||
|
||||
module.directive("tgEditableWikiContent", ["$window", "$document", "$tgRepo", "$tgConfirm", "$tgLoading",
|
||||
"$tgLocation", "$tgNavUrls", "$tgAnalytics",
|
||||
"$tgLocation", "$tgNavUrls", "$tgAnalytics", "$tgQqueue",
|
||||
EditableWikiContentDirective])
|
||||
|
|
|
@ -23,6 +23,17 @@ nl2br = (str) =>
|
|||
breakTag = '<br />'
|
||||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2')
|
||||
|
||||
bindMethods = (object) =>
|
||||
dependencies = _.keys(object)
|
||||
|
||||
methods = []
|
||||
|
||||
_.forIn object, (value, key) =>
|
||||
if key not in dependencies
|
||||
methods.push(key)
|
||||
|
||||
_.bindAll(object, methods)
|
||||
|
||||
bindOnce = (scope, attr, continuation) =>
|
||||
val = scope.$eval(attr)
|
||||
if val != undefined
|
||||
|
@ -106,9 +117,11 @@ joinStr = (str, coll) ->
|
|||
debounce = (wait, func) ->
|
||||
return _.debounce(func, wait, {leading: true, trailing: false})
|
||||
|
||||
|
||||
debounceLeading = (wait, func) ->
|
||||
return _.debounce(func, wait, {leading: false, trailing: true})
|
||||
|
||||
|
||||
startswith = (str1, str2) ->
|
||||
return _.str.startsWith(str1, str2)
|
||||
|
||||
|
@ -130,6 +143,7 @@ sizeFormat = (input, precision=1) ->
|
|||
|
||||
taiga = @.taiga
|
||||
taiga.nl2br = nl2br
|
||||
taiga.bindMethods = bindMethods
|
||||
taiga.bindOnce = bindOnce
|
||||
taiga.mixOf = mixOf
|
||||
taiga.trim = trim
|
||||
|
|
Binary file not shown.
|
@ -49,4 +49,9 @@
|
|||
<glyph unicode="N" d="M125 68l40-41 91 91 91-91 40 41-131 131z m262 376l-40 41-91-91-91 91-40-41 131-131z"/>
|
||||
<glyph unicode="O" d="M256 435l87-88 39 39-126 126-126-126 39-39z m0-358l-87 88-39-39 126-126 126 126-39 39z"/>
|
||||
<glyph unicode="P" d="M427 491l-363 0c-35 0-64-29-64-64l0-256c0-36 43-43 64-43l0-128 192 128 171 0c35 0 64 29 64 64l0 235c0 35-29 64-64 64z m21-299c0-12-10-21-21-21l-192 0-128-107 0 107-43 0c-12 0-21 9-21 21l0 235c0 11 9 21 21 21l363 0c11 0 21-10 21-21z"/>
|
||||
<glyph unicode="Q" d="M194 434c-26-29-19-74-19-74 0 0 27-32 80-32 53 0 80 32 80 32 0 0 7 45-19 73 17 9 26 22 22 31-4 10-24 11-44 2-7-3-14-7-18-12-7 1-13 2-21 2-7 0-13-1-19-2-5 5-11 9-18 12-20 9-40 8-44-2-4-9 5-21 20-30z m201-207c-4 1-8 1-12 2 0 1 0 2 0 3 0 17-2 33-6 48 8-1 19 1 29 6 20 9 33 24 28 34-4 10-24 11-44 2-9-4-17-10-22-15-4 9-8 18-13 25-15-12-44-31-84-35l0-161c0 0 0-16-16-16-16 0-16 16-16 16l0 161c-40 4-68 23-83 35-5-7-9-16-13-24-5 5-13 10-21 14-20 9-40 8-44-2-5-10 8-25 28-34 10-4 19-6 27-6-3-15-6-31-6-48 0-1 0-2 0-3-3-1-6-1-10-2-26-5-46-19-44-30 1-11 23-16 49-10 4 1 7 1 10 2 5-20 12-39 22-55-6-3-12-8-18-14-16-16-22-35-14-43 8-8 27-1 43 15 5 4 9 9 11 14 22-21 49-34 79-34 30 0 58 13 80 35 3-5 7-10 12-15 16-16 35-23 43-15 8 8 2 27-14 43-7 7-13 11-20 14 10 17 18 36 22 56 4-1 8-2 12-3 26-6 48-1 49 10 2 11-18 25-44 30z"/>
|
||||
<glyph unicode="R" d="M183 439l146 0 0 36-146 0z m329-183l0-137c0-13-4-23-13-32-9-9-20-14-33-14l-420 0c-13 0-24 5-33 14-9 9-13 19-13 32l0 137 192 0 0-46c0-5 2-9 5-13 4-3 8-5 13-5l92 0c5 0 9 2 13 5 3 4 5 8 5 13l0 46z m-219 0l0-37-74 0 0 37z m219 137l0-110-512 0 0 110c0 13 4 23 13 32 9 9 20 14 33 14l100 0 0 46c0 7 3 14 8 19 6 5 12 8 20 8l164 0c8 0 14-3 20-8 5-5 8-12 8-19l0-46 100 0c13 0 24-5 33-14 9-9 13-19 13-32z"/>
|
||||
<glyph unicode="S" d="M107 6c-2-7-7-8-14-4-6 3-8 9-8 17 2 35 10 73 26 116-34 53-43 107-27 162 4-11 9-24 17-40 7-16 15-29 22-41 8-12 13-17 17-15 2 1 2 15 0 42-3 27-5 55-6 85-1 30 3 57 13 81 7 15 21 31 41 48 19 17 37 29 53 36-8-16-14-32-17-49-3-16-4-29-2-40 2-10 5-15 11-16 4 0 18 21 43 62 24 40 42 61 54 62 16 1 35-4 58-15 24-11 38-22 42-33 4-8 4-22 0-41-4-18-11-33-20-42-15-15-40-26-75-32-35-6-54-10-58-12-6-4-4-9 6-18 18-16 48-19 90-10-19-27-42-47-70-58-27-12-49-18-67-20-18-1-27-3-28-5-1-8 7-17 25-27 18-11 36-13 52-8-10-19-21-33-32-43-12-9-21-15-28-17-7-3-20-5-39-6-19-1-33-3-43-4 0 0-36-115-36-115"/>
|
||||
<glyph unicode="T" d="M433 458l-106 0c-10 29-38 51-71 51-33 0-61-22-71-51l-106 0c-28 0-50-22-50-50l0-354c0-28 22-51 50-51l354 0c28 0 50 23 50 51l0 354c0 28-22 50-50 50z m-177 0c14 0 25-11 25-25 0-14-11-25-25-25-14 0-25 11-25 25 0 14 11 25 25 25z m-51-354l-101 101 36 36 65-65 167 166 36-35z"/>
|
||||
<glyph unicode="U" d="M293 119l0 55c0 2-1 5-3 6-2 2-4 3-7 3l-54 0c-3 0-5-1-7-3-2-1-3-4-3-6l0-55c0-3 1-5 3-7 2-1 4-2 7-2l54 0c3 0 5 1 7 2 2 2 3 4 3 7z m73 192c0 17-6 32-16 46-11 15-24 26-40 34-16 7-32 11-48 11-47 0-82-20-106-61-3-4-2-8 2-12l38-28c1-1 3-2 5-2 3 0 6 1 7 4 10 13 19 21 25 26 6 4 15 7 24 7 10 0 18-3 25-8 7-5 11-10 11-17 0-7-2-13-6-17-4-4-10-9-20-13-12-5-23-13-33-25-10-11-15-23-15-35l0-11c0-2 1-5 3-6 2-2 4-3 7-3l54 0c3 0 5 1 7 3 2 1 3 4 3 6 0 4 2 9 6 14 4 6 9 11 15 15 6 3 11 6 14 8 4 2 8 5 13 10 6 4 10 9 13 13 3 5 6 11 8 18 3 7 4 14 4 23z m109-55c0-40-9-77-29-110-20-34-46-60-80-80-33-20-70-29-110-29-40 0-77 9-110 29-34 20-60 46-80 80-20 33-29 70-29 110 0 40 9 77 29 110 20 34 46 60 80 80 33 20 70 29 110 29 40 0 77-9 110-29 34-20 60-46 80-80 20-33 29-70 29-110z"/>
|
||||
</font></defs></svg>
|
||||
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
Binary file not shown.
|
@ -4,7 +4,7 @@ block head
|
|||
title Taiga Your agile, free, and open source project management tool
|
||||
|
||||
block content
|
||||
div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl",
|
||||
div.wrapper(tg-project-default-values, ng-controller="ProjectProfileController as ctrl",
|
||||
ng-init="section='admin'; sectionName='Default values'")
|
||||
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
|
||||
include views/modules/admin-menu
|
||||
|
|
|
@ -93,5 +93,5 @@ block content
|
|||
option(value="") Select a videoconference system
|
||||
input(type="text", ng-model="project.videoconferences_salt",
|
||||
placeholder="If you want you can append a salt code to the name of the chat room")
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green(href="") Save
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
|
|
@ -54,8 +54,8 @@ block content
|
|||
|
||||
p All projects are private during Taiga's beta period.
|
||||
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green(href="") Save
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
a.delete-project(href="", title="Delete this project", ng-click="ctrl.openDeleteLightbox()") Delete this project
|
||||
|
||||
div.lightbox.lightbox-delete-project(tg-lb-delete-project)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
block head
|
||||
title Taiga Your agile, free, and open source project management tool
|
||||
|
||||
block content
|
||||
div.wrapper.roles(tg-bitbucket-webhooks, ng-controller="BitbucketController as ctrl",
|
||||
ng-init="section='admin'")
|
||||
sidebar.menu-secondary.sidebar(tg-admin-navigation="third-parties")
|
||||
include views/modules/admin-menu
|
||||
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-bitbucket")
|
||||
include views/modules/admin-submenu-third-parties
|
||||
|
||||
section.main.admin-common.admin-third-parties
|
||||
include views/components/mainTitle
|
||||
|
||||
form
|
||||
fieldset
|
||||
label(for="secret-key") Secret key
|
||||
input(type="text", name="secret-key", ng-model="bitbucket.secret", placeholder="Secret key", id="secret-key")
|
||||
|
||||
fieldset
|
||||
.select-input-text(tg-select-input-text)
|
||||
div
|
||||
label(for="payload-url") Payload URL
|
||||
.field-with-option
|
||||
input(type="text", ng-model="bitbucket.webhooks_url", name="payload-url", readonly="readonly", placeholder="Payload URL", id="payload-url")
|
||||
.option-wrapper.select-input-content
|
||||
.icon.icon-copy
|
||||
.help-copy Copy to clipboard: Ctrl+C
|
||||
|
||||
fieldset
|
||||
label(for="valid-origin-ips") Valid origin ips (separated by ,)
|
||||
input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="bitbucket.valid_origin_ips", placeholder="Bitbucket requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation.", id="valid-origin-ips")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
||||
|
||||
a.help-button(href="https://taiga.io/support/bitbucket-integration/", target="_blank")
|
||||
span.icon.icon-help
|
||||
span Do you need help? Check out our support page!
|
|
@ -27,69 +27,10 @@ block content
|
|||
.icon.icon-copy
|
||||
.help-copy Copy to clipboard: Ctrl+C
|
||||
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green(href="") Save
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
||||
|
||||
.help
|
||||
h2 How to use it
|
||||
|
||||
h3 Configure Taiga
|
||||
ol
|
||||
li Fill
|
||||
span Secret key
|
||||
| or use the auto generated one
|
||||
|
||||
li Copy the
|
||||
span Payload URL field.
|
||||
|
||||
h3 Configure Github
|
||||
ol
|
||||
li Go to your github repository.
|
||||
li Click on
|
||||
span Settings
|
||||
| >
|
||||
span Webhooks & Services
|
||||
| >
|
||||
span Add webhook
|
||||
|
||||
li On that screen set the payload url with the payload url of this screen.
|
||||
li Secret must be filled with the same content as the secret field of this screen.
|
||||
li Content type must be
|
||||
span application/json.
|
||||
li Taiga currently listen for three different kind of events:
|
||||
ol
|
||||
li Push events: changing element status via commit message
|
||||
li Issues: issues created in github appear automatically in Taiga
|
||||
li Issue comment: issue comments created in github appear automatically in Taiga
|
||||
|
||||
p Just check "send me everything" or just the events you want Taiga to listen for.
|
||||
|
||||
.img
|
||||
.alt-image Github Webhooke page
|
||||
img(src="/images/github-help.png", alt="webhook")
|
||||
|
||||
h2 Changing elements status via commit message
|
||||
|
||||
p
|
||||
| The status of any issue, task or user story can be changed via commit message.
|
||||
| Just add to your commit message something like:
|
||||
|
||||
code
|
||||
| TG-REF #STATUS
|
||||
|
||||
ul.code-info
|
||||
li
|
||||
span REF:
|
||||
| US/Issue/Task reference of the element you want to modify
|
||||
li
|
||||
span STATUS:
|
||||
| New status slug to set, you can find all of them in:
|
||||
a(href="", tg-nav="project-admin-project-values-us-status:project=project.slug") US STATUSES.
|
||||
|
||||
h3 An example please!
|
||||
|
||||
code
|
||||
| TG-123 #closed
|
||||
|
||||
p In this example, 123 is an issue reference and with this command, the issue will change its status to closed.
|
||||
a.help-button(href="https://taiga.io/support/github-integration/", target="_blank")
|
||||
span.icon.icon-help
|
||||
span Do you need help? Check out our support page!
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
block head
|
||||
title Taiga Your agile, free, and open source project management tool
|
||||
|
||||
block content
|
||||
div.wrapper.roles(tg-gitlab-webhooks, ng-controller="GitlabController as ctrl",
|
||||
ng-init="section='admin'")
|
||||
sidebar.menu-secondary.sidebar(tg-admin-navigation="third-parties")
|
||||
include views/modules/admin-menu
|
||||
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-gitlab")
|
||||
include views/modules/admin-submenu-third-parties
|
||||
|
||||
section.main.admin-common.admin-third-parties
|
||||
include views/components/mainTitle
|
||||
|
||||
form
|
||||
fieldset
|
||||
label(for="secret-key") Secret key
|
||||
input(type="text", name="secret-key", ng-model="gitlab.secret", placeholder="Secret key", id="secret-key")
|
||||
|
||||
fieldset
|
||||
.select-input-text(tg-select-input-text)
|
||||
div
|
||||
label(for="payload-url") Payload URL
|
||||
.field-with-option
|
||||
input(type="text", ng-model="gitlab.webhooks_url", name="payload-url", readonly="readonly", placeholder="Payload URL", id="payload-url")
|
||||
.option-wrapper.select-input-content
|
||||
.icon.icon-copy
|
||||
.help-copy Copy to clipboard: Ctrl+C
|
||||
|
||||
fieldset
|
||||
label(for="valid-origin-ips") Valid origin ips (separated by ,)
|
||||
input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="gitlab.valid_origin_ips", placeholder="Gitlab requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation.", id="valid-origin-ips")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
||||
a.help-button(href="https://taiga.io/support/gitlab-integration/", target="_blank")
|
||||
span.icon.icon-help
|
||||
span Do you need help? Check out our support page!
|
|
@ -16,7 +16,7 @@ block content
|
|||
span.us-number(tg-bo-ref="issue.ref")
|
||||
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:
|
||||
p.us-related-task(ng-if="issue.generated_user_stories.length") This issue has been promoted to US:
|
||||
a(ng-repeat="us in issue.generated_user_stories",
|
||||
tg-check-permission="view_us", href="",
|
||||
tg-bo-title="'#' + us.ref + ' ' + us.subject",
|
||||
|
|
|
@ -5,7 +5,7 @@ block head
|
|||
|
||||
block content
|
||||
div.wrapper(ng-controller="TaskDetailController as ctrl",
|
||||
ng-init="section='backlog'")
|
||||
ng-init="section='backlog-kanban'")
|
||||
div.main.us-detail
|
||||
div.us-detail-header.header-with-actions
|
||||
include views/components/mainTitle
|
||||
|
|
|
@ -11,7 +11,7 @@ block content
|
|||
span(tg-bo-bind="project.name", class="project-name-short")
|
||||
span.green(tg-bo-bind="sprint.name")
|
||||
span.date(tg-date-range="sprint.estimated_start,sprint.estimated_finish")
|
||||
include views/components/sprint-summary
|
||||
//- include views/components/sprint-summary
|
||||
|
||||
div.graphics-container
|
||||
div.burndown(tg-sprint-graph)
|
||||
|
|
|
@ -5,7 +5,7 @@ block head
|
|||
|
||||
block content
|
||||
div.wrapper(ng-controller="UserStoryDetailController as ctrl",
|
||||
ng-init="section='backlog'")
|
||||
ng-init="section='backlog-kanban'")
|
||||
div.main.us-detail
|
||||
div.us-detail-header.header-with-actions
|
||||
include views/components/mainTitle
|
||||
|
|
|
@ -25,5 +25,5 @@ block content
|
|||
label(for="retype-password") Retype Password
|
||||
input(type="password", placeholder="Retype Password", id="retype-password", ng-model="newPassword2")
|
||||
fieldset
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green(href="", ng-click="ctrl.save()") Save
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
|
|
@ -55,8 +55,8 @@ block content
|
|||
ng-model="user.bio")
|
||||
|
||||
fieldset.submit
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green.save-profile(href="") Save
|
||||
button(type="submit", title="Save", class="hidden")
|
||||
a.button.button-green.save-profile.submit-button(href="") Save
|
||||
a.delete-account(href="", title="Delete Taiga account",
|
||||
ng-click="ctrl.openDeleteLightbox()") Delete Taiga account
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
div.taskboard-tagline(tg-colorize-tags="task.tags", tg-colorize-tags-type="taskboard")
|
||||
div.taskboard-task-inner
|
||||
div.taskboard-user-avatar(tg-taskboard-user-avatar="task.assigned_to", ng-model="task",
|
||||
ng-class="{iocaine: task.is_iocaine}")
|
||||
div.taskboard-user-avatar(tg-taskboard-user-avatar, users="usersById", task="task", project="project", ng-class="{iocaine: task.is_iocaine}")
|
||||
span.icon.icon-iocaine(ng-if="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!")
|
||||
p.taskboard-text
|
||||
a.task-assigned(href="", title="Assign task")
|
||||
|
|
|
@ -8,3 +8,11 @@ section.admin-submenu
|
|||
a(href="", tg-nav="project-admin-third-parties-github:project=project.slug")
|
||||
span.title Github
|
||||
span.icon.icon-arrow-right
|
||||
li#adminmenu-third-parties-gitlab
|
||||
a(href="", tg-nav="project-admin-third-parties-gitlab:project=project.slug")
|
||||
span.title Gitlab
|
||||
span.icon.icon-arrow-right
|
||||
li#adminmenu-third-parties-bitbucket
|
||||
a(href="", tg-nav="project-admin-third-parties-bitbucket:project=project.slug")
|
||||
span.title Bitbucket
|
||||
span.icon.icon-arrow-right
|
||||
|
|
|
@ -36,5 +36,5 @@ section.default-values
|
|||
ng-options="s.id as s.name for s in issueStatusList")
|
||||
|
||||
fieldset
|
||||
input(type="submit", class="hidden")
|
||||
a.button.button-green(href="", title="Save") Save
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
|
|
@ -3,10 +3,10 @@ div.change-email-form-container(tg-cancel-account)
|
|||
strong Cancel your account <br />
|
||||
span We're sorry you are leaving the taiga, we hope you enjoyed your stay :)
|
||||
|
||||
form(ng-submit="ctrl.submit()")
|
||||
form
|
||||
fieldset
|
||||
input(type="hidden", name="cancel_token", ng-model="data.cancel_token", data-required="true",
|
||||
placeholder="cancel account token")
|
||||
|
||||
a.button.button-cancel-account.button-gray(href="", title="Yes, I'm leaving") Yes, I'm leaving!
|
||||
input(type="submit", style="display:none")
|
||||
button(type="submit", class="hidden")
|
||||
|
|
|
@ -3,10 +3,10 @@ div.change-email-form-container(tg-change-email)
|
|||
strong Change your email <br />
|
||||
span One click more and your email will be updated!
|
||||
|
||||
form(ng-submit="ctrl.submit()")
|
||||
form
|
||||
fieldset
|
||||
input(type="hidden", name="email_token", ng-model="data.email_token", data-required="true",
|
||||
placeholder="change email token")
|
||||
|
||||
a.button.button-change-email.button-gray(href="", title="Change email") Change email
|
||||
input(type="submit", style="display:none")
|
||||
button(type="submit", class="hidden")
|
||||
|
|
|
@ -3,7 +3,7 @@ div.change-password-form-container(tg-change-password-from-recovery)
|
|||
strong Create a new Taiga pass <br />
|
||||
span And hey, you may want to eat some more iron-rich food, it's good for your brain :P
|
||||
|
||||
form(ng-submit="ctrl.submit()")
|
||||
form
|
||||
fieldset.token-change-password(ng-hide="tokenInParams")
|
||||
input(type="text", name="token", ng-model="data.token", data-required="true",
|
||||
placeholder="Recover password token")
|
||||
|
@ -16,5 +16,5 @@ div.change-password-form-container(tg-change-password-from-recovery)
|
|||
input(type="password", name="password2", id="password2", ng-model="data.password2",
|
||||
data-required="true", data-equalto="#password", placeholder="Re-type new password")
|
||||
fieldset
|
||||
a.button.button-change-password.button-gray(href="", title="Reset Password") Reset Password
|
||||
input(type="submit", style="display:none")
|
||||
a.button.button-change-password.button-gray.submit-button(href="", title="Reset Password") Reset Password
|
||||
button(type="submit", class="hidden")
|
||||
|
|
|
@ -10,7 +10,7 @@ div.forgot-form-container(tg-forgot-password)
|
|||
input(type="text", name="username", ng-model="data.username", data-required="true",
|
||||
placeholder="Username or email")
|
||||
fieldset
|
||||
a.button.button-forgot.button-gray(href="", title="Reset Password") Reset Password
|
||||
input(type="submit", style="display:none")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-gray.submit-button.button-forgot(href="", title="Reset Password") Reset Password
|
||||
|
||||
a(href="", title="Login", tg-nav="login") Nah, take me back. I think I remember it.
|
||||
|
|
|
@ -8,7 +8,7 @@ form.login-form
|
|||
placeholder="Password")
|
||||
a.forgot-pass(href="", tg-nav="forgot-password", title="Did you forgot your password?") Forgot it?
|
||||
fieldset
|
||||
a.button.button-login.button-gray(href="", title="Log in") Enter
|
||||
input(type="submit", style="display:none")
|
||||
a.button.button-login.button-gray.submit-button(href="", title="Log in") Enter
|
||||
button(type="submit", class="hidden")
|
||||
|
||||
fieldset(tg-github-login-button)
|
||||
|
|
|
@ -20,7 +20,7 @@ form.register-form
|
|||
placeholder="Set a password")
|
||||
|
||||
fieldset
|
||||
a.button.button-register.button-gray(href="", title="Sign up") Sign up
|
||||
input(type="submit", style="display:none")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-register.button-gray.submit-button(href="", title="Sign up") Sign up
|
||||
|
||||
tg-terms-notice
|
||||
|
|
|
@ -6,7 +6,9 @@ form
|
|||
//- Form is set in a directive
|
||||
.add-member-forms
|
||||
|
||||
a.button.button-green(href="", title="Save")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Create")
|
||||
span Create
|
||||
|
||||
p.help-text If users are already registered on Taiga, they will be added automatically. Otherwise they will receive an invitation.
|
||||
|
|
|
@ -20,6 +20,6 @@ form
|
|||
textarea.description(placeholder="Description", ng-model="issue.description")
|
||||
|
||||
// include lightbox-attachments
|
||||
input(type="submit", style="display:none")
|
||||
a.button.button-green(href="", title="Save")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save")
|
||||
span Create
|
||||
|
|
|
@ -6,6 +6,6 @@ form
|
|||
textarea(ng-model="feedback.comment", data-required="true",
|
||||
placeholder="...a bug, some suggestions, something cool... or even your worst nightmare with Taiga")
|
||||
fieldset
|
||||
input.hidden(type="submit")
|
||||
a.button.button-green(href="", title="Send feedback")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Send feedback")
|
||||
span Send feedback
|
||||
|
|
|
@ -4,5 +4,6 @@ form
|
|||
h2.title(tr="common.new-bulk")
|
||||
fieldset
|
||||
textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="new.bulk", data-required="true", data-linewidth="200")
|
||||
a.button.button-green(href="", tr="title:common.save")
|
||||
span(tr="common.save")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
|
|
@ -5,6 +5,5 @@ form
|
|||
fieldset
|
||||
input(type="text", name="text", id="search-text", placeholder="What are you looking for?", data-required="true")
|
||||
fieldset
|
||||
input.hidden(type="submit")
|
||||
a.button.button-green(href="", title="Accept")
|
||||
span Search
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Search") Search
|
|
@ -15,7 +15,8 @@ form
|
|||
input.date-end(type="text", name="estimated_finish", placeholder="Estimated End",
|
||||
ng-model="sprint.estimated_finish", data-required="true", tg-date-selector)
|
||||
|
||||
a.button.button-green(href="", title="Save")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Create")
|
||||
span Create
|
||||
|
||||
div(tg-check-permission="delete_milestone")
|
||||
|
|
|
@ -4,5 +4,6 @@ form
|
|||
h2.title(tr="common.new-bulk")
|
||||
fieldset
|
||||
textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="form.data", data-required="true")
|
||||
a.button.button-green(href="", tr="title:common.save")
|
||||
span(tr="common.save")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
|
|
@ -34,5 +34,6 @@ form
|
|||
|
||||
tg-blocking-message-input(watch="task.is_blocked", ng-model="task.blocked_note")
|
||||
|
||||
a.button.button-green(href="", title="Save")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save")
|
||||
span Create
|
||||
|
|
|
@ -4,5 +4,6 @@ form
|
|||
h2.title(tr="common.new-bulk")
|
||||
fieldset
|
||||
textarea(cols="200", wrap="off", tg-limit-line-length, tr="placeholder:common.one-item-line", ng-model="new.bulk", data-required="true", data-linewidth="200")
|
||||
a.button.button-green(href="", tr="title:common.save")
|
||||
span(tr="common.save")
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
|
@ -36,5 +36,5 @@ form
|
|||
|
||||
tg-blocking-message-input(watch="us.is_blocked", ng-model="us.blocked_note")
|
||||
|
||||
a.button.button-green(href="", title="Save")
|
||||
span Create
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Submit") Create
|
||||
|
|
|
@ -10,8 +10,8 @@ div.login-form-container(tg-login)
|
|||
a.forgot-pass(href="", tg-nav="forgot-password", title="Did you forgot your password?") Forgot it?
|
||||
|
||||
fieldset
|
||||
a.button.button-login.button-gray(href="", title="Sign in") Sign in
|
||||
input(type="submit", style="display:none")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-login.button-gray.submit-button(href="", title="Sign in") Sign in
|
||||
|
||||
fieldset(tg-github-login-button)
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ div.register-form-container(tg-register)
|
|||
placeholder="Set a password (case sensitive)")
|
||||
|
||||
fieldset
|
||||
a.button.button-register.button-gray(href="", title="Sign up") Sign up
|
||||
input(type="submit", class="hidden")
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-register.button-gray.submit-button(href="", title="Sign up") Sign up
|
||||
|
||||
fieldset(tg-github-login-button)
|
||||
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
div.taskboard-table
|
||||
div.taskboard-table(tg-taskboard-squish-column)
|
||||
div.taskboard-table-header
|
||||
div.taskboard-table-inner(tg-taskboard-row-width-fixer)
|
||||
div.taskboard-table-inner
|
||||
h2.task-colum-name "User story"
|
||||
h2.task-colum-name(ng-repeat="s in taskStatusList track by s.id",
|
||||
ng-style="{'border-top-color':s.color}")
|
||||
h2.task-colum-name(ng-repeat="s in taskStatusList track by s.id", ng-style="{'border-top-color':s.color}", ng-class="{'column-fold':statusesFolded[s.id]}", class="squish-status-{{s.id}}", tg-bo-title="s.name")
|
||||
span(tg-bo-bind="s.name")
|
||||
a.icon.icon-vfold.hfold(href="", ng-click='foldStatus(s)', title="Fold Column", ng-class='{hidden:statusesFolded[s.id]}')
|
||||
a.icon.icon-vunfold.hunfold(href="", title="Unfold Column", ng-click='foldStatus(s)', ng-class='{hidden:!statusesFolded[s.id]}')
|
||||
|
||||
div.taskboard-table-body(tg-taskboard-table-height-fixer)
|
||||
div.taskboard-table-inner(tg-taskboard-row-width-fixer)
|
||||
div.task-row(ng-repeat="us in userstories track by us.id", ng-class="{blocked: us.is_blocked}")
|
||||
div.taskboard-table-inner
|
||||
div.task-row(ng-repeat="us in userstories track by us.id", ng-class="{blocked: us.is_blocked, 'row-fold':usFolded[us.id]}")
|
||||
div.taskboard-userstory-box.task-column(tg-bo-title="us.blocked_note")
|
||||
a.icon.icon-vfold.vfold(href="", title="Fold Row", ng-click='foldUs(us)', ng-class='{hidden:usFolded[us.id]}')
|
||||
a.icon.icon-vunfold.vunfold(href="", title="Unfold Row", ng-click='foldUs(us)', ng-class='{hidden:!usFolded[us.id]}')
|
||||
h3.us-title
|
||||
a(href="", tg-nav="project-userstories-detail:project=project.slug,ref=us.ref",
|
||||
tg-bo-title="'#' + us.ref + ' ' + us.subject")
|
||||
|
@ -18,22 +21,20 @@ div.taskboard-table
|
|||
p.points-value
|
||||
span(ng-bind="us.total_points")
|
||||
span points
|
||||
include ../components/addnewtask.jade
|
||||
|
||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id",
|
||||
tg-taskboard-sortable)
|
||||
include ../components/addnewtask
|
||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}")
|
||||
div.taskboard-task(ng-repeat="task in usTasks[us.id][st.id] track by task.id",
|
||||
tg-taskboard-task)
|
||||
include ../components/taskboard-task
|
||||
|
||||
div.task-row(ng-init="us = null")
|
||||
div.task-row(ng-init="us = null", ng-class="{'row-fold':usFolded['unassigned']}")
|
||||
div.taskboard-userstory-box.task-column
|
||||
a.icon.icon-vfold.vfold(href="", title="Fold Row", ng-click='foldUs()', ng-class="{hidden:usFolded['unassigned']}")
|
||||
a.icon.icon-vunfold.vunfold(href="", title="Unfold Row", ng-click='foldUs()', ng-class="{hidden:!usFolded['unassigned']}")
|
||||
h3.us-title
|
||||
span Unassigned tasks
|
||||
include ../components/addnewtask.jade
|
||||
|
||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id",
|
||||
tg-taskboard-sortable)
|
||||
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}")
|
||||
div.taskboard-task(ng-repeat="task in usTasks[null][st.id] track by task.id",
|
||||
tg-taskboard-task)
|
||||
include ../components/taskboard-task
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
section.team-filters
|
||||
div.team-filters-inner
|
||||
header
|
||||
h1 filters
|
||||
|
||||
form.search-in
|
||||
fieldset
|
||||
input(type="text", placeholder="Search by username or role...", ng-model="filtersQ")
|
||||
a.icon.icon-search(href="", title="search")
|
||||
|
||||
nav(tg-team-filters)
|
|
@ -0,0 +1,36 @@
|
|||
section.table-team.basic-table
|
||||
header.row.team-header
|
||||
div.username
|
||||
div.member-stats
|
||||
div.attribute.attribute-name(ng-if="issuesEnabled")
|
||||
span Mr. Wolf
|
||||
div.popover.attribute-explanation
|
||||
span I see, you solve issues!
|
||||
div.attribute(ng-if="tasksEnabled")
|
||||
span Poison Drinker
|
||||
div.popover.attribute-explanation
|
||||
span Hey, are you a iocaine-holic?
|
||||
div.attribute(ng-if="wikiEnabled")
|
||||
span Cervantes
|
||||
div.popover.attribute-explanation
|
||||
span You have no fear to the blank page!
|
||||
div.attribute(ng-if="issuesEnabled")
|
||||
Total Bug Hunter
|
||||
div.popover.attribute-explanation
|
||||
span Thaks to you, this project still alive.
|
||||
div.attribute(ng-if="tasksEnabled")
|
||||
span Night Shift
|
||||
div.popover.attribute-explanation
|
||||
span Poor Devil, you work too much.
|
||||
div.attribute
|
||||
Total Power
|
||||
div.popover.attribute-explanation
|
||||
span How far did you go into this Taiga?
|
||||
|
||||
div.hero(tg-team-current-user, stats="stats", currentuser="currentUser", projectid="projectId", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled")
|
||||
|
||||
h2(ng-show="memberships")
|
||||
span Team >
|
||||
span {{filtersRole.name || "All"}}
|
||||
|
||||
section.table-team.basic-table(tg-team-members, memberships="memberships", stats="stats", filtersq="filtersQ", filtersrole="filtersRole", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled")
|
|
@ -28,7 +28,9 @@ form
|
|||
fieldset.wizard-action
|
||||
div
|
||||
a.button-prev.button.button-gray(href="", title="Prev") Prev
|
||||
a.button-submit.button.button-green(href="", title="Create") Create
|
||||
a.submit-button.button.button-green(href="", title="Create") Create
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
|
||||
div.progress-bar
|
||||
div.progress-state
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
extends ../../dummy-layout
|
||||
|
||||
block head
|
||||
title Taiga Your agile, free, and open source project management tool
|
||||
|
||||
block content
|
||||
div.wrapper(ng-controller="TeamController as ctrl", ng-init="section='team'")
|
||||
sidebar.menu-secondary
|
||||
include ../modules/team/team-filters
|
||||
section.main.team
|
||||
include ../components/mainTitle
|
||||
include ../modules/team/team-table
|
|
@ -1,7 +1,7 @@
|
|||
.single-filter {
|
||||
@extend %large;
|
||||
@include clearfix;
|
||||
@extend %title;
|
||||
@include clearfix;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 32px;
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
}
|
||||
}
|
||||
&.ui-sortable-helper {
|
||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||
@include transition(box-shadow .3s linear);
|
||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||
}
|
||||
&.blocked {
|
||||
background: $red;
|
||||
|
@ -68,8 +68,8 @@
|
|||
display: block;
|
||||
}
|
||||
.task-text {
|
||||
@include table-flex-child($flex-grow: 10, $flex-basis: 50px);
|
||||
@extend %small;
|
||||
@include table-flex-child($flex-grow: 10, $flex-basis: 50px);
|
||||
padding: 0 .5rem 0 .8rem;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
@ -79,12 +79,11 @@
|
|||
}
|
||||
.task-name {
|
||||
@extend %bold;
|
||||
color: $grayer;
|
||||
}
|
||||
.icon-edit,
|
||||
.icon-drag-h {
|
||||
@include transition(opacity .2s linear);
|
||||
@extend %large;
|
||||
@include transition(opacity .2s linear);
|
||||
color: $postit-hover;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
a.help-markdown,
|
||||
a.help-button {
|
||||
@extend %small;
|
||||
color: $gray-light;
|
||||
&:hover {
|
||||
span {
|
||||
@include transition(color .2s linear);
|
||||
color: $grayer;
|
||||
}
|
||||
.icon {
|
||||
@include transition(color .2s linear);
|
||||
color: $fresh-taiga;
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
color: $gray-light;
|
||||
margin-right: .2rem;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
.taskboard-task {
|
||||
@include transition (all .4s linear);
|
||||
background: $postit;
|
||||
border: 1px solid $postit-hover;
|
||||
box-shadow: none;
|
||||
|
@ -17,8 +16,8 @@
|
|||
}
|
||||
}
|
||||
&.ui-sortable-helper {
|
||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||
@include transition(box-shadow .3s linear);
|
||||
box-shadow: 1px 1px 15px rgba($black, .4);
|
||||
}
|
||||
&.ui-sortable-placeholder {
|
||||
background: $grayer;
|
||||
|
@ -45,7 +44,6 @@
|
|||
}
|
||||
.taskboard-task-inner {
|
||||
@include table-flex();
|
||||
min-height: 7rem;
|
||||
padding: .5rem;
|
||||
}
|
||||
.taskboard-user-avatar {
|
||||
|
@ -100,7 +98,6 @@
|
|||
}
|
||||
.task-name {
|
||||
@extend %bold;
|
||||
color: $grayer;
|
||||
}
|
||||
.taskboard-text {
|
||||
@extend %small;
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
}
|
||||
}
|
||||
.watcher-name {
|
||||
@include table-flex-child(8, 0);
|
||||
@extend %small;
|
||||
@include table-flex-child(8, 0);
|
||||
color: $grayer;
|
||||
margin-left: 1rem;
|
||||
position: relative;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
$black: #000;
|
||||
$blackish: #050505;
|
||||
$gray: #555;
|
||||
$grayer: #444;
|
||||
$gray: #555;
|
||||
$gray-light: #b8b8b8;
|
||||
$whitish: #f5f5f5;
|
||||
$very-light-gray: #fcfcfc;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Bourbon
|
||||
$prefix-for-webkit: true;
|
||||
$prefix-for-mozilla: true;
|
||||
$prefix-for-microsoft: true;
|
||||
$prefix-for-opera: true;
|
||||
$prefix-for-spec: true;
|
||||
//$prefix-for-microsoft: true;
|
||||
//$prefix-for-opera: true;
|
||||
//$prefix-for-spec: true;
|
||||
@import '../bourbon/bourbon';
|
||||
|
||||
//#################################################
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Basic layout styles
|
||||
html {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -9,9 +8,7 @@ body {
|
|||
background: #fff; // fallback
|
||||
color: #444;
|
||||
-webkit-font-smoothing: antialiased; // Fix for webkit renderin
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden; // open-projects-nav
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
width: 100%;
|
||||
|
@ -90,7 +87,7 @@ body {
|
|||
|
||||
.wrapper {
|
||||
@include table-flex();
|
||||
min-height: 100%;
|
||||
min-height: 100vh;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.invitation-main {
|
||||
@include table-flex(center, center, flex, row, wrap, center);
|
||||
@extend %background-taiga;
|
||||
@include table-flex(center, center, flex, row, wrap, center);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
|
@ -62,8 +62,8 @@
|
|||
}
|
||||
}
|
||||
.forgot-pass {
|
||||
@include transition(all .3s linear);
|
||||
@extend %small;
|
||||
@include transition(all .3s linear);
|
||||
color: $gray-light;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
.login-main {
|
||||
@extend %triangled-bg;
|
||||
//@include table-flex(center, center, flex, row, wrap, center);
|
||||
@include display(flex);
|
||||
@include align-items(center);
|
||||
@include flex-direction(row);
|
||||
@include justify-content(center);
|
||||
@extend %triangled-bg;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.error-main {
|
||||
@extend %background-taiga;
|
||||
@include display(flex);
|
||||
@include align-items(center);
|
||||
@include flex-direction(row);
|
||||
@include justify-content(center);
|
||||
@extend %background-taiga;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
.team {
|
||||
h2 {
|
||||
margin: 1rem 0;
|
||||
span {
|
||||
&:last-child {
|
||||
color: $green-taiga;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue