Merge branch 'master' into stable

stable
Alejandro Alonso 2015-05-21 10:38:06 +02:00
commit f154afb5d9
305 changed files with 10331 additions and 2884 deletions

View File

@ -1,3 +1,3 @@
{
"directory" : "app/vendor"
"directory" : "vendor"
}

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
dist/
node_modules/
app/vendor
vendor/
config/main.coffee
bower_components
app/coffee/modules/locales/locale*.coffee

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
lang_map = sr@latin:sr-latn, zh-Hans:zh-hans, zh-Hant:zh-hant
[taiga-front.locale-enjson]
file_filter = app/locales/locale-<lang>.json
source_file = app/locales/locale-en.json
source_lang = en
type = KEYVALUEJSON

View File

@ -1,6 +1,23 @@
# Changelog #
## 1.7.0 Empetrum Nigrum (unreleased)
### Features
- Make Taiga translatable (i18n support).
- i18n.
- Add spanish (es) translation.
- Add french (fr) translation.
- Add finish (fi) translation.
- Add catalan (ca) translation.
- Add traditional chinese (zh-Hant) translation.
- Add Jitsi to our supported videoconference apps list
### Misc
- New contrib plugin for letschat (by Δndrea Stagi)
- Lots of small and not so small bugfixes.
## 1.6.0 Abies Bifolia (2015-03-17)
### Features

View File

@ -44,3 +44,10 @@ All the information about the different installation methods (production, develo
[Taiga has a mailing list](http://groups.google.com/d/forum/taigaio). Feel free to join it and ask any questions you may have.
To subscribe for announcements of releases, important changes and so on, please follow [@taigaio](https://twitter.com/taigaio) on Twitter.
## Donations ##
We are grateful for your emails volunteering donations to Taiga. We feel comfortable accepting them under these conditions: The first that we will only do so while we are in the current beta / pre-revenue stage and that whatever money is donated will go towards a bounty fund. Starting Q2 2015 we will be engaging much more actively with our community to help further the development of Taiga, and we will use these donations to reward people working alongside us.
If you wish to make a donation to this Taiga fund, you can do so via http://www.paypal.com using the email: eposner@taiga.io

View File

@ -3,6 +3,7 @@ window.taigaConfig = {
"api": "http://localhost:8000/api/v1/",
"eventsUrl": null,
"debug": true,
"defaultLanguage": "en",
"publicRegisterEnabled": true,
"feedbackEnabled": true,
"privacyPolicyUrl": null,

View File

@ -36,7 +36,8 @@ taiga.generateUniqueSessionIdentifier = ->
taiga.sessionId = taiga.generateUniqueSessionIdentifier()
configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider, $compileProvider) ->
configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider,
$compileProvider, $translateProvider) ->
$routeProvider.when("/",
{templateUrl: "project/projects.html", resolve: {loader: tgLoaderProvider.add()}})
@ -159,12 +160,12 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$routeProvider.when("/permission-denied",
{templateUrl: "error/permission-denied.html"})
$routeProvider.otherwise({redirectTo: '/not-found'})
$routeProvider.otherwise({redirectTo: "/not-found"})
$locationProvider.html5Mode({enabled: true, requireBase: false})
defaultHeaders = {
"Content-Type": "application/json"
"Accept-Language": "en"
"Accept-Language": window.taigaConfig.defaultLanguage || "en"
"X-Session-Id": taiga.sessionId
}
@ -195,30 +196,30 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
responseError: httpResponseError
}
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgNavUrls", "lightboxService", authHttpIntercept])
$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
$httpProvider.interceptors.push("authHttpIntercept")
# If there is an error in the version throw a notify error.
# IMPROVEiMENT: Move this version error handler to USs, issues and tasks repository
versionCheckHttpIntercept = ($q) ->
httpResponseError = (response) ->
if response.status == 400 && response.data.version
$confirm.notify("error", versionErrorMsg, null, 10000)
return $q.reject(response)
# HACK: to prevent circular dependencies with [$tgConfirm, $translate]
$injector = angular.element("body").injector()
$injector.invoke(["$tgConfirm", "$translate", ($confirm, $translate) =>
versionErrorMsg = $translate.instant("ERROR.VERSION_ERROR")
$confirm.notify("error", versionErrorMsg, null, 10000)
])
return $q.reject(response)
return {
responseError: httpResponseError
}
return {responseError: httpResponseError}
$provide.factory("versionCheckHttpIntercept", ["$q", "$tgConfirm", versionCheckHttpIntercept])
$provide.factory("versionCheckHttpIntercept", ["$q", versionCheckHttpIntercept])
$httpProvider.interceptors.push('versionCheckHttpIntercept');
$httpProvider.interceptors.push("versionCheckHttpIntercept")
window.checksley.updateValidators({
linewidth: (val, width) ->
@ -230,21 +231,78 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
return valid
})
window.checksley.updateMessages("default", {
linewidth: "The subject must have a maximum size of %s"
})
$compileProvider.debugInfoEnabled(window.taigaConfig.debugInfo || false)
init = ($log, $i18n, $config, $rootscope, $auth, $events, $analytics) ->
$i18n.initialize($config.get("defaultLanguage"))
if localStorage.userInfo
userInfo = JSON.parse(localStorage.userInfo)
# i18n
preferedLangCode = userInfo?.lang || window.taigaConfig.defaultLanguage || "en"
$translateProvider
.useStaticFilesLoader({
prefix: "/locales/locale-",
suffix: ".json"
})
.addInterpolation('$translateMessageFormatInterpolation')
.preferredLanguage(preferedLangCode)
if not window.taigaConfig.debugInfo
$translateProvider.fallbackLanguage(preferedLangCode)
i18nInit = (lang, $translate) ->
# i18n - moment.js
moment.locale(lang)
# i18n - checksley.js
messages = {
defaultMessage: $translate.instant("COMMON.FORM_ERRORS.DEFAULT_MESSAGE")
type: {
email: $translate.instant("COMMON.FORM_ERRORS.TYPE_EMAIL")
url: $translate.instant("COMMON.FORM_ERRORS.TYPE_URL")
urlstrict: $translate.instant("COMMON.FORM_ERRORS.TYPE_URLSTRICT")
number: $translate.instant("COMMON.FORM_ERRORS.TYPE_NUMBER")
digits: $translate.instant("COMMON.FORM_ERRORS.TYPE_DIGITS")
dateIso: $translate.instant("COMMON.FORM_ERRORS.TYPE_DATEISO")
alphanum: $translate.instant("COMMON.FORM_ERRORS.TYPE_ALPHANUM")
phone: $translate.instant("COMMON.FORM_ERRORS.TYPE_PHONE")
}
notnull: $translate.instant("COMMON.FORM_ERRORS.NOTNULL")
notblank: $translate.instant("COMMON.FORM_ERRORS.NOT_BLANK")
required: $translate.instant("COMMON.FORM_ERRORS.REQUIRED")
regexp: $translate.instant("COMMON.FORM_ERRORS.REGEXP")
min: $translate.instant("COMMON.FORM_ERRORS.MIN")
max: $translate.instant("COMMON.FORM_ERRORS.MAX")
range: $translate.instant("COMMON.FORM_ERRORS.RANGE")
minlength: $translate.instant("COMMON.FORM_ERRORS.MIN_LENGTH")
maxlength: $translate.instant("COMMON.FORM_ERRORS.MAX_LENGTH")
rangelength: $translate.instant("COMMON.FORM_ERRORS.RANGE_LENGTH")
mincheck: $translate.instant("COMMON.FORM_ERRORS.MIN_CHECK")
maxcheck: $translate.instant("COMMON.FORM_ERRORS.MAX_CHECK")
rangecheck: $translate.instant("COMMON.FORM_ERRORS.RANGE_CHECK")
equalto: $translate.instant("COMMON.FORM_ERRORS.EQUAL_TO")
}
checksley.updateMessages('default', messages)
init = ($log, $config, $rootscope, $auth, $events, $analytics, $translate) ->
$log.debug("Initialize application")
# Taiga Plugins
$rootscope.contribPlugins = @.taigaContribPlugins
$rootscope.adminPlugins = _.where(@.taigaContribPlugins, {"type": "admin"})
$rootscope.$on "$translateChangeEnd", (e, ctx) ->
lang = ctx.language
i18nInit(lang, $translate)
# Load user
if $auth.isAuthenticated()
$events.setupConnection()
user = $auth.getUser()
# Analytics
$analytics.initialize()
@ -253,7 +311,6 @@ modules = [
"taigaBase",
"taigaCommon",
"taigaResources",
"taigaLocales",
"taigaAuth",
"taigaEvents",
@ -261,7 +318,7 @@ modules = [
"taigaRelatedTasks",
"taigaBacklog",
"taigaTaskboard",
"taigaKanban"
"taigaKanban",
"taigaIssues",
"taigaUserStories",
"taigaTasks",
@ -275,13 +332,15 @@ modules = [
"taigaFeedback",
"taigaPlugins",
"taigaIntegrations",
"taigaComponents",
# template cache
"templates"
"templates",
# Vendor modules
"ngRoute",
"ngAnimate",
"pascalprecht.translate"
].concat(_.map(@.taigaContribPlugins, (plugin) -> plugin.module))
# Main module definition
@ -295,16 +354,17 @@ module.config([
"$tgEventsProvider",
"tgLoaderProvider",
"$compileProvider",
"$translateProvider",
configure
])
module.run([
"$log",
"$tgI18n",
"$tgConfig",
"$rootScope",
"$tgAuth",
"$tgEvents",
"$tgAnalytics",
"$translate"
init
])

View File

@ -30,19 +30,19 @@ MAX_MEMBERSHIP_FIELDSETS = 4
## Create Members Lightbox Directive
#############################################################################
CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService) ->
CreateMembersDirective = ($rs, $rootScope, $confirm, $loading, lightboxService, $compile) ->
extraTextTemplate = """
<fieldset class="extra-text">
<textarea placeholder="(Optional) Add a personalized text to the invitation. Tell something lovely to your new members ;-)"
maxlength="255">
</textarea>
<textarea ng-attr-placeholder="{{'LIGHTBOX.CREATE_MEMBER.PLACEHOLDER_INVITATION_TEXT' | translate}}"
maxlength="255"></textarea>
</fieldset>
"""
template = _.template("""
<div class="add-member-wrapper">
<fieldset>
<input type="email" placeholder="Type an Email" <% if(required) { %> data-required="true" <% } %> data-type="email" />
<input type="email" placeholder="{{'LIGHTBOX.CREATE_MEMBER.PLACEHOLDER_TYPE_EMAIL' | translate}}"
<% if(required) { %> data-required="true" <% } %> data-type="email" />
</fieldset>
<fieldset>
<select <% if(required) { %> data-required="true" <% } %> data-required="true">
@ -53,19 +53,19 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService)
<a class="icon icon-plus add-fieldset" href=""></a>
</fieldset>
</div>
""") # i18n
""")
link = ($scope, $el, $attrs) ->
createFieldSet = (required = true)->
ctx = {roleList: $scope.roles, required: required}
return template(ctx)
return $compile(template(ctx))($scope)
resetForm = ->
$el.find("form textarea").remove("")
$el.find("form textarea").remove()
$el.find("form .add-member-wrapper").remove()
invitations = $el.find(".add-member-forms")
invitations.html(extraTextTemplate)
invitations.html($compile(extraTextTemplate)($scope))
fieldSet = createFieldSet()
invitations.prepend(fieldSet)
@ -84,7 +84,7 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService)
fieldSet.remove()
lastActionButton = $el.find("fieldset:last > a")
lastActionButton = $el.find(".add-member-wrapper fieldset:last > a")
if lastActionButton.hasClass("icon-delete delete-fieldset")
lastActionButton.removeClass("icon-delete delete-fieldset")
.addClass("icon-plus add-fieldset")
@ -98,9 +98,10 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService)
.addClass("icon-delete delete-fieldset")
newFieldSet = createFieldSet(false)
fieldSet.after(newFieldSet)
$scope.$digest() # To compile newFieldSet and translate text
if $el.find(".add-member-wrapper").length == MAX_MEMBERSHIP_FIELDSETS
$el.find(".add-member-wrapper fieldset:last > a").removeClass("icon-plus add-fieldset")
.addClass("icon-delete delete-fieldset")
@ -147,7 +148,9 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService)
if invitations.length
invitation_extra_text = $el.find("form textarea").val()
$rs.memberships.bulkCreateMemberships($scope.project.id, invitations, invitation_extra_text).then(onSuccess, onError)
promise = $rs.memberships.bulkCreateMemberships($scope.project.id,
invitations, invitation_extra_text)
promise.then(onSuccess, onError)
submitButton = $el.find(".submit-button")
@ -155,5 +158,5 @@ CreateMembersDirective = ($rs, $rootScope, $confirm, $loading ,lightboxService)
return {link: link}
module.directive("tgLbCreateMembers", ["$tgResources", "$rootScope", "$tgConfirm", "$tgLoading", "lightboxService",
CreateMembersDirective])
module.directive("tgLbCreateMembers", ["$tgResources", "$rootScope", "$tgConfirm", "$tgLoading",
"lightboxService", "$compile", CreateMembersDirective])

View File

@ -50,7 +50,6 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
@location, @navUrls, @analytics, @appTitle) ->
bindMethods(@)
@scope.sectionName = "Manage Members" #i18n
@scope.project = {}
@scope.filters = {}
@ -236,7 +235,7 @@ module.directive("tgMembershipsRowAvatar", ["$log", "$tgTemplate", MembershipsRo
## Member IsAdminCheckbox Directive
#############################################################################
MembershipsRowAdminCheckboxDirective = ($log, $repo, $confirm, $template) ->
MembershipsRowAdminCheckboxDirective = ($log, $repo, $confirm, $template, $compile) ->
template = $template.get("admin/admin-memberships-row-checkbox.html", true)
link = ($scope, $el, $attrs) ->
@ -244,6 +243,8 @@ MembershipsRowAdminCheckboxDirective = ($log, $repo, $confirm, $template) ->
ctx = {inputId: "is-admin-#{member.id}"}
html = template(ctx)
html = $compile(html)($scope)
$el.html(html)
if not $attrs.tgMembershipsRowAdminCheckbox?
@ -274,8 +275,8 @@ MembershipsRowAdminCheckboxDirective = ($log, $repo, $confirm, $template) ->
return {link: link}
module.directive("tgMembershipsRowAdminCheckbox", ["$log", "$tgRepo", "$tgConfirm", "$tgTemplate",
MembershipsRowAdminCheckboxDirective])
module.directive("tgMembershipsRowAdminCheckbox", ["$log", "$tgRepo", "$tgConfirm",
"$tgTemplate", "$compile", MembershipsRowAdminCheckboxDirective])
#############################################################################
@ -338,32 +339,31 @@ module.directive("tgMembershipsRowRoleSelector", ["$log", "$tgRepo", "$tgConfirm
## Member Actions Directive
#############################################################################
MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm) ->
activedTemplate = _.template("""
<div class="active">
Active
MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $translate) ->
activedTemplate = """
<div class="active", translate="ADMIN.MEMBERSHIP.STATUS_ACTIVE">
</div>
<a class="delete" href="">
<span class="icon icon-delete"></span>
</a>
""") # TODO: i18n
"""
pendingTemplate = _.template("""
pendingTemplate = """
<a class="pending" href="">
Pending
{{'ADMIN.MEMBERSHIP.STATUS_PENDING' | translate}}
<span class="icon icon-reload"></span>
</a>
<a class="delete" href="" title="Delete">
<a class="delete" href="">
<span class="icon icon-delete"></span>
</a>
""") # TODO: i18n
"""
link = ($scope, $el, $attrs) ->
render = (member) ->
if member.user
html = activedTemplate()
html = $compile(activedTemplate)($scope)
else
html = pendingTemplate()
html = $compile(pendingTemplate)($scope)
$el.html(html)
@ -377,29 +377,34 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm) ->
$el.on "click", ".pending", (event) ->
event.preventDefault()
onSuccess = ->
# TODO: i18n
$confirm.notify("success", "We've sent the invitationi again to '#{$scope.member.email}'.")
text = $translate.instant("ADMIN.MEMBERSHIP.SUCCESS_SEND_INVITATION", {email: $scope.member.email})
$confirm.notify("success", text)
onError = ->
$confirm.notify("error", "We haven't sent the invitation.") # TODO: i18n
text = $translate.instant("ADMIM.MEMBERSHIP.ERROR_SEND_INVITATION")
$confirm.notify("error", text)
$rs.memberships.resendInvitation($scope.member.id).then(onSuccess, onError)
$el.on "click", ".delete", (event) ->
event.preventDefault()
title = "Delete member" # TODO: i18n
message = if member.user then member.full_name else "the invitation to #{member.email}" # TODO: i18n
title = $translate.instant("ADMIN.MEMBERSHIP.DELETE_MEMBER")
defaultMsg = $translate.instant("ADMIN.MEMBERSHIP.DEFAULT_DELETE_MESSAGE")
message = if member.user then member.full_name else defaultMsg
$confirm.askOnDelete(title, message).then (finish) ->
onSuccess = ->
finish()
$ctrl.loadMembers()
$confirm.notify("success", null, "We've deleted #{message}.") # TODO: i18n
text = $translate.instant("ADMIN.MEMBERSHIP.SUCCESS_DELETE")
$confirm.notify("success", null, text)
onError = ->
finish(false)
# TODO: i18in
$confirm.notify("error", null, "We have not been able to delete #{message}.")
text = $translate.instant("ADMIN.MEMBERSHIP.ERROR_DELETE", {message: message})
$confirm.notify("error", null, text)
$repo.remove(member).then(onSuccess, onError)
@ -409,5 +414,4 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm) ->
return {link: link}
module.directive("tgMembershipsRowActions", ["$log", "$tgRepo", "$tgResources", "$tgConfirm",
MembershipsRowActionsDirective])
module.directive("tgMembershipsRowActions", ["$log", "$tgRepo", "$tgResources", "$tgConfirm", "$compile", "$translate", MembershipsRowActionsDirective])

View File

@ -47,21 +47,28 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin)
"$q",
"$tgLocation",
"$tgNavUrls",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle, @translate) ->
@scope.project = {}
promise = @.loadInitialData()
promise.then =>
@appTitle.set("Project profile - " + @scope.sectionName + " - " + @scope.project.name)
sectionName = @translate.instant( @scope.sectionName)
appTitle = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", {
sectionName: sectionName, projectName: @scope.project.name})
@appTitle.set(appTitle)
promise.then null, @.onInitialDataError.bind(@)
@scope.$on "project:loaded", =>
@appTitle.set("Project profile - " + @scope.sectionName + " - " + @scope.project.name)
sectionName = @translate.instant(@scope.sectionName)
appTitle = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", {
sectionName: sectionName, projectName: @scope.project.name})
@appTitle.set(appTitle)
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>
@ -79,12 +86,22 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin)
@scope.$emit('project:loaded', project)
return project
loadTagsColors: ->
return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) =>
@scope.project.tags_colors = tags_colors
loadProjectProfile: ->
return @q.all([
@.loadProject(),
@.loadTagsColors()
])
loadInitialData: ->
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
@scope.projectId = data.project
return data
return promise.then(=> @.loadProject())
return promise.then(=> @.loadProjectProfile())
openDeleteLightbox: ->
@rootscope.$broadcast("deletelightbox:new", @scope.project)
@ -115,7 +132,7 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) ->
$scope.$emit("project:loaded", $scope.project)
promise.then null, (data) ->
$loading.finish(target)
$loading.finish(submitButton)
form.setErrors(data)
if data._error_message
$confirm.notify("error", data._error_message)
@ -126,7 +143,8 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location) ->
return {link:link}
module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation", ProjectProfileDirective])
module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation",
ProjectProfileDirective])
#############################################################################
## Project Default Values Directive
@ -162,7 +180,8 @@ ProjectDefaultValuesDirective = ($repo, $confirm, $loading) ->
return {link:link}
module.directive("tgProjectDefaultValues", ["$tgRepo", "$tgConfirm", "$tgLoading", ProjectDefaultValuesDirective])
module.directive("tgProjectDefaultValues", ["$tgRepo", "$tgConfirm", "$tgLoading",
ProjectDefaultValuesDirective])
#############################################################################
## Project Modules Directive
@ -217,7 +236,7 @@ module.directive("tgProjectModules", ["$tgRepo", "$tgConfirm", "$tgLoading", Pro
## Project Export Directive
#############################################################################
ProjectExportDirective = ($window, $rs, $confirm) ->
ProjectExportDirective = ($window, $rs, $confirm, $translate) ->
link = ($scope, $el, $attrs) ->
buttonsEl = $el.find(".admin-project-export-buttons")
showButtons = -> buttonsEl.removeClass("hidden")
@ -232,16 +251,23 @@ ProjectExportDirective = ($window, $rs, $confirm) ->
hideSpinner = -> spinnerEl.addClass("hidden")
resultTitleEl = $el.find(".result-title")
setLoadingTitle = -> resultTitleEl.html("We are generating your dump file") # TODO: i18n
setAsyncTitle = -> resultTitleEl.html("We are generating your dump file") # TODO: i18n
setSyncTitle = -> resultTitleEl.html("Your dump file is ready!") # TODO: i18n
loading_title = $translate.instant("ADMIN.PROJECT_EXPORT.LOADING_TITLE")
loading_msg = $translate.instant("ADMIN.PROJECT_EXPORT.LOADING_MESSAGE")
dump_ready_text = -> resultTitleEl.html($translate.instant("ADMIN.PROJECT_EXPORT.DUMP_READY"))
asyn_message = -> resultTitleEl.html($translate.instant("ADMIN.PROJECT_EXPORT.ASYNC_MESSAGE"))
syn_message = (url) -> resultTitleEl.html($translate.instant("ADMIN.PROJECT_EXPORT.SYNC_MESSAGE", {
url: url}))
setLoadingTitle = -> resultTitleEl.html(loading_title)
setAsyncTitle = -> resultTitleEl.html(loading_msg)
setSyncTitle = -> resultTitleEl.html(dump_ready_text)
resultMessageEl = $el.find(".result-message ")
setLoadingMessage = -> resultMessageEl.html("Please don't close this page.") # TODO: i18n
setAsyncMessage = -> resultMessageEl.html("We will send you an email when ready.") # TODO: i18n
setSyncMessage = (url) -> resultMessageEl.html("If the download doesn't start automatically click
<a href='#{url}' download title='Download
the dump file'>here.") # TODO: i18n
setLoadingMessage = -> resultMessageEl.html(loading_msg)
setAsyncMessage = -> resultMessageEl.html(asyn_message)
setSyncMessage = (url) -> resultMessageEl.html(syn_message(url))
showLoadingMode = ->
showSpinner()
@ -279,15 +305,13 @@ ProjectExportDirective = ($window, $rs, $confirm) ->
onError = (result) =>
showErrorMode()
errorMsg = "Our oompa loompas have some problems generasting your dump.
Please try again. " # TODO: i18n
errorMsg = $translate.instant("ADMIN.PROJECT_EXPORT.ERROR")
if result.status == 429 # TOO MANY REQUESTS
errorMsg = "Sorry, our oompa loompas are very busy right now.
Please try again in a few minutes. " # TODO: i18n
errorMsg = $translate.instant("ADMIN.PROJECT_EXPORT.ERROR_BUSY")
else if result.data?._error_message
errorMsg = "Our oompa loompas have some problems generasting your dump:
#{result.data._error_message}" # TODO: i18n
errorMsg = $translate.instant("ADMIN.PROJECT_EXPORT.ERROR_BUSY", {
message: result.data._error_message})
$confirm.notify("error", errorMsg)
@ -296,7 +320,8 @@ ProjectExportDirective = ($window, $rs, $confirm) ->
return {link:link}
module.directive("tgProjectExport", ["$window", "$tgResources", "$tgConfirm", ProjectExportDirective])
module.directive("tgProjectExport", ["$window", "$tgResources", "$tgConfirm", "$translate",
ProjectExportDirective])
#############################################################################
@ -310,9 +335,10 @@ class CsvExporterController extends taiga.Controller
"$tgUrls",
"$tgConfirm",
"$tgResources",
"$translate"
]
constructor: (@scope, @rootscope, @urls, @confirm, @rs) ->
constructor: (@scope, @rootscope, @urls, @confirm, @rs, @translate) ->
@rootscope.$on("project:loaded", @.setCsvUuid)
@scope.$watch "csvUuid", (value) =>
if value
@ -337,10 +363,10 @@ class CsvExporterController extends taiga.Controller
return promise
regenerateUuid: ->
#TODO: i18n
if @scope.csvUuid
title = "Change URL"
subtitle = "You going to change the CSV data access url. The previous url will be disabled. Are you sure?"
title = @translate.instant("ADMIN.REPORTS.REGENERATE_TITLE")
subtitle = @translate.instant("ADMIN.REPORTS.REGENERATE_SUBTITLE")
@confirm.ask(title, subtitle).then @._generateUuid
else
@._generateUuid(_.identity)
@ -361,3 +387,52 @@ class CsvExporterIssuesController extends CsvExporterController
module.controller("CsvExporterUserstoriesController", CsvExporterUserstoriesController)
module.controller("CsvExporterTasksController", CsvExporterTasksController)
module.controller("CsvExporterIssuesController", CsvExporterIssuesController)
#############################################################################
## CSV Directive
#############################################################################
CsvUsDirective = ($translate) ->
link = ($scope) ->
$scope.sectionTitle = "ADMIN.CSV.SECTION_TITLE_US"
return {
controller: "CsvExporterUserstoriesController",
controllerAs: "ctrl",
templateUrl: "admin/project-csv.html",
link: link,
scope: true
}
module.directive("tgCsvUs", ["$translate", CsvUsDirective])
CsvTaskDirective = ($translate) ->
link = ($scope) ->
$scope.sectionTitle = "ADMIN.CSV.SECTION_TITLE_TASK"
return {
controller: "CsvExporterTasksController",
controllerAs: "ctrl",
templateUrl: "admin/project-csv.html",
link: link,
scope: true
}
module.directive("tgCsvTask", ["$translate", CsvTaskDirective])
CsvIssueDirective = ($translate) ->
link = ($scope) ->
$scope.sectionTitle = "ADMIN.CSV.SECTION_TITLE_ISSUE"
return {
controller: "CsvExporterIssuesController",
controllerAs: "ctrl",
templateUrl: "admin/project-csv.html",
link: link,
scope: true
}
module.directive("tgCsvIssue", ["$translate", CsvIssueDirective])

View File

@ -46,16 +46,24 @@ class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageM
"$q",
"$tgLocation",
"$tgNavUrls",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle, @translate) ->
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Project values - " + @scope.sectionName + " - " + @scope.project.name)
sectionName = @translate.instant(@scope.sectionName)
title = @translate.instant("ADMIN.PROJECT_VALUES.APP_TITLE", {
"sectionName": sectionName,
"projectName": @scope.project.name
})
@appTitle.set(title)
promise.then null, @.onInitialDataError.bind(@)
@ -118,7 +126,7 @@ module.controller("ProjectValuesController", ProjectValuesController)
## Project values directive
#############################################################################
ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, @translate, $rootscope) ->
## Drag & Drop Link
linkDragAndDrop = ($scope, $el, $attrs) ->
@ -149,6 +157,7 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
linkValue = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
valueType = $attrs.type
objName = $attrs.objname
initializeNewValue = ->
$scope.newValue = {
@ -157,7 +166,14 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
"is_archived": false
}
initializeTextTranslations = ->
$scope.addNewElementText = @translate.instant("ADMIN.PROJECT_VALUES_#{objName.toUpperCase()}.ACTION_ADD")
initializeNewValue()
initializeTextTranslations()
$rootscope.$on "$translateChangeEnd", ->
$scope.$evalAsync(initializeTextTranslations)
goToBottomList = (focus = false) =>
table = $el.find(".table-main")
@ -275,14 +291,12 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
if value.id != option.id
choices[option.id] = option.name
#TODO: i18n
title = "Delete value"
subtitle = value.name
replacement = "All items with this value will be changed to"
if _.keys(choices).length == 0
return $confirm.error("You can't delete all values.")
return $confirm.askChoice(title, subtitle, choices, replacement).then (response) ->
if _.keys(choices).length == 0
return $confirm.error("ADMIN.PROJECT_VALUES.ERROR_DELETE_ALL")
$confirm.askChoice("PROJECT.TITLE_ACTION_DELETE_VALUE", subtitle, choices, "ADMIN.PROJECT_VALUES.REPLACEMENT").then (response) ->
onSucces = ->
$ctrl.loadValues().finally ->
response.finish()
@ -299,8 +313,7 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame) ->
return {link:link}
module.directive("tgProjectValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame",
ProjectValuesDirective])
module.directive("tgProjectValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame", "$translate", "$rootScope", ProjectValuesDirective])
#############################################################################
@ -601,11 +614,9 @@ ProjectCustomAttributesDirective = ($log, $confirm, animationFrame) ->
deleteCustomAttribute = (formEl) ->
attr = formEl.scope().attr
title = "Delete custom attribute" # i18n
subtitle = "Remeber that all values in this custom field will be deleted.</br> Are you sure you want to continue?"
message = attr.name
$confirm.ask(title, subtitle, message).then (finish) ->
$confirm.ask("COMMON.CUSTOM_ATTRIBUTES.DELETE", "COMMON.CUSTOM_ATTRIBUTES.CONFIRM_DELETE", message).then (finish) ->
onSucces = ->
$ctrl.loadCustomAttributes().finally ->
finish()

View File

@ -44,20 +44,23 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
"$q",
"$tgLocation",
"$tgNavUrls",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle,
@translate) ->
bindMethods(@)
@scope.sectionName = "Permissions" #i18n
@scope.sectionName = "ADMIN.MENU.PERMISSIONS"
@scope.project = {}
@scope.anyComputableRole = true
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Roles - " + @scope.project.name)
title = @translate.instant("ADMIN.ROLES.SECTION_NAME", {projectName: @scope.project.name})
@appTitle.set(title)
promise.then null, @.onInitialDataError.bind(@)
@ -65,7 +68,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
return @rs.projects.get(@scope.projectId).then (project) =>
if not project.i_am_owner
@location.path(@navUrls.resolve("permission-denied"))
@scope.project = project
@scope.$emit('project:loaded', project)
@ -80,7 +83,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
return role
public_permission = {
"name": "External User",
"name": @translate.instant("ADMIN.ROLES.EXTERNAL_USER"),
"permissions": @scope.project.public_permissions,
"external_user": true
}
@ -112,29 +115,28 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
@scope.$broadcast("role:changed", @scope.role)
delete: ->
# TODO: i18n
title = "Delete Role" # TODO: i18n
subtitle = @scope.role.name
replacement = "All the users with this role will be moved to" # TODO: i18n
warning = "<strong>Be careful, all role estimations will be removed</strong>" # TODO: i18n
choices = {}
for role in @scope.roles
if role.id != @scope.role.id
choices[role.id] = role.name
if _.keys(choices).length == 0
return @confirm.error("You can't delete all values.") # TODO: i18n
return @confirm.error(@translate.instant("ADMIN.ROLES.ERROR_DELETE_ALL"))
title = @translate.instant("ADMIN.ROLES.TITLE_DELETE_ROLE")
subtitle = @scope.role.name
replacement = @translate.instant("ADMIN.ROLES.REPLACEMENT_ROLE")
warning = @translate.instant("ADMIN.ROLES.WARNING_DELETE_ROLE")
return @confirm.askChoice(title, subtitle, choices, replacement, warning).then (response) =>
promise = @repo.remove(@scope.role, {moveTo: response.selected})
promise.then =>
onSuccess = =>
@.loadProject()
@.loadRoles().finally ->
@.loadRoles().finally =>
response.finish()
promise.then null, =>
onError = =>
@confirm.notify('error')
return @repo.remove(@scope.role, {moveTo: response.selected}).then onSuccess, onError
setComputable: debounce 2000, ->
onSuccess = =>
@confirm.notify("success")
@ -256,14 +258,14 @@ module.directive("tgNewRole", ["$tgRepo", "$tgConfirm", NewRoleDirective])
# Use category-config.scss styles
RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
RolePermissionsDirective = ($rootscope, $repo, $confirm, $compile) ->
resumeTemplate = _.template("""
<div class="resume-title"><%- category.name %></div>
<div class="resume-title" translate="<%- category.name %>"></div>
<div class="summary-role">
<div class="count"><%- category.activePermissions %>/<%- category.permissions.length %></div>
<% _.each(category.permissions, function(permission) { %>
<div class="role-summary-single <% if(permission.active) { %>active<% } %>"
title="<%- permission.description %>"></div>
title="{{ '<%- permission.name %>' | translate }}"></div>
<% }) %>
</div>
<div class="icon icon-arrow-bottom"></div>
@ -277,12 +279,14 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
<div class="items-container">
<% _.each(category.permissions, function(permission) { %>
<div class="category-item" data-id="<%- permission.key %>">
<span><%- permission.description %></span>
<span translate="<%- permission.name %>"></span>
<div class="check">
<input type="checkbox" <% if(!permission.editable) { %>disabled="disabled"<% } %> <% if(permission.active) { %>checked="checked"<% } %>/>
<input type="checkbox"
<% if(!permission.editable) { %> disabled="disabled" <% } %>
<% if(permission.active) { %> checked="checked" <% } %>/>
<div></div>
<span class="check-text check-yes">Yes</span>
<span class="check-text check-no">No</span>
<span class="check-text check-yes" translate="COMMON.YES"></span>
<span class="check-text check-no" translate="COMMON.NO"></span>
</div>
</div>
<% }) %>
@ -325,58 +329,73 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
categories = []
milestonePermissions = [
{ key: "view_milestones", description: "View sprints" }
{ key: "add_milestone", description: "Add sprint" }
{ key: "modify_milestone", description: "Modify sprint" }
{ key: "delete_milestone", description: "Delete sprint" }
{ key: "view_milestones", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.VIEW_SPRINTS"}
{ key: "add_milestone", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.ADD_SPRINTS"}
{ key: "modify_milestone", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.MODIFY_SPRINTS"}
{ key: "delete_milestone", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.DELETE_SPRINTS"}
]
categories.push({ name: "Sprints", permissions: setActivePermissions(milestonePermissions) })
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.NAME",
permissions: setActivePermissions(milestonePermissions)
})
userStoryPermissions = [
{ key: "view_us", description: "View user story" }
{ key: "add_us", description: "Add user story" }
{ key: "modify_us", description: "Modify user story" }
{ key: "delete_us", description: "Delete user story" }
{ key: "view_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.VIEW_USER_STORIES"}
{ key: "add_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.ADD_USER_STORIES"}
{ key: "modify_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.MODIFY_USER_STORIES"}
{ key: "delete_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.DELETE_USER_STORIES"}
]
categories.push({ name: "User Stories", permissions: setActivePermissions(userStoryPermissions) })
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.NAME",
permissions: setActivePermissions(userStoryPermissions)
})
taskPermissions = [
{ key: "view_tasks", description: "View tasks" }
{ key: "add_task", description: "Add task" }
{ key: "modify_task", description: "Modify task" }
{ key: "delete_task", description: "Delete task" }
{ key: "view_tasks", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.VIEW_TASKS"}
{ key: "add_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.ADD_TASKS"}
{ key: "modify_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.MODIFY_TASKS"}
{ key: "delete_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.DELETE_TASKS"}
]
categories.push({ name: "Tasks", permissions: setActivePermissions(taskPermissions) })
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.TASKS.NAME" ,
permissions: setActivePermissions(taskPermissions)
})
issuePermissions = [
{ key: "view_issues", description: "View issues" }
{ key: "add_issue", description: "Add issue" }
{ key: "modify_issue", description: "Modify issue" }
{ key: "delete_issue", description: "Delete issue" }
{ key: "view_issues", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.VIEW_ISSUES"}
{ key: "add_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.ADD_ISSUES"}
{ key: "modify_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.MODIFY_ISSUES"}
{ key: "delete_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.DELETE_ISSUES"}
]
categories.push({ name: "Issues", permissions: setActivePermissions(issuePermissions) })
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.NAME",
permissions: setActivePermissions(issuePermissions)
})
wikiPermissions = [
{ key: "view_wiki_pages", description: "View wiki pages" }
{ key: "add_wiki_page", description: "Add wiki page" }
{ key: "modify_wiki_page", description: "Modify wiki page" }
{ key: "delete_wiki_page", description: "Delete wiki page" }
{ key: "view_wiki_links", description: "View wiki links" }
{ key: "add_wiki_link", description: "Add wiki link" }
{ key: "delete_wiki_link", description: "Delete wiki link" }
{ key: "view_wiki_pages", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.VIEW_WIKI_PAGES"}
{ key: "add_wiki_page", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.ADD_WIKI_PAGES"}
{ key: "modify_wiki_page", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.MODIFY_WIKI_PAGES"}
{ key: "delete_wiki_page", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.DELETE_WIKI_PAGES"}
{ key: "view_wiki_links", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.VIEW_WIKI_LINKS"}
{ key: "add_wiki_link", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.ADD_WIKI_LINKS"}
{ key: "delete_wiki_link", name: "COMMON.PERMISIONS_CATEGORIES.WIKI.DELETE_WIKI_LINKS"}
]
categories.push({ name: "Wiki", permissions: setActivePermissions(wikiPermissions) })
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.WIKI.NAME",
permissions: setActivePermissions(wikiPermissions)
})
return setActivePermissionsPerCategory(categories)
renderResume = (element, category) ->
element.find(".resume").html(resumeTemplate({category: category}))
element.find(".resume").html($compile(resumeTemplate({category: category}))($scope))
renderCategory = (category, index) ->
html = categoryTemplate({category: category, index: index})
html = angular.element(html)
renderResume(html, category)
return html
return $compile(html)($scope)
renderPermissions = () ->
$el.off()
@ -437,4 +456,5 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
return {link:link}
module.directive("tgRolePermissions", ["$rootScope", "$tgRepo", "$tgConfirm", RolePermissionsDirective])
module.directive("tgRolePermissions", ["$rootScope", "$tgRepo", "$tgConfirm", "$compile",
RolePermissionsDirective])

View File

@ -40,19 +40,21 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.
"$routeParams",
"$tgLocation",
"$tgNavUrls",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appTitle) ->
constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appTitle, @translate) ->
bindMethods(@)
@scope.sectionName = "Webhooks" #i18n
@scope.sectionName = "ADMIN.WEBHOOKS.SECTION_NAME"
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Webhooks - " + @scope.project.name)
text = @translate.instant("ADMIN.WEBHOOKS.APP_TITLE", {"projectName": @scope.project.name})
@appTitle.set(text)
promise.then null, @.onInitialDataError.bind(@)
@ -85,17 +87,19 @@ module.controller("WebhooksController", WebhooksController)
## Webhook Directive
#############################################################################
WebhookDirective = ($rs, $repo, $confirm, $loading) ->
WebhookDirective = ($rs, $repo, $confirm, $loading, $translate) ->
link = ($scope, $el, $attrs) ->
webhook = $scope.$eval($attrs.tgWebhook)
updateLogs = () ->
prettyDate = $translate.instant("ADMIN.WEBHOOKS.DATE")
$rs.webhooklogs.list(webhook.id).then (webhooklogs) =>
for log in webhooklogs
log.validStatus = 200 <= log.status < 300
log.prettySentHeaders = _.map(_.pairs(log.request_headers), ([header, value]) -> "#{header}: #{value}").join("\n")
log.prettySentData = JSON.stringify(log.request_data)
log.prettyDate = moment(log.created).format("DD MMM YYYY [at] hh:mm:ss") # TODO: i18n
log.prettyDate = moment(log.created).format(prettyDate)
webhook.logs_counter = webhooklogs.length
webhook.logs = webhooklogs
@ -104,10 +108,16 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) ->
updateShowHideHistoryText = () ->
textElement = $el.find(".toggle-history")
historyElement = textElement.parents(".single-webhook-wrapper").find(".webhooks-history")
if historyElement.hasClass("open")
textElement.text("(Hide history)") # TODO: i18n
text = $translate.instant("ADMIN.WEBHOOKS.ACTION_HIDE_HISTORY")
title = $translate.instant("ADMIN.WEBHOOKS.ACTION_HIDE_HISTORY_TITLE")
else
textElement.text("(Show history)") # TODO: i18n
text = $translate.instant("ADMIN.WEBHOOKS.ACTION_SHOW_HISTORY")
title = $translate.instant("ADMIN.WEBHOOKS.ACTION_SHOW_HISTORY_TITLE")
textElement.text(text)
textElement.prop("title", title)
showVisualizationMode = () ->
$el.find(".edition-mode").addClass("hidden")
@ -161,8 +171,8 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) ->
cancel(target)
$el.on "click", ".delete-webhook", () ->
title = "Delete webhook" #TODO: i18n
message = "Webhook '#{webhook.name}'" #TODO: i18n
title = $translate.instant("ADMIN.WEBHOOKS.DELETE")
message = $translate.instant("ADMIN.WEBHOOKS.WEBHOOK_NAME", {name: webhook.name})
$confirm.askOnDelete(title, message).then (finish) =>
onSucces = ->
@ -203,7 +213,7 @@ WebhookDirective = ($rs, $repo, $confirm, $loading) ->
return {link:link}
module.directive("tgWebhook", ["$tgResources", "$tgRepo", "$tgConfirm", "$tgLoading", WebhookDirective])
module.directive("tgWebhook", ["$tgResources", "$tgRepo", "$tgConfirm", "$tgLoading", "$translate", WebhookDirective])
#############################################################################
@ -279,19 +289,21 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgRepo",
"$tgResources",
"$routeParams",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) ->
bindMethods(@)
@scope.sectionName = "Github" #i18n
@scope.sectionName = @translate.instant("ADMIN.GITHUB.SECTION_NAME")
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Github - " + @scope.project.name)
title = @translate.instant("ADMIN.GITHUB.APP_TITLE", {projectName: @scope.project.name})
@appTitle.set(title)
promise.then null, @.onInitialDataError.bind(@)
@ -327,18 +339,20 @@ class GitlabController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgRepo",
"$tgResources",
"$routeParams",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) ->
bindMethods(@)
@scope.sectionName = "Gitlab" #i18n
@scope.sectionName = @translate.instant("ADMIN.GITLAB.SECTION_NAME")
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Gitlab - " + @scope.project.name)
title = @translate.instant("ADMIN.GITLAB.APP_TITLE", {projectName: @scope.project.name})
@appTitle.set(title)
promise.then null, @.onInitialDataError.bind(@)
@ -377,18 +391,20 @@ class BitbucketController extends mixOf(taiga.Controller, taiga.PageMixin, taiga
"$tgRepo",
"$tgResources",
"$routeParams",
"$appTitle"
"$appTitle",
"$translate"
]
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) ->
bindMethods(@)
@scope.sectionName = "Bitbucket" #i18n
@scope.sectionName = @translate.instant("ADMIN.BITBUCKET.SECTION_NAME")
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Bitbucket - " + @scope.project.name)
title = @translate.instant("ADMIN.BITBUCKET.APP_TITLE", {projectName: @scope.project.name})
@appTitle.set(title)
promise.then null, @.onInitialDataError.bind(@)

View File

@ -34,11 +34,17 @@ class AuthService extends taiga.Service
"$tgModel",
"$tgResources",
"$tgHttp",
"$tgUrls"]
"$tgUrls",
"$tgConfig",
"$translate"]
constructor: (@rootscope, @storage, @model, @rs, @http, @urls) ->
constructor: (@rootscope, @storage, @model, @rs, @http, @urls, @config, @translate) ->
super()
_setLocales: ->
lang = @rootscope.user.lang || @config.get("defaultLanguage") || "en"
@translate.use(lang)
getUser: ->
if @rootscope.user
return @rootscope.user
@ -47,16 +53,18 @@ class AuthService extends taiga.Service
if userData
user = @model.make_model("users", userData)
@rootscope.user = user
@._setLocales()
return user
return null
setUser: (user) ->
@rootscope.auth = user
@rootscope.$broadcast("i18n:change", user.default_language)
@storage.set("userInfo", user.getAttrs())
@rootscope.user = user
@._setLocales()
clear: ->
@rootscope.auth = null
@rootscope.user = null
@ -171,7 +179,7 @@ PublicRegisterMessageDirective = ($config, $navUrls, templates) ->
module.directive("tgPublicRegisterMessage", ["$tgConfig", "$tgNavUrls", "$tgTemplate", PublicRegisterMessageDirective])
LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events) ->
LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events, $translate) ->
link = ($scope, $el, $attrs) ->
onSuccess = (response) ->
if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("login")
@ -183,8 +191,8 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
$location.path(nextUrl)
onError = (response) ->
$confirm.notify("light-error", "According to our Oompa Loompas, your username/email
or password are incorrect.") #TODO: i18n
$confirm.notify("light-error", $translate.instant("LOGIN_FORM.ERROR_AUTH_INCORRECT"))
submit = debounce 2000, (event) =>
event.preventDefault()
@ -207,13 +215,13 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
return {link:link}
module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgConfig", "$routeParams",
"$tgNavUrls", "$tgEvents", LoginDirective])
"$tgNavUrls", "$tgEvents", "$translate", LoginDirective])
#############################################################################
## Register Directive
#############################################################################
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics) ->
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics, $translate) ->
link = ($scope, $el, $attrs) ->
if not $config.get("publicRegisterEnabled")
$location.path($navUrls.resolve("not-found"))
@ -224,12 +232,15 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics)
onSuccessSubmit = (response) ->
$analytics.trackEvent("auth", "register", "user registration", 1)
$confirm.notify("success", "Our Oompa Loompas are happy, welcome to Taiga.") #TODO: i18n
$confirm.notify("success", $translate.instant("LOGIN_FORM.SUCCESS"))
$location.path($navUrls.resolve("home"))
onErrorSubmit = (response) ->
if response.data._error_message?
$confirm.notify("light-error", "According to our Oompa Loompas there was an error. #{response.data._error_message}") #TODO: i18n
text = $translate.instant("LOGIN_FORM.ERROR_GENERIC") + " " + response.data._error_message
$confirm.notify("light-error", text + " " + response.data._error_message)
form.setErrors(response.data)
@ -247,25 +258,27 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics)
return {link:link}
module.directive("tgRegister", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls", "$tgConfig",
"$tgAnalytics", RegisterDirective])
"$tgAnalytics", "$translate", RegisterDirective])
#############################################################################
## Forgot Password Directive
#############################################################################
ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls) ->
ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls, $translate) ->
link = ($scope, $el, $attrs) ->
$scope.data = {}
form = $el.find("form").checksley()
onSuccessSubmit = (response) ->
$location.path($navUrls.resolve("login"))
$confirm.success("<strong>Check your inbox!</strong><br />
We have sent you an email with the instructions to set a new password") #TODO: i18n
text = $translate.instant("FORGOT_PASSWORD_FORM.SUCCESS")
$confirm.success(text)
onErrorSubmit = (response) ->
$confirm.notify("light-error", "According to our Oompa Loompas,
your are not registered yet.") #TODO: i18n
text = $translate.instant("FORGOT_PASSWORD_FORM.ERROR")
$confirm.notify("light-error", text)
submit = debounce 2000, (event) =>
event.preventDefault()
@ -280,14 +293,14 @@ ForgotPasswordDirective = ($auth, $confirm, $location, $navUrls) ->
return {link:link}
module.directive("tgForgotPassword", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls",
module.directive("tgForgotPassword", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls", "$translate",
ForgotPasswordDirective])
#############################################################################
## Change Password from Recovery Directive
#############################################################################
ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $navUrls) ->
ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $navUrls, $translate) ->
link = ($scope, $el, $attrs) ->
$scope.data = {}
@ -301,12 +314,15 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav
onSuccessSubmit = (response) ->
$location.path($navUrls.resolve("login"))
$confirm.success("Our Oompa Loompas saved your new password.<br />
Try to <strong>sign in</strong> with it.") #TODO: i18n
text = $translate.instant("CHANGE_PASSWORD_RECOVERY_FORM.SUCCESS")
$confirm.success(text)
onErrorSubmit = (response) ->
$confirm.notify("light-error", "One of our Oompa Loompas say
'#{response.data._error_message}'.") #TODO: i18n
text = $translate.instant("COMMON.GENERIC_ERROR", {error: response.data._error_message})
$confirm.notify("light-error", text)
submit = debounce 2000, (event) =>
event.preventDefault()
@ -322,13 +338,13 @@ ChangePasswordFromRecoveryDirective = ($auth, $confirm, $location, $params, $nav
return {link:link}
module.directive("tgChangePasswordFromRecovery", ["$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", ChangePasswordFromRecoveryDirective])
"$tgNavUrls", "$translate", ChangePasswordFromRecoveryDirective])
#############################################################################
## Invitation
#############################################################################
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics) ->
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics, $translate) ->
link = ($scope, $el, $attrs) ->
token = $params.token
@ -338,8 +354,9 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
promise.then null, (response) ->
$location.path($navUrls.resolve("login"))
$confirm.success("<strong>Ooops, we have a problem</strong><br />
Our Oompa Loompas can't find your invitation.") #TODO: i18n
text = $translate.instant("INVITATION_LOGIN_FORM.NOT_FOUND")
$confirm.success(text)
# Login form
$scope.dataLogin = {token: token}
@ -348,12 +365,14 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
onSuccessSubmitLogin = (response) ->
$analytics.trackEvent("auth", "invitationAccept", "invitation accept with existing user", 1)
$location.path($navUrls.resolve("project", {project: $scope.invitation.project_slug}))
$confirm.notify("success", "You've successfully joined this project",
"Welcome to #{_.escape($scope.invitation.project_name)}")
text = $translate.instant("INVITATION_LOGIN_FORM.SUCCESS", {"project_name": $scope.invitation.project_name})
$confirm.notify("success", text)
onErrorSubmitLogin = (response) ->
$confirm.notify("light-error", "According to our Oompa Loompas, your are not registered yet or
typed an invalid password.") #TODO: i18n
text = $translate.instant("INVITATION_LOGIN_FORM.ERROR")
$confirm.notify("light-error", text)
submitLogin = debounce 2000, (event) =>
event.preventDefault()
@ -378,8 +397,9 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
"Welcome to #{_.escape($scope.invitation.project_name)}")
onErrorSubmitRegister = (response) ->
$confirm.notify("light-error", "According to our Oompa Loompas, that
username or email is already in use.") #TODO: i18n
text = $translate.instant("LOGIN_FORM.ERROR_AUTH_INCORRECT")
$confirm.notify("light-error", text)
submitRegister = debounce 2000, (event) =>
event.preventDefault()
@ -396,13 +416,13 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
return {link:link}
module.directive("tgInvitation", ["$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", "$tgAnalytics", InvitationDirective])
"$tgNavUrls", "$tgAnalytics", "$translate", InvitationDirective])
#############################################################################
## Change Email
#############################################################################
ChangeEmailDirective = ($repo, $model, $auth, $confirm, $location, $params, $navUrls) ->
ChangeEmailDirective = ($repo, $model, $auth, $confirm, $location, $params, $navUrls, $translate) ->
link = ($scope, $el, $attrs) ->
$scope.data = {}
$scope.data.email_token = $params.email_token
@ -412,11 +432,14 @@ ChangeEmailDirective = ($repo, $model, $auth, $confirm, $location, $params, $nav
$repo.queryOne("users", $auth.getUser().id).then (data) =>
$auth.setUser(data)
$location.path($navUrls.resolve("home"))
$confirm.success("Our Oompa Loompas updated your email") #TODO: i18n
text = $translate.instant("CHANGE_EMAIL_FORM.SUCCESS")
$confirm.success(text)
onErrorSubmit = (response) ->
$confirm.notify("error", "One of our Oompa Loompas says
'#{response.data._error_message}'.") #TODO: i18n
text = $translate.instant("COMMON.GENERIC_ERROR", {error: response.data._error_message})
$confirm.notify("light-error", text)
submit = ->
if not form.validate()
@ -436,7 +459,7 @@ ChangeEmailDirective = ($repo, $model, $auth, $confirm, $location, $params, $nav
return {link:link}
module.directive("tgChangeEmail", ["$tgRepo", "$tgModel", "$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", ChangeEmailDirective])
"$tgNavUrls", "$translate", ChangeEmailDirective])
#############################################################################
## Cancel account
@ -451,11 +474,15 @@ CancelAccountDirective = ($repo, $model, $auth, $confirm, $location, $params, $n
onSuccessSubmit = (response) ->
$auth.logout()
$location.path($navUrls.resolve("home"))
$confirm.success("Our Oompa Loompas removed your account") #TODO: i18n
text = $translate.instant("CANCEL_ACCOUNT.SUCCESS")
$confirm.success(text)
onErrorSubmit = (response) ->
$confirm.notify("error", "One of our Oompa Loompas says
'#{response.data._error_message}'.") #TODO: i18n
text = $translate.instant("COMMON.GENERIC_ERROR", {error: response.data._error_message})
$confirm.notify("error", text)
submit = debounce 2000, (event) =>
event.preventDefault()

View File

@ -116,6 +116,9 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
$scope.$on "filters:loaded", (ctx, filters) ->
initializeSelectedFilters(filters)
$scope.$on "filters:update", (ctx, filters) ->
renderFilters(filters)
## Dom Event Handlers
$el.on "click", ".filters-cats > ul > li > a", (event) ->
event.preventDefault()

View File

@ -29,7 +29,7 @@ module = angular.module("taigaBacklog")
## Creare/Edit Sprint Lightbox Directive
#############################################################################
CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading) ->
CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading, $translate) ->
link = ($scope, $el, attrs) ->
hasErrors = false
createSprint = true
@ -43,8 +43,10 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
submit = debounce 2000, (event) =>
event.preventDefault()
target = angular.element(event.currentTarget)
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
submitButton = $el.find(".submit-button")
form = $el.find("form").checksley()
if not form.validate()
@ -57,13 +59,15 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
broadcastEvent = null
if createSprint
newSprint.estimated_start = moment(newSprint.estimated_start).format("YYYY-MM-DD")
newSprint.estimated_finish = moment(newSprint.estimated_finish).format("YYYY-MM-DD")
newSprint.estimated_start = moment(newSprint.estimated_start, prettyDate).format("YYYY-MM-DD")
newSprint.estimated_finish = moment(newSprint.estimated_finish,prettyDate).format("YYYY-MM-DD")
promise = $repo.create("milestones", newSprint)
broadcastEvent = "sprintform:create:success"
else
newSprint.setAttr("estimated_start", moment(newSprint.estimated_start).format("YYYY-MM-DD"))
newSprint.setAttr("estimated_finish", moment(newSprint.estimated_finish).format("YYYY-MM-DD"))
newSprint.setAttr("estimated_start",
moment(newSprint.estimated_start, prettyDate).format("YYYY-MM-DD"))
newSprint.setAttr("estimated_finish",
moment(newSprint.estimated_finish, prettyDate).format("YYYY-MM-DD"))
promise = $repo.save(newSprint)
broadcastEvent = "sprintform:edit:success"
@ -86,8 +90,7 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
$confirm.notify("light-error", data.__all__[0])
remove = ->
#TODO: i18n
title = "Delete sprint"
title = $translate.instant("LIGHTBOX.DELETE_SPRINT.TITLE")
message = $scope.sprint.name
$confirm.askOnDelete(title, message).then (finish) =>
@ -107,6 +110,7 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
form.reset()
createSprint = true
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
$scope.sprint.project = projectId
$scope.sprint.name = null
$scope.sprint.slug = null
@ -118,36 +122,50 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
estimatedStart = moment($scope.sprint.estimated_start)
else if lastSprint?
estimatedStart = moment(lastSprint.estimated_finish)
$scope.sprint.estimated_start = estimatedStart.format("DD MMM YYYY")
$scope.sprint.estimated_start = estimatedStart.format(prettyDate)
estimatedFinish = moment().add(2, "weeks")
if $scope.sprint.estimated_finish
estimatedFinish = moment($scope.sprint.estimated_finish)
else if lastSprint?
estimatedFinish = moment(lastSprint.estimated_finish).add(2, "weeks")
$scope.sprint.estimated_finish = estimatedFinish.format("DD MMM YYYY")
$scope.sprint.estimated_finish = estimatedFinish.format(prettyDate)
lastSprintNameDom = $el.find(".last-sprint-name")
if lastSprint?.name?
lastSprintNameDom.html(" last sprint is <strong> #{lastSprint.name} ;-) </strong>")
text = $translate.instant("LIGHTBOX.ADD_EDIT_SPRINT.LAST_SPRINT_NAME", {
lastSprint: lastSprint.name})
lastSprintNameDom.html(text)
$el.find(".delete-sprint").addClass("hidden")
$el.find(".title").text("New sprint") #TODO i18n
$el.find(".button-green").text("Create") #TODO i18n
text = $translate.instant("LIGHTBOX.ADD_EDIT_SPRINT.TITLE")
$el.find(".title").text(text)
text = $translate.instant("COMMON.CREATE")
$el.find(".button-green").text(text)
lightboxService.open($el)
$el.find(".sprint-name").focus()
$el.find(".last-sprint-name").removeClass("disappear")
$scope.$on "sprintform:edit", (ctx, sprint) ->
createSprint = false
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
$scope.$apply ->
$scope.sprint = sprint
$scope.sprint.estimated_start = moment($scope.sprint.estimated_start).format("DD MMM YYYY")
$scope.sprint.estimated_finish = moment($scope.sprint.estimated_finish).format("DD MMM YYYY")
$scope.sprint.estimated_start = moment($scope.sprint.estimated_start).format(prettyDate)
$scope.sprint.estimated_finish = moment($scope.sprint.estimated_finish).format(prettyDate)
$el.find(".delete-sprint").removeClass("hidden")
$el.find(".title").text("Edit sprint") #TODO i18n
$el.find(".button-green").text("Save") #TODO i18n
editSprint = $translate.instant("BACKLOG.EDIT_SPRINT")
$el.find(".title").text(editSprint)
save = $translate.instant("COMMON.SAVE")
$el.find(".button-green").text(save)
lightboxService.open($el)
$el.find(".sprint-name").focus().select()
$el.find(".last-sprint-name").addClass("disappear")
@ -158,8 +176,6 @@ CreateEditSprint = ($repo, $confirm, $rs, $rootscope, lightboxService, $loading)
else
$el.find(".last-sprint-name").removeClass("disappear")
submitButton = $el.find(".submit-button")
$el.on "submit", "form", submit
$el.on "click", ".delete-sprint .icon-delete", (event) ->
@ -178,6 +194,7 @@ module.directive("tgLbCreateEditSprint", [
"$tgResources",
"$rootScope",
"lightboxService"
"$tgLoading"
"$tgLoading",
"$translate",
CreateEditSprint
])

View File

@ -49,14 +49,15 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"tgLoader"
"tgLoader",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
@location, @appTitle, @navUrls, @events, @analytics, tgLoader) ->
@location, @appTitle, @navUrls, @events, @analytics, tgLoader, @translate) ->
bindMethods(@)
@scope.sectionName = "Backlog"
@scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME")
@showTags = false
@activeFilters = false
@ -439,12 +440,16 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return _.pick(@location.search(), "statuses", "tags", "q")
generateFilters: ->
urlfilters = @.getUrlFilters()
@scope.filters = {}
#tags
plainTags = _.flatten(_.filter(_.map(@scope.visibleUserstories, "tags")))
plainTags.sort()
if plainTags.length == 0 and urlfilters["tags"]
plainTags.push(urlfilters["tags"])
@scope.filters.tags = _.map _.countBy(plainTags), (v, k) =>
obj = {
id: k,
@ -466,6 +471,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
if status
return status
if plainStatuses.length == 0 and urlfilters["statuses"]
plainStatuses.push(urlfilters["statuses"])
@scope.filters.statuses = _.map _.countBy(plainStatuses), (v, k) =>
obj = {
id: k,
@ -491,12 +499,19 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
## Template actions
updateUserStoryStatus: () ->
@.setSearchDataFilters()
@.filterVisibleUserstories()
@.generateFilters()
@rootscope.$broadcast("filters:update", @scope.filters['statuses'])
@.loadProjectStats()
editUserStory: (us) ->
@rootscope.$broadcast("usform:edit", us)
deleteUserStory: (us) ->
#TODO: i18n
title = "Delete User Story"
title = @translate.instant("US.TITLE_DELETE_ACTION")
message = us.subject
@confirm.askOnDelete(title, message).then (finish) =>
@ -527,12 +542,11 @@ module.controller("BacklogController", BacklogController)
## Backlog Directive
#############################################################################
BacklogDirective = ($repo, $rootscope) ->
BacklogDirective = ($repo, $rootscope, $translate) ->
## Doom line Link
doomLineTemplate = _.template("""
<div class="doom-line"><span>Project Scope [Doomline]</span></div>
<div class="doom-line"><span><%- text %></span></div>
""")
# TODO: i18n
linkDoomLine = ($scope, $el, $attrs, $ctrl) ->
reloadDoomLine = ->
@ -559,7 +573,8 @@ BacklogDirective = ($repo, $rootscope) ->
$el.find(".doom-line").remove()
addDoomLineDom = (element) ->
$(element).before(doomLineTemplate({}))
text = $translate.instant("BACKLOG.DOOMLINE")
$(element).before(doomLineTemplate({"text": text}))
getUsItems = ->
rowElements = $el.find('.backlog-table-body .us-item-row')
@ -629,10 +644,14 @@ BacklogDirective = ($repo, $rootscope) ->
if $ctrl.showTags
elm.addClass("active")
elm.find(".text").text("Hide Tags") # TODO: i18n
text = $translate.instant("BACKLOG.TAGS.HIDE")
elm.find(".text").text(text)
else
elm.removeClass("active")
elm.find(".text").text("Show Tags") # TODO: i18n
text = $translate.instant("BACKLOG.TAGS.SHOW")
elm.find(".text").text(text)
showHideFilter = ($scope, $el, $ctrl) ->
sidebar = $el.find("sidebar.filters-bar")
@ -646,7 +665,10 @@ BacklogDirective = ($repo, $rootscope) ->
sidebar.toggleClass("active")
target.toggleClass("active")
toggleText(target.find(".text"), ["Remove Filters", "Show Filters"]) # TODO: i18n
hideText = $translate.instant("BACKLOG.FILTERS.HIDE")
showText = $translate.instant("BACKLOG.FILTERS.SHOW")
toggleText(target.find(".text"), [hideText, showText])
if !sidebar.hasClass("active")
$ctrl.resetFilters()
@ -687,14 +709,13 @@ BacklogDirective = ($repo, $rootscope) ->
return {link: link}
module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
module.directive("tgBacklog", ["$tgRepo", "$rootScope", "$translate", BacklogDirective])
#############################################################################
## User story points directive
#############################################################################
UsRolePointsSelectorDirective = ($rootscope, $template) ->
#TODO: i18n
UsRolePointsSelectorDirective = ($rootscope, $template, $compile, $translate) ->
selectionTemplate = $template.get("backlog/us-role-points-popover.html", true)
link = ($scope, $el, $attrs) ->
@ -704,7 +725,7 @@ UsRolePointsSelectorDirective = ($rootscope, $template) ->
numberOfRoles = _.size(roles)
if numberOfRoles > 1
$el.append(selectionTemplate({"roles":roles}))
$el.append($compile(selectionTemplate({"roles": roles}))($scope))
else
$el.find(".icon-arrow-bottom").remove()
$el.find(".header-points").addClass("not-clickable")
@ -715,7 +736,9 @@ UsRolePointsSelectorDirective = ($rootscope, $template) ->
$scope.$on "uspoints:clear-selection", (ctx, roleId) ->
$el.find(".popover").popover().close()
$el.find(".header-points").text("Points") #TODO: i18n
text = $translate.instant("COMMON.FIELDS.POINTS")
$el.find(".header-points").text(text)
# Dom Event Handlers
$el.on "click", (event) ->
@ -743,7 +766,7 @@ UsRolePointsSelectorDirective = ($rootscope, $template) ->
return {link: link}
module.directive("tgUsRolePointsSelector", ["$rootScope", "$tgTemplate", UsRolePointsSelectorDirective])
module.directive("tgUsRolePointsSelector", ["$rootScope", "$tgTemplate", "$compile", UsRolePointsSelectorDirective])
UsPointsDirective = ($tgEstimationsService, $repo, $tgTemplate) ->
@ -848,11 +871,12 @@ UsPointsDirective = ($tgEstimationsService, $repo, $tgTemplate) ->
module.directive("tgBacklogUsPoints", ["$tgEstimationsService", "$tgRepo", "$tgTemplate", UsPointsDirective])
#############################################################################
## Burndown graph directive
#############################################################################
tgBacklogGraphDirective = ->
BurndownBacklogGraphDirective = ($translate) ->
redrawChart = (element, dataToDraw) ->
width = element.width()
element.height(width/6)
@ -908,13 +932,20 @@ tgBacklogGraphDirective = ->
}
xaxis: {
ticks: dataToDraw.milestones.length
axisLabel: "Sprints"
axisLabel: $translate.instant("BACKLOG.CHART.XAXIS_LABEL"),
axisLabelUseCanvas: true
axisLabelFontSizePixels: 14
axisLabelFontSizePixels: 12
axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif"
axisLabelPadding: 15
axisLabelPadding: 5
tickFormatter: (val, axis) -> ""
}
yaxis: {
axisLabel: $translate.instant("BACKLOG.CHART.YAXIS_LABEL"),
axisLabelUseCanvas: true
axisLabelFontSizePixels: 12
axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif"
axisLabelPadding: 5
}
series: {
shadowSize: 0
lines: {
@ -932,18 +963,18 @@ tgBacklogGraphDirective = ->
tooltip: true
tooltipOpts: {
content: (label, xval, yval, flotItem) ->
#TODO: i18n
if flotItem.seriesIndex == 1
return "Optimal pending points for sprint #{xval} should be #{yval}"
ctx = {xval: xval, yval: yval}
return $translate.instant("BACKLOG.CHART.OPTIMAL", ctx)
else if flotItem.seriesIndex == 2
return "Real pending points for sprint #{xval} is #{yval}"
ctx = {xval: xval, yval: yval}
return $translate.instant("BACKLOG.CHART.REAL", ctx)
else if flotItem.seriesIndex == 3
return "Incremented points by team requirements for sprint #{xval} is #{Math.abs(yval)}"
ctx = {xval: xval, yval: Math.abs(yval)}
return $translate.instant("BACKLOG.CHART.INCREMENT_TEAM", ctx)
else
return "Incremented points by client requirements for sprint #{xval} is #{Math.abs(yval)}"
ctx = {xval: xval, yval: Math.abs(yval)}
return $translate.instant("BACKLOG.CHART.INCREMENT_CLIENT", ctx)
}
}
@ -965,8 +996,7 @@ tgBacklogGraphDirective = ->
return {link: link}
module.directive("tgGmBacklogGraph", tgBacklogGraphDirective)
module.directive("tgBurndownBacklogGraph", ["$translate", BurndownBacklogGraphDirective])
#############################################################################

View File

@ -39,7 +39,7 @@ deleteElement = (el) ->
el.off()
el.remove()
BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm) ->
BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
# Notes about jquery bug:
# http://stackoverflow.com/questions/5791886/jquery-draggable-shows-
# helper-in-wrong-place-when-scrolled-down-page
@ -54,7 +54,7 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm) ->
return
filterError = ->
text = "You can't drop on backlog when filters are open"
text = $translate.instant("BACKLOG.SORTABLE_FILTER_ERROR")
$tgConfirm.notify("error", text)
$el.sortable({
@ -215,6 +215,7 @@ module.directive("tgBacklogSortable", [
"$tgResources",
"$rootScope",
"$tgConfirm",
"$translate",
BacklogSortableDirective
])

View File

@ -85,10 +85,12 @@ module.directive("tgBacklogSprint", ["$tgRepo", "$rootScope", BacklogSprintDirec
## Sprint Header Directive
#############################################################################
BacklogSprintHeaderDirective = ($navUrls, $template) ->
template = $template.get("backlog/sprint-header.html", true)
BacklogSprintHeaderDirective = ($navUrls, $template, $compile, $translate) ->
template = $template.get("backlog/sprint-header.html")
link = ($scope, $el, $attrs, $model) ->
prettyDate = $translate.instant("BACKLOG.SPRINTS.DATE")
isEditable = ->
return $scope.project.my_permissions.indexOf("modify_milestone") != -1
@ -99,8 +101,8 @@ BacklogSprintHeaderDirective = ($navUrls, $template) ->
taskboardUrl = $navUrls.resolve("project-taskboard",
{project: $scope.project.slug, sprint: sprint.slug})
start = moment(sprint.estimated_start).format("DD MMM YYYY")
finish = moment(sprint.estimated_finish).format("DD MMM YYYY")
start = moment(sprint.estimated_start).format(prettyDate)
finish = moment(sprint.estimated_finish).format(prettyDate)
estimatedDateRange = "#{start}-#{finish}"
ctx = {
@ -112,8 +114,13 @@ BacklogSprintHeaderDirective = ($navUrls, $template) ->
isVisible: isVisible()
isEditable: isEditable()
}
$el.html(template(ctx))
templateScope = $scope.$new()
_.assign(templateScope, ctx)
compiledTemplate = $compile(template)(templateScope)
$el.html(compiledTemplate)
$scope.$watch $attrs.ngModel, (sprint) ->
render(sprint)
@ -130,13 +137,15 @@ BacklogSprintHeaderDirective = ($navUrls, $template) ->
require: "ngModel"
}
module.directive("tgBacklogSprintHeader", ["$tgNavUrls", "$tgTemplate", BacklogSprintHeaderDirective])
module.directive("tgBacklogSprintHeader", ["$tgNavUrls", "$tgTemplate", "$compile", "$translate"
BacklogSprintHeaderDirective])
#############################################################################
## Toggle Closed Sprints Directive
#############################################################################
ToggleExcludeClosedSprintsVisualization = ($rootscope, $loading) ->
ToggleExcludeClosedSprintsVisualization = ($rootscope, $loading, $translate) ->
excludeClosedSprints = true
link = ($scope, $el, $attrs) ->
@ -162,14 +171,16 @@ ToggleExcludeClosedSprintsVisualization = ($rootscope, $loading) ->
$scope.$on "closed-sprints:reloaded", (ctx, sprints) =>
$loading.finish(loadingElm)
#TODO: i18n
if sprints.length > 0
text = "Hide closed sprints"
key = "BACKLOG.SPRINTS.ACTION_HIDE_CLOSED_SPRINTS"
else
text = "Show closed sprints"
key = "BACKLOG.SPRINTS.ACTION_SHOW_CLOSED_SPRINTS"
text = $translate.instant(key)
$el.find(".text").text(text)
return {link: link}
module.directive("tgBacklogToggleClosedSprintsVisualization", ["$rootScope", "$tgLoading", ToggleExcludeClosedSprintsVisualization])
module.directive("tgBacklogToggleClosedSprintsVisualization", ["$rootScope", "$tgLoading", "$translate",
ToggleExcludeClosedSprintsVisualization])

View File

@ -23,7 +23,7 @@ taiga = @.taiga
groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
module = angular.module("taigaBase", ["taigaLocales"])
module = angular.module("taigaBase", [])
#############################################################################
## Main Directive

View File

@ -22,16 +22,25 @@
taiga = @.taiga
class HttpService extends taiga.Service
@.$inject = ["$http", "$q", "$tgStorage"]
@.$inject = ["$http", "$q", "$tgStorage", "$rootScope"]
constructor: (@http, @q, @storage, @rootScope) ->
super()
headers: ->
headers = {}
# Authorization
token = @storage.get('token')
if token
return {"Authorization":"Bearer #{token}"}
return {}
headers["Authorization"] = "Bearer #{token}"
constructor: (@http, @q, @storage) ->
super()
# Accept-Language
lang = @rootScope.user?.lang
if lang
headers["Accept-Language"] = lang
return headers
request: (options) ->
options.headers = _.merge({}, options.headers or {}, @.headers())

View File

@ -1,74 +0,0 @@
###
# 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/base/i18n.coffee
###
taiga = @.taiga
bindOnce = @.taiga.bindOnce
defaults = {
ns: "app"
fallbackLng: "en"
async: false
lng: "en"
}
class I18nService extends taiga.Service
constructor: (@rootscope, localesEn) ->
@.options = _.clone(defaults, true)
@.options.resStore = {
en: { app: localesEn }
}
setLanguage: (language) ->
i18n.setLng(language)
@rootscope.currentLang = language
@rootscope.$broadcast("i18n:changeLang", language)
initialize: ->
i18n.init(@.options)
@rootscope.t = i18n.t
t: (path, opts) ->
return i18n.t(path, opts)
I18nDirective = ($rootscope, $i18n) ->
link = ($scope, $el, $attrs) ->
values = $attrs.tr.split(",")
options = $attrs.trOpts or '{}'
opts = $scope.$eval(options)
for v in values
if v.indexOf(":") == -1
$el.html(_.escape($i18n.t(v, opts)))
else
[ns, v] = v.split(":")
$el.attr(ns, _.escape($i18n.t(v, opts)))
return {
link: link
restrict: "A"
scope: false
}
module = angular.module("taigaBase")
module.service("$tgI18n", ["$rootScope", "localesEn", I18nService])
module.directive("tr", ["$rootScope", "$tgI18n", I18nDirective])

View File

@ -28,9 +28,9 @@ module = angular.module("taigaCommon")
class AttachmentsController extends taiga.Controller
@.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q"]
@.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q", "$translate"]
constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q) ->
constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q, @translate) ->
bindMethods(@)
@.type = null
@.objectId = null
@ -75,10 +75,13 @@ class AttachmentsController extends taiga.Controller
promise = promise.then null, (data) =>
@scope.$emit("attachments:size-error") if data.status == 413
index = @.uploadingAttachments.indexOf(attachment)
@.uploadingAttachments.splice(index, 1)
@confirm.notify("error", "We have not been able to upload '#{attachment.name}'.
#{data.data._error_message}")
message = @translate.instant("ATTACHMENT.ERROR_UPLOAD_ATTACHMENT", {
fileName: attachment.name, errorMessage: data.data._error_message})
@confirm.notify("error", message)
return @q.reject(data)
return promise
@ -130,8 +133,8 @@ class AttachmentsController extends taiga.Controller
# Remove one concrete attachment.
removeAttachment: (attachment) ->
title = "Delete attachment" #TODO: i18in
message = "the attachment '#{attachment.name}'" #TODO: i18in
title = @translate.instant("ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT")
message = @translate.instant("ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT", {fileName: attachment.name})
return @confirm.askOnDelete(title, message).then (finish) =>
onSuccess = =>
@ -143,7 +146,8 @@ class AttachmentsController extends taiga.Controller
onError = =>
finish(false)
@confirm.notify("error", null, "We have not been able to delete #{message}.")
message = @translate.instant("ATTACHMENT.ERROR_DELETE_ATTACHMENT", {errorMessage: message})
@confirm.notify("error", null, message)
return @q.reject()
return @repo.remove(attachment).then(onSuccess, onError)
@ -155,7 +159,7 @@ class AttachmentsController extends taiga.Controller
return not item.is_deprecated
AttachmentsDirective = ($config, $confirm, $templates) ->
AttachmentsDirective = ($config, $confirm, $templates, $translate) ->
template = $templates.get("attachment/attachments.html", true)
link = ($scope, $el, $attrs, $ctrls) ->
@ -220,8 +224,7 @@ AttachmentsDirective = ($config, $confirm, $templates) ->
templateFn = ($el, $attrs) ->
maxFileSize = $config.get("maxUploadFileSize", null)
maxFileSize = sizeFormat(maxFileSize) if maxFileSize
maxFileSizeMsg = if maxFileSize then "Maximum upload size is #{maxFileSize}" else "" # TODO: i18n
maxFileSizeMsg = if maxFileSize then $translate.instant("ATTACHMENT.MAX_UPLOAD_SIZE", {maxFileSize: maxFileSize}) else ""
ctx = {
type: $attrs.type
maxFileSize: maxFileSize
@ -239,10 +242,10 @@ AttachmentsDirective = ($config, $confirm, $templates) ->
template: templateFn
}
module.directive("tgAttachments", ["$tgConfig", "$tgConfirm", "$tgTemplate", AttachmentsDirective])
module.directive("tgAttachments", ["$tgConfig", "$tgConfirm", "$tgTemplate", "$translate", AttachmentsDirective])
AttachmentDirective = ($template) ->
AttachmentDirective = ($template, $compile, $translate) ->
template = $template.get("attachment/attachment.html", true)
templateEdit = $template.get("attachment/attachment-edit.html", true)
@ -254,7 +257,9 @@ AttachmentDirective = ($template) ->
ctx = {
id: attachment.id
name: attachment.name
created_date: moment(attachment.created_date).format("DD MMM YYYY [at] hh:mm") #TODO: i18n
title : $translate.instant("ATTACHMENT.TITLE", {
fileName: attachment.name,
date: moment(attachment.created_date).format($translate.instant("ATTACHMENT.DATE"))})
url: attachment.url
size: sizeFormat(attachment.size)
description: attachment.description
@ -263,9 +268,9 @@ AttachmentDirective = ($template) ->
}
if edit
html = templateEdit(ctx)
html = $compile(templateEdit(ctx))($scope)
else
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
@ -322,4 +327,4 @@ AttachmentDirective = ($template) ->
restrict: "AE"
}
module.directive("tgAttachment", ["$tgTemplate", AttachmentDirective])
module.directive("tgAttachment", ["$tgTemplate", "$compile", "$translate", AttachmentDirective])

View File

@ -29,10 +29,11 @@ module = angular.module("taigaCommon")
## Date Range Directive (used mainly for sprint date range)
#############################################################################
DateRangeDirective = ->
DateRangeDirective = ($translate) ->
renderRange = ($el, first, second) ->
initDate = moment(first).format("DD MMM YYYY")
endDate = moment(second).format("DD MMM YYYY")
prettyDate = $translate.instant("BACKLOG.SPRINTS.DATE")
initDate = moment(first).format(prettyDate)
endDate = moment(second).format(prettyDate)
$el.html("#{initDate}-#{endDate}")
link = ($scope, $el, $attrs) ->
@ -44,34 +45,75 @@ DateRangeDirective = ->
return {link:link}
module.directive("tgDateRange", DateRangeDirective)
module.directive("tgDateRange", ["$translate", DateRangeDirective])
#############################################################################
## Date Selector Directive (using pikaday)
#############################################################################
DateSelectorDirective =->
DateSelectorDirective = ($rootscope, $translate) ->
link = ($scope, $el, $attrs, $model) ->
selectedDate = null
$el.picker = new Pikaday({
field: $el[0]
format: "DD MMM YYYY"
onSelect: (date) =>
selectedDate = date
onOpen: =>
$el.picker.setDate(selectedDate) if selectedDate?
})
initialize = () ->
$el.picker = new Pikaday({
field: $el[0]
onSelect: (date) =>
selectedDate = date
onOpen: =>
$el.picker.setDate(selectedDate) if selectedDate?
i18n: {
previousMonth: $translate.instant("COMMON.PICKERDATE.PREV_MONTH"),
nextMonth: $translate.instant("COMMON.PICKERDATE.NEXT_MONTH"),
months: [$translate.instant("COMMON.PICKERDATE.MONTHS.JAN"),
$translate.instant("COMMON.PICKERDATE.MONTHS.FEB"),
$translate.instant("COMMON.PICKERDATE.MONTHS.MAR"),
$translate.instant("COMMON.PICKERDATE.MONTHS.APR"),
$translate.instant("COMMON.PICKERDATE.MONTHS.MAY"),
$translate.instant("COMMON.PICKERDATE.MONTHS.JUN"),
$translate.instant("COMMON.PICKERDATE.MONTHS.JUL"),
$translate.instant("COMMON.PICKERDATE.MONTHS.AUG"),
$translate.instant("COMMON.PICKERDATE.MONTHS.SEP"),
$translate.instant("COMMON.PICKERDATE.MONTHS.OCT"),
$translate.instant("COMMON.PICKERDATE.MONTHS.NOV"),
$translate.instant("COMMON.PICKERDATE.MONTHS.DEC")],
weekdays: [$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SUN"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.MON"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.TUE"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.WED"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.THU"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.FRI"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS.SAT")],
weekdaysShort: [$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SUN"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.MON"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.TUE"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.WED"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.THU"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.FRI"),
$translate.instant("COMMON.PICKERDATE.WEEK_DAYS_SHORT.SAT")]
},
isRTL: $translate.instant("COMMON.PICKERDATE.IS_RTL") == "true",
firstDay: parseInt($translate.instant("COMMON.PICKERDATE.FIRST_DAY_OF_WEEK"), 10),
format: $translate.instant("COMMON.PICKERDATE.FORMAT")
})
unbind = $rootscope.$on "$translateChangeEnd", (ctx) => initialize()
$scope.$watch $attrs.ngModel, (val) ->
initialize() if val? and not $el.picker
$el.picker.setDate(val) if val?
$scope.$on "$destroy", ->
$el.off()
unbind()
return {
link: link
require: "ngModel"
}
module.directive("tgDateSelector", DateSelectorDirective)
module.directive("tgDateSelector", ["$rootScope", "$translate", DateSelectorDirective])
#############################################################################
@ -98,6 +140,9 @@ SprintProgressBarDirective = ->
renderProgress($el, percentage, visual_percentage)
$scope.$on "$destroy", ->
$el.off()
return {link: link}
module.directive("tgSprintProgressbar", SprintProgressBarDirective)
@ -107,7 +152,7 @@ module.directive("tgSprintProgressbar", SprintProgressBarDirective)
## Created-by display directive
#############################################################################
CreatedByDisplayDirective = ($template)->
CreatedByDisplayDirective = ($template, $compile, $translate)->
# Display the owner information (full name and photo) and the date of
# creation of an object (like USs, tasks and issues).
#
@ -119,18 +164,22 @@ CreatedByDisplayDirective = ($template)->
# 'owner'(ng-model)
# - scope.usersById object is required.
template = $template.get("common/components/created-by.html", true) # TODO: i18n
template = $template.get("common/components/created-by.html", true)
link = ($scope, $el, $attrs) ->
render = (model) ->
owner = $scope.usersById?[model.owner] or {
full_name_display: "external user"
full_name_display: $translate.instant("COMMON.EXTERNAL_USER")
photo: "/images/unnamed.png"
}
html = template({
owner: owner
date: moment(model.created_date).format("DD MMM YYYY HH:mm")
date: moment(model.created_date).format($translate.instant("COMMON.DATETIME"))
})
html = $compile(html)($scope)
$el.html(html)
bindOnce $scope, $attrs.ngModel, (model) ->
@ -145,18 +194,16 @@ CreatedByDisplayDirective = ($template)->
require: "ngModel"
}
module.directive("tgCreatedByDisplay", ["$tgTemplate", CreatedByDisplayDirective])
module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", CreatedByDisplayDirective])
#############################################################################
## Watchers directive
#############################################################################
WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template) ->
WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile, $translate) ->
# You have to include a div with the tg-lb-watchers directive in the page
# where use this directive
#
# TODO: i18n
template = $template.get("common/components/watchers.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -200,7 +247,7 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template) ->
isEditable: isEditable()
}
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
if isEditable() and watchers.length == 0
@ -213,7 +260,7 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template) ->
target = angular.element(event.currentTarget)
watcherId = target.data("watcher-id")
title = "Delete watcher"
title = $translate.instant("COMMON.WATCHERS.TITLE_LIGHTBOX_DELETE_WARTCHER")
message = $scope.usersById[watcherId].full_name_display
$confirm.askOnDelete(title, message).then (finish) =>
@ -247,18 +294,17 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template) ->
return {link:link, require:"ngModel"}
module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQqueue", "$tgTemplate", WatchersDirective])
module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQqueue", "$tgTemplate", "$compile",
"$translate", WatchersDirective])
#############################################################################
## Assigned to directive
#############################################################################
AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template) ->
AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template, $translate, $compile) ->
# You have to include a div with the tg-lb-assignedto directive in the page
# where use this directive
#
# TODO: i18n
template = $template.get("common/components/assigned-to.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -291,7 +337,7 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template
assignedTo: assignedTo
isEditable: isEditable()
}
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
$el.on "click", ".user-assigned", (event) ->
@ -303,7 +349,7 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template
$el.on "click", ".icon-delete", (event) ->
event.preventDefault()
return if not isEditable()
title = "Are you sure you want to leave it unassigned?" # TODO: i18n
title = $translate.instant("COMMON.ASSIGNED_TO.CONFIRM_UNASSIGNED")
$confirm.ask(title).then (finish) =>
finish()
@ -326,7 +372,8 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template
require:"ngModel"
}
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQqueue", "$tgTemplate", AssignedToDirective])
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQqueue", "$tgTemplate", "$translate", "$compile",
AssignedToDirective])
#############################################################################
@ -392,7 +439,7 @@ DeleteButtonDirective = ($log, $repo, $confirm, $location, $template) ->
return $log.error "DeleteButtonDirective requires on-delete-title set in scope."
$el.on "click", ".button", (event) ->
title = $scope.$eval($attrs.onDeleteTitle)
title = $attrs.onDeleteTitle
subtitle = $model.$modelValue.subject
$confirm.askOnDelete(title, subtitle).then (finish) =>
@ -471,7 +518,6 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $qqueue, $tem
$el.find('div.edit-subject').hide()
$el.find('div.view-subject span.edit').hide()
$scope.$watch $attrs.ngModel, (value) ->
return if not value
$scope.item = value
@ -490,7 +536,8 @@ EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $qqueue, $tem
template: template
}
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", EditableSubjectDirective])
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
"$tgTemplate", EditableSubjectDirective])
#############################################################################
@ -498,9 +545,9 @@ module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$
#############################################################################
EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading, $selectedText, $qqueue, $template) ->
template = $template.get("common/components/editable-description.html") # TODO: i18n
noDescriptionMegEditMode = $template.get("common/components/editable-description-msg-edit-mode.html") # TODO: i18n
noDescriptionMegReadMode = $template.get("common/components/editable-description-msg-read-mode.html") # TODO: i18n
template = $template.get("common/components/editable-description.html")
noDescriptionMegEditMode = $template.get("common/components/editable-description-msg-edit-mode.html")
noDescriptionMegReadMode = $template.get("common/components/editable-description-msg-read-mode.html")
link = ($scope, $el, $attrs, $model) ->
$el.find('.edit-description').hide()
@ -555,9 +602,9 @@ EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading,
if isEditable()
$el.find('.view-description .edit').show()
$el.find('.view-description .us-content').addClass('editable')
$scope.noDescriptionMsg = noDescriptionMegEditMode
$scope.noDescriptionMsg = $compile(noDescriptionMegEditMode)($scope)
else
$scope.noDescriptionMsg = noDescriptionMegReadMode
$scope.noDescriptionMsg = $compile(noDescriptionMegReadMode)($scope)
$scope.$on "$destroy", ->
$el.off()
@ -569,8 +616,8 @@ EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading,
template: template
}
module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm",
"$compile", "$tgLoading", "$selectedText", "$tgQqueue", "$tgTemplate", EditableDescriptionDirective])
module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm", "$compile", "$tgLoading",
"$selectedText", "$tgQqueue", "$tgTemplate", EditableDescriptionDirective])
#############################################################################
@ -580,14 +627,16 @@ module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm"
## completely bindonce, they only serves for visualization of data.
#############################################################################
ListItemIssueStatusDirective = ->
ListItemUsStatusDirective = ->
link = ($scope, $el, $attrs) ->
issue = $scope.$eval($attrs.tgListitemIssueStatus)
bindOnce $scope, "issueStatusById", (issueStatusById) ->
$el.html(issueStatusById[issue.status].name)
us = $scope.$eval($attrs.tgListitemUsStatus)
bindOnce $scope, "usStatusById", (usStatusById) ->
$el.html(usStatusById[us.status].name)
return {link:link}
module.directive("tgListitemUsStatus", ListItemUsStatusDirective)
ListItemTaskStatusDirective = ->
link = ($scope, $el, $attrs) ->
@ -597,14 +646,7 @@ ListItemTaskStatusDirective = ->
return {link:link}
ListItemUsStatusDirective = ->
link = ($scope, $el, $attrs) ->
us = $scope.$eval($attrs.tgListitemUsStatus)
bindOnce $scope, "usStatusById", (usStatusById) ->
$el.html(usStatusById[us.status].name)
return {link:link}
module.directive("tgListitemTaskStatus", ListItemTaskStatusDirective)
ListItemAssignedtoDirective = ($template) ->
@ -626,6 +668,41 @@ ListItemAssignedtoDirective = ($template) ->
module.directive("tgListitemAssignedto", ["$tgTemplate", ListItemAssignedtoDirective])
ListItemIssueStatusDirective = ->
link = ($scope, $el, $attrs) ->
issue = $scope.$eval($attrs.tgListitemIssueStatus)
bindOnce $scope, "issueStatusById", (issueStatusById) ->
$el.html(issueStatusById[issue.status].name)
return {link:link}
module.directive("tgListitemIssueStatus", ListItemIssueStatusDirective)
ListItemTypeDirective = ->
link = ($scope, $el, $attrs) ->
render = (issueTypeById, issue) ->
type = issueTypeById[issue.type]
domNode = $el.find(".level")
domNode.css("background-color", type.color)
domNode.attr("title", type.name)
bindOnce $scope, "issueTypeById", (issueTypeById) ->
issue = $scope.$eval($attrs.tgListitemType)
render(issueTypeById, issue)
$scope.$watch $attrs.tgListitemType, (issue) ->
render($scope.issueTypeById, issue)
return {
link: link
templateUrl: "common/components/level.html"
}
module.directive("tgListitemType", ListItemTypeDirective)
ListItemPriorityDirective = ->
link = ($scope, $el, $attrs) ->
render = (priorityById, issue) ->
@ -648,6 +725,7 @@ ListItemPriorityDirective = ->
module.directive("tgListitemPriority", ListItemPriorityDirective)
ListItemSeverityDirective = ->
link = ($scope, $el, $attrs) ->
render = (severityById, issue) ->
@ -668,26 +746,7 @@ ListItemSeverityDirective = ->
templateUrl: "common/components/level.html"
}
ListItemTypeDirective = ->
link = ($scope, $el, $attrs) ->
render = (issueTypeById, issue) ->
type = issueTypeById[issue.type]
domNode = $el.find(".level")
domNode.css("background-color", type.color)
domNode.attr("title", type.name)
bindOnce $scope, "issueTypeById", (issueTypeById) ->
issue = $scope.$eval($attrs.tgListitemType)
render(issueTypeById, issue)
$scope.$watch $attrs.tgListitemType, (issue) ->
render($scope.issueTypeById, issue)
return {
link: link
templateUrl: "common/components/level.html"
}
module.directive("tgListitemSeverity", ListItemSeverityDirective)
#############################################################################
@ -715,35 +774,27 @@ TgProgressBarDirective = ($template) ->
module.directive("tgProgressBar", ["$tgTemplate", TgProgressBarDirective])
#############################################################################
## Main title directive
#############################################################################
TgMainTitleDirective = ($template) ->
template = $template.get("common/components/main-title.html", true)
render = (el, projectName, sectionName) ->
el.html(template({
projectName: projectName
sectionName: sectionName
}))
TgMainTitleDirective = ($translate) ->
link = ($scope, $el, $attrs) ->
element = angular.element($el)
$scope.$watch "project", (project) ->
render($el, project.name, $scope.sectionName) if project
$scope.$on "project:loaded", (ctx, project) =>
render($el, project.name, $scope.sectionName)
$attrs.$observe "i18nSectionName", (i18nSectionName) ->
trans = $translate(i18nSectionName)
trans.then (sectionName) -> $scope.sectionName = sectionName
trans.catch (sectionName) -> $scope.sectionName = sectionName
$scope.$on "$destroy", ->
$el.off()
return {link: link}
return {
link: link
templateUrl: "common/components/main-title.html"
scope: {
projectName : "=projectName"
}
}
module.directive("tgMainTitle", ["$tgTemplate", TgMainTitleDirective])
module.directive("tgListitemType", ListItemTypeDirective)
module.directive("tgListitemIssueStatus", ListItemIssueStatusDirective)
module.directive("tgListitemSeverity", ListItemSeverityDirective)
module.directive("tgListitemTaskStatus", ListItemTaskStatusDirective)
module.directive("tgListitemUsStatus", ListItemUsStatusDirective)
module.directive("tgMainTitle", ["$translate", TgMainTitleDirective])

View File

@ -27,21 +27,21 @@ bindMethods = @.taiga.bindMethods
NOTIFICATION_MSG = {
"success":
title: "Everything is ok"
message: "Our Oompa Loompas saved all your changes!"
title: "NOTIFICATION.OK"
message: "NOTIFICATION.SAVED"
"error":
title: "Oops, something happened..."
message: "Our Oompa Loompas are sad, your changes were not saved!"
title: "NOTIFICATION.WARNING"
message: "NOTIFICATION.WARNING_TEXT"
"light-error":
title: "Oops, something happened..."
message: "Our Oompa Loompas are sad, your changes were not saved!"
title: "NOTIFICATION.WARNING"
message: "NOTIFICATION.WARNING_TEXT"
}
class ConfirmService extends taiga.Service
@.$inject = ["$q", "lightboxService", "$tgLoading"]
@.$inject = ["$q", "lightboxService", "$tgLoading", "$translate"]
constructor: (@q, @lightboxService, @loading) ->
constructor: (@q, @lightboxService, @loading, @translate) ->
bindMethods(@)
hide: (el)->
@ -51,6 +51,8 @@ class ConfirmService extends taiga.Service
el.off(".confirm-dialog")
ask: (title, subtitle, message, lightboxSelector=".lightbox-generic-ask") ->
defered = @q.defer()
el = angular.element(lightboxSelector)
# Render content
@ -58,8 +60,6 @@ class ConfirmService extends taiga.Service
el.find("span.subtitle").html(subtitle)
el.find("span.message").html(message)
defered = @q.defer()
# Assign event handlers
el.on "click.confirm-dialog", "a.button-green", debounce 2000, (event) =>
event.preventDefault()
@ -80,9 +80,11 @@ class ConfirmService extends taiga.Service
return defered.promise
askOnDelete: (title, message) ->
return @.ask(title, "Are you sure you want to delete?", message) #TODO: i18n
return @.ask(title, @translate.instant("NOTIFICATION.ASK_DELETE"), message)
askChoice: (title, subtitle, choices, replacement, warning, lightboxSelector=".lightbox-ask-choice") ->
defered = @q.defer()
el = angular.element(lightboxSelector)
# Render content
@ -103,7 +105,6 @@ class ConfirmService extends taiga.Service
choicesField.html('')
_.each choices, (value, key) ->
choicesField.append(angular.element("<option value='#{key}'>#{value}</option>"))
defered = @q.defer()
# Assign event handlers
el.on "click.confirm-dialog", "a.button-green", debounce 2000, (event) =>
@ -127,11 +128,12 @@ class ConfirmService extends taiga.Service
return defered.promise
error: (message) ->
defered = @q.defer()
el = angular.element(".lightbox-generic-error")
# Render content
el.find("h2.title").html(message)
defered = @q.defer()
# Assign event handlers
el.on "click.confirm-dialog", "a.button-green", (event) =>
@ -149,12 +151,13 @@ class ConfirmService extends taiga.Service
return defered.promise
success: (title, message) ->
defered = @q.defer()
el = angular.element(".lightbox-generic-success")
# Render content
el.find("h2.title").html(title) if title
el.find("p.message").html(message) if message
defered = @q.defer()
# Assign event handlers
el.on "click.confirm-dialog", "a.button-green", (event) =>
@ -208,12 +211,12 @@ class ConfirmService extends taiga.Service
if title
el.find("h4").html(title)
else
el.find("h4").html(NOTIFICATION_MSG[type].title)
el.find("h4").html(@translate.instant(NOTIFICATION_MSG[type].title))
if message
el.find("p").html(message)
else
el.find("p").html(NOTIFICATION_MSG[type].message)
el.find("p").html(@translate.instant(NOTIFICATION_MSG[type].message))
body = angular.element("body")
body.find(".notification-message .notification-light")

View File

@ -121,7 +121,7 @@ CustomAttributesValuesDirective = ($templates, $storage) ->
module.directive("tgCustomAttributesValues", ["$tgTemplate", "$tgStorage", CustomAttributesValuesDirective])
CustomAttributeValueDirective = ($template, $selectedText) ->
CustomAttributeValueDirective = ($template, $selectedText, $compile) ->
template = $template.get("custom-attributes/custom-attribute-value.html", true)
templateEdit = $template.get("custom-attributes/custom-attribute-value-edit.html", true)
@ -139,8 +139,10 @@ CustomAttributeValueDirective = ($template, $selectedText) ->
if editable and (edit or not value)
html = templateEdit(ctx)
html = $compile(html)($scope)
else
html = template(ctx)
html = $compile(html)($scope)
$el.html(html)
@ -168,11 +170,13 @@ CustomAttributeValueDirective = ($template, $selectedText) ->
return if $selectedText.get().length
render(attributeValue, true)
$el.find("input[name='description']").focus().select()
$scope.$apply()
$el.on "click", "a.icon-edit", (event) ->
event.preventDefault()
render(attributeValue, true)
$el.find("input[name='description']").focus().select()
$scope.$apply()
## Actions (on edit mode)
submit = debounce 2000, (event) =>
@ -195,4 +199,4 @@ CustomAttributeValueDirective = ($template, $selectedText) ->
restrict: "AE"
}
module.directive("tgCustomAttributeValue", ["$tgTemplate", "$selectedText", CustomAttributeValueDirective])
module.directive("tgCustomAttributeValue", ["$tgTemplate", "$selectedText", "$compile", CustomAttributeValueDirective])

View File

@ -28,7 +28,7 @@ module = angular.module("taigaCommon")
## User story estimation directive (for Lightboxes)
#############################################################################
LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $template) ->
LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $template, $compile) ->
# Display the points of a US and you can edit it.
#
# Example:
@ -56,6 +56,7 @@ LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $
mainTemplate = "common/estimation/us-estimation-points-per-role.html"
template = $template.get(mainTemplate, true)
html = template(ctx)
html = $compile(html)($scope)
@$el.html(html)
estimationProcess.render()
@ -68,14 +69,14 @@ LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $
require: "ngModel"
}
module.directive("tgLbUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgTemplate", LbUsEstimationDirective])
module.directive("tgLbUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgTemplate", "$compile", LbUsEstimationDirective])
#############################################################################
## User story estimation directive
#############################################################################
UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qqueue, $template) ->
UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qqueue, $template, $compile) ->
# Display the points of a US and you can edit it.
#
# Example:
@ -102,6 +103,7 @@ UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qq
mainTemplate = "common/estimation/us-estimation-points-per-role.html"
template = $template.get(mainTemplate, true)
html = template(ctx)
html = $compile(html)($scope)
@$el.html(html)
estimationProcess.render()
@ -115,7 +117,7 @@ UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qq
require: "ngModel"
}
module.directive("tgUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$tgTemplate",
module.directive("tgUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$tgTemplate", "$compile"
UsEstimationDirective])

View File

@ -33,15 +33,14 @@ defaultFilter = ->
module.filter("default", defaultFilter)
yesNoFilter = ->
#TODO: i18n
yesNoFilter = ($translate) ->
return (value) ->
if value
return "Yes"
return $translate.instant("COMMON.YES")
return "No"
return $translate.instant("COMMON.NO")
module.filter("yesNo", yesNoFilter)
module.filter("yesNo", ["$translate", yesNoFilter])
unslugify = ->

View File

@ -68,7 +68,7 @@ class HistoryController extends taiga.Controller
return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $compile) ->
templateChangeDiff = $template.get("common/history/history-change-diff.html", true)
templateChangePoints = $template.get("common/history/history-change-points.html", true)
templateChangeGeneric = $template.get("common/history/history-change-generic.html", true)
@ -87,6 +87,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
showAllComments = false
showAllActivity = false
getPrettyDateFormat = ->
return $translate.instant("ACTIVITY.DATETIME")
bindOnce $scope, $attrs.ngModel, (model) ->
type = $attrs.type
objectId = model.id
@ -97,24 +100,40 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
# Helpers
getHumanizedFieldName = (field) ->
humanizedFieldNames = {
# US
assigned_to: "assigned to"
is_closed: "is closed"
finish_date: "finish date"
client_requirement: "client requirement"
team_requirement: "team requirement"
subject : $translate.instant("ACTIVITY.FIELDS.SUBJECT")
name: $translate.instant("ACTIVITY.FIELDS.NAME")
description : $translate.instant("ACTIVITY.FIELDS.DESCRIPTION")
content: $translate.instant("ACTIVITY.FIELDS.CONTENT")
status: $translate.instant("ACTIVITY.FIELDS.STATUS")
is_closed : $translate.instant("ACTIVITY.FIELDS.IS_CLOSED")
finish_date : $translate.instant("ACTIVITY.FIELDS.FINISH_DATE")
type: $translate.instant("ACTIVITY.FIELDS.TYPE")
priority: $translate.instant("ACTIVITY.FIELDS.PRIORITY")
severity: $translate.instant("ACTIVITY.FIELDS.SEVERITY")
assigned_to : $translate.instant("ACTIVITY.FIELDS.ASSIGNED_TO")
watchers : $translate.instant("ACTIVITY.FIELDS.WATCHERS")
milestone : $translate.instant("ACTIVITY.FIELDS.MILESTONE")
user_story: $translate.instant("ACTIVITY.FIELDS.USER_STORY")
project: $translate.instant("ACTIVITY.FIELDS.PROJECT")
is_blocked: $translate.instant("ACTIVITY.FIELDS.IS_BLOCKED")
blocked_note: $translate.instant("ACTIVITY.FIELDS.BLOCKED_NOTE")
points: $translate.instant("ACTIVITY.FIELDS.POINTS")
client_requirement : $translate.instant("ACTIVITY.FIELDS.CLIENT_REQUIREMENT")
team_requirement : $translate.instant("ACTIVITY.FIELDS.TEAM_REQUIREMENT")
is_iocaine: $translate.instant("ACTIVITY.FIELDS.IS_IOCAINE")
tags: $translate.instant("ACTIVITY.FIELDS.TAGS")
attachments : $translate.instant("ACTIVITY.FIELDS.ATTACHMENTS")
is_deprecated: $translate.instant("ACTIVITY.FIELDS.IS_DEPRECATED")
blocked_note: $translate.instant("ACTIVITY.FIELDS.BLOCKED_NOTE")
is_blocked: $translate.instant("ACTIVITY.FIELDS.IS_BLOCKED")
order: $translate.instant("ACTIVITY.FIELDS.ORDER")
backlog_order: $translate.instant("ACTIVITY.FIELDS.BACKLOG_ORDER")
sprint_order: $translate.instant("ACTIVITY.FIELDS.SPRINT_ORDER")
kanban_order: $translate.instant("ACTIVITY.FIELDS.KANBAN_ORDER")
taskboard_order: $translate.instant("ACTIVITY.FIELDS.TASKBOARD_ORDER")
us_order: $translate.instant("ACTIVITY.FIELDS.US_ORDER")
}
# Task
milestone: "sprint"
user_story: "user story"
is_iocaine: "is iocaine"
# Attachment
is_deprecated: "is deprecated"
blocked_note: "blocked note"
is_blocked: "is blocked"
} # TODO i18n
return humanizedFieldNames[field] or field
getUserFullName = (userId) ->
@ -132,17 +151,17 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
formatChange = (change) ->
if _.isArray(change)
if change.length == 0
return "empty"
return $translate.instant("ACTIVITY.VALUES.EMPTY")
return change.join(", ")
if change == ""
return "empty"
return $translate.instant("ACTIVITY.VALUES.EMPTY")
if not change? or change == false
return "no"
return $translate.instant("ACTIVITY.VALUES.NO")
if change == true
return "yes"
return $translate.instant("ACTIVITY.VALUES.YES")
return change
@ -152,22 +171,26 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
attachments = _.map value, (changes, type) ->
if type == "new"
return _.map changes, (change) ->
# TODO: i18n
return templateChangeDiff({name: "new attachment", diff: change.filename})
return templateChangeDiff({
name: $translate.instant("ACTIVITY.NEW_ATTACHMENT"),
diff: change.filename
})
else if type == "deleted"
return _.map changes, (change) ->
# TODO: i18n
return templateChangeDiff({name: "deleted attachment", diff: change.filename})
return templateChangeDiff({
name: $translate.instant("ACTIVITY.DELETED_ATTACHMENT"),
diff: change.filename
})
else
return _.map changes, (change) ->
# TODO: i18n
name = "updated attachment #{change.filename}"
name = $translate.instant("ACTIVITY.UPDATED_ATTACHMENT", {filename: change.filename})
diff = _.map change.changes, (values, name) ->
return {
name: getHumanizedFieldName(name)
from: formatChange(values[0])
to: formatChange(values[1])
}
}
return templateChangeAttachment({name: name, diff: diff})
@ -177,16 +200,19 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
customAttributes = _.map value, (changes, type) ->
if type == "new"
return _.map changes, (change) ->
return templateChangeGeneric({
html = templateChangeGeneric({
name: change.name,
from: formatChange(""),
to: formatChange(change.value)
})
html = $compile(html)($scope)
return html[0].outerHTML
else if type == "deleted"
return _.map changes, (change) ->
# TODO: i18n
return templateChangeDiff({
name: "deleted custom attribute",
name: $translate.instant("ACTIVITY.DELETED_CUSTOM_ATTRIBUTE")
diff: change.name
})
else
@ -207,7 +233,11 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
else if field == "blocked_note"
return templateChangeDiff({name: getHumanizedFieldName("blocked_note"), diff: value[1]})
else if field == "points"
return templateChangePoints({points: value})
html = templateChangePoints({points: value})
html = $compile(html)($scope)
return html[0].outerHTML
else if field == "attachments"
return renderAttachmentEntry(value)
else if field == "custom_attributes"
@ -216,11 +246,15 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
name = getHumanizedFieldName(field)
removed = _.difference(value[0], value[1])
added = _.difference(value[1], value[0])
return templateChangeList({name:name, removed:removed, added: added})
html = templateChangeList({name:name, removed:removed, added: added})
html = $compile(html)($scope)
return html[0].outerHTML
else if field == "assigned_to"
name = getHumanizedFieldName(field)
from = formatChange(value[0] or "Unassigned")
to = formatChange(value[1] or "Unassigned")
from = formatChange(value[0] or $translate.instant("ACTIVITY.VALUES.UNASSIGNED"))
to = formatChange(value[1] or $translate.instant("ACTIVITY.VALUES.UNASSIGNED"))
return templateChangeGeneric({name:name, from:from, to: to})
else
name = getHumanizedFieldName(field)
@ -233,44 +267,51 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
renderChangesHelperText = (change) ->
size = countChanges(change)
if size == 1
return "Made #{size} change" # TODO: i18n
return "Made #{size} changes" # TODO: i18n
return $translate.instant("ACTIVITY.SIZE_CHANGE", {size: size}, 'messageformat')
renderComment = (comment) ->
if (comment.delete_comment_date or comment.delete_comment_user?.name)
return templateDeletedComment({
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date
html = templateDeletedComment({
deleteCommentDate: moment(comment.delete_comment_date).format(getPrettyDateFormat()) if comment.delete_comment_date
deleteCommentUser: comment.delete_comment_user.name
deleteComment: comment.comment_html
activityId: comment.id
canRestoreComment: comment.delete_comment_user.pk == $scope.user.id or $scope.project.my_permissions.indexOf("modify_project") > -1
canRestoreComment: (comment.delete_comment_user.pk == $scope.user.id or
$scope.project.my_permissions.indexOf("modify_project") > -1)
})
return templateActivity({
html = $compile(html)($scope)
return html[0].outerHTML
html = templateActivity({
avatar: getUserAvatar(comment.user.pk)
userFullName: comment.user.name
creationDate: moment(comment.created_at).format("DD MMM YYYY HH:mm")
creationDate: moment(comment.created_at).format(getPrettyDateFormat())
comment: comment.comment_html
changesText: renderChangesHelperText(comment)
changes: renderChangeEntries(comment)
mode: "comment"
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date
deleteCommentDate: moment(comment.delete_comment_date).format(getPrettyDateFormat()) if comment.delete_comment_date
deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name
activityId: comment.id
canDeleteComment: comment.user.pk == $scope.user?.id or $scope.project.my_permissions.indexOf("modify_project") > -1
})
html = $compile(html)($scope)
return html[0].outerHTML
renderChange = (change) ->
return templateActivity({
avatar: getUserAvatar(change.user.pk)
userFullName: change.user.name
creationDate: moment(change.created_at).format("DD MMM YYYY HH:mm")
creationDate: moment(change.created_at).format(getPrettyDateFormat())
comment: change.comment_html
changes: renderChangeEntries(change)
changesText: ""
mode: "activity"
deleteCommentDate: moment(change.delete_comment_date).format("DD MMM YYYY HH:mm") if change.delete_comment_date
deleteCommentDate: moment(change.delete_comment_date).format(getPrettyDateFormat()) if change.delete_comment_date
deleteCommentUser: change.delete_comment_user.name if change.delete_comment_user?.name
activityId: change.id
})
@ -281,7 +322,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
else
showMore = totalEntries - entries.length
return templateBaseEntries({entries: entries, showMore:showMore})
html = templateBaseEntries({entries: entries, showMore:showMore})
html = $compile(html)($scope)
return html
# Render into DOM (operations with dom mutability)
@ -394,7 +437,9 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
$el.off()
templateFn = ($el, $attrs) ->
return templateBase({ngmodel: $attrs.ngModel, type: $attrs.type, mode: $attrs.mode})
html = templateBase({ngmodel: $attrs.ngModel, type: $attrs.type, mode: $attrs.mode})
return html
return {
controller: HistoryController
@ -405,4 +450,5 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
}
module.directive("tgHistory", ["$log", "$tgLoading", "$tgQqueue", "$tgTemplate", "$tgConfirm", HistoryDirective])
module.directive("tgHistory", ["$log", "$tgLoading", "$tgQqueue", "$tgTemplate", "$tgConfirm", "$translate",
"$compile", HistoryDirective])

View File

@ -22,7 +22,7 @@
module = angular.module("taigaCommon")
ImportProjectButtonDirective = ($rs, $confirm, $location, $navUrls) ->
ImportProjectButtonDirective = ($rs, $confirm, $location, $navUrls, $translate) ->
link = ($scope, $el, $attrs) ->
$el.on "click", ".import-project-button", (event) ->
event.preventDefault()
@ -34,34 +34,29 @@ ImportProjectButtonDirective = ($rs, $confirm, $location, $navUrls) ->
file = event.target.files[0]
return if not file
loader = $confirm.loader("Uploading dump file")
loader = $confirm.loader($translate.instant("PROJECT.IMPORT.UPLOADING_FILE"))
onSuccess = (result) ->
loader.stop()
if result.status == 202 # Async mode
title = "Our Oompa Loompas are importing your project" # TODO: i18n
message = "This process could take a few minutes <br/> We will send you
an email when ready" # TODO: i18n
title = $translate.instant("PROJECT.IMPORT.ASYNC_IN_PROGRESS_TITLE")
message = $translate.instant("PROJECT.IMPORT.ASYNC_IN_PROGRESS_MESSAGE")
$confirm.success(title, message)
else # result.status == 201 # Sync mode
ctx = {project: result.data.slug}
$location.path($navUrls.resolve("project-admin-project-profile-details", ctx))
$confirm.notify("success", "Your project has been imported successfuly.") # TODO: i18n
msg = $translate.instant("PROJECT.IMPORT.SYNC_SUCCESS")
$confirm.notify("success", msg)
onError = (result) ->
loader.stop()
console.log "Error", result
errorMsg = "Our oompa loompas have some problems importing your dump data.
Please try again. " # TODO: i18n
errorMsg = $translate.instant("PROJECT.IMPORT.ERROR")
if result.status == 429 # TOO MANY REQUESTS
errorMsg = "Sorry, our oompa loompas are very busy right now.
Please try again in a few minutes. " # TODO: i18n
errorMsg = $translate.instant("PROJECT.IMPORT.ERROR_TOO_MANY_REQUEST")
else if result.data?._error_message
errorMsg = "Our oompa loompas have some problems importing your dump data:
#{result.data._error_message}" # TODO: i18n
errorMsg = $translate.instant("PROJECT.IMPORT.ERROR_MESSAGE", {error_message: result.data._error_message})
$confirm.notify("error", errorMsg)
loader.start()
@ -69,5 +64,5 @@ ImportProjectButtonDirective = ($rs, $confirm, $location, $navUrls) ->
return {link: link}
module.directive("tgImportProjectButton", ["$tgResources", "$tgConfirm", "$location", "$tgNavUrls",
module.directive("tgImportProjectButton", ["$tgResources", "$tgConfirm", "$location", "$tgNavUrls", "$translate",
ImportProjectButtonDirective])

View File

@ -86,7 +86,10 @@ class LightboxKeyboardNavigationService extends taiga.Service
# Key: enter
if code == 13
activeElement.trigger("click")
if $el.find(".watcher-single").length == 1
$el.find('.watcher-single:first').trigger("click")
else
activeElement.trigger("click")
# Key: down
else if code == 40
@ -143,9 +146,10 @@ module.directive("lightbox", ["lightboxService", LightboxDirective])
# Issue/Userstory blocking message lightbox directive.
BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loading, $qqueue) ->
BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loading, $qqueue, $translate) ->
link = ($scope, $el, $attrs, $model) ->
$el.find("h2.title").text($attrs.title)
$translate($attrs.title).then (title) ->
$el.find("h2.title").text(title)
unblock = $qqueue.bindAdd (item, finishCallback) =>
promise = $tgrepo.save(item)
@ -213,14 +217,14 @@ BlockLightboxDirective = ($rootscope, $tgrepo, $confirm, lightboxService, $loadi
require: "ngModel"
}
module.directive("tgLbBlock", ["$rootScope", "$tgRepo", "$tgConfirm", "lightboxService", "$tgLoading", "$tgQqueue", BlockLightboxDirective])
module.directive("tgLbBlock", ["$rootScope", "$tgRepo", "$tgConfirm", "lightboxService", "$tgLoading", "$tgQqueue", "$translate", BlockLightboxDirective])
#############################################################################
## Generic Lightbox Blocking-Message Input Directive
#############################################################################
BlockingMessageInputDirective = ($log, $template) ->
BlockingMessageInputDirective = ($log, $template, $compile) ->
template = $template.get("common/lightbox/lightbox-blocking-message-input.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -243,14 +247,14 @@ BlockingMessageInputDirective = ($log, $template) ->
restrict: "EA"
}
module.directive("tgBlockingMessageInput", ["$log", "$tgTemplate", BlockingMessageInputDirective])
module.directive("tgBlockingMessageInput", ["$log", "$tgTemplate", "$compile", BlockingMessageInputDirective])
#############################################################################
## Create/Edit Userstory Lightbox Directive
#############################################################################
CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading) ->
CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading, $translate) ->
link = ($scope, $el, attrs) ->
$scope.isNew = true
@ -267,8 +271,8 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
})
# Update texts for creation
$el.find(".button-green").html("Create") #TODO: i18n
$el.find(".title").html("New user story ") #TODO: i18n
$el.find(".button-green").html($translate.instant("COMMON.CREATE"))
$el.find(".title").html($translate.instant("LIGHTBOX.CREATE_EDIT_US.NEW_US"))
$el.find(".tag-input").val("")
$el.find(".blocked-note").addClass("hidden")
@ -283,8 +287,8 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
$scope.isNew = false
# Update texts for edition
$el.find(".button-green").html("Save") #TODO: i18n
$el.find(".title").html("Edit user story ") #TODO: i18n
$el.find(".button-green").html($translate.instant("COMMON.SAVE"))
$el.find(".title").html($translate.instant("LIGHTBOX.CREATE_EDIT_US.EDIT_US"))
$el.find(".tag-input").val("")
# Update requirement info (team, client or blocked)
@ -362,6 +366,7 @@ module.directive("tgLbCreateEditUserstory", [
"$rootScope",
"lightboxService",
"$tgLoading",
"$translate",
CreateEditUserstoryDirective
])
@ -424,7 +429,7 @@ module.directive("tgLbCreateBulkUserstories", [
## AssignedTo Lightbox Directive
#############################################################################
AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationService, $template) ->
AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationService, $template, $compile) ->
link = ($scope, $el, $attrs) ->
selectedUser = null
selectedItem = null
@ -458,6 +463,9 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
}
html = usersTemplate(ctx)
html = $compile(html)($scope)
$el.find("div.watchers").html(html)
lightboxKeyboardNavigationService.init($el)
@ -517,7 +525,7 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
}
module.directive("tgLbAssignedto", ["lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", AssignedToLightboxDirective])
module.directive("tgLbAssignedto", ["lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", AssignedToLightboxDirective])
#############################################################################
@ -554,6 +562,7 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
html = usersTemplate(ctx)
$el.find("div.watchers").html(html)
lightboxKeyboardNavigationService.init($el)
closeLightbox = () ->
lightboxKeyboardNavigationService.stop()

View File

@ -99,7 +99,7 @@ module.directive("tgColorizeTags", ColorizeTagsDirective)
## TagLine Directive (for Lightboxes)
#############################################################################
LbTagLineDirective = ($rs, $template) ->
LbTagLineDirective = ($rs, $template, $compile) ->
ENTER_KEY = 13
COMMA_KEY = 188
@ -116,7 +116,7 @@ LbTagLineDirective = ($rs, $template) ->
if tag.color
tag.style = "border-left: 5px solid #{tag.color}"
html = templateTags(ctx)
html = $compile(templateTags(ctx))($scope)
$el.find("div.tags-container").html(html)
showSaveButton = -> $el.find(".save").removeClass("hidden")
@ -221,14 +221,14 @@ LbTagLineDirective = ($rs, $template) ->
templateUrl: "common/tag/lb-tag-line.html"
}
module.directive("tgLbTagLine", ["$tgResources", "$tgTemplate", LbTagLineDirective])
module.directive("tgLbTagLine", ["$tgResources", "$tgTemplate", "$compile", LbTagLineDirective])
#############################################################################
## TagLine Directive (for detail pages)
#############################################################################
TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template, $compile) ->
ENTER_KEY = 13
ESC_KEY = 27
COMMA_KEY = 188
@ -237,7 +237,10 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
link = ($scope, $el, $attrs, $model) ->
isEditable = ->
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
if $attrs.requiredPerm?
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
return true
## Render
renderTags = (tags, tagsColors) ->
@ -245,7 +248,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]})
isEditable: isEditable()
}
html = templateTags(ctx)
html = $compile(templateTags(ctx))($scope)
$el.find("div.tags-container").html(html)
renderInReadModeOnly = ->
@ -362,7 +365,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
deleteValue(value)
bindOnce $scope, "project", (project) ->
bindOnce $scope, "project.tags_colors", (tags_colors) ->
if not isEditable()
renderInReadModeOnly()
return
@ -376,7 +379,7 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
menu.css("left", position.left)
$el.find("input").autocomplete({
source: _.keys(project.tags_colors)
source: _.keys(tags_colors)
position: {
my: "left top",
using: positioningFunction
@ -406,4 +409,5 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template) ->
templateUrl: "common/tag/tag-line.html"
}
module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$tgQqueue", "$tgTemplate", TagLineDirective])
module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$tgQqueue",
"$tgTemplate", "$compile", TagLineDirective])

View File

@ -55,16 +55,13 @@ module = angular.module("taigaCommon")
#############################################################################
## WYSIWYG markitup editor directive
#############################################################################
tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) ->
MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $translate) ->
previewTemplate = $template.get("common/wysiwyg/wysiwyg-markitup-preview.html", true)
link = ($scope, $el, $attrs, $model) ->
element = angular.element($el)
previewDomNode = $("<div/>", {class: "preview"})
#openHelp = ->
# window.open($rootscope.urls.wikiHelpUrl(), "_blank")
closePreviewMode = ->
element.parents(".markdown").find(".preview").remove()
element.parents(".markItUp").show()
@ -76,7 +73,10 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) ->
markdownDomNode = element.parents(".markdown")
markItUpDomNode = element.parents(".markItUp")
$rs.mdrender.render($scope.projectId, $model.$modelValue).then (data) ->
markdownDomNode.append(previewTemplate({data: data.data}))
html = previewTemplate({data: data.data})
html = $compile(html)($scope)
markdownDomNode.append(html)
markItUpDomNode.hide()
markdown = element.closest(".markdown")
@ -130,168 +130,14 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) ->
else
return cursorPosition
markdownSettings =
nameSpace: "markdown"
onShiftEnter: {keepDefault:false, openWith:"\n\n"}
onEnter:
keepDefault: false,
replaceWith: () -> "\n"
afterInsert: (data) ->
lines = data.textarea.value.split("\n")
cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
lastLine = lines[cursorLine - 1]
# unordered list -
match = lastLine.match /^(\s*- ).*/
if match
emptyListItem = lastLine.match /^(\s*)\-\s$/
if emptyListItem
markdownCaretPositon = addLine(data.textarea, cursorLine - 1)
else
markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1]}")
# unordered list *
match = lastLine.match /^(\s*\* ).*/
if match
emptyListItem = lastLine.match /^(\s*\* )$/
if emptyListItem
markdownCaretPositon = addLine(data.textarea, cursorLine - 1)
else
markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1]}")
# ordered list
match = lastLine.match /^(\s*)(\d+)\.\s/
if match
emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/
if emptyListItem
markdownCaretPositon = addLine(data.textarea, cursorLine - 1)
else
markdownCaretPositon = addLine(data.textarea, cursorLine, "#{match[1] + (parseInt(match[2], 10) + 1)}. ")
setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon
markupSet: [
{
name: $tr.t("markdown-editor.heading-1")
key: "1"
placeHolder: $tr.t("markdown-editor.placeholder")
closeWith: (markItUp) -> markdownTitle(markItUp, "=")
},
{
name: $tr.t("markdown-editor.heading-2")
key: "2"
placeHolder: $tr.t("markdown-editor.placeholder")
closeWith: (markItUp) -> markdownTitle(markItUp, "-")
},
{
name: $tr.t("markdown-editor.heading-3")
key: "3"
openWith: "### "
placeHolder: $tr.t("markdown-editor.placeholder")
},
{
separator: "---------------"
},
{
name: $tr.t("markdown-editor.bold")
key: "B"
openWith: "**"
closeWith: "**"
},
{
name: $tr.t("markdown-editor.italic")
key: "I"
openWith: "_"
closeWith: "_"
},
{
name: $tr.t("markdown-editor.strike")
key: "S"
openWith: "~~"
closeWith: "~~"
},
{
separator: "---------------"
},
{
name: $tr.t("markdown-editor.bulleted-list")
openWith: "- "
},
{
name: $tr.t("markdown-editor.numeric-list")
openWith: (markItUp) -> markItUp.line+". "
},
{
separator: "---------------"
},
{
name: $tr.t("markdown-editor.picture")
key: "P"
replaceWith: '![[![Alternative text]!]](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
name: $tr.t("markdown-editor.link")
key: "L"
openWith: "["
closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
placeHolder: $tr.t("markdown-editor.link-placeholder")
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
separator: "---------------"
},
{
name: $tr.t("markdown-editor.quotes")
openWith: "> "
},
{
name: $tr.t("markdown-editor.code-block")
openWith: "```\n"
closeWith: "\n```"
},
{
separator: "---------------"
},
{
name: $tr.t("markdown-editor.preview")
call: preview
className: "preview-icon"
},
# {
# separator: "---------------"
# },
# {
# name: $tr.t("markdown-editor.help")
# call: openHelp
# className: "help"
# }
]
afterInsert: (event) ->
target = angular.element(event.textarea)
$model.$setViewValue(target.val())
prepareUrlFormatting = (markItUp) ->
console.log(markItUp)
regex = /(<<<|>>>)/gi
result = 0
indices = []
(indices.push(result.index)) while ( (result = regex.exec(markItUp.textarea.value)) )
markItUp.donotparse = indices
console.log(indices)
urlFormatting = (markItUp) ->
console.log(markItUp.donotparse)
regex = /<<</gi
result = 0
startIndex = 0
@ -329,13 +175,186 @@ tgMarkitupDirective = ($rootscope, $rs, $tr, $selectedText, $template) ->
return "\n"+heading+"\n"
element.markItUp(markdownSettings)
renderMarkItUp = () ->
markdownSettings =
nameSpace: "markdown"
onShiftEnter: {keepDefault:false, openWith:"\n\n"}
onEnter:
keepDefault: false,
replaceWith: () -> "\n"
afterInsert: (data) ->
lines = data.textarea.value.split("\n")
cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
lastLine = lines[cursorLine - 1]
# unordered list -
match = lastLine.match /^(\s*- ).*/
if match
emptyListItem = lastLine.match /^(\s*)\-\s$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1]}"
markdownCaretPositon = addLine(data.textarea, nline, replace)
# unordered list *
match = lastLine.match /^(\s*\* ).*/
if match
emptyListItem = lastLine.match /^(\s*\* )$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1]}"
markdownCaretPositon = addLine(data.textarea, nline, replace)
# ordered list
match = lastLine.match /^(\s*)(\d+)\.\s/
if match
emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1] + (parseInt(match[2], 10) + 1)}. "
markdownCaretPositon = addLine(data.textarea, nline, replace)
setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon
markupSet: [
{
name: $translate.instant("COMMON.WYSIWYG.H1_BUTTON")
key: "1"
placeHolder: $translate.instant("COMMON.WYSIWYG.H1_SAMPLE_TEXT")
closeWith: (markItUp) -> markdownTitle(markItUp, "=")
},
{
name: $translate.instant("COMMON.WYSIWYG.H2_BUTTON")
key: "2"
placeHolder: $translate.instant("COMMON.WYSIWYG.H2_SAMPLE_TEXT")
closeWith: (markItUp) -> markdownTitle(markItUp, "-")
},
{
name: $translate.instant("COMMON.WYSIWYG.H3_BUTTON")
key: "3"
openWith: "### "
placeHolder: $translate.instant("COMMON.WYSIWYG.H3_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON")
key: "B"
openWith: "**"
closeWith: "**"
placeHolder: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
key: "I"
openWith: "_"
closeWith: "_"
placeHolder: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.STRIKE_BUTTON")
key: "S"
openWith: "~~"
closeWith: "~~"
placeHolder: $translate.instant("COMMON.WYSIWYG.STRIKE_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_BUTTON")
openWith: "- "
placeHolder: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_BUTTON")
openWith: (markItUp) -> markItUp.line+". "
placeHolder: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.PICTURE_BUTTON")
key: "P"
openWith: "!["
closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
placeHolder: $translate.instant("COMMON.WYSIWYG.PICTURE_SAMPLE_TEXT")
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
name: $translate.instant("COMMON.WYSIWYG.LINK_BUTTON")
key: "L"
openWith: "["
closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
placeHolder: $translate.instant("COMMON.WYSIWYG.LINK_SAMPLE_TEXT")
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_BUTTON")
openWith: "> "
placeHolder: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_BUTTON")
openWith: "```\n"
placeHolder: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_SAMPLE_TEXT")
closeWith: "\n```"
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.PREVIEW_BUTTON")
call: preview
className: "preview-icon"
},
]
afterInsert: (event) ->
target = angular.element(event.textarea)
$model.$setViewValue(target.val())
element
.markItUpRemove()
.markItUp(markdownSettings)
renderMarkItUp()
unbind = $rootscope.$on "$translateChangeEnd", renderMarkItUp
element.on "keypress", (event) ->
$scope.$apply()
$scope.$on "$destroy", ->
$el.off()
unbind()
return {link:link, require:"ngModel"}
module.directive("tgMarkitup", ["$rootScope", "$tgResources", "$tgI18n", "$selectedText", "$tgTemplate", tgMarkitupDirective])
module.directive("tgMarkitup", ["$rootScope", "$tgResources", "$selectedText", "$tgTemplate", "$compile",
"$translate", MarkitupDirective])

View File

@ -47,13 +47,14 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$appTitle",
"$tgAnalytics",
"$tgNavUrls",
"$translate",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @analytics, @navUrls, tgLoader) ->
@log, @appTitle, @analytics, @navUrls, @translate, tgLoader) ->
@scope.issueRef = @params.issueref
@scope.sectionName = "Issue Details"
@scope.sectionName = @translate.instant("ISSUES.SECTION_NAME")
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -145,7 +146,7 @@ module.controller("IssueDetailController", IssueDetailController)
## Issue status display directive
#############################################################################
IssueStatusDisplayDirective = ($template)->
IssueStatusDisplayDirective = ($template, $compile)->
# Display if a Issue is open or closed and its issueboard status.
#
# Example:
@ -165,6 +166,9 @@ IssueStatusDisplayDirective = ($template)->
is_closed: status.is_closed
status: status
})
html = $compile(html)($scope)
$el.html(html)
$scope.$watch $attrs.ngModel, (issue) ->
@ -179,14 +183,14 @@ IssueStatusDisplayDirective = ($template)->
require: "ngModel"
}
module.directive("tgIssueStatusDisplay", ["$tgTemplate", IssueStatusDisplayDirective])
module.directive("tgIssueStatusDisplay", ["$tgTemplate", "$compile", IssueStatusDisplayDirective])
#############################################################################
## Issue status button directive
#############################################################################
IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template) ->
IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template, $compile) ->
# Display the status of Issue and you can edit it.
#
# Example:
@ -211,6 +215,9 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $t
statuses: $scope.statusList
editable: isEditable()
})
html = $compile(html)($scope)
$el.html(html)
save = $qqueue.bindAdd (statusId) =>
@ -262,13 +269,13 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $t
require: "ngModel"
}
module.directive("tgIssueStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", IssueStatusButtonDirective])
module.directive("tgIssueStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile", IssueStatusButtonDirective])
#############################################################################
## Issue type button directive
#############################################################################
IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template) ->
IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template, $compile) ->
# Display the type of Issue and you can edit it.
#
# Example:
@ -293,6 +300,9 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $tem
typees: $scope.typeList
editable: isEditable()
})
html = $compile(html)($scope)
$el.html(html)
save = $qqueue.bindAdd (type) =>
@ -343,14 +353,14 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $tem
require: "ngModel"
}
module.directive("tgIssueTypeButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", IssueTypeButtonDirective])
module.directive("tgIssueTypeButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile", IssueTypeButtonDirective])
#############################################################################
## Issue severity button directive
#############################################################################
IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template) ->
IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template, $compile) ->
# Display the severity of Issue and you can edit it.
#
# Example:
@ -375,6 +385,9 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue,
severityes: $scope.severityList
editable: isEditable()
})
html = $compile(html)($scope)
$el.html(html)
save = $qqueue.bindAdd (severity) =>
@ -427,14 +440,14 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue,
require: "ngModel"
}
module.directive("tgIssueSeverityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", IssueSeverityButtonDirective])
module.directive("tgIssueSeverityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile", IssueSeverityButtonDirective])
#############################################################################
## Issue priority button directive
#############################################################################
IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template) ->
IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $template, $compile) ->
# Display the priority of Issue and you can edit it.
#
# Example:
@ -459,6 +472,9 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue,
priorityes: $scope.priorityList
editable: isEditable()
})
html = $compile(html)($scope)
$el.html(html)
save = $qqueue.bindAdd (priority) =>
@ -511,14 +527,14 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue,
require: "ngModel"
}
module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", IssuePriorityButtonDirective])
module.directive("tgIssuePriorityButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile", IssuePriorityButtonDirective])
#############################################################################
## Promote Issue to US button directive
#############################################################################
PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue) ->
PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue, $translate) ->
link = ($scope, $el, $attrs, $model) ->
save = $qqueue.bindAdd (issue, finish) =>
@ -548,8 +564,8 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue) ->
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
title = $translate.instant("ISSUES.CONFIRM_PROMOTE.TITLE")
message = $translate.instant("ISSUES.CONFIRM_PROMOTE.MESSAGE")
subtitle = issue.subject
$confirm.ask(title, subtitle, message).then (finish) =>
@ -566,5 +582,5 @@ PromoteIssueToUsButtonDirective = ($rootScope, $repo, $confirm, $qqueue) ->
link: link
}
module.directive("tgPromoteIssueToUsButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue",
module.directive("tgPromoteIssueToUsButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$translate"
PromoteIssueToUsButtonDirective])

View File

@ -51,12 +51,14 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"tgLoader"
"tgLoader",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @urls, @params, @q, @location, @appTitle,
@navUrls, @events, @analytics, tgLoader) ->
@scope.sectionName = "Issues"
@navUrls, @events, @analytics, tgLoader, @translate) ->
@scope.sectionName = @translate.instant("ISSUES.LIST_SECTION_NAME")
@scope.filters = {}
if _.isEmpty(@location.search())
@ -310,7 +312,7 @@ module.controller("IssuesController", IssuesController)
## Issues Directive
#############################################################################
IssuesDirective = ($log, $location, $template) ->
IssuesDirective = ($log, $location, $template, $compile) ->
## Issues Pagination
template = $template.get("issue/issue-paginator.html", true)
@ -360,7 +362,11 @@ IssuesDirective = ($log, $location, $template) ->
else
pages.push({classes: "page", num: i, type: "page"})
$pagEl.html(template(options))
html = template(options)
html = $compile(html)($scope)
$pagEl.html(html)
$scope.$watch "issues", (value) ->
# Do nothing if value is not logical true
@ -427,14 +433,14 @@ IssuesDirective = ($log, $location, $template) ->
return {link:link}
module.directive("tgIssues", ["$log", "$tgLocation", "$tgTemplate", IssuesDirective])
module.directive("tgIssues", ["$log", "$tgLocation", "$tgTemplate", "$compile", IssuesDirective])
#############################################################################
## Issues Filters Directive
#############################################################################
IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) ->
IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $translate, $compile) ->
template = $template.get("issue/issues-filters.html", true)
templateSelected = $template.get("issue/issues-filters-selected.html", true)
@ -468,6 +474,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
f.style = "border-left: 3px solid #{f.color}"
html = templateSelected({filters:selectedFilters})
html = $compile(html)($scope)
$el.find(".filters-applied").html(html)
if selectedFilters.length > 0
@ -481,6 +488,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
f.style = "border-left: 3px solid #{f.color}"
html = template({filters:filters})
html = $compile(html)($scope)
$el.find(".filter-list").html(html)
toggleFilterSelection = (type, id) ->
@ -533,12 +541,13 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
$scope.$on "filters:issueupdate", (ctx, filters) ->
html = template({filters:filters.statuses})
html = $compile(html)($scope)
$el.find(".filter-list").html(html)
selectQFilter = debounceLeading 100, (value) ->
return if value is undefined
$ctrl.replaceFilter("page", null)
$ctrl.replaceFilter("page", null, true)
if value.length == 0
$ctrl.replaceFilter("q", null)
@ -590,8 +599,8 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
target = angular.element(event.currentTarget)
customFilterName = target.parent().data('id')
title = "Delete custom filter" # TODO: i18n
message = "the custom filter '#{customFilterName}'" # TODO: i18n
title = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.TITLE")
message = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.MESSAGE", {customFilterName: customFilterName})
$confirm.askOnDelete(title, message).then (finish) ->
promise = $ctrl.deleteMyFilter(customFilterName)
@ -615,6 +624,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
$el.find('.save-filters').hide()
$el.find('.my-filter-name').removeClass("hidden")
$el.find('.my-filter-name').focus()
$scope.$apply()
$el.on "keyup", ".my-filter-name", (event) ->
event.preventDefault()
@ -652,8 +662,8 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template) -
return {link:link}
module.directive("tgIssuesFilters", ["$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading", "$tgTemplate",
IssuesFiltersDirective])
module.directive("tgIssuesFilters", ["$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
"$tgTemplate", "$translate", "$compile", IssuesFiltersDirective])
#############################################################################
@ -708,7 +718,28 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
updateIssueStatus($el, issue, $scope.issueStatusById)
$scope.$apply () ->
$repo.save(issue).then
$repo.save(issue).then ->
for filter in $scope.filters.statuses
if filter.id == issue.status
filter.count++
$rootscope.$broadcast("filters:issueupdate", $scope.filters)
filtering = false
for filter in $scope.filters.statuses
if filter.selected == true
filtering = true
if filter.id == issue.status
return
if not filtering
return
for el, i in $scope.issues
if el and el.id == issue.id
$scope.issues.splice(i, 1)
for filter in $scope.filters.statuses
if filter.id == issue.status
@ -732,7 +763,8 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
return {link: link}
module.directive("tgIssueStatusInlineEdition", ["$tgRepo", "$tgTemplate", "$rootScope", IssueStatusInlineEditionDirective])
module.directive("tgIssueStatusInlineEdition", ["$tgRepo", "$tgTemplate", "$rootScope",
IssueStatusInlineEditionDirective])
#############################################################################
@ -783,4 +815,5 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) ->
return {link: link}
module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope", IssueAssignedToInlineEditionDirective])
module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope",
IssueAssignedToInlineEditionDirective])

View File

@ -62,15 +62,16 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"tgLoader"
"tgLoader",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@appTitle, @navUrls, @events, @analytics, tgLoader) ->
@appTitle, @navUrls, @events, @analytics, tgLoader, @translate) ->
bindMethods(@)
@scope.sectionName = "Kanban"
@scope.sectionName = @translate.instant("KANBAN.SECTION_NAME")
@scope.statusViewModes = {}
@.initializeEventHandlers()
@ -307,10 +308,9 @@ module.directive("tgKanban", ["$tgRepo", "$rootScope", KanbanDirective])
## Kanban Archived Status Column Header Control
#############################################################################
KanbanArchivedStatusHeaderDirective = ($rootscope) ->
#TODO: i18N
showArchivedText = "Show archived"
hideArchivedText = "Hide archived"
KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
showArchivedText = $translate.instant("KANBAN.ACTION_SHOW_ARCHIVED")
hideArchivedText = $translate.instant("KANBAN.ACTION_HIDE_ARCHIVED")
link = ($scope, $el, $attrs) ->
status = $scope.$eval($attrs.tgKanbanArchivedStatusHeader)
@ -338,19 +338,18 @@ KanbanArchivedStatusHeaderDirective = ($rootscope) ->
return {link:link}
module.directive("tgKanbanArchivedStatusHeader", [ "$rootScope", KanbanArchivedStatusHeaderDirective])
module.directive("tgKanbanArchivedStatusHeader", [ "$rootScope", "$translate", KanbanArchivedStatusHeaderDirective])
#############################################################################
## Kanban Archived Status Column Intro Directive
#############################################################################
KanbanArchivedStatusIntroDirective = ->
# TODO: i18n
hiddenUserStoriexText = "The user stories in this status are hidden by default"
KanbanArchivedStatusIntroDirective = ($translate) ->
userStories = []
link = ($scope, $el, $attrs) ->
hiddenUserStoriexText = $translate.instant("KANBAN.HIDDEN_USER_STORIES")
status = $scope.$eval($attrs.tgKanbanArchivedStatusIntro)
$el.text(hiddenUserStoriexText)
@ -397,7 +396,7 @@ KanbanArchivedStatusIntroDirective = ->
return {link:link}
module.directive("tgKanbanArchivedStatusIntro", KanbanArchivedStatusIntroDirective)
module.directive("tgKanbanArchivedStatusIntro", ["$translate", KanbanArchivedStatusIntroDirective])
#############################################################################
@ -471,17 +470,20 @@ KanbanWipLimitDirective = ->
link = ($scope, $el, $attrs) ->
$el.disableSelection()
redrawWipLimit = ->
status = $scope.$eval($attrs.tgKanbanWipLimit)
redrawWipLimit = =>
$el.find(".kanban-wip-limit").remove()
timeout 200, ->
element = $el.find(".kanban-task")[$scope.$eval($attrs.tgKanbanWipLimit)]
timeout 200, =>
element = $el.find(".kanban-task")[status.wip_limit]
if element
angular.element(element).before("<div class='kanban-wip-limit'></div>")
$scope.$on "redraw:wip", redrawWipLimit
$scope.$on "kanban:us:move", redrawWipLimit
$scope.$on "usform:new:success", redrawWipLimit
$scope.$on "usform:bulk:success", redrawWipLimit
if status and not status.is_archived
$scope.$on "redraw:wip", redrawWipLimit
$scope.$on "kanban:us:move", redrawWipLimit
$scope.$on "usform:new:success", redrawWipLimit
$scope.$on "usform:bulk:success", redrawWipLimit
$scope.$on "$destroy", ->
$el.off()
@ -495,14 +497,14 @@ module.directive("tgKanbanWipLimit", KanbanWipLimitDirective)
## Kanban User Directive
#############################################################################
KanbanUserDirective = ($log) ->
KanbanUserDirective = ($log, $compile) ->
template = _.template("""
<figure class="avatar">
<a href="#" title="Assign User Story" <% if (!clickable) {%>class="not-clickable"<% } %>>
<a href="#" title="{{'US.ASSIGN' | translate}}" <% if (!clickable) {%>class="not-clickable"<% } %>>
<img src="<%- imgurl %>" alt="<%- name %>" class="avatar">
</a>
</figure>
""") # TODO: i18n
""")
clickable = false
@ -527,7 +529,7 @@ KanbanUserDirective = ($log) ->
else
ctx = {name: user.full_name_display, imgurl: user.photo, clickable: clickable}
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
username_label.text(ctx.name)
@ -556,4 +558,4 @@ KanbanUserDirective = ($log) ->
return {link: link, require:"ngModel"}
module.directive("tgKanbanUserAvatar", ["$log", KanbanUserDirective])
module.directive("tgKanbanUserAvatar", ["$log", "$compile", KanbanUserDirective])

View File

@ -249,15 +249,19 @@ ProjectMenuDirective = ($log, $compile, $auth, $rootscope, $tgAuth, $location, $
container.replaceWith(dom)
videoConferenceUrl = (project) ->
urlSeparator = "-"
if project.videoconferences == "appear-in"
baseUrl = "https://appear.in/"
else if project.videoconferences == "talky"
baseUrl = "https://talky.io/"
else if project.videoconferences == "jitsi"
urlSeparator = ""
baseUrl = "https://meet.jit.si/"
else
return ""
if project.videoconferences_salt
url = "#{project.slug}-#{project.videoconferences_salt}"
url = "#{project.slug}#{urlSeparator}#{project.videoconferences_salt}"
else
url = "#{project.slug}"

View File

@ -26,7 +26,7 @@ debounce = @.taiga.debounce
module = angular.module("taigaProject")
CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, $loading, lightboxService, $cacheFactory) ->
CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, $loading, lightboxService, $cacheFactory, $translate) ->
link = ($scope, $el, attrs) ->
$scope.data = {}
$scope.templates = []
@ -41,7 +41,9 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
$loading.finish(submitButton)
$rootscope.$broadcast("projects:reload")
$confirm.notify("success", "Success") #TODO: i18n
$confirm.notify("success", $translate.instant("COMMON.SAVE"))
$location.url($projectUrl.get(response))
lightboxService.close($el)
@ -126,7 +128,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
return {link:link}
module.directive("tgLbCreateProject", ["$rootScope", "$tgRepo", "$tgConfirm", "$location", "$tgNavUrls",
"$tgResources", "$projectUrl", "$tgLoading", "lightboxService", "$cacheFactory", CreateProject])
"$tgResources", "$projectUrl", "$tgLoading", "lightboxService", "$cacheFactory", "$translate", CreateProject])
#############################################################################

View File

@ -25,7 +25,7 @@ debounce = @.taiga.debounce
module = angular.module("taigaRelatedTasks", [])
RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $template) ->
RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $template, $translate) ->
templateView = $template.get("task/related-task-row.html", true)
templateEdit = $template.get("task/related-task-row-edit.html", true)
@ -79,9 +79,8 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem
$el.find('input').focus().select()
$el.on "click", ".delete-task", (event) ->
#TODO: i18n
title = $translate.instant("TASK.TITLE_DELETE_ACTION")
task = $model.$modelValue
title = "Delete Task"
message = task.subject
$confirm.askOnDelete(title, message).then (finish) ->
@ -109,7 +108,7 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem
return {link:link, require:"ngModel"}
module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", "$tgTemplate", RelatedTaskRowDirective])
module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", "$tgTemplate", "$translate", RelatedTaskRowDirective])
RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $analytics, $template) ->
template = $template.get("task/related-task-create-form.html", true)

View File

@ -24,59 +24,42 @@ taiga = @.taiga
class ResourcesService extends taiga.Service
urls = {
# Auth
"auth": "/auth"
"auth-register": "/auth/register"
"invitations": "/invitations"
"permissions": "/permissions"
"roles": "/roles"
"projects": "/projects"
"memberships": "/memberships"
"notify-policies": "/notify-policies"
"bulk-create-memberships": "/memberships/bulk_create"
"milestones": "/milestones"
"userstories": "/userstories"
"bulk-create-us": "/userstories/bulk_create"
"bulk-update-us-backlog-order": "/userstories/bulk_update_backlog_order"
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
"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"
"issues-restore": "/issues/%s/restore"
"wiki": "/wiki"
"wiki-restore": "/wiki/%s/restore"
"wiki-links": "/wiki-links"
"choices/userstory-statuses": "/userstory-statuses"
"choices/userstory-statuses/bulk-update-order": "/userstory-statuses/bulk_update_order"
"choices/points": "/points"
"choices/points/bulk-update-order": "/points/bulk_update_order"
"choices/task-statuses": "/task-statuses"
"choices/task-statuses/bulk-update-order": "/task-statuses/bulk_update_order"
"choices/issue-statuses": "/issue-statuses"
"choices/issue-statuses/bulk-update-order": "/issue-statuses/bulk_update_order"
"choices/issue-types": "/issue-types"
"choices/issue-types/bulk-update-order": "/issue-types/bulk_update_order"
"choices/priorities": "/priorities"
"choices/priorities/bulk-update-order": "/priorities/bulk_update_order"
"choices/severities": "/severities"
"choices/severities/bulk-update-order": "/severities/bulk_update_order"
"search": "/search"
"sites": "/sites"
"project-templates": "/project-templates"
"site-members": "/site-members"
"site-projects": "/site-projects"
# User
"users": "/users"
"users-password-recovery": "/users/password_recovery"
"users-change-password-from-recovery": "/users/change_password_from_recovery"
"users-change-password": "/users/change_password"
"users-change-email": "/users/change_email"
"users-cancel-account": "/users/cancel"
# User - Notification
"notify-policies": "/notify-policies"
# User - Storage
"user-storage": "/user-storage"
# Memberships
"memberships": "/memberships"
"bulk-create-memberships": "/memberships/bulk_create"
# Roles & Permissions
"roles": "/roles"
"permissions": "/permissions"
# Resolver
"resolver": "/resolver"
# Project
"projects": "/projects"
"project-templates": "/project-templates"
"project-modules": "/projects/%s/modules"
# Project Values - Choises
"userstory-statuses": "/userstory-statuses"
"points": "/points"
"task-statuses": "/task-statuses"
@ -84,11 +67,30 @@ urls = {
"issue-types": "/issue-types"
"priorities": "/priorities"
"severities": "/severities"
"project-modules": "/projects/%s/modules"
"webhooks": "/webhooks"
"webhooks-test": "/webhooks/%s/test"
"webhooklogs": "/webhooklogs"
"webhooklogs-resend": "/webhooklogs/%s/resend"
# Milestones/Sprints
"milestones": "/milestones"
# User stories
"userstories": "/userstories"
"bulk-create-us": "/userstories/bulk_create"
"bulk-update-us-backlog-order": "/userstories/bulk_update_backlog_order"
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
# Tasks
"tasks": "/tasks"
"bulk-create-tasks": "/tasks/bulk_create"
"bulk-update-task-taskboard-order": "/tasks/bulk_update_taskboard_order"
# Issues
"issues": "/issues"
"bulk-create-issues": "/issues/bulk_create"
# Wiki pages
"wiki": "/wiki"
"wiki-restore": "/wiki/%s/restore"
"wiki-links": "/wiki-links"
# History
"history/us": "/history/userstory"
@ -107,22 +109,34 @@ urls = {
"custom-attributes/issue": "/issue-custom-attributes"
"custom-attributes/task": "/task-custom-attributes"
# Custom field values
# Custom Attributess - Values
"custom-attributes-values/userstory": "/userstories/custom-attributes-values"
"custom-attributes-values/issue": "/issues/custom-attributes-values"
"custom-attributes-values/task": "/tasks/custom-attributes-values"
# Feedback
"feedback": "/feedback"
# Webhooks
"webhooks": "/webhooks"
"webhooks-test": "/webhooks/%s/test"
"webhooklogs": "/webhooklogs"
"webhooklogs-resend": "/webhooklogs/%s/resend"
# Reports - CSV
"userstories-csv": "/userstories/csv?uuid=%s"
"tasks-csv": "/tasks/csv?uuid=%s"
"issues-csv": "/issues/csv?uuid=%s"
# Search
"search": "/search"
# Export/Import
"exporter": "/exporter"
"importer": "/importer/load_dump"
# CSV
"userstories-csv": "/userstories/csv?uuid=%s"
"tasks-csv": "/tasks/csv?uuid=%s"
"issues-csv": "/issues/csv?uuid=%s"
# Feedback
"feedback": "/feedback"
# locales
"locales": "/locales"
}
# Initialize api urls service
@ -168,5 +182,6 @@ module.run([
"$tgModulesResourcesProvider",
"$tgWebhooksResourcesProvider",
"$tgWebhookLogsResourcesProvider",
"$tgLocalesResourcesProvider",
initResources
])

View File

@ -0,0 +1,38 @@
###
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2015 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/resources/locales.coffee
###
taiga = @.taiga
sizeFormat = @.taiga.sizeFormat
resourceProvider = ($repo) ->
service = {
list: -> return $repo.queryMany("locales")
}
return (instance) ->
instance.locales = service
module = angular.module("taigaResources")
module.factory("$tgLocalesResourcesProvider", ["$tgRepo", resourceProvider])

View File

@ -24,7 +24,7 @@ taiga = @.taiga
sizeFormat = @.taiga.sizeFormat
resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $rootScope) ->
resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $translate) ->
service = {}
service.get = (projectId) ->
@ -85,21 +85,31 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $rootScope) ->
maxFileSize = $config.get("maxUploadFileSize", null)
if maxFileSize and file.size > maxFileSize
errorMsg = $translate.instant("PROJECT.IMPORT.ERROR_MAX_SIZE_EXCEEDED", {
fileName: file.name
fileSize: sizeFormat(file.size)
maxFileSize: sizeFormat(maxFileSize)
})
response = {
status: 413,
data: _error_message: "'#{file.name}' (#{sizeFormat(file.size)}) is too heavy for our oompa
loompas, try it with a smaller than (#{sizeFormat(maxFileSize)})"
data: _error_message: errorMsg
}
defered.reject(response)
return defered.promise
uploadProgress = (evt) =>
percent = Math.round((evt.loaded / evt.total) * 100)
message = "Uloaded #{sizeFormat(evt.loaded)} of #{sizeFormat(evt.total)}"
message = $translate.instant("PROJECT.IMPORT.UPLOAD_IN_PROGRESS_MESSAGE", {
uploadedSize: sizeFormat(evt.loaded)
totalSize: sizeFormat(evt.total)
})
statusUpdater("in-progress", null, message, percent)
uploadComplete = (evt) =>
statusUpdater("done", "Importing Project", "This process can take a while, please keep the window open.") # i18n
statusUpdater("done",
$translate.instant("PROJECT.IMPORT.TITLE"),
$translate.instant("PROJECT.IMPORT.DESCRIPTION"))
uploadFailed = (evt) =>
statusUpdater("error")
@ -141,5 +151,5 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $rootScope) ->
module = angular.module("taigaResources")
module.factory("$tgProjectsResourcesProvider", ["$tgConfig", "$tgRepo", "$tgHttp", "$tgUrls", "$tgAuth", "$q",
resourceProvider])
module.factory("$tgProjectsResourcesProvider", ["$tgConfig", "$tgRepo", "$tgHttp", "$tgUrls", "$tgAuth",
"$q", "$translate", resourceProvider])

View File

@ -34,7 +34,7 @@ module = angular.module("taigaTaskboard")
## Sprint burndown graph directive
#############################################################################
SprintGraphDirective = ->
SprintGraphDirective = ($translate)->
redrawChart = (element, dataToDraw) ->
width = element.width()
element.height(240)
@ -64,13 +64,18 @@ SprintGraphDirective = ->
max: _.last(days)
mode: "time"
daysNames: days
axisLabel: 'Day'
axisLabel: $translate.instant("TASKBOARD.CHARTS.XAXIS_LABEL")
axisLabelUseCanvas: true
axisLabelFontSizePixels: 12
axisLabelFontFamily: 'Verdana, Arial, Helvetica, Tahoma, sans-serif'
axisLabelPadding: 5
yaxis:
min: 0
axisLabel: $translate.instant("TASKBOARD.CHARTS.YAXIS_LABEL")
axisLabelUseCanvas: true
axisLabelFontSizePixels: 12
axisLabelFontFamily: 'Verdana, Arial, Helvetica, Tahoma, sans-serif'
axisLabelPadding: 5
series:
shadowSize: 0
lines:
@ -85,14 +90,20 @@ SprintGraphDirective = ->
tooltip: true
tooltipOpts:
content: (label, xval, yval, flotItem) ->
#TODO: i18n
formattedDate = moment(xval).format("DD MMM")
formattedDate = moment(xval).format($translate.instant("TASKBOARD.CHARTS.DATE"))
roundedValue = Math.round(yval)
if flotItem.seriesIndex == 1
return "Optimal pending points for day #{formattedDate} should be #{roundedValue}"
return $translate.instant("TASKBOARD.CHARTS.OPTIMAL", {
formattedDate: formattedDate,
roundedValue: roundedValue
})
else
return "Real pending points for day #{formattedDate} is #{roundedValue}"
return $translate.instant("TASKBOARD.CHARTS.REAL", {
formattedDate: formattedDate,
roundedValue: roundedValue
})
element.empty()
element.plot(data, options).data("plot")
@ -121,5 +132,4 @@ SprintGraphDirective = ->
return {link: link}
module.directive("tgSprintGraph", SprintGraphDirective)
module.directive("tgSprintGraph", ["$translate", SprintGraphDirective])

View File

@ -23,7 +23,7 @@ taiga = @.taiga
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService) ->
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService, $translate) ->
link = ($scope, $el, attrs) ->
$scope.isNew = true
@ -40,10 +40,13 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
$scope.isNew = true
# Update texts for creation
$el.find(".button-green").html("Create") #TODO: i18n
$el.find(".title").html("New task ") #TODO: i18n
$el.find(".tag-input").val("")
create = $translate.instant("COMMON.CREATE")
$el.find(".button-green").html(create)
newTask = $translate.instant("LIGHTBOX.CREATE_EDIT_TASK.TITLE")
$el.find(".title").html(newTask + " ")
$el.find(".tag-input").val("")
lightboxService.open($el)
$scope.$on "taskform:edit", (ctx, task) ->
@ -51,10 +54,13 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
$scope.isNew = false
# Update texts for edition
$el.find(".button-green").html("Save") #TODO: i18n
$el.find(".title").html("Edit task ") #TODO: i18n
$el.find(".tag-input").val("")
save = $translate.instant("COMMON.SAVE")
edit = $translate.instant("LIGHTBOX.CREATE_EDIT_TASK.ACTION_EDIT")
$el.find(".button-green").html(save)
$el.find(".title").html(edit + " ")
$el.find(".tag-input").val("")
lightboxService.open($el)
@ -142,6 +148,7 @@ module.directive("tgLbCreateEditTask", [
"$rootScope",
"$tgLoading",
"lightboxService",
"$translate"
CreateEditTaskDirective
])

View File

@ -50,13 +50,14 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgEvents"
"$tgAnalytics",
"tgLoader"
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appTitle, @location, @navUrls,
@events, @analytics, tgLoader) ->
@events, @analytics, tgLoader, @translate) ->
bindMethods(@)
@scope.sectionName = "Taskboard"
@scope.sectionName = @translate.instant("TASKBOARD.SECTION_NAME")
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -259,7 +260,6 @@ TaskboardDirective = ($rootscope) ->
event.preventDefault()
target = angular.element(event.currentTarget)
target.toggleClass('active');
#toggleText(target, ["Hide statistics", "Show statistics"]) # TODO: i18n
$rootscope.$broadcast("taskboard:graph:toggle-visibility")
tableBodyDom = $el.find(".taskboard-table-body")

View File

@ -26,6 +26,7 @@ groupBy = @.taiga.groupBy
module = angular.module("taigaTasks")
#############################################################################
## Task Detail Controller
#############################################################################
@ -44,13 +45,14 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"$translate",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @navUrls, @analytics, tgLoader) ->
@log, @appTitle, @navUrls, @analytics, @translate, tgLoader) ->
@scope.taskRef = @params.taskref
@scope.sectionName = "Task Details"
@scope.sectionName = @translate.instant("TASK.SECTION_NAME")
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -145,7 +147,7 @@ module.controller("TaskDetailController", TaskDetailController)
## Task status display directive
#############################################################################
TaskStatusDisplayDirective = ($template) ->
TaskStatusDisplayDirective = ($template, $compile) ->
# Display if a Task is open or closed and its taskboard status.
#
# Example:
@ -165,6 +167,9 @@ TaskStatusDisplayDirective = ($template) ->
is_closed: status.is_closed
status: status
})
html = $compile(html)($scope)
$el.html(html)
$scope.$watch $attrs.ngModel, (task) ->
@ -179,14 +184,14 @@ TaskStatusDisplayDirective = ($template) ->
require: "ngModel"
}
module.directive("tgTaskStatusDisplay", ["$tgTemplate", TaskStatusDisplayDirective])
module.directive("tgTaskStatusDisplay", ["$tgTemplate", "$compile", TaskStatusDisplayDirective])
#############################################################################
## Task status button directive
#############################################################################
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $compile, $translate) ->
# Display the status of Task and you can edit it.
#
# Example:
@ -202,7 +207,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
<span class="level" style="background-color:<%- status.color %>"></span>
<span class="status-status"><%- status.name %></span>
<% if(editable){ %><span class="icon icon-arrow-bottom"></span><% }%>
<span class="level-name">status</span>
<span class="level-name" translate="COMMON.FIELDS.STATUS"></span>
<ul class="popover pop-status">
<% _.each(statuses, function(st) { %>
@ -211,7 +216,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
<% }); %>
</ul>
</div>
""") #TODO: i18n
""")
link = ($scope, $el, $attrs, $model) ->
isEditable = ->
@ -220,11 +225,12 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
render = (task) =>
status = $scope.statusById[task.status]
html = template({
html = $compile(template({
status: status
statuses: $scope.statusList
editable: isEditable()
})
}))($scope)
$el.html(html)
save = $qqueue.bindAdd (status) =>
@ -278,14 +284,15 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue) ->
}
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
TaskStatusButtonDirective])
"$compile", "$translate", TaskStatusButtonDirective])
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue) ->
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile) ->
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!">
<fieldset title="{{ 'TASK.TITLE_ACTION_IOCAINE' | translate }}">
<label for="is-iocaine"
class="button button-gray is-iocaine <% if(isEditable){ %>editable<% }; %> <% if(isIocaine){ %>active<% }; %>">
translate="TASK.ACTION_IOCAINE"
class="button button-gray is-iocaine <% if(isEditable){ %>editable<% }; %> <% if(isIocaine){ %>active<% }; %>">
Iocaine
</label>
<input type="checkbox" id="is-iocaine" name="is-iocaine"/>
@ -305,7 +312,7 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue
isIocaine: task.is_iocaine
isEditable: isEditable()
}
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
save = $qqueue.bindAdd (is_iocaine) =>
@ -347,4 +354,5 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue
require: "ngModel"
}
module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", TaskIsIocaineButtonDirective])
module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
"$compile", TaskIsIocaineButtonDirective])

View File

@ -41,18 +41,20 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgNavUrls",
"$appTitle",
"$tgAuth",
"tgLoader"
"tgLoader",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle, @auth, tgLoader) ->
@scope.sectionName = "Team"
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle, @auth, tgLoader,
@translate) ->
@scope.sectionName = "TEAM.SECTION_NAME"
promise = @.loadInitialData()
# On Success
promise.then =>
#TODO: i18n
@appTitle.set("Team - " + @scope.project.name)
text = @translate.instant("TEAM.APP_TITLE", {"projectName": @scope.project.name})
@appTitle.set(text)
# On Error
promise.then null, @.onInitialDataError.bind(@)
@ -137,6 +139,7 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
module.controller("TeamController", TeamController)
#############################################################################
## Team Filters Directive
#############################################################################
@ -148,6 +151,7 @@ TeamFiltersDirective = () ->
module.directive("tgTeamFilters", [TeamFiltersDirective])
#############################################################################
## Team Member Stats Directive
#############################################################################
@ -166,6 +170,7 @@ TeamMemberStatsDirective = () ->
module.directive("tgTeamMemberStats", TeamMemberStatsDirective)
#############################################################################
## Team Current User Directive
#############################################################################
@ -185,6 +190,7 @@ TeamMemberCurrentUserDirective = () ->
module.directive("tgTeamCurrentUser", TeamMemberCurrentUserDirective)
#############################################################################
## Team Members Directive
#############################################################################
@ -207,15 +213,18 @@ TeamMembersDirective = () ->
module.directive("tgTeamMembers", TeamMembersDirective)
#############################################################################
## Leave project Directive
#############################################################################
LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls) ->
LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls, $translate) ->
link = ($scope, $el, $attrs) ->
$scope.leave = () ->
#TODO: i18n
$confirm.ask("Leave this project", "Are you sure you want to leave the project?").then (finish) =>
leave_project_text = $translate.instant("TEAM.ACTION_LEAVE_PROJECT")
confirm_leave_project_text = $translate.instant("TEAM.CONFIRM_LEAVE_PROJECT")
$confirm.ask(leave_project_text, confirm_leave_project_text).then (finish) =>
promise = $rs.projects.leave($attrs.projectid)
promise.then =>
@ -233,10 +242,17 @@ LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls) ->
link: link
}
module.directive("tgLeaveProject", ["$tgRepo", "$tgConfirm", "$tgLocation", "$tgResources", "$tgNavUrls", LeaveProjectDirective])
module.directive("tgLeaveProject", ["$tgRepo", "$tgConfirm", "$tgLocation", "$tgResources", "$tgNavUrls", "$translate",
LeaveProjectDirective])
module.filter 'membersRoleFilter', () ->
(input, filtersRole) ->
if filtersRole?
return _.filter(input, {role: filtersRole.id})
return input
#############################################################################
## Team Filters
#############################################################################
membersFilter = ->
return (members, filtersQ, filtersRole) ->
return _.filter members, (m) -> (not filtersRole or m.role == filtersRole.id) and
(not filtersQ or m.full_name.search(new RegExp(filtersQ, "i")) >= 0)
module.filter('membersFilter', membersFilter)

View File

@ -42,11 +42,12 @@ class UserChangePasswordController extends mixOf(taiga.Controller, taiga.PageMix
"$q",
"$tgLocation",
"$tgNavUrls",
"$tgAuth"
"$tgAuth",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) ->
@scope.sectionName = "Change Password" #i18n
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth, @translate) ->
@scope.sectionName = @translate.instant("CHANGE_PASSWORD.SECTION_NAME")
@scope.project = {}
@scope.user = @auth.getUser()
@ -74,13 +75,13 @@ module.controller("UserChangePasswordController", UserChangePasswordController)
## User ChangePassword Directive
#############################################################################
UserChangePasswordDirective = ($rs, $confirm, $loading) ->
UserChangePasswordDirective = ($rs, $confirm, $loading, $translate) ->
link = ($scope, $el, $attrs, ctrl) ->
submit = debounce 2000, (event) =>
event.preventDefault()
if $scope.newPassword1 != $scope.newPassword2
$confirm.notify('error', "The passwords dosn't match")
$confirm.notify('error', $translate.instant("CHANGE_PASSWORD.ERROR_PASSWORD_MATCH"))
return
$loading.start(submitButton)

View File

@ -41,17 +41,21 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin)
"$q",
"$tgLocation",
"$tgNavUrls",
"$tgAuth"
"$tgAuth",
"$translate"
]
constructor: (@scope, @rootscope, @config, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) ->
@scope.sectionName = "User Profile" #i18n
constructor: (@scope, @rootscope, @config, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth, @translate) ->
@scope.sectionName = "USER_SETTINGS.MENU.SECTION_TITLE"
@scope.project = {}
@scope.user = @auth.getUser()
@scope.lang = @getLan()
maxFileSize = @config.get("maxUploadFileSize", null)
if maxFileSize
@scope.maxFileSizeMsg = "[Max, size: #{sizeFormat(maxFileSize)}" # TODO: i18n
@translate("USER_SETTINGS.AVATAR_MAX_SIZE", {"maxFileSize": sizeFormat(maxFileSize)}).then (text) =>
@scope.maxFileSizeMsg = text
promise = @.loadInitialData()
@ -63,16 +67,26 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin)
@scope.$emit('project:loaded', project)
return project
loadLocales: ->
return @rs.locales.list().then (locales) =>
@scope.locales = locales
return locales
loadInitialData: ->
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
@scope.projectId = data.project
return data
return promise.then(=> @.loadProject())
return @q.all([promise.then(=> @.loadProject()),
@.loadLocales()])
openDeleteLightbox: ->
@rootscope.$broadcast("deletelightbox:new", @scope.user)
getLan: ->
return @scope.user.lang ||
@translate.preferredLanguage()
module.controller("UserSettingsController", UserSettingsController)
@ -80,7 +94,7 @@ module.controller("UserSettingsController", UserSettingsController)
## User Profile Directive
#############################################################################
UserProfileDirective = ($confirm, $auth, $repo) ->
UserProfileDirective = ($confirm, $auth, $repo, $translate) ->
link = ($scope, $el, $attrs) ->
submit = debounce 2000, (event) =>
event.preventDefault()
@ -89,14 +103,14 @@ UserProfileDirective = ($confirm, $auth, $repo) ->
return if not form.validate()
changeEmail = $scope.user.isAttributeModified("email")
$scope.user.lang = $scope.lang
onSuccess = (data) =>
$auth.setUser(data)
if changeEmail
$confirm.success("<strong>Check your inbox!</strong><br />
We have sent a mail to your account<br />
with the instructions to set your new address") #TODO: i18n
text = $translate.instant("USER_PROFILE.CHANGE_EMAIL_SUCCESS")
$confirm.success(text)
else
$confirm.notify('success')
@ -113,7 +127,7 @@ UserProfileDirective = ($confirm, $auth, $repo) ->
return {link:link}
module.directive("tgUserProfile", ["$tgConfirm", "$tgAuth", "$tgRepo", UserProfileDirective])
module.directive("tgUserProfile", ["$tgConfirm", "$tgAuth", "$tgRepo", "$translate", UserProfileDirective])
#############################################################################

View File

@ -45,7 +45,7 @@ class UserNotificationsController extends mixOf(taiga.Controller, taiga.PageMixi
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) ->
@scope.sectionName = "Email Notifications" #i18n
@scope.sectionName = "USER_SETTINGS.NOTIFICATIONS.SECTION_NAME"
@scope.project = {}
@scope.user = @auth.getUser()
@ -94,7 +94,7 @@ module.directive("tgUserNotifications", UserNotificationsDirective)
## User Notifications List Directive
#############################################################################
UserNotificationsListDirective = ($repo, $confirm) ->
UserNotificationsListDirective = ($repo, $confirm, $compile) ->
template = _.template("""
<% _.each(notifyPolicies, function (notifyPolicy, index) { %>
<div class="policy-table-row" data-index="<%- index %>">
@ -104,7 +104,8 @@ UserNotificationsListDirective = ($repo, $confirm) ->
<input type="radio"
name="policy-<%- notifyPolicy.id %>" id="policy-all-<%- notifyPolicy.id %>"
value="2" <% if (notifyPolicy.notify_level == 2) { %>checked="checked"<% } %>/>
<label for="policy-all-<%- notifyPolicy.id %>">All</label>
<label for="policy-all-<%- notifyPolicy.id %>"
translate="USER_SETTINGS.NOTIFICATIONS.OPTION_ALL"></label>
</fieldset>
</div>
<div class="policy-table-involved">
@ -112,7 +113,8 @@ UserNotificationsListDirective = ($repo, $confirm) ->
<input type="radio"
name="policy-<%- notifyPolicy.id %>" id="policy-involved-<%- notifyPolicy.id %>"
value="1" <% if (notifyPolicy.notify_level == 1) { %>checked="checked"<% } %> />
<label for="policy-involved-<%- notifyPolicy.id %>">Involved</label>
<label for="policy-involved-<%- notifyPolicy.id %>"
translate="USER_SETTINGS.NOTIFICATIONS.OPTION_INVOLVED"></label>
</fieldset>
</div>
<div class="policy-table-none">
@ -120,7 +122,8 @@ UserNotificationsListDirective = ($repo, $confirm) ->
<input type="radio"
name="policy-<%- notifyPolicy.id %>" id="policy-none-<%- notifyPolicy.id %>"
value="3" <% if (notifyPolicy.notify_level == 3) { %>checked="checked"<% } %> />
<label for="policy-none-<%- notifyPolicy.id %>">None</label>
<label for="policy-none-<%- notifyPolicy.id %>"
translate="USER_SETTINGS.NOTIFICATIONS.OPTION_NONE"></label>
</fieldset>
</div>
</div>
@ -130,13 +133,17 @@ UserNotificationsListDirective = ($repo, $confirm) ->
link = ($scope, $el, $attrs) ->
render = ->
$el.off()
$el.html(template({notifyPolicies: $scope.notifyPolicies}))
ctx = {notifyPolicies: $scope.notifyPolicies}
html = template(ctx)
$el.html($compile(html)($scope))
$el.on "change", "input[type=radio]", (event) ->
target = angular.element(event.currentTarget)
policyIndex = target.parents(".policy-table-row").data('index')
policy = $scope.notifyPolicies[policyIndex]
prev_level = policy.notify_level
policy.notify_level = parseInt(target.val(), 10)
@ -145,7 +152,9 @@ UserNotificationsListDirective = ($repo, $confirm) ->
onError = ->
$confirm.notify("error")
target.parents(".policy-table-row").find("input[value=#{prev_level}]").prop("checked", true)
target.parents(".policy-table-row")
.find("input[value=#{prev_level}]")
.prop("checked", true)
$repo.save(policy).then(onSuccess, onError)
@ -156,4 +165,5 @@ UserNotificationsListDirective = ($repo, $confirm) ->
return {link:link}
module.directive("tgUserNotificationsList", ["$tgRepo", "$tgConfirm", UserNotificationsListDirective])
module.directive("tgUserNotificationsList", ["$tgRepo", "$tgConfirm", "$compile",
UserNotificationsListDirective])

View File

@ -45,13 +45,14 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"$translate",
"tgLoader"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appTitle, @navUrls, @analytics, tgLoader) ->
@log, @appTitle, @navUrls, @analytics, @translate, tgLoader) ->
@scope.usRef = @params.usref
@scope.sectionName = "User Story Details"
@scope.sectionName = @translate.instant("US.SECTION_NAME")
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -178,7 +179,7 @@ module.controller("UserStoryDetailController", UserStoryDetailController)
## User story status display directive
#############################################################################
UsStatusDisplayDirective = ($template) ->
UsStatusDisplayDirective = ($template, $compile) ->
# Display if a US is open or closed and its kanban status.
#
# Example:
@ -192,10 +193,14 @@ UsStatusDisplayDirective = ($template) ->
link = ($scope, $el, $attrs) ->
render = (us) ->
status = $scope.statusById[us.status]
html = template({
is_closed: us.is_closed
status: $scope.statusById[us.status]
status: status
})
html = $compile(html)($scope)
$el.html(html)
$scope.$watch $attrs.ngModel, (us) ->
@ -210,14 +215,14 @@ UsStatusDisplayDirective = ($template) ->
require: "ngModel"
}
module.directive("tgUsStatusDisplay", ["$tgTemplate", UsStatusDisplayDirective])
module.directive("tgUsStatusDisplay", ["$tgTemplate", "$compile", UsStatusDisplayDirective])
#############################################################################
## User story related tasts progress splay Directive
#############################################################################
UsTasksProgressDisplayDirective = ($template) ->
UsTasksProgressDisplayDirective = ($template, $compile) ->
# Display a progress bar with the stats of completed tasks.
#
# Example:
@ -227,8 +232,6 @@ UsTasksProgressDisplayDirective = ($template) ->
# - Task object list (ng-model)
# - scope.taskStatusById object
template = $template.get("us/us-task-progress.html", true)
link = ($scope, $el, $attrs) ->
render = (tasks) ->
totalTasks = tasks.length
@ -236,12 +239,14 @@ UsTasksProgressDisplayDirective = ($template) ->
progress = if totalTasks > 0 then 100 * totalClosedTasks / totalTasks else 0
html = template({
_.assign($scope, {
totalTasks: totalTasks
totalClosedTasks: totalClosedTasks
progress: progress
progress: progress,
style: {
width: progress + "%"
}
})
$el.html(html)
$scope.$watch $attrs.ngModel, (tasks) ->
render(tasks) if tasks?
@ -250,12 +255,14 @@ UsTasksProgressDisplayDirective = ($template) ->
$el.off()
return {
templateUrl: "us/us-task-progress.html"
link: link
restrict: "EA"
require: "ngModel"
scope: true
}
module.directive("tgUsTasksProgressDisplay", ["$tgTemplate", UsTasksProgressDisplayDirective])
module.directive("tgUsTasksProgressDisplay", ["$tgTemplate", "$compile", UsTasksProgressDisplayDirective])
#############################################################################
@ -350,7 +357,7 @@ module.directive("tgUsStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$t
## User story team requirements button directive
#############################################################################
UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $template) ->
UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $template, $compile) ->
template = $template.get("us/us-team-requirement-button.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -367,6 +374,8 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qq
isRequired: us.team_requirement
}
html = template(ctx)
html = $compile(html)($scope)
$el.html(html)
save = $qqueue.bindAdd (team_requirement) =>
@ -407,13 +416,13 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qq
require: "ngModel"
}
module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", UsTeamRequirementButtonDirective])
module.directive("tgUsTeamRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile", UsTeamRequirementButtonDirective])
#############################################################################
## User story client requirements button directive
#############################################################################
UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $template) ->
UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $template, $compile) ->
template = $template.get("us/us-client-requirement-button.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -429,7 +438,7 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $
canEdit: canEdit()
isRequired: us.client_requirement
}
html = template(ctx)
html = $compile(template(ctx))($scope)
$el.html(html)
save = $qqueue.bindAdd (client_requirement) =>
@ -467,5 +476,5 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $
require: "ngModel"
}
module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate",
module.directive("tgUsClientRequirementButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", "$tgTemplate", "$compile",
UsClientRequirementButtonDirective])

View File

@ -49,11 +49,12 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$appTitle",
"$tgNavUrls",
"$tgAnalytics",
"tgLoader"
"tgLoader",
"$translate"
]
constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location,
@filter, @log, @appTitle, @navUrls, @analytics, tgLoader) ->
@filter, @log, @appTitle, @navUrls, @analytics, tgLoader, @translate) ->
@scope.projectSlug = @params.pslug
@scope.wikiSlug = @params.slug
@scope.sectionName = "Wiki"
@ -111,8 +112,7 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@q.all([@.loadWikiLinks(), @.loadWiki()])
delete: ->
# TODO: i18n
title = "Delete Wiki Page"
title = @translate.instant("WIKI.DELETE_LIGHTBOX_TITLE")
message = unslugify(@scope.wiki.slug)
@confirm.askOnDelete(title, message).then (finish) =>
@ -135,7 +135,7 @@ module.controller("WikiDetailController", WikiDetailController)
## Wiki Summary Directive
#############################################################################
WikiSummaryDirective = ($log, $template) ->
WikiSummaryDirective = ($log, $template, $compile, $translate) ->
template = $template.get("wiki/wiki-summary.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -152,10 +152,11 @@ WikiSummaryDirective = ($log, $template) ->
ctx = {
totalEditions: wiki.editions
lastModifiedDate: moment(wiki.modified_date).format("DD MMM YYYY HH:mm")
lastModifiedDate: moment(wiki.modified_date).format($translate.instant("WIKI.DATETIME"))
user: user
}
html = template(ctx)
html = $compile(html)($scope)
$el.html(html)
$scope.$watch $attrs.ngModel, (wikiPage) ->
@ -171,7 +172,7 @@ WikiSummaryDirective = ($log, $template) ->
require: "ngModel"
}
module.directive("tgWikiSummary", ["$log", "$tgTemplate", WikiSummaryDirective])
module.directive("tgWikiSummary", ["$log", "$tgTemplate", "$compile", "$translate", WikiSummaryDirective])
#############################################################################
@ -215,7 +216,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
if not wiki.id?
$analytics.trackEvent("wikipage", "create", "create wiki page", 1)
$model.$setViewValue wikiPage
$model.$setViewValue wikiPage.clone()
$confirm.notify("success")
switchToReadMode()

View File

@ -34,7 +34,7 @@ module = angular.module("taigaWiki")
## Wiki Main Directive
#############################################################################
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $loading, $template) ->
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $loading, $template, $compile, $translate) ->
template = $template.get("wiki/wiki-nav.html", true)
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
@ -53,6 +53,8 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $l
deleteWikiLinkPermission: deleteWikiLinkPermission
})
html = $compile(html)($scope)
$el.off()
$el.html(html)
@ -80,8 +82,7 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $l
target = angular.element(event.currentTarget)
linkId = target.parents('.wiki-link').data('id')
# TODO: i18n
title = "Delete Wiki Link"
title = $translate.instant("WIKI.DELETE_LIGHTBOX_TITLE")
message = $scope.wikiLinks[linkId].title
$confirm.askOnDelete(title, message).then (finish) =>
@ -143,4 +144,4 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $analytics, $l
return {link:link}
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls",
"$tgAnalytics", "$tgLoading", "$tgTemplate", WikiNavDirective])
"$tgAnalytics", "$tgLoading", "$tgTemplate", "$compile", "$translate", WikiNavDirective])

11
app/js/jquery.ui.touch-punch.min.js vendored Normal file
View File

@ -0,0 +1,11 @@
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 20112014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);

View File

@ -1,63 +0,0 @@
{
"checksley": {
"defaultMessage": "This value seems to be invalid.",
"type-email": "This value should be a valid email.",
"type-url": "This value should be a valid url.",
"type-urlstrict": "This value should be a valid url.",
"type-number": "This value should be a valid number.",
"type-digits": "This value should be digits.",
"type-dateIso": "This value should be a valid date (YYYY-MM-DD).",
"type-alphanum": "This value should be alphanumeric.",
"type-phone": "This value should be a valid phone number.",
"notnull": "This value should not be null.",
"notblank": "This value should not be blank.",
"required": "This value is required.",
"regexp": "This value seems to be invalid.",
"min": "This value should be greater than or equal to %s.",
"max": "This value should be lower than or equal to %s.",
"range": "This value should be between %s and %s.",
"minlength": "This value is too short. It should have %s characters or more.",
"maxlength": "This value is too long. It should have %s characters or less.",
"rangelength": "This value length is invalid. It should be between %s and %s characters long.",
"mincheck": "You must select at least %s choices.",
"maxcheck": "You must select %s choices or less.",
"rangecheck": "You must select between %s and %s choices.",
"equalto": "This value should be the same."
},
"common": {
"subject": "Subject",
"save": "Save",
"blocked": "Blocked",
"cancel": "Cancel",
"status": "Status",
"new-bulk": "New bulk insert",
"one-item-line": "One item per line..."
},
"pagination": {
"next": "Next",
"prev": "Previous"
},
"markdown-editor": {
"heading-1": "First Level Heading",
"heading-2": "Second Level Heading",
"heading-3": "Third Level Heading",
"bold": "Bold",
"italic": "Italic",
"strike": "Strike",
"bulleted-list": "Bulleted List",
"numeric-list": "Numeric List",
"picture": "Picture",
"link": "Link",
"quotes": "Quotes",
"code-block": "Code Block / Code",
"preview": "Preview",
"help": "Help",
"placeholder": "Your title here...",
"link-placeholder": "Your text to link here..."
},
"us": {
"title-new": "New User Story",
"team-requirement": "Team Requirement",
"client-requirement": "Client Requirement"
}
}

1091
app/locales/locale-ca.json Normal file

File diff suppressed because it is too large Load Diff

1093
app/locales/locale-en.json Normal file

File diff suppressed because it is too large Load Diff

1091
app/locales/locale-es.json Normal file

File diff suppressed because it is too large Load Diff

1091
app/locales/locale-fi.json Normal file

File diff suppressed because it is too large Load Diff

1091
app/locales/locale-fr.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,5 @@
###
# 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>
# Copyright (C) 2015 Taiga Agile LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -16,7 +14,7 @@
# 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/locales.coffee
# File: components.module.coffee
###
module = angular.module("taigaLocales", [])
angular.module("taigaComponents", [])

View File

@ -2,7 +2,7 @@ ul.paginator
<% if (showPrevious) { %>
li.previous
a(href="", class="previous next_prev_button", class="disabled")
span(i18next="pagination.prev") Prev
span(translate="PAGINATION.PREVIOUS")
<% } %>
<% _.each(pages, function(item) { %>
@ -19,5 +19,5 @@ ul.paginator
<% if (showNext) { %>
li.next
a(href="", class="next next_prev_button", class="disabled")
span(i18next="pagination.next") Next
span(translate="PAGINATION.NEXT")
<% } %>

View File

@ -1,5 +1,5 @@
.check
input(type="checkbox", id!="<%- inputId %>")
div
span.check-text.check-yes Yes
span.check-text.check-no No
span.check-text.check-yes(translate="COMMON.YES")
span.check-text.check-no(translate="COMMON.NO")

View File

@ -1,5 +1,7 @@
doctype html
div.wrapper.memberships(ng-controller="MembershipsController as ctrl",
ng-init="section='admin'", tg-memberships)
ng-init="section='admin'; sectionName='ADMIN.MEMBERSHIPS.TITLE'", tg-memberships)
sidebar.menu-secondary.sidebar(tg-admin-navigation="memberships")
include ../includes/modules/admin-menu
@ -9,8 +11,9 @@ div.wrapper.memberships(ng-controller="MembershipsController as ctrl",
include ../includes/components/mainTitle
.action-buttons
a.button-green(title="Add new member" href="" ng-click="ctrl.addNewMembers()")
span.text + New member
a.button-green(href="", title="{{ ADMIN.MEMBERSHIPS.ADD_BUTTON_TITLE | translate }}",
ng-click="ctrl.addNewMembers()")
span.text(translate="ADMIN.MEMBERSHIPS.ADD_BUTTON")
include ../includes/modules/admin/admin-membership-table

View File

@ -1,5 +1,7 @@
doctype html
div.wrapper(tg-project-default-values, ng-controller="ProjectProfileController as ctrl",
ng-init="section='admin'; sectionName='Default values'")
ng-init="section='admin'; sectionName='ADMIN.PROJECT_DEFAULT_VALUES.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
include ../includes/modules/admin-menu
@ -9,7 +11,6 @@ div.wrapper(tg-project-default-values, ng-controller="ProjectProfileController a
section.main.admin-common
header
include ../includes/components/mainTitle
p.total Default Values
p.admin-subtitle(translate="ADMIN.PROJECT_DEFAULT_VALUES.SUBTITLE")
include ../includes/modules/admin/default-values

View File

@ -1,5 +1,8 @@
doctype html
div.wrapper(ng-controller="ProjectProfileController as ctrl",
ng-init="section='admin'; sectionName='Export'")
ng-init="section='admin'; sectionName='ADMIN.PROJECT_EXPORT.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
include ../includes/modules/admin-menu
@ -9,18 +12,18 @@ div.wrapper(ng-controller="ProjectProfileController as ctrl",
section.main.admin-common(tg-project-export)
header
include ../includes/components/mainTitle
p.admin-subtitle Export your project to save a backup or to create a new one based on this.
p.admin-subtitle(translate="ADMIN.PROJECT_EXPORT.SUBTITLE")
div.admin-project-export-buttons
a.button-green.button-export(href="", title="Export your project")
span Export
a.button-green.button-export(href="", title="{{ 'ADMIN.PROJECT_EXPORT.EXPORT_BUTTON_TITLE' | translate }}")
span(translate="ADMIN.PROJECT_EXPORT.EXPORT_BUTTON")
div.admin-project-export-result.hidden
div.spin.hidden
img(src="/svg/spinner-circle.svg", alt="loading...")
img(src="/svg/spinner-circle.svg", alt="{{ 'COMMON.LOADING' | translate }}")
h3.result-title
p.result-message
a.help-button(href="https://taiga.io/support/import-export-projects/", target="_blank")
span.icon.icon-help
span Do you need help? Check out our support page!
span(translate="ADMIN.HELP")

View File

@ -1,5 +1,7 @@
doctype html
div.wrapper(tg-project-modules, ng-controller="ProjectProfileController as ctrl",
ng-init="section='admin'; sectionName='Modules'")
ng-init="section='admin'; sectionName='ADMIN.MODULES.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
include ../includes/modules/admin-menu
@ -15,76 +17,76 @@ div.wrapper(tg-project-modules, ng-controller="ProjectProfileController as ctrl"
div.icon.icon-backlog
div.desc
p
span Backlog
| Manage your user stories to maintain an organized view of upcoming and prioritized work.
span.title(translate="ADMIN.MODULES.BACKLOG")
span(translate="ADMIN.MODULES.BACKLOG_DESCRIPTION")
div.activate
input.activate-input(type="checkbox", id="functionality-backlog",
ng-model="project.is_backlog_activated")
label.button.button-gray(ng-switch="project.is_backlog_activated",
for="functionality-backlog")
span(ng-switch-when="true") Disable
span(ng-switch-when="false") Enable
span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE")
span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE")
div.functionality(ng-class="{true:'active', false:''}[project.is_kanban_activated]")
div.icon.icon-kanban
div.desc
p
span Kanban
| Organize your project in a lean way with this board.
span.title(translate="ADMIN.MODULES.KANBAN")
span(translate="ADMIN.MODULES.KANBAN_DESCRIPTION")
div.activate
input.activate-input(type="checkbox", id="functionality-kanban",
ng-model="project.is_kanban_activated")
label.button.button-gray(ng-switch="project.is_kanban_activated",
for="functionality-kanban")
span(ng-switch-when="true") Disable
span(ng-switch-when="false") Enable
span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE")
span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE")
div.functionality(ng-class="{true:'active', false:''}[project.is_issues_activated]")
div.icon.icon-issues
div.desc
p
span Issues
| Track the bugs, questions and enhancements related to your project. Don't miss anything!
span.title(translate="ADMIN.MODULES.ISSUES")
span(translate="ADMIN.MODULES.ISSUES_DESCRIPTION")
div.activate
input.activate-input(type="checkbox", id="functionality-issues",
ng-model="project.is_issues_activated")
label.button.button-gray(ng-switch="project.is_issues_activated",
for="functionality-issues")
span(ng-switch-when="true") Disable
span(ng-switch-when="false") Enable
span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE")
span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE")
div.functionality(ng-class="{true:'active', false:''}[project.is_wiki_activated]")
div.icon.icon-wiki
div.desc
p
span Wiki
| Add, modify, or delete content in collaboration with others. This is the right place for your project documentation.
span.title(translate="ADMIN.MODULES.WIKI")
span(translate="ADMIN.MODULES.WIKI_DESCRIPTION")
div.activate
input.activate-input(type="checkbox", id="functionality-wiki",
ng-model="project.is_wiki_activated")
label.button.button-gray(ng-switch="project.is_wiki_activated",
for="functionality-wiki")
span(ng-switch-when="true") Disable
span(ng-switch-when="false") Enable
span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE")
span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE")
div.functionality(ng-class="{true:'active', false:''}[isVideoconferenceActivated]")
div.icon.icon-video
div.desc
p
span Meet Up
| Choose your videoconference system. Even developers need face to face contact.
span.title(translate="ADMIN.MODULES.MEETUP")
span(translate="ADMIN.MODULES.MEETUP_DESCRIPTION")
div.activate
input.activate-input(type="checkbox", id="functionality-video",
ng-model="isVideoconferenceActivated")
label.button.button-gray(ng-switch="isVideoconferenceActivated",
for="functionality-video")
span(ng-switch-when="true") Disable
span(ng-switch-when="false") Enable
span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE")
span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE")
div.videoconference-attributes.hidden
select(ng-model="project.videoconferences",
ng-options="e.id as e.name for e in [{'id':'appear-in', 'name':'AppearIn'},{'id':'talky', 'name': 'Talky'}]")
option(value="") Select a videoconference system
ng-options="e.id as e.name for e in [{'id':'appear-in', 'name':'AppearIn'},{'id':'jitsi', 'name': 'Jitsi'},{'id':'talky', 'name': 'Talky'}]")
option(value="", translate="ADMIN.MODULES.SELECT_VIDEOCONFERENCE")
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")
button.button-green.submit-button(type="submit", title="Save") Save
placeholder="{{'ADMIN.MODULES.SALT_CHAT_ROOM' | translate}}")
button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.SAVE")

View File

@ -1,5 +1,7 @@
doctype html
div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl",
ng-init="section='admin'; sectionName='Project details'")
ng-init="section='admin'; sectionName='ADMIN.PROJECT_PROFILE.PROJECT_DETAILS'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
include ../includes/modules/admin-menu
@ -12,29 +14,33 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl"
form
fieldset
label(for="project-name") Project Name
input(type="text", name="name", placeholder="Project name", id="project-name",
label(for="project-name", translate="ADMIN.PROJECT_PROFILE.PROJECT_NAME")
input(type="text", name="name", placeholder="{{'ADMIN.PROJECT_PROFILE.PROJECT_NAME' | translate}}", id="project-name",
ng-model="project.name", data-required="true", maxlength="45")
fieldset
label(for="project-slug") Project Slug
input(type="text", name="slug", placeholder="Slug", id="project-slug",
label(for="project-slug", translate="ADMIN.PROJECT_PROFILE.PROJECT_SLUG")
input(type="text", name="slug", placeholder="{{'ADMIN.PROJECT_PROFILE.PROJECT_SLUG' | translate}}", id="project-slug",
ng-model="project.slug", data-required="true")
fieldset
label(for="project-sprints") Number of sprints
input(type="number", name="total_milestones", min="0", placeholder="Number of sprints",
label(for="project-sprints", translate="ADMIN.PROJECT_PROFILE.NUMBER_SPRINTS")
input(type="number", name="total_milestones", min="0", placeholder="{{'ADMIN.PROJECT_PROFILE.NUMBER_SPRINTS' | translate}}",
id="project-sprints", ng-model="project.total_milestones", data-type="digits")
fieldset
label(for="total-story-points") Number of US points
input(type="number", name="total_story_points", min="0", placeholder="Number of US points",
label(for="total-story-points", translate="ADMIN.PROJECT_PROFILE.NUMBER_US_POINTS")
input(type="number", name="total_story_points", min="0", placeholder="{{'ADMIN.PROJECT_PROFILE.NUMBER_US_POINTS' | translate}}",
id="total-story-points", ng-model="project.total_story_points",
data-type="digits", data-required="true")
fieldset
label(for="project-description") Description
textarea(name="description", placeholder="Description", id="project-description",
label(for="tags", translate="ADMIN.PROJECT_PROFILE.TAGS")
div.tags-block(tg-lb-tag-line, ng-model="project.tags")
fieldset
label(for="project-description", translate="ADMIN.PROJECT_PROFILE.DESCRIPTION")
textarea(name="description", ng-attr-placeholder="{{'ADMIN.PROJECT_PROFILE.DESCRIPTION' | translate}}", id="project-description",
ng-model="project.description", data-required="true")
div
@ -42,14 +48,14 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl"
div
input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="false")
label.trans-button(for="public-project")
span Public Project
span(translate="ADMIN.PROJECT_PROFILE.PUBLIC_PROJECT")
div
input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="true")
label.trans-button(for="private-project")
span Private Project
span(translate="ADMIN.PROJECT_PROFILE.PRIVATE_PROJECT")
button.button-green.submit-button(type="submit", title="Save") Save
a.delete-project(href="", title="Delete this project", ng-click="ctrl.openDeleteLightbox()") Delete this project
button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.SAVE")
a.delete-project(href="", title="{{'ADMIN.PROJECT_PROFILE.DELETE' | translate}}", ng-click="ctrl.openDeleteLightbox()", translate="ADMIN.PROJECT_PROFILE.DELETE")
div.lightbox.lightbox-delete-project(tg-lb-delete-project)
include ../includes/modules/lightbox-delete-project

View File

@ -1,5 +1,7 @@
doctype html
div.wrapper(ng-controller="ProjectProfileController as ctrl",
ng-init="section='admin'; sectionName='Reports'")
ng-init="section='admin'; sectionName='ADMIN.REPORTS.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-profile")
include ../includes/modules/admin-menu
@ -9,24 +11,15 @@ div.wrapper(ng-controller="ProjectProfileController as ctrl",
section.main.admin-common(tg-project-export)
header
include ../includes/components/mainTitle
p.admin-subtitle Export your project data in CSV format and make your own reports.
p.admin-subtitle(translate="ADMIN.REPORTS.SUBTITLE")
p Download a CSV file or copy the generated URL and open it in your favourite text editor or spreadsheet to make your own project data reports. You will be able to visualize and analize all your data easily.
p(translate="ADMIN.REPORTS.DESCRIPTION")
- var csvType = "US";
- var controller = "CsvExporterUserstoriesController";
div.admin-attributes-section
include ../includes/modules/admin/project-csv
div.admin-attributes-section(tg-csv-us)
div.admin-attributes-section(tg-csv-task)
div.admin-attributes-section(tg-csv-issue)
- var csvType = "Task";
- var controller = "CsvExporterTasksController";
div.admin-attributes-section
include ../includes/modules/admin/project-csv
- var csvType = "Issues";
- var controller = "CsvExporterIssuesController";
div.admin-attributes-section
include ../includes/modules/admin/project-csv
div
a.help-button(href="https://taiga.io/support/csv-reports/", target="_blank")
span.icon.icon-help
span How to use this on my own spreadsheet?
span(translate="ADMIN.REPORTS.HELP")

View File

@ -1,4 +1,7 @@
div.wrapper(ng-controller="ProjectValuesSectionController")
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController",
ng-init="sectionName='ADMIN.CUSTOM_FIELDS.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,25 +10,19 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the custom fields for your user stories, tasks and issues
p.admin-subtitle(translate="ADMIN.CUSTOM_FIELDS.SUBTITLE")
div.admin-attributes-section(tg-project-custom-attributes,
ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="type='userstory';")
- var customFieldSectionTitle = "User stories custom fields"
- var customFieldButtonTitle = "Add a custom field in user stories"
ng-init="type='userstory'; customFieldSectionTitle='ADMIN.CUSTOM_FIELDS.US_DESCRIPTION'; customFieldButtonTitle='ADMIN.CUSTOM_FIELDS.US_ADD'")
include ../includes/modules/admin/admin-custom-attributes
div.admin-attributes-section(tg-project-custom-attributes,
ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="type='task';")
- var customFieldSectionTitle = "Tasks custom fields"
- var customFieldButtonTitle = "Add a custom field in tasks"
ng-init="type='task'; customFieldSectionTitle='ADMIN.CUSTOM_FIELDS.TASK_DESCRIPTION'; customFieldButtonTitle='ADMIN.CUSTOM_FIELDS.TASK_ADD'")
include ../includes/modules/admin/admin-custom-attributes
div.admin-attributes-section(tg-project-custom-attributes,
ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="type='issue';")
- var customFieldSectionTitle = "Issues custom fields"
- var customFieldButtonTitle = "Add a custom field in issues"
ng-init="type='issue'; customFieldSectionTitle='ADMIN.CUSTOM_FIELDS.ISSUE_DESCRIPTION'; customFieldButtonTitle='ADMIN.CUSTOM_FIELDS.ISSUE_ADD'")
include ../includes/modules/admin/admin-custom-attributes

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,9 +9,10 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the points your user stories could be estimated to
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_POINTS.SUBTITLE")
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='points'; sectionName='Us points'",
ng-init="section='admin'; resource='userstories'; type='points'; sectionName='ADMIN.PROJECT_VALUES_POINTS.TITLE'",
objName="points",
type="points")
include ../includes/modules/admin/project-points

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,9 +9,10 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the priorities your issues will have
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_PRIORITIES.SUBTITLE")
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='Issue priorities'; objName='priority'",
ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='ADMIN.PROJECT_VALUES_PRIORITIES.TITLE';",
objName="priorities",
type="priorities")
include ../includes/modules/admin/project-types

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,9 +9,10 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the severities your issues will have
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_SEVERITIES.SUBTITLE")
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='severities'; sectionName='Issue severities'; objName='severity'",
ng-init="section='admin'; resource='issues'; type='severities'; sectionName='ADMIN.PROJECT_VALUES_SEVERITIES.TITLE';",
objName="severities",
type="severities")
include ../includes/modules/admin/project-types

View File

@ -1,4 +1,7 @@
div.wrapper(ng-controller="ProjectValuesSectionController")
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController",
ng-init="section='admin'; sectionName='ADMIN.PROJECT_VALUES_STATUS.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,19 +10,22 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the statuses your user stories, tasks and issues will go through
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_STATUS.SUBTITLE")
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='userstory-statuses'; sectionName='Us Statuses'",
type="userstory-statuses")
div.admin-attributes-section(tg-project-values, type="userstory-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='userstory-statuses'; sectionName='ADMIN.PROJECT_VALUES_STATUS.US_TITLE'",
objName="status")
include ../includes/modules/admin/project-us-status
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='tasks'; type='task-statuses'; sectionName='Task Statuses'",
type="task-statuses")
div.admin-attributes-section(tg-project-values, type="task-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='tasks'; type='task-statuses'; sectionName='ADMIN.PROJECT_VALUES_STATUS.TASK_TITLE'"
objName="status")
include ../includes/modules/admin/project-status
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='issue-statuses'; sectionName='Issue Statuses'",
type="issue-statuses")
div.admin-attributes-section(tg-project-values, type="issue-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='issue-statuses'; sectionName='ADMIN.PROJECT_VALUES_STATUS.ISSUE_TITLE'",
objName="status")
include ../includes/modules/admin/project-status

View File

@ -1,4 +1,7 @@
div.wrapper(ng-controller="ProjectValuesSectionController")
doctype html
div.wrapper(ng-controller="ProjectValuesSectionController"
ng-init="sectionName='ADMIN.PROJECT_VALUES_TYPES.TITLE'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -7,9 +10,10 @@ div.wrapper(ng-controller="ProjectValuesSectionController")
section.main.admin-common.admin-attributes
include ../includes/components/mainTitle
p.admin-subtitle Specify the types your user stories could be estimated to
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_TYPES.SUBTITLE")
div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='issue-types'; sectionName='Issue types'; objName='type'",
ng-init="section='admin'; resource='issues'; sectionName='ADMIN.PROJECT_VALUES_TYPES.ISSUE_TITLE'; type='issue-types';",
objName="types",
type="issue-types")
include ../includes/modules/admin/project-types

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper.roles(ng-controller="RolesController as ctrl",
ng-init="section='admin'", tg-roles)
sidebar.menu-secondary.sidebar(tg-admin-navigation="roles")
@ -6,32 +8,31 @@ div.wrapper.roles(ng-controller="RolesController as ctrl",
include ../includes/modules/admin-submenu-roles
section.main.admin-roles.admin-common
.header-with-actions
header.header-with-actions
include ../includes/components/mainTitle
.action-buttons(ng-if="!role.external_user")
a.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()")
span Delete
a.button-red.delete-role(href="", title="{{'COMMON.DELETE' | translate}}", ng-click="ctrl.delete()")
span(translate="COMMON.DELETE")
div(ng-if="!role.external_user")
div(tg-edit-role)
.edit-role
input(type="text", value="{{ role.name }}")
a.save.icon.icon-floppy(href="", title="Save")
a.save.icon.icon-floppy(href="", title="{{'COMMON.SAVE' | translate}}")
p.total
span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }}
span.role-name(title="{{'ADMIN.ROLES.COUNT_MEMBERS' | translate}}") {{ role.name }}
a.edit-value.icon.icon-edit
div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories
div.any-computable-role(ng-hide="anyComputableRole", translate="ADMIN.ROLES.WARNING_NO_ROLE")
div.general-category
| When enabled, members assigned to this role will be able to estimate the point value for user stories
div.general-category(translate="ADMIN.ROLES.HELP_ROLE_ENABLED")
div.check
input(type="checkbox", ng-model="role.computable", ng-change="ctrl.setComputable()")
div
span.check-text.check-yes Yes
span.check-text.check-no No
span.check-text.check-yes(translate="COMMON.YES")
span.check-text.check-no(translate="COMMON.NO")
div(ng-if="role.external_user")
p.total

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper.roles(tg-bitbucket-webhooks, ng-controller="BitbucketController as ctrl",
ng-init="section='admin'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="third-parties")
@ -10,25 +12,25 @@ div.wrapper.roles(tg-bitbucket-webhooks, ng-controller="BitbucketController as c
form
fieldset
label(for="secret-key") Secret key
input(type="text", name="secret-key", ng-model="bitbucket.secret", placeholder="Secret key", id="secret-key")
label(for="secret-key", translate="ADMIN.THIRD_PARTIES.SECRET_KEY")
input(type="text", name="secret-key", ng-model="bitbucket.secret", placeholder="{{'ADMIN.THIRD_PARTIES.SECRET_KEY' | translate}}", id="secret-key")
fieldset
.select-input-text(tg-select-input-text)
div
label(for="payload-url") Payload URL
label(for="payload-url", translate="ADMIN.THIRD_PARTIES.PAYLOAD_URL")
.field-with-option
input(type="text", ng-model="bitbucket.webhooks_url", name="payload-url", readonly="readonly", placeholder="Payload URL", id="payload-url")
input(type="text", ng-model="bitbucket.webhooks_url", name="payload-url", readonly="readonly", placeholder="{{'ADMIN.THIRD_PARTIES.PAYLOAD_URL' | translate}}", id="payload-url")
.option-wrapper.select-input-content
.icon.icon-copy
.help-copy Copy to clipboard: Ctrl+C
.help-copy(translate="COMMON.COPY_TO_CLIPBOARD")
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")
label(for="valid-origin-ips", translate="ADMIN.THIRD_PARTIES.VALID_IPS")
input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="bitbucket.valid_origin_ips", placeholder="{{'ADMIN.BITBUCKET.INFO_VERIFYING_IP' | translate}}", id="valid-origin-ips")
button.button-green.submit-button(type="submit", title="Save") Save
button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.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!
span(translate="ADMIN.HELP")

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper.roles(tg-github-webhooks, ng-controller="GithubController as ctrl",
ng-init="section='admin'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="third-parties")
@ -10,21 +12,21 @@ div.wrapper.roles(tg-github-webhooks, ng-controller="GithubController as ctrl",
form
fieldset
label(for="secret-key") Secret key
input(type="text", name="secret-key", ng-model="github.secret", placeholder="Secret key", id="secret-key")
label(for="secret-key", translate="ADMIN.THIRD_PARTIES.SECRET_KEY")
input(type="text", name="secret-key", ng-model="github.secret", placeholder="{{'ADMIN.THIRD_PARTIES.SECRET_KEY' | translate}}", id="secret-key")
fieldset
.select-input-text(tg-select-input-text)
div
label(for="payload-url") Payload URL
label(for="payload-url", translate="ADMIN.THIRD_PARTIES.PAYLOAD_URL")
.field-with-option
input(type="text", ng-model="github.webhooks_url", name="payload-url", readonly="readonly", placeholder="Payload URL", id="payload-url")
input(type="text", ng-model="github.webhooks_url", name="payload-url", readonly="readonly", placeholder="{{'ADMIN.THIRD_PARTIES.PAYLOAD_URL' | translate}}", id="payload-url")
.option-wrapper.select-input-content
.icon.icon-copy
.help-copy Copy to clipboard: Ctrl+C
.help-copy(translate="COMMON.COPY_TO_CLIPBOARD")
button.button-green.submit-button(type="submit", title="Save") Save
button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.SAVE")
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!
span(translate="ADMIN.HELP")

View File

@ -1,38 +1,36 @@
block head
title Taiga Your agile, free, and open source project management tool
doctype html
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 ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-gitlab")
include ../includes/modules/admin-submenu-third-parties
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 ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-gitlab")
include ../includes/modules/admin-submenu-third-parties
section.main.admin-common.admin-third-parties
include ../includes/components/mainTitle
section.main.admin-common.admin-third-parties
include ../includes/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")
form
fieldset
label(for="secret-key", translate="ADMIN.THIRD_PARTIES.SECRET_KEY")
input(type="text", name="secret-key", ng-model="gitlab.secret", placeholder="{{'ADMIN.THIRD_PARTIES.SECRET_KEY' | translate}}", 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
.select-input-text(tg-select-input-text)
div
label(for="payload-url", translate="ADMIN.THIRD_PARTIES.PAYLOAD_URL")
.field-with-option
input(type="text", ng-model="gitlab.webhooks_url", name="payload-url", readonly="readonly", placeholder="{{'ADMIN.THIRD_PARTIES.PAYLOAD_URL' | translate}}", id="payload-url")
.option-wrapper.select-input-content
.icon.icon-copy
.help-copy(translate="COMMON.COPY_TO_CLIPBOARD")
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")
fieldset
label(for="valid-origin-ips", translate="ADMIN.THIRD_PARTIES.VALID_IPS")
input(type="text", name="valid-origin-ips", tg-valid-origin-ips, ng-model="gitlab.valid_origin_ips", placeholder="{{'ADMIN.GITLAB.INFO_VERIFYING_IP' | translate}}", id="valid-origin-ips")
button.button-green.submit-button(type="submit", title="Save") Save
button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.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!
a.help-button(href="https://taiga.io/support/gitlab-integration/", target="_blank")
span.icon.icon-help
span(translate="ADMIN.HELP")

View File

@ -1,99 +1,97 @@
block head
title Taiga Your agile, free, and open source project management tool
doctype html
block content
div.wrapper.roles(ng-controller="WebhooksController as ctrl",
ng-init="section='admin'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="Webhooks")
include ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-webhooks")
include ../includes/modules/admin-submenu-third-parties
div.wrapper.roles(ng-controller="WebhooksController as ctrl",
ng-init="section='admin'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="third-parties")
include ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-webhooks")
include ../includes/modules/admin-submenu-third-parties
section.main.admin-common.admin-webhooks(tg-new-webhook)
include ../includes/components/mainTitle
section.main.admin-common.admin-webhooks(tg-new-webhook)
include ../includes/components/mainTitle
p.admin-subtitle Webhooks notify external services about events in Taiga, like comments, user stories....
div.webhooks-options
a.button-green.hidden.add-webhook(href="",title="Add a New Webhook") Add Webhook
p.admin-subtitle(translate="ADMIN.WEBHOOKS.SUBTITLE")
div.webhooks-options
a.button-green.hidden.add-webhook(href="", title="{{'ADMIN.WEBHOOKS.ADD_NEW' | translate}}", translate="ADMIN.WEBHOOKS.ADD_NEW")
section.webhooks-table.basic-table
div.table-header
div.row
div.webhook-service Name
div.webhook-url URL
div.webhook-options
div.table-body
div.single-webhook-wrapper(tg-webhook="webhook", ng-repeat="webhook in webhooks")
div.edition-mode.hidden
form.row
fieldset.webhook-service
input(type="text", name="name", placeholder="Type the service name", data-required="true", ng-model="webhook.name")
div.webhook-url
div.webhook-url-inputs
fieldset
input(type="text", name="url", data-type="url", placeholder="Type the service payload url", data-required="true", ng-model="webhook.url")
fieldset
input(type="text", name="key", placeholder="Type the service secret key", data-required="true", ng-model="webhook.key")
div.webhook-options
a.edit-existing.icon.icon-floppy(href="", title="Save Webhook")
a.cancel-existing.icon.icon-delete(href="", title="Cancel Webhook")
section.webhooks-table.basic-table
div.table-header
div.row
div.webhook-service(translate="COMMON.FIELDS.NAME")
div.webhook-url(translate="COMMON.FIELDS.URL")
div.webhook-options
div.table-body
div.single-webhook-wrapper(tg-webhook="webhook", ng-repeat="webhook in webhooks")
div.edition-mode.hidden
form.row
fieldset.webhook-service
input(type="text", name="name", placeholder="{{'ADMIN.WEBHOOKS.TYPE_NAME' | translate}}", data-required="true", ng-model="webhook.name")
div.webhook-url
div.webhook-url-inputs
fieldset
input(type="text", name="url", data-type="url", placeholder="{{'ADMIN.WEBHOOKS.TYPE_PAYLOAD_URL' | translate}}", data-required="true", ng-model="webhook.url")
fieldset
input(type="text", name="key", placeholder="{{'ADMIN.WEBHOOKS.TYPE_SERVICE_SECRET' | translate}}", data-required="true", ng-model="webhook.key")
div.webhook-options
a.edit-existing.icon.icon-floppy(href="", title="{{'ADMIN.WEBHOOKS.SAVE' | translate}}")
a.cancel-existing.icon.icon-delete(href="", title="{{'ADMIN.WEBHOOKS.CANCEL' | translate}}")
div.visualization-mode
div.row
div.webhook-service
span(ng-bind="webhook.name")
div.webhook-url
span(ng-bind="webhook.url")
a.show-history.toggle-history(href="", title="Toggle history", ng-show="webhook.logs_counter") (Show history)
div.visualization-mode
div.row
div.webhook-service
span(ng-bind="webhook.name")
div.webhook-url
span(ng-bind="webhook.url")
a.show-history.toggle-history(href="", title="{{'ADMIN.WEBHOOKS.SHOW_HISTORY_TITLE' | translate}}", ng-show="webhook.logs_counter", translate="ADMIN.WEBHOOKS.SHOW_HISTORY")
div.webhook-options
div.webhook-options-wrapper
a.test-webhook.icon.icon-check-square(href="", title="Test Webhook")
a.edit-webhook.icon.icon-edit(href="", title="Edit Webhook")
a.delete-webhook.icon.icon-delete(href="", title="Delete Webhook")
div.webhook-options
div.webhook-options-wrapper
a.test-webhook.icon.icon-check-square(href="", title="{{'ADMIN.WEBHOOKS.TEST' | translate}}")
a.edit-webhook.icon.icon-edit(href="", title="{{'ADMIN.WEBHOOKS.EDIT' | translate}}")
a.delete-webhook.icon.icon-delete(href="", title="{{'ADMIN.WEBHOOKS.DELETE' | translate}}")
div.webhooks-history(ng-show="webhook.logs")
div.history-single-wrapper(ng-repeat="log in webhook.logs")
div.history-single
div
span.history-response-icon(ng-class="log.validStatus ? 'history-success' : 'history-error'", title="{{log.status}}")
span.history-date(ng-bind="log.prettyDate")
span.toggle-log.icon.icon-arrow-bottom
div.webhooks-history(ng-show="webhook.logs")
div.history-single-wrapper(ng-repeat="log in webhook.logs")
div.history-single
div
span.history-response-icon(ng-class="log.validStatus ? 'history-success' : 'history-error'", title="{{log.status}}")
span.history-date(ng-bind="log.prettyDate")
span.toggle-log.icon.icon-arrow-bottom
div.history-single-response
div.history-single-request-header
span Request
a.resend-request(href="", title="Resend request", data-log="{{log.id}}")
span.icon.icon-reload
span Resend request
div.history-single-request-body
div.response-container
span.response-title Headers
textarea(name="headers", ng-bind="log.prettySentHeaders")
div.history-single-response
div.history-single-request-header
span(translate="ADMIN.WEBHOOKS.REQUEST")
a.resend-request(href="", title="{{'ADMIN.WEBHOOKS.RESEND_REQUEST' | translate}}", data-log="{{log.id}}")
span.icon.icon-reload
span(translate="ADMIN.WEBHOOKS.RESEND_REQUEST")
div.history-single-request-body
div.response-container
span.response-title(translate="ADMIN.WEBHOOKS.HEADERS")
textarea(name="headers", ng-bind="log.prettySentHeaders")
div.response-container
span.response-title Payload
textarea(name="payload", ng-bind="log.prettySentData")
div.response-container
span.response-title(translate="ADMIN.WEBHOOKS.PAYLOAD")
textarea(name="payload", ng-bind="log.prettySentData")
div.history-single-response-header
span Response
div.history-single-response-body
div.response-container
textarea(name="response-data", ng-bind="log.response_data")
div.history-single-response-header
span(translate="ADMIN.WEBHOOKS.RESPONSE")
div.history-single-response-body
div.response-container
textarea(name="response-data", ng-bind="log.response_data")
form.new-webhook-form.row.hidden
fieldset.webhook-service
input(type="text", name="name", placeholder="Type the service name", data-required="true", ng-model="newValue.name")
div.webhook-url
div.webhook-url-inputs
fieldset
input(type="text", name="url", data-type="url", placeholder="Type the service payload url", data-required="true", ng-model="newValue.url")
fieldset
input(type="text", name="key", placeholder="Type the service secret key", data-required="true", ng-model="newValue.key")
div.webhook-options
a.add-new.icon.icon-floppy(href="", title="Save Webhook")
a.cancel-new.icon.icon-delete(href="", title="Cancel Webhook")
form.new-webhook-form.row.hidden
fieldset.webhook-service
input(type="text", name="name", placeholder="{{'ADMIN.WEBHOOKS.TYPE_NAME' | translate}}", data-required="true", ng-model="newValue.name")
div.webhook-url
div.webhook-url-inputs
fieldset
input(type="text", name="url", data-type="url", placeholder="{{'ADMIN.WEBHOOKS.TYPE_PAYLOAD_URL' | translate}}", data-required="true", ng-model="newValue.url")
fieldset
input(type="text", name="key", placeholder="{{'ADMIN.WEBHOOKS.TYPE_SERVICE_SECRET' | translate}}", data-required="true", ng-model="newValue.key")
div.webhook-options
a.add-new.icon.icon-floppy(href="", title="{{'ADMIN.WEBHOOKS.SAVE' | translate}}")
a.cancel-new.icon.icon-delete(href="", title="{{'ADMIN.WEBHOOKS.CANCEL' | translate}}")
a.help-button(href="https://taiga.io/support/webhooks/", target="_blank")
span.icon.icon-help
span Do you need help? Check out our support page!
a.help-button(href="https://taiga.io/support/webhooks/", target="_blank")
span.icon.icon-help
span(translate="ADMIN.HELP")

View File

@ -0,0 +1,20 @@
section.project-csv(tg-select-input-text)
div.project-values-title
h2(translate="{{::sectionTitle}}")
a.button.button-gray(title="{{'ADMIN.CSV.DOWNLOAD' | translate}}",
ng-href="{{csvUrl}}", ng-show="csvUrl", target="_blank")
span(translate="ADMIN.CSV.DOWNLOAD")
div.csv-regenerate-field
div.field-with-options
input(type="text", placeholder="{{'ADMIN.CSV.URL_FIELD_PLACEHOLDER' | translate}}",
readonly, ng-model="csvUrl")
div.option-wrapper.select-input-content
div.icon.icon-copy
a(href="", title="{{'ADMIN.CSV.TITLE_REGENERATE_URL' | translate}}", ng-click="ctrl.regenerateUuid()")
span.icon.icon-plus(ng-hide="csvUrl")
span(ng-hide="csvUrl", translate="ADMIN.CSV.ACTION_GENERATE_URL")
span.icon.icon-reload(ng-Show="csvUrl")
span(ng-Show="csvUrl", translate="ADMIN.CSV.ACTION_REGENERATE")

View File

@ -1,19 +1,19 @@
.attachment-name
span.icon.icon-document
a(href!="<%- url %>", title!="<%- name %> uploaded on <%- created_date %>", target="_blank")
a(href!="<%- url %>", title!="<%- title %>", target="_blank")
| <%- name %>
.attachment-size
span <%- size %>
.editable.editable-attachment-comment
input(type="text", name="description", maxlength="140",
value!="<%- description %>", placeholder="Type a short description")
value!="<%- description %>", placeholder="{{'ATTACHMENT.DESCRIPTION' | translate}}")
.editable.editable-attachment-deprecated
input(type="checkbox", name="is-deprecated",
id!="attach-<%- id %>-is-deprecated")
label(for!="attach-<%- id %>-is-deprecated") Deprecated?
label(for!="attach-<%- id %>-is-deprecated", translate="{{'ATTACHMENT.DEPRECATED_FILE' | translate}}")
.attachment-settings
a.editable-settings.icon.icon-floppy(href="", title="Save")
a.editable-settings.icon.icon-delete(href="", title="Cancel")
a.editable-settings.icon.icon-floppy(href="", title="{{'COMMON.SAVE' | translate}}")
a.editable-settings.icon.icon-delete(href="", title="{{'COMMON.CANCEL' | translate}}")

View File

@ -1,5 +1,5 @@
.attachment-name
a(href!="<%- url %>", title!="<%- name %> uploaded on <%- created_date %>", target="_blank")
a(href!="<%- url %>", title!="<%- title %>", target="_blank")
span.icon.icon-documents
span <%- name %>
.attachment-size
@ -7,13 +7,13 @@
.attachment-comments
<% if (isDeprecated){ %>
span.deprecated-file (deprecated)
span.deprecated-file {{'ATTACHMENT.DEPRECATED' | translate}}
<% } %>
span <%- description %>
<% if (modifyPermission) {%>
.attachment-settings
a.settings.icon.icon-edit(href="", title="Edit")
a.settings.icon.icon-delete(href="", title="Delete")
a.settings.icon.icon-drag-v(href="", title="Drag")
<% } %>
a.settings.icon.icon-edit(href="", title="{{'COMMON.EDIT' | translate}}")
a.settings.icon.icon-delete(href="", title="{{'COMMON.DELETE' | translate}}")
a.settings.icon.icon-drag-v(href="", title="{{'COMMON.DRAG' | translate}}")
<% } %>

View File

@ -2,10 +2,10 @@ section.attachments
.attachments-header
h3.attachments-title
span.attachments-num(tg-bind-html="ctrl.attachmentsCount")
span.attachments-text attachments
.add-attach(tg-check-permission!="modify_<%- type %>", title!="Add new attachment. <%- maxFileSizeMsg %>")
span.attachments-text(translate="ATTACHMENT.SECTION_NAME")
.add-attach(tg-check-permission!="modify_<%- type %>", title!="{{'ATTACHMENT.ADD' | translate}}")
<% if (maxFileSize){ %>
span.size-info.hidden [Max. size: <%- maxFileSize %>]
span.size-info.hidden(translate="ATTACHMENT.MAX_FILE_SIZE", translate-values!="{ 'maxFileSize': '<%- maxFileSize %>'}")
<% }; %>
label(for="add-attach", class="icon icon-plus related-tasks-buttons")
input(id="add-attach", type="file", multiple="multiple")
@ -22,9 +22,7 @@ section.attachments
span(ng-bind="file.progressMessage")
.percentage(ng-style="{'width': file.progressPercent}")
a.more-attachments(href="", title="show deprecated atachments", ng-if="ctrl.deprecatedAttachmentsCount > 0")
span.text(data-type="show") + show deprecated atachments
span.text.hidden(data-type="hide")
| - hide deprecated atachments
span.more-attachments-num
| ({{ctrl.deprecatedAttachmentsCount }} deprecated)
a.more-attachments(href="", title="{{'ATTACHMENT.SHOW_DEPRECATED' | translate}}", ng-if="ctrl.deprecatedAttachmentsCount > 0")
span.text(data-type="show", translate="ATTACHMENT.SHOW_DEPRECATED")
span.text.hidden(data-type="hide", translate="ATTACHMENT.HIDE_DEPRECATED")
span.more-attachments-num(translate="ATTACHMENT.COUNT_DEPRECATED", translate-values="{counter: '{{ctrl.deprecatedAttachmentsCount}}'}")

View File

@ -1,8 +1,10 @@
doctype html
div.wrapper
div.login-main
div.login-container
h1.logo
img.logo-svg(src="/svg/logo.svg", alt="TAIGA")
p.tagline Your agile, free, and open source project management tool
p.tagline(translate="COMMON.TAG_LINE")
include ../includes/modules/change-password-from-recovery-form

View File

@ -1,9 +1,11 @@
doctype html
include ../includes/components/beta
div.wrapper
div.login-main
div.login-container
img.logo-svg(src="/svg/logo.svg", alt="TAIGA")
h1.logo Taiga
h2.tagline LOVE YOUR PROJECT
h2.tagline(translate="COMMON.TAG_LINE_2")
include ../includes/modules/forgot-form

View File

@ -1,3 +1,5 @@
doctype html
div.wrapper
div.invitation-main
div.invitation-container(tg-invitation)
@ -8,7 +10,7 @@ div.wrapper
span.person-name(tg-bo-bind="invitation.invited_by.full_name_display")
span.invitation-text
p has invited you to join the project
p(translate="AUTH.INVITED_YOU")
p.project-name(tg-bo-bind="invitation.project_name")
div.invitation-form

View File

@ -1,3 +1,4 @@
p.login-text
span Not registered yet?
a(href!='<%- url %>', tg-nav='register', title='Register') create your free account here
span(translate="AUTH.NOT_REGISTERED_YET")
| &nbsp;
a(href!='<%- url %>', tg-nav='register', title='{{"AUTH.REGISTER" | translate}}', translate="AUTH.CREATE_ACCOUNT")

View File

@ -1,9 +1,11 @@
doctype html
include ../includes/components/beta
div.wrapper
div.login-main
div.login-container
img.logo-svg(src="/svg/logo.svg", alt="TAIGA")
h1.logo Taiga
h2.tagline LOVE YOUR PROJECT
h2.tagline(translate="COMMON.TAG_LINE_2")
include ../includes/modules/login-form

View File

@ -1,8 +1,10 @@
doctype html
div.wrapper
div.login-main
div.login-container
img.logo-svg(src="/svg/logo.svg", alt="TAIGA")
h1.logo Taiga
h2.tagline LOVE YOUR PROJECT
h2.tagline(translate="COMMON.TAG_LINE_2")
include ../includes/modules/register-form

View File

@ -1,32 +1,48 @@
doctype html
div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
ng-init="section='backlog'")
sidebar.menu-secondary.extrabar.filters-bar(tg-backlog-filters)
include ../includes/modules/backlog-filters
section.main.backlog
include ../includes/components/mainTitle
include ../includes/components/summary
div.graphics-container.burndown-container
div.burndown(tg-gm-backlog-graph)
div.burndown(tg-burndown-backlog-graph)
include ../includes/modules/burndown
div.backlog-menu
div.backlog-table-options
a.trans-button.move-to-current-sprint(href="", title="Move to Current Sprint",
a.trans-button.move-to-current-sprint(href="",
title="{{'BACKLOG.MOVE_US_TO_CURRENT_SPRINT' | translate}}",
id="move-to-current-sprint")
span.icon.icon-move
span.text Move to current Sprint
a.trans-button(href="", title="Show Filters", id="show-filters-button")
span.text(translate="BACKLOG.MOVE_US_TO_CURRENT_SPRINT")
a.trans-button(href="",
title="{{'BACKLOG.FILTERS.TOGGLE' | translate}}",
id="show-filters-button")
span.icon.icon-filter
span.text Show Filters
a.trans-button(href="", title="Show Tags", id="show-tags")
span.text(translate="BACKLOG.FILTERS.SHOW")
a.trans-button(href="",
title="{{'BACKLOG.TAGS.TOGGLE' | translate}}",
id="show-tags")
span.icon.icon-tag
span.text Show Tags
span.text(translate="BACKLOG.TAGS.SHOW")
include ../includes/components/addnewus
section.backlog-table(ng-class="{'hidden': !visibleUserstories.length}")
include ../includes/modules/backlog-table
div.empty.empty-backlog(ng-class="{'hidden': visibleUserstories.length}", tg-backlog-empty-sortable)
span.icon.icon-backlog
span.title Your backlog is empty!
a(href="", title+"Create a new US", ng-click="ctrl.addNewUs('standard')", tg-check-permission="add_us") You may want to create a new user story
span.title(translate="BACKLOG.EMPTY")
a(href="", title="{{'BACKLOG.CREATE_NEW_US' | translate}}",
ng-click="ctrl.addNewUs('standard')",
tg-check-permission="add_us",
translate="BACKLOG.CREATE_NEW_US_EMPTY_HELP")
sidebar.menu-secondary.sidebar
include ../includes/modules/sprints

View File

@ -1,3 +1,6 @@
.defined-points(title="Excess of points")
.project-points-progress(title="Pending Points", style!="width: <%- projectPointsPercentaje %>%")
.closed-points-progress(title="Closed points", style!="width: <%- closedPointsPercentaje %>%")
.defined-points(title="{{'BACKLOG.EXCESS_OF_POINTS' | translate}}")
.project-points-progress(title="{{'BACKLOG.PENDING_POINTS' | translate}}",
style!="width: <%- projectPointsPercentaje %>%")
.closed-points-progress(title="{{'BACKLOG.CLOSED_POINTS' | translate}}",
style!="width: <%- closedPointsPercentaje %>%")

View File

@ -1,20 +1,16 @@
.sprint-name
a.icon.icon-arrow-up(href="", title="Compact Sprint")
<% if(isVisible){ %>
a(href!="<%- taskboardUrl %>", title!="'Go to the taskboard of '<%- name %>'")
span <%- name %>
<% } %>
a.icon.icon-arrow-up(href="", title="{{'BACKLOG.COMPACT_SPRINT' | translate}}")
a(ng-if="::isVisible", href="{{::taskboardUrl}}", title="{{'BACKLOG.GO_TO_TASKBOARD' | translate}}")
span {{::name}}
<% if(isEditable){ %>
a.icon.icon-edit(href="", title="Edit Sprint")
<% } %>
a.icon.icon-edit(ng-if="::isEditable", href="", title="{{'BACKLOG.EDIT_SPRINT' | translate}}")
.sprint-summary
.sprint-date <%- estimatedDateRange %>
.sprint-date {{::estimatedDateRange}}
ul
li
span.number <%- closedPoints %>
span.description closed
span.number {{::closedPoints}}
span.description(translate="BACKLOG.CLOSED_POINTS")
li
span.number <%- totalPoints %>
span.description total
span.number {{::totalPoints}}
span.description(translate="BACKLOG.TOTAL_POINTS")

View File

@ -1,6 +1,6 @@
ul.popover.pop-role
li
a.clear-selection(href="", title="All") All
a.clear-selection(href="", title="{{'COMMON.ROLES.ALL' | translate}}", translate="COMMON.ROLES.ALL")
<% _.each(roles, function(role) { %>
li
a(href="", class="role", title!="<%- role.name %>", data-role-id!="<%- role.id %>")

View File

@ -4,18 +4,19 @@
<% } %>
.assigned-to
span.assigned-title Assigned to
span.assigned-title(translate="COMMON.FIELDS.ASSIGNED_TO")
a(href="" title="edit assignment", class!="user-assigned <% if(isEditable){ %>editable<% }; %>")
a(href="" title="{{ 'COMMON.ASSIGNED_TO.TITLE_ACTION_EDIT_ASSIGNMENT'|translate }}",
class!="user-assigned <% if(isEditable){ %>editable<% }; %>")
span.assigned-name
<% if (assignedTo) { %>
<%- assignedTo.full_name_display %>
<% } else { %>
| Not assigned
| {{ 'COMMON.ASSIGNED_TO.NOT_ASSIGNED'|translate }}
<% } %>
<% if(isEditable){ %>
span.icon.icon-arrow-bottom
<% }; %>
<% if (assignedTo!==null && isEditable) { %>
a.icon.icon-delete(href="" title="delete assignment")
a.icon.icon-delete(href="" title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}")
<% } %>

View File

@ -1,4 +1,4 @@
a(href="#", class="button button-gray item-block")
span Block
span(translate="COMMON.BLOCK")
a(href="#", class="button button-red item-unblock")
span Unblock
span(translate="COMMON.UNBLOCK")

View File

@ -2,7 +2,6 @@
img(src!="<%- owner.photo %>", alt!="<%- owner.full_name_display %>")
.created-by
span.created-title
| Created by <%- owner.full_name_display %>
span.created-title(translate="COMMON.CREATED_BY", translate-values!="{ 'fullDisplayName': '<%- owner.full_name_display %>'}")
span.created-date
| <%- date %>

View File

@ -1,2 +1,2 @@
a(href="", class="button button-red")
span Delete
span(translate="COMMON.DELETE")

Some files were not shown because too many files have changed in this diff Show More