Merge branch 'master' into stable

stable
David Barragán Merino 2016-09-30 19:23:25 +02:00
commit 384df341e9
571 changed files with 23560 additions and 7014 deletions

3
.gitignore vendored
View File

@ -9,9 +9,10 @@ app/coffee/modules/locales/locale*.coffee
*.swp
*.swo
.#*
tags
/tags
tmp/
app/config/main.coffee
scss-lint.log
e2e/screenshots/
e2e/reports/
app/modules/compile-modules/

View File

@ -26,8 +26,10 @@ answer newbie questions, and generally made Taiga that much better:
- Guilhem Got <guilhem.got@gmail.com>
- Jordan Rinke
- Miguel de la Cruz <miguel.delacruz@kaleidos.net>
- Mika Andrianarijaona <mikaoelitiana@gmail.com>
- Pilar Esteban <pilar.esteban@gmail.com>
- Ramiro Sánchez <ramiro.sanzhez@kaleidos.net>
- Ryan Swanstrom
- Vlad Topala <topalavlad@gmail.com>
- Wil Wade
- Iago Last

View File

@ -1,11 +1,42 @@
# Changelog #
## 2.2.0 ???? (Unreleased)
## 3.0.0 Stellaria Borealis (2016-10-02)
### Features
- Show a confirmation notice when you exit edit mode by pressing ESC in the markdown inputs.
- Add Epics.
- Add the tribe button to link stories from tree.taiga.io with gigs in tribe.taiga.io.
- Show a confirmation notice when you exit edit mode by pressing ESC in the markdown inputs.
- Errors (not found, server error, permissions and blocked project) don't change the current url.
- Neew Attachments image slider in preview mode.
- New admin area to edit the tag colors used in your project.
- Set color when add a new tags to epics, stories, tasks or issues.
- Display the current user (me) at first in assignment lightbox (thanks to [@mikaoelitiana](https://github.com/mikaoelitiana))
- Divide the user dashboard in two columns in large screens.
- Upvote and downvote issues from the issues list.
- Show points per role in statsection of the taskboard panel. (thanks to [@fmartingr](https://github.com/fmartingr))
- Show a funny randon animals/color for users with no avatar (like project logos).
- Show Open Sprints in the left navigation menu (backlog submenu).
- Filters:
- Refactor the filter module.
- Add filters in the kanban panel.
- Add filter in the sprint taskboard panel.
- Cards UI improvements:
- Add zoom levels.
- Show information according the zoom level.
- Show voters, watchers, taks and attachments.
- Improve performance.
- Comments:
- Add a new permissions to allow add comments instead of use the existent modify permission for this purpose.
- Ability to edit comments, view edition history and redesign comments module UI.
- Wiki:
- Drag & Drop ordering for wiki links.
- Add a list of all wiki pages
- Add Wiki history
- Third party integrations:
- Included gogs as builtin integration.
- i18n:
- Add norwegian Bokmal (nb) translation.
### Misc
- Lots of small and not so small bugfixes.

View File

@ -33,7 +33,7 @@ loadStylesheet = (path) ->
loadPlugin = (pluginPath) ->
return new Promise (resolve, reject) ->
$.getJSON(pluginPath).then (plugin) ->
success = (plugin) ->
window.taigaContribPlugins.push(plugin)
if plugin.css
@ -45,6 +45,11 @@ loadPlugin = (pluginPath) ->
else
resolve()
fail = () ->
console.error("error loading", pluginPath);
$.getJSON(pluginPath).then(success, fail)
loadPlugins = (plugins) ->
promises = []
_.map plugins, (pluginPath) ->

View File

@ -46,7 +46,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$animateProvider.classNameFilter(/^(?:(?!ng-animate-disabled).)*$/)
# wait until the trasnlation is ready to resolve the page
# wait until the translation is ready to resolve the page
originalWhen = $routeProvider.when
$routeProvider.when = (path, route) ->
@ -57,12 +57,26 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$translate().then () -> deferred.resolve()
return deferred.promise
],
projectLoaded: ["$q", "tgProjectService", "$route", ($q, projectService, $route) ->
deferred = $q.defer()
projectService.setSection($route.current.$$route?.section)
if $route.current.params.pslug
projectService.setProjectBySlug($route.current.params.pslug).then(deferred.resolve)
else
projectService.cleanProject()
deferred.resolve()
return deferred.promise
]
})
return originalWhen.call($routeProvider, path, route)
# Home
$routeProvider.when("/",
{
templateUrl: "home/home.html",
@ -76,6 +90,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
}
)
# Discover
$routeProvider.when("/discover",
{
templateUrl: "discover/discover-home/discover-home.html",
@ -97,6 +112,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
}
)
# My Projects
$routeProvider.when("/projects/",
{
templateUrl: "projects/listing/projects-listing.html",
@ -110,17 +126,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
controllerAs: "vm"
}
)
$routeProvider.when("/blocked-project/:pslug/",
{
templateUrl: "projects/project/blocked-project.html",
loader: true,
controller: "Project",
controllerAs: "vm"
}
)
# Project
$routeProvider.when("/project/:pslug/",
{
templateUrl: "projects/project/project.html",
@ -140,6 +146,25 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
}
)
# Epics
$routeProvider.when("/project/:pslug/epics",
{
section: "epics",
templateUrl: "epics/dashboard/epics-dashboard.html",
loader: true,
controller: "EpicsDashboardCtrl",
controllerAs: "vm"
}
)
$routeProvider.when("/project/:pslug/epic/:epicref",
{
templateUrl: "epic/epic-detail.html",
loader: true,
section: "epics"
}
)
$routeProvider.when("/project/:pslug/backlog",
{
templateUrl: "backlog/backlog.html",
@ -188,6 +213,13 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
# Wiki
$routeProvider.when("/project/:pslug/wiki",
{redirectTo: (params) -> "/project/#{params.pslug}/wiki/home"}, )
$routeProvider.when("/project/:pslug/wiki-list",
{
templateUrl: "wiki/wiki-list.html",
loader: true,
section: "wiki"
}
)
$routeProvider.when("/project/:pslug/wiki/:slug",
{
templateUrl: "wiki/wiki.html",
@ -289,7 +321,12 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
section: "admin"
}
)
$routeProvider.when("/project/:pslug/admin/project-values/tags",
{
templateUrl: "admin/admin-project-values-tags.html",
section: "admin"
}
)
$routeProvider.when("/project/:pslug/admin/memberships",
{
templateUrl: "admin/admin-memberships.html",
@ -329,6 +366,12 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
section: "admin"
}
)
$routeProvider.when("/project/:pslug/admin/third-parties/gogs",
{
templateUrl: "admin/admin-third-parties-gogs.html",
section: "admin"
}
)
# Admin - Contrib Plugins
$routeProvider.when("/project/:pslug/admin/contrib/:plugin",
{templateUrl: "contrib/main.html"})
@ -436,6 +479,12 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
)
# Errors/Exceptions
$routeProvider.when("/blocked-project/:pslug/",
{
templateUrl: "projects/project/blocked-project.html",
loader: true,
}
)
$routeProvider.when("/error",
{templateUrl: "error/error.html"})
$routeProvider.when("/not-found",
@ -443,7 +492,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$routeProvider.when("/permission-denied",
{templateUrl: "error/permission-denied.html"})
$routeProvider.otherwise({redirectTo: "/not-found"})
$routeProvider.otherwise({templateUrl: "error/not-found.html"})
$locationProvider.html5Mode({enabled: true, requireBase: false})
defaultHeaders = {
@ -465,15 +514,22 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$tgEventsProvider.setSessionId(taiga.sessionId)
# Add next param when user try to access to a secction need auth permissions.
authHttpIntercept = ($q, $location, $navUrls, $lightboxService) ->
authHttpIntercept = ($q, $location, $navUrls, $lightboxService, errorHandlingService) ->
httpResponseError = (response) ->
if response.status == 0 || (response.status == -1 && !response.config.cancelable)
$lightboxService.closeAll()
$location.path($navUrls.resolve("error"))
$location.replace()
errorHandlingService.error()
else if response.status == 401 and $location.url().indexOf('/login') == -1
nextUrl = encodeURIComponent($location.url())
$location.url($navUrls.resolve("login")).search("next=#{nextUrl}")
nextUrl = $location.url()
search = $location.search()
if search.force_next
$location.url($navUrls.resolve("login"))
.search("force_next", search.force_next)
else
$location.url($navUrls.resolve("login"))
.search("next", nextUrl)
return $q.reject(response)
@ -482,7 +538,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
}
$provide.factory("authHttpIntercept", ["$q", "$location", "$tgNavUrls", "lightboxService",
authHttpIntercept])
"tgErrorHandlingService", authHttpIntercept])
$httpProvider.interceptors.push("authHttpIntercept")
@ -536,18 +592,14 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$httpProvider.interceptors.push("versionCheckHttpIntercept")
blockingIntercept = ($q, $routeParams, $location, $navUrls) ->
blockingIntercept = ($q, errorHandlingService) ->
# API calls can return blocked elements and in that situation the user will be redirected
# to the blocked project page
# This can happens in two scenarios
# - An ok response containing a blocked_code in the data
# - An error reponse when updating/creating/deleting including a 451 error code
redirectToBlockedPage = ->
pslug = $routeParams.pslug
blockedUrl = $navUrls.resolve("blocked-project", {project: pslug})
currentUrl = $location.url()
if currentUrl.indexOf(blockedUrl) == -1
$location.replace().path(blockedUrl)
errorHandlingService.block()
responseOk = (response) ->
if response.data.blocked_code
@ -566,7 +618,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
responseError: responseError
}
$provide.factory("blockingIntercept", ["$q", "$routeParams", "$location", "$tgNavUrls", blockingIntercept])
$provide.factory("blockingIntercept", ["$q", "tgErrorHandlingService", blockingIntercept])
$httpProvider.interceptors.push("blockingIntercept")
@ -637,7 +689,8 @@ i18nInit = (lang, $translate) ->
checksley.updateMessages('default', messages)
init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService, projectService, loaderService, navigationBarService) ->
init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService,
loaderService, navigationBarService, errorHandlingService) ->
$log.debug("Initialize application")
$rootscope.$on '$translatePartialLoaderStructureChanged', () ->
@ -680,6 +733,10 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
# Analytics
$analytics.initialize()
# Initialize error handling service when location change start
$rootscope.$on '$locationChangeStart', (event) ->
errorHandlingService.init()
# On the first page load the loader is painted in `$routeChangeSuccess`
# because we need to hide the tg-navigation-bar.
# In the other cases the loader is in `$routeChangeSuccess`
@ -690,7 +747,7 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
un()
$rootscope.$on '$routeChangeSuccess', (event, next) ->
$rootscope.$on '$routeChangeSuccess', (event, next) ->
if next.loader
loaderService.start(true)
@ -698,13 +755,6 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
if !$auth.isAuthenticated()
$location.path($navUrls.resolve("login"))
projectService.setSection(next.section)
if next.params.pslug
projectService.setProjectBySlug(next.params.pslug)
else
projectService.cleanProject()
if next.title or next.description
title = $translate.instant(next.title or "")
description = $translate.instant(next.description or "")
@ -712,7 +762,7 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
if next.mobileViewport
appMetaService.addMobileViewport()
else
else
appMetaService.removeMobileViewport()
if next.disableHeader
@ -720,10 +770,13 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na
else
navigationBarService.enableHeader()
pluginsWithModule = _.filter(@.taigaContribPlugins, (plugin) -> plugin.module)
# Config for infinite scroll
angular.module('infinite-scroll').value('THROTTLE_MILLISECONDS', 500)
# Load modules
pluginsWithModule = _.filter(@.taigaContribPlugins, (plugin) -> plugin.module)
pluginsModules = _.map(pluginsWithModule, (plugin) -> plugin.module)
modules = [
# Main Global Modules
"taigaBase",
@ -754,12 +807,17 @@ modules = [
"taigaPlugins",
"taigaIntegrations",
"taigaComponents",
# new modules
"taigaProfile",
"taigaHome",
"taigaUserTimeline",
"taigaExternalApps",
"taigaDiscover",
"taigaHistory",
"taigaWikiHistory",
"taigaEpics",
"taigaUtils"
# template cache
"templates",
@ -772,7 +830,7 @@ modules = [
"pascalprecht.translate",
"infinite-scroll",
"tgRepeat"
].concat(_.map(pluginsWithModule, (plugin) -> plugin.module))
].concat(pluginsModules)
# Main module definition
module = angular.module("taiga", modules)
@ -800,9 +858,8 @@ module.run([
"$tgLocation",
"$tgNavUrls",
"tgAppMetaService",
"tgProjectService",
"tgLoader",
"tgNavigationBarService",
"$route",
"tgErrorHandlingService",
init
])

View File

@ -28,11 +28,9 @@ class TaigaController extends TaigaBase
onInitialDataError: (xhr) =>
if xhr
if xhr.status == 404
@location.path(@navUrls.resolve("not-found"))
@location.replace()
@errorHandlingService.notfound()
else if xhr.status == 403
@location.path(@navUrls.resolve("permission-denied"))
@location.replace()
@errorHandlingService.permissionDenied()
return @q.reject(xhr)

View File

@ -99,7 +99,14 @@ class LightboxAddMembersController
_onErrorInvite: (response) ->
@.submitInvites = false
@.form.setErrors(response.data)
errors = {}
_.each response.data.bulk_memberships, (value, index) =>
if value.email
errors["email-#{index}"] = value.email[0]
if value.role
errors["role-#{index}"] = value.role[0]
@.form.setErrors(errors)
if response.data._error_message
@confirm.notify("error", response.data._error_message)

View File

@ -48,12 +48,13 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
"$tgAnalytics",
"tgAppMetaService",
"$translate",
"$tgAuth"
"tgLightboxFactory"
"$tgAuth",
"tgLightboxFactory",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @analytics,
@appMetaService, @translate, @auth, @lightboxFactory) ->
@appMetaService, @translate, @auth, @lightboxFactory, @errorHandlingService) ->
bindMethods(@)
@scope.project = {}
@ -75,7 +76,7 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.i_am_admin
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -240,16 +241,19 @@ module.directive("tgMemberships", ["$tgTemplate", "$compile", MembershipsDirecti
## Member Avatar Directive
#############################################################################
MembershipsRowAvatarDirective = ($log, $template, $translate, $compile) ->
MembershipsRowAvatarDirective = ($log, $template, $translate, $compile, avatarService) ->
template = $template.get("admin/memberships-row-avatar.html", true)
link = ($scope, $el, $attrs) ->
pending = $translate.instant("ADMIN.MEMBERSHIP.STATUS_PENDING")
render = (member) ->
avatar = avatarService.getAvatar(member)
ctx = {
full_name: if member.full_name then member.full_name else ""
email: if member.user_email then member.user_email else member.email
imgurl: if member.photo then member.photo else "/" + window._version + "/images/unnamed.png"
imgurl: avatar.url
bg: avatar.bg
pending: if !member.is_user_active then pending else ""
isOwner: member.is_owner
}
@ -271,7 +275,7 @@ MembershipsRowAvatarDirective = ($log, $template, $translate, $compile) ->
return {link: link}
module.directive("tgMembershipsRowAvatar", ["$log", "$tgTemplate", '$translate', "$compile", MembershipsRowAvatarDirective])
module.directive("tgMembershipsRowAvatar", ["$log", "$tgTemplate", '$translate', "$compile", "tgAvatarService", MembershipsRowAvatarDirective])
#############################################################################

View File

@ -53,14 +53,16 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin)
"tgAppMetaService",
"$translate",
"$tgAuth",
"tgCurrentUserService"
"tgCurrentUserService",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls,
@appMetaService, @translate, @tgAuth, @currentUserService) ->
@appMetaService, @translate, @tgAuth, @currentUserService, @errorHandlingService) ->
@scope.project = {}
promise = @.loadInitialData()
@scope.projectTags = []
promise.then =>
sectionName = @translate.instant( @scope.sectionName)
@ -83,18 +85,23 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin)
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.i_am_admin
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@scope.pointsList = _.sortBy(project.points, "order")
@scope.epicStatusList = _.sortBy(project.epic_statuses, "order")
@scope.usStatusList = _.sortBy(project.us_statuses, "order")
@scope.pointsList = _.sortBy(project.points, "order")
@scope.taskStatusList = _.sortBy(project.task_statuses, "order")
@scope.prioritiesList = _.sortBy(project.priorities, "order")
@scope.severitiesList = _.sortBy(project.severities, "order")
@scope.issueTypesList = _.sortBy(project.issue_types, "order")
@scope.issueStatusList = _.sortBy(project.issue_statuses, "order")
@scope.prioritiesList = _.sortBy(project.priorities, "order")
@scope.severitiesList = _.sortBy(project.severities, "order")
@scope.$emit('project:loaded', project)
@scope.projectTags = _.map @scope.project.tags, (it) =>
return [it, @scope.project.tags_colors[it]]
return project
loadInitialData: ->
@ -106,6 +113,21 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin)
openDeleteLightbox: ->
@rootscope.$broadcast("deletelightbox:new", @scope.project)
addTag: (name, color) ->
tags = _.clone(@scope.project.tags)
tags.push(name)
@scope.projectTags.push([name, null])
@scope.project.tags = tags
deleteTag: (tag) ->
tags = _.clone(@scope.project.tags)
_.pull(tags, tag[0])
_.remove @scope.projectTags, (it) => it[0] == tag[0]
@scope.project.tags = tags
module.controller("ProjectProfileController", ProjectProfileController)
@ -222,10 +244,12 @@ ProjectModulesDirective = ($repo, $confirm, $loading, projectService) ->
$el.on "change", ".module-activation.module-direct-active input", (event) ->
event.preventDefault()
submit()
$scope.$applyAsync(submit)
$el.on "submit", "form", (event) ->
event.preventDefault()
submit()
$el.on "click", ".save", (event) ->
@ -401,6 +425,10 @@ class CsvExporterController extends taiga.Controller
@._generateUuid()
class CsvExporterEpicsController extends CsvExporterController
type: "epics"
class CsvExporterUserstoriesController extends CsvExporterController
type: "userstories"
@ -413,6 +441,7 @@ class CsvExporterIssuesController extends CsvExporterController
type: "issues"
module.controller("CsvExporterEpicsController", CsvExporterEpicsController)
module.controller("CsvExporterUserstoriesController", CsvExporterUserstoriesController)
module.controller("CsvExporterTasksController", CsvExporterTasksController)
module.controller("CsvExporterIssuesController", CsvExporterIssuesController)
@ -422,6 +451,21 @@ module.controller("CsvExporterIssuesController", CsvExporterIssuesController)
## CSV Directive
#############################################################################
CsvEpicDirective = ($translate) ->
link = ($scope) ->
$scope.sectionTitle = "ADMIN.CSV.SECTION_TITLE_EPIC"
return {
controller: "CsvExporterEpicsController",
controllerAs: "ctrl",
templateUrl: "admin/project-csv.html",
link: link,
scope: true
}
module.directive("tgCsvEpic", ["$translate", CsvEpicDirective])
CsvUsDirective = ($translate) ->
link = ($scope) ->
$scope.sectionTitle = "ADMIN.CSV.SECTION_TITLE_US"

View File

@ -31,6 +31,8 @@ joinStr = @.taiga.joinStr
groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
getDefaulColorList = @.taiga.getDefaulColorList
module = angular.module("taigaAdmin")
@ -50,11 +52,12 @@ class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageM
"$tgLocation",
"$tgNavUrls",
"tgAppMetaService",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls,
@appMetaService, @translate) ->
@appMetaService, @translate, @errorHandlingService) ->
@scope.project = {}
promise = @.loadInitialData()
@ -74,7 +77,7 @@ class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageM
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.i_am_admin
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -156,7 +159,7 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
pixels: 30,
scrollWhenOutside: true,
autoScroll: () ->
return this.down && drake.dragging;
return this.down && drake.dragging
})
$scope.$on "$destroy", ->
@ -178,7 +181,9 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
}
initializeTextTranslations = ->
$scope.addNewElementText = $translate.instant("ADMIN.PROJECT_VALUES_#{objName.toUpperCase()}.ACTION_ADD")
$scope.addNewElementText = $translate.instant(
"ADMIN.PROJECT_VALUES_#{objName.toUpperCase()}.ACTION_ADD"
)
initializeNewValue()
initializeTextTranslations()
@ -265,14 +270,6 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
editionRow.removeClass('hidden')
editionRow.find('input:visible').first().focus().select()
$el.on "keyup", ".edition input", (event) ->
if event.keyCode == 13
target = angular.element(event.currentTarget)
saveValue(target)
else if event.keyCode == 27
target = angular.element(event.currentTarget)
cancel(target)
$el.on "keyup", ".new-value input", (event) ->
if event.keyCode == 13
target = $el.find(".new-value")
@ -327,7 +324,8 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
return {link:link}
module.directive("tgProjectValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame", "$translate", "$rootScope", ProjectValuesDirective])
module.directive("tgProjectValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame",
"$translate", "$rootScope", ProjectValuesDirective])
#############################################################################
@ -338,6 +336,12 @@ ColorSelectionDirective = () ->
## Color selection Link
link = ($scope, $el, $attrs, $model) ->
$scope.colorList = getDefaulColorList()
$scope.allowEmpty = false
if $attrs.tgAllowEmpty
$scope.allowEmpty = true
$ctrl = $el.controller()
$scope.$watch $attrs.ngModel, (element) ->
@ -348,7 +352,7 @@ ColorSelectionDirective = () ->
event.preventDefault()
event.stopPropagation()
target = angular.element(event.currentTarget)
$el.find(".select-color").hide()
$(".select-color").hide()
target.siblings(".select-color").show()
# Hide when click outside
body = angular.element("body")
@ -371,6 +375,16 @@ ColorSelectionDirective = () ->
$model.$modelValue.color = $scope.color
$el.find(".select-color").hide()
$el.on "keyup", "input", (event) ->
event.stopPropagation()
if event.keyCode == 13
$scope.$apply ->
$model.$modelValue.color = $scope.color
$el.find(".select-color").hide()
else if event.keyCode == 27
$el.find(".select-color").hide()
$scope.$on "$destroy", ->
$el.off()
@ -688,3 +702,289 @@ ProjectCustomAttributesDirective = ($log, $confirm, animationFrame, $translate)
module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", "$translate",
ProjectCustomAttributesDirective])
#############################################################################
## Tags Controller
#############################################################################
class ProjectTagsController extends taiga.Controller
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"$tgModel",
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @model) ->
@.loading = true
@rootscope.$on("project:loaded", @.loadTags)
loadTags: =>
return @rs.projects.tagsColors(@scope.projectId).then (tags) =>
@scope.projectTagsAll = _.map tags.getAttrs(), (color, name) =>
@model.make_model('tag', {name: name, color: color})
@.filterAndSortTags()
@.loading = false
filterAndSortTags: =>
@scope.projectTags = _.filter(
_.sortBy(@scope.projectTagsAll, "name"),
(tag) => tag.name.indexOf(@scope.tagsFilter.name) != -1
)
createTag: (tag, color) =>
return @rs.projects.createTag(@scope.projectId, tag, color)
editTag: (from_tag, to_tag, color) =>
if from_tag == to_tag
to_tag = null
return @rs.projects.editTag(@scope.projectId, from_tag, to_tag, color)
deleteTag: (tag) =>
@scope.loadingDelete = true
return @rs.projects.deleteTag(@scope.projectId, tag).finally =>
@scope.loadingDelete = false
startMixingTags: (tag) =>
@scope.mixingTags.toTag = tag.name
toggleMixingFromTags: (tag) =>
if tag.name != @scope.mixingTags.toTag
index = @scope.mixingTags.fromTags.indexOf(tag.name)
if index == -1
@scope.mixingTags.fromTags.push(tag.name)
else
@scope.mixingTags.fromTags.splice(index, 1)
confirmMixingTags: () =>
toTag = @scope.mixingTags.toTag
fromTags = @scope.mixingTags.fromTags
@scope.loadingMixing = true
@rs.projects.mixTags(@scope.projectId, toTag, fromTags)
.then =>
@.cancelMixingTags()
@.loadTags()
.finally =>
@scope.loadingMixing = false
cancelMixingTags: () =>
@scope.mixingTags.toTag = null
@scope.mixingTags.fromTags = []
mixingClass: (tag) =>
if @scope.mixingTags.toTag != null
if tag.name == @scope.mixingTags.toTag
return "mixing-tags-to"
else if @scope.mixingTags.fromTags.indexOf(tag.name) != -1
return "mixing-tags-from"
module.controller("ProjectTagsController", ProjectTagsController)
#############################################################################
## Tags directive
#############################################################################
ProjectTagsDirective = ($log, $repo, $confirm, $location, animationFrame, $translate, $rootscope) ->
link = ($scope, $el, $attrs) ->
$window = $(window)
$ctrl = $el.controller()
valueType = $attrs.type
objName = $attrs.objname
initializeNewValue = ->
$scope.newValue = {
"tag": ""
"color": ""
}
initializeTagsFilter = ->
$scope.tagsFilter = {
"name": ""
}
initializeMixingTags = ->
$scope.mixingTags = {
"toTag": null,
"fromTags": []
}
initializeTextTranslations = ->
$scope.addNewElementText = $translate.instant("ADMIN.PROJECT_VALUES_TAGS.ACTION_ADD")
initializeNewValue()
initializeTagsFilter()
initializeMixingTags()
initializeTextTranslations()
$rootscope.$on "$translateChangeEnd", ->
$scope.$evalAsync(initializeTextTranslations)
goToBottomList = (focus = false) =>
table = $el.find(".table-main")
$(document.body).scrollTop(table.offset().top + table.height())
if focus
$el.find(".new-value input:visible").first().focus()
saveValue = (target) =>
formEl = target.parents("form")
form = formEl.checksley()
return if not form.validate()
tag = formEl.scope().tag
originalTag = tag.clone()
originalTag.revert()
$scope.loadingEdit = true
promise = $ctrl.editTag(originalTag.name, tag.name, tag.color)
promise.then =>
$ctrl.loadTags().then =>
row = target.parents(".row.table-main")
row.addClass("hidden")
$scope.loadingEdit = false
row.siblings(".visualization").removeClass('hidden')
promise.then null, (response) ->
$scope.loadingEdit = false
form.setErrors(response.data)
saveNewValue = (target) =>
formEl = target.parents("form")
formEl = target
form = formEl.checksley()
return if not form.validate()
$scope.loadingCreate = true
promise = $ctrl.createTag($scope.newValue.tag, $scope.newValue.color)
promise.then (data) =>
$ctrl.loadTags().then =>
$scope.loadingCreate = false
target.addClass("hidden")
initializeNewValue()
promise.then null, (response) ->
$scope.loadingCreate = false
form.setErrors(response.data)
cancel = (target) ->
row = target.parents(".row.table-main")
formEl = target.parents("form")
tag = formEl.scope().tag
$scope.$apply ->
row.addClass("hidden")
tag.revert()
row.siblings(".visualization").removeClass('hidden')
$scope.$watch "tagsFilter.name", (tagsFilter) ->
$ctrl.filterAndSortTags()
$window.on "keyup", (event) ->
if event.keyCode == 27
$scope.$apply ->
initializeMixingTags()
$el.on "click", ".show-add-new", (event) ->
event.preventDefault()
$el.find(".new-value").removeClass('hidden')
$el.on "click", ".add-new", debounce 2000, (event) ->
event.preventDefault()
target = $el.find(".new-value")
saveNewValue(target)
$el.on "click", ".delete-new", (event) ->
event.preventDefault()
$el.find(".new-value").addClass("hidden")
initializeNewValue()
$el.on "click", ".mix-tags", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
$scope.$apply ->
$ctrl.startMixingTags(target.parents('form').scope().tag)
$el.on "click", ".mixing-row", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
$scope.$apply ->
$ctrl.toggleMixingFromTags(target.parents('form').scope().tag)
$el.on "click", ".mixing-confirm", (event) ->
event.preventDefault()
event.stopPropagation()
$scope.$apply ->
$ctrl.confirmMixingTags()
$el.on "click", ".mixing-cancel", (event) ->
event.preventDefault()
event.stopPropagation()
$scope.$apply ->
$ctrl.cancelMixingTags()
$el.on "click", ".edit-value", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
row = target.parents(".row.table-main")
row.addClass("hidden")
editionRow = row.siblings(".edition")
editionRow.removeClass('hidden')
editionRow.find('input:visible').first().focus().select()
$el.on "keyup", ".new-value input", (event) ->
if event.keyCode == 13
target = $el.find(".new-value")
saveNewValue(target)
else if event.keyCode == 27
$el.find(".new-value").addClass("hidden")
initializeNewValue()
$el.on "keyup", ".status-name input", (event) ->
target = angular.element(event.currentTarget)
if event.keyCode == 13
saveValue(target)
else if event.keyCode == 27
cancel(target)
$el.on "click", ".save", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
saveValue(target)
$el.on "click", ".cancel", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
cancel(target)
$el.on "click", ".delete-tag", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
formEl = target.parents("form")
tag = formEl.scope().tag
title = $translate.instant("ADMIN.COMMON.TITLE_ACTION_DELETE_TAG")
$confirm.askOnDelete(title, tag.name).then (response) ->
onSucces = ->
$ctrl.loadTags().finally ->
response.finish()
onError = ->
$confirm.notify("error")
$ctrl.deleteTag(tag.name).then(onSucces, onError)
$scope.$on "$destroy", ->
$el.off()
$window.off()
return {link:link}
module.directive("tgProjectTags", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame",
"$translate", "$rootScope", ProjectTagsDirective])

View File

@ -48,11 +48,12 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
"$tgLocation",
"$tgNavUrls",
"tgAppMetaService",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls,
@appMetaService, @translate) ->
@appMetaService, @translate, @errorHandlingService) ->
bindMethods(@)
@scope.sectionName = "ADMIN.MENU.PERMISSIONS"
@ -71,7 +72,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.i_am_admin
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -98,6 +99,7 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
@scope.roles = roles
@scope.role = @scope.roles[0]
return roles
loadInitialData: ->
@ -351,6 +353,18 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm, $compile) ->
categories = []
epicPermissions = [
{ key: "view_epics", name: "COMMON.PERMISIONS_CATEGORIES.EPICS.VIEW_EPICS"}
{ key: "add_epic", name: "COMMON.PERMISIONS_CATEGORIES.EPICS.ADD_EPICS"}
{ key: "modify_epic", name: "COMMON.PERMISIONS_CATEGORIES.EPICS.MODIFY_EPICS"}
{ key: "comment_epic", name: "COMMON.PERMISIONS_CATEGORIES.EPICS.COMMENT_EPICS"}
{ key: "delete_epic", name: "COMMON.PERMISIONS_CATEGORIES.EPICS.DELETE_EPICS"}
]
categories.push({
name: "COMMON.PERMISIONS_CATEGORIES.EPICS.NAME" ,
permissions: setActivePermissions(epicPermissions)
})
milestonePermissions = [
{ key: "view_milestones", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.VIEW_SPRINTS"}
{ key: "add_milestone", name: "COMMON.PERMISIONS_CATEGORIES.SPRINTS.ADD_SPRINTS"}
@ -366,6 +380,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm, $compile) ->
{ 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: "comment_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.COMMENT_USER_STORIES"}
{ key: "delete_us", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.DELETE_USER_STORIES"}
]
categories.push({
@ -377,6 +392,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm, $compile) ->
{ 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: "comment_task", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.COMMENT_TASKS"}
{ key: "delete_task", name: "COMMON.PERMISIONS_CATEGORIES.TASKS.DELETE_TASKS"}
]
categories.push({
@ -388,6 +404,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm, $compile) ->
{ 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: "comment_issue", name: "COMMON.PERMISIONS_CATEGORIES.USER_STORIES.COMMENT_ISSUES"}
{ key: "delete_issue", name: "COMMON.PERMISIONS_CATEGORIES.ISSUES.DELETE_ISSUES"}
]
categories.push({

View File

@ -45,10 +45,11 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.
"$tgLocation",
"$tgNavUrls",
"tgAppMetaService",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appMetaService, @translate) ->
constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appMetaService, @translate, @errorHandlingService) ->
bindMethods(@)
@scope.sectionName = "ADMIN.WEBHOOKS.SECTION_NAME"
@ -72,7 +73,7 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.i_am_admin
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -193,15 +194,22 @@ WebhookDirective = ($rs, $repo, $confirm, $loading, $translate) ->
$el.on "click", ".toggle-history", (event) ->
target = angular.element(event.currentTarget)
if not webhook.logs? or webhook.logs.length == 0
updateLogs().then ->
#Waiting for ng-repeat to finish
timeout 0, ->
$el.find(".webhooks-history").toggleClass("open")
$el.find(".webhooks-history")
.toggleClass("open")
.slideToggle()
updateShowHideHistoryText()
else
$el.find(".webhooks-history").toggleClass("open")
$el.find(".webhooks-history")
.toggleClass("open")
.slideToggle()
$scope.$apply () ->
updateShowHideHistoryText()
@ -578,3 +586,50 @@ ValidOriginIpsDirective = ->
}
module.directive("tgValidOriginIps", ValidOriginIpsDirective)
#############################################################################
## Gogs Controller
#############################################################################
class GogsController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
@.$inject = [
"$scope",
"$tgRepo",
"$tgResources",
"$routeParams",
"tgAppMetaService",
"$translate"
]
constructor: (@scope, @repo, @rs, @params, @appMetaService, @translate) ->
bindMethods(@)
@scope.sectionName = @translate.instant("ADMIN.GOGS.SECTION_NAME")
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
title = @translate.instant("ADMIN.GOGS.PAGE_TITLE", {projectName: @scope.project.name})
description = @scope.project.description
@appMetaService.setAll(title, description)
promise.then null, @.onInitialDataError.bind(@)
loadModules: ->
return @rs.modules.list(@scope.projectId, "gogs").then (gogs) =>
@scope.gogs = gogs
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
@scope.projectId = project.id
@scope.project = project
@scope.$emit('project:loaded', project)
return project
loadInitialData: ->
promise = @.loadProject()
promise.then(=> @.loadModules())
return promise
module.controller("GogsController", GogsController)

View File

@ -37,12 +37,13 @@ class LoginPage
constructor: (currentUserService, $location, $navUrls, $routeParams) ->
if currentUserService.isAuthenticated()
url = $navUrls.resolve("home")
if $routeParams['next']
url = $routeParams['next']
$location.search('next', null)
if not $routeParams['force_login']
url = $navUrls.resolve("home")
if $routeParams['next']
url = decodeURIComponent($routeParams['next'])
$location.search('next', null)
$location.path(url)
$location.url(url)
module.controller('LoginPage', LoginPage)
@ -243,8 +244,9 @@ PublicRegisterMessageDirective = ($config, $navUrls, $routeParams, templates) ->
return ""
url = $navUrls.resolve("register")
if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("register")
nextUrl = encodeURIComponent($routeParams['next'])
if $routeParams['force_next']
nextUrl = encodeURIComponent($routeParams['force_next'])
url += "?next=#{nextUrl}"
return template({url:url})
@ -259,7 +261,7 @@ module.directive("tgPublicRegisterMessage", ["$tgConfig", "$tgNavUrls", "$routeP
"$tgTemplate", PublicRegisterMessageDirective])
LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events, $translate) ->
LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events, $translate, $window) ->
link = ($scope, $el, $attrs) ->
form = new checksley.Form($el.find("form.login-form"))
@ -268,9 +270,16 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
else
$scope.nextUrl = $navUrls.resolve("home")
if $routeParams['force_next']
$scope.nextUrl = decodeURIComponent($routeParams['force_next'])
onSuccess = (response) ->
$events.setupConnection()
$location.url($scope.nextUrl)
if $scope.nextUrl.indexOf('http') == 0
$window.location.href = $scope.nextUrl
else
$location.url($scope.nextUrl)
onError = (response) ->
$confirm.notify("light-error", $translate.instant("LOGIN_FORM.ERROR_AUTH_INCORRECT"))
@ -308,14 +317,14 @@ LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $
return {link:link}
module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgConfig", "$routeParams",
"$tgNavUrls", "$tgEvents", "$translate", LoginDirective])
"$tgNavUrls", "$tgEvents", "$translate", "$window", LoginDirective])
#############################################################################
## Register Directive
#############################################################################
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $routeParams, $analytics, $translate) ->
RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $routeParams, $analytics, $translate, $window) ->
link = ($scope, $el, $attrs) ->
if not $config.get("publicRegisterEnabled")
$location.path($navUrls.resolve("not-found"))
@ -324,12 +333,18 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $routeParams
$scope.data = {}
form = $el.find("form").checksley({onlyOneErrorElement: true})
$scope.nextUrl = $navUrls.resolve("home")
if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("login")
$scope.nextUrl = decodeURIComponent($routeParams['next'])
else
$scope.nextUrl = $navUrls.resolve("home")
onSuccessSubmit = (response) ->
$analytics.trackEvent("auth", "register", "user registration", 1)
$location.url($scope.nextUrl)
if $scope.nextUrl.indexOf('http') == 0
$window.location.href = $scope.nextUrl
else
$location.url($scope.nextUrl)
onErrorSubmit = (response) ->
if response.data._error_message
@ -357,7 +372,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $routeParams
return {link:link}
module.directive("tgRegister", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls", "$tgConfig",
"$routeParams", "$tgAnalytics", "$translate", RegisterDirective])
"$routeParams", "$tgAnalytics", "$translate", "$window", RegisterDirective])
#############################################################################
@ -458,13 +473,14 @@ module.directive("tgChangePasswordFromRecovery", ["$tgAuth", "$tgConfirm", "$tgL
## Invitation
#############################################################################
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics, $translate) ->
InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics, $translate, config) ->
link = ($scope, $el, $attrs) ->
token = $params.token
promise = $auth.getInvitation(token)
promise.then (invitation) ->
$scope.invitation = invitation
$scope.publicRegisterEnabled = config.get("publicRegisterEnabled")
promise.then null, (response) ->
$location.path($navUrls.resolve("login"))
@ -535,7 +551,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics
return {link:link}
module.directive("tgInvitation", ["$tgAuth", "$tgConfirm", "$tgLocation", "$routeParams",
"$tgNavUrls", "$tgAnalytics", "$translate", InvitationDirective])
"$tgNavUrls", "$tgAnalytics", "$translate", "$tgConfig", InvitationDirective])
#############################################################################

View File

@ -1,185 +0,0 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/backlog/main.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
toggleText = @.taiga.toggleText
scopeDefer = @.taiga.scopeDefer
bindOnce = @.taiga.bindOnce
groupBy = @.taiga.groupBy
debounceLeading = @.taiga.debounceLeading
module = angular.module("taigaBacklog")
#############################################################################
## Issues Filters Directive
#############################################################################
BacklogFiltersDirective = ($q, $log, $location, $template, $compile) ->
template = $template.get("backlog/filters.html", true)
templateSelected = $template.get("backlog/filter-selected.html", true)
link = ($scope, $el, $attrs) ->
currentFiltersType = ''
$ctrl = $el.closest(".wrapper").controller()
selectedFilters = []
showFilters = (title, type) ->
$el.find(".filters-cats").hide()
$el.find(".filter-list").removeClass("hidden")
$el.find("h2.breadcrumb").removeClass("hidden")
$el.find("h2 a.subfilter span.title").html(title)
$el.find("h2 a.subfilter span.title").prop("data-type", type)
currentFiltersType = getFiltersType()
showCategories = ->
$el.find(".filters-cats").show()
$el.find(".filter-list").addClass("hidden")
$el.find("h2.breadcrumb").addClass("hidden")
initializeSelectedFilters = () ->
showCategories()
selectedFilters = []
for name, values of $scope.filters
for val in values
selectedFilters.push(val) if val.selected
renderSelectedFilters()
renderSelectedFilters = ->
_.map selectedFilters, (f) =>
if f.color
f.style = "border-left: 3px solid #{f.color}"
html = templateSelected({filters: selectedFilters})
html = $compile(html)($scope)
$el.find(".filters-applied").html(html)
renderFilters = (filters) ->
_.map filters, (f) =>
if f.color
f.style = "border-left: 3px solid #{f.color}"
html = template({filters:filters})
html = $compile(html)($scope)
$el.find(".filter-list").html(html)
getFiltersType = () ->
return $el.find("h2 a.subfilter span.title").prop('data-type')
reloadUserstories = () ->
currentFiltersType = getFiltersType()
$q.all([$ctrl.loadUserstories(true), $ctrl.generateFilters()]).then () ->
currentFilters = $scope.filters[currentFiltersType]
renderFilters(_.reject(currentFilters, "selected"))
toggleFilterSelection = (type, id) ->
currentFiltersType = getFiltersType()
filters = $scope.filters[type]
filter = _.find(filters, {id: id})
filter.selected = (not filter.selected)
if filter.selected
selectedFilters.push(filter)
$scope.$apply ->
$ctrl.selectFilter(type, id)
else
selectedFilters = _.reject selectedFilters, (selected) ->
return filter.type == selected.type && filter.id == selected.id
$ctrl.unselectFilter(type, id)
renderSelectedFilters(selectedFilters)
if type == currentFiltersType
renderFilters(_.reject(filters, "selected"))
reloadUserstories()
selectQFilter = debounceLeading 100, (value) ->
return if value is undefined
if value.length == 0
$ctrl.replaceFilter("q", null)
else
$ctrl.replaceFilter("q", value)
reloadUserstories()
$scope.$watch("filtersQ", selectQFilter)
## Angular Watchers
$scope.$on "backlog:loaded", (ctx) ->
initializeSelectedFilters()
$scope.$on "filters:update", (ctx) ->
$ctrl.generateFilters().then () ->
filters = $scope.filters[currentFiltersType]
if currentFiltersType
renderFilters(_.reject(filters, "selected"))
## Dom Event Handlers
$el.on "click", ".filters-cats > ul > li > a", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
tags = $scope.filters[target.data("type")]
renderFilters(_.reject(tags, "selected"))
showFilters(target.attr("title"), target.data('type'))
$el.on "click", ".filters-inner > .filters-step-cat > .breadcrumb > .back", (event) ->
event.preventDefault()
showCategories()
$el.on "click", ".remove-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget).parent()
id = target.data("id")
type = target.data("type")
toggleFilterSelection(type, id)
$el.on "click", ".filter-list .single-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
if target.hasClass("active")
target.removeClass("active")
else
target.addClass("active")
id = target.data("id")
type = target.data("type")
toggleFilterSelection(type, id)
return {link:link}
module.directive("tgBacklogFilters", ["$q", "$log", "$tgLocation", "$tgTemplate", "$compile", BacklogFiltersDirective])

View File

@ -39,7 +39,7 @@ module = angular.module("taigaBacklog")
## Backlog Controller
#############################################################################
class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin, taiga.UsFiltersMixin)
@.$inject = [
"$scope",
"$rootScope",
@ -56,18 +56,32 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
"$translate",
"$tgLoading",
"tgResources",
"$tgQueueModelTransformation"
"$tgQueueModelTransformation",
"tgErrorHandlingService",
"$tgStorage",
"tgFilterRemoteStorageService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q,
@location, @appMetaService, @navUrls, @events, @analytics, @translate, @loading, @rs2, @modelTransform) ->
storeCustomFiltersName: 'backlog-custom-filters'
storeFiltersName: 'backlog-filters'
backlogOrder: {}
milestonesOrder: {}
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @appMetaService, @navUrls,
@events, @analytics, @translate, @loading, @rs2, @modelTransform, @errorHandlingService,
@storage, @filterRemoteStorageService) ->
bindMethods(@)
@.backlogOrder = {}
@.milestonesOrder = {}
@.page = 1
@.disablePagination = false
@.firstLoadComplete = false
@scope.userstories = []
return if @.applyStoredFilters(@params.pslug, "backlog-filters")
@scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME")
@showTags = false
@activeFilters = false
@ -96,15 +110,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
# On Error
promise.then null, @.onInitialDataError.bind(@)
filtersReloadContent: () ->
@.loadUserstories(true)
initializeEventHandlers: ->
@scope.$on "usform:bulk:success", =>
@.loadUserstories(true)
@.loadProjectStats()
@confirm.notify("success")
@analytics.trackEvent("userstory", "create", "bulk create userstory on backlog", 1)
@scope.$on "sprintform:create:success", =>
@.loadSprints()
@.loadProjectStats()
@confirm.notify("success")
@analytics.trackEvent("sprint", "create", "create sprint on backlog", 1)
@scope.$on "usform:new:success", =>
@ -112,6 +131,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@.loadProjectStats()
@rootscope.$broadcast("filters:update")
@confirm.notify("success")
@analytics.trackEvent("userstory", "create", "create userstory on backlog", 1)
@scope.$on "sprintform:edit:success", =>
@ -174,6 +194,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@scope.showGraphPlaceholder = !(stats.total_points? && stats.total_milestones?)
return stats
setMilestonesOrder: (sprints) ->
for sprint in sprints
@.milestonesOrder[sprint.id] = {}
for it in sprint.user_stories
@.milestonesOrder[sprint.id][it.id] = it.sprint_order
unloadClosedSprints: ->
@scope.$apply =>
@scope.closedSprints = []
@ -184,6 +210,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return @rs.sprints.list(@scope.projectId, params).then (result) =>
sprints = result.milestones
@.setMilestonesOrder(sprints)
@scope.totalClosedMilestones = result.closed
# NOTE: Fix order of USs because the filter orderBy does not work propertly in partials files
@ -199,6 +227,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return @rs.sprints.list(@scope.projectId, params).then (result) =>
sprints = result.milestones
@.setMilestonesOrder(sprints)
@scope.totalMilestones = sprints
@scope.totalClosedMilestones = result.closed
@scope.totalOpenMilestones = result.open
@ -220,47 +250,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return sprints
restoreFilters: ->
selectedTags = @scope.oldSelectedTags
selectedStatuses = @scope.oldSelectedStatuses
return if !selectedStatuses and !selectedStatuses
@scope.filtersQ = @scope.filtersQOld
@.replaceFilter("q", @scope.filtersQ)
_.each [selectedTags, selectedStatuses], (filterGrp) =>
_.each filterGrp, (item) =>
filters = @scope.filters[item.type]
filter = _.find(filters, {id: item.id})
filter.selected = true
@.selectFilter(item.type, item.id)
@.loadUserstories()
resetFilters: ->
selectedTags = _.filter(@scope.filters.tags, "selected")
selectedStatuses = _.filter(@scope.filters.status, "selected")
@scope.oldSelectedTags = selectedTags
@scope.oldSelectedStatuses = selectedStatuses
@scope.filtersQOld = @scope.filtersQ
@scope.filtersQ = undefined
@.replaceFilter("q", @scope.filtersQ)
_.each [selectedTags, selectedStatuses], (filterGrp) =>
_.each filterGrp, (item) =>
filters = @scope.filters[item.type]
filter = _.find(filters, {id: item.id})
filter.selected = false
@.unselectFilter(item.type, item.id)
@.loadUserstories()
loadAllPaginatedUserstories: () ->
page = @.page
@ -272,15 +261,15 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@.loadingUserstories = true
@.disablePagination = true
@scope.httpParams = @.getUrlFilters()
@rs.userstories.storeQueryParams(@scope.projectId, @scope.httpParams)
params = _.clone(@location.search())
@rs.userstories.storeQueryParams(@scope.projectId, params)
if resetPagination
@.page = 1
@scope.httpParams.page = @.page
params.page = @.page
promise = @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams, pageSize)
promise = @rs.userstories.listUnassigned(@scope.projectId, params, pageSize)
return promise.then (result) =>
userstories = result[0]
@ -292,7 +281,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
# NOTE: Fix order of USs because the filter orderBy does not work propertly in the partials files
@scope.userstories = @scope.userstories.concat(_.sortBy(userstories, "backlog_order"))
@.setSearchDataFilters()
for it in @scope.userstories
@.backlogOrder[it.id] = it.backlog_order
@.loadingUserstories = false
@ -317,7 +307,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.is_backlog_activated
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -343,252 +333,152 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
prepareBulkUpdateData: (uses, field="backlog_order") ->
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
resortUserStories: (uses, field="backlog_order") ->
items = []
for item, index in uses
item[field] = index
if item.isModified()
items.push(item)
return items
# --move us api behavior--
# If your are moving multiples USs you must use the bulk api
# If there is only one US you must use patch (repo.save)
#
# The new US position is the position of the previous US + 1.
# If the previous US has a position value that it is equal to
# other USs, you must send all the USs with that position value
# only if they are before of the target position with this USs
# if it's a patch you must add them to the header, if is a bulk
# you must send them with the other USs
moveUs: (ctx, usList, newUsIndex, newSprintId) ->
oldSprintId = usList[0].milestone
project = usList[0].project
movedFromClosedSprint = false
movedToClosedSprint = false
if oldSprintId
sprint = @scope.sprintsById[oldSprintId] || @scope.closedSprintsById[oldSprintId]
sprint = @scope.sprintsById[oldSprintId]
if newSprintId
newSprint = @scope.sprintsById[newSprintId] || @scope.closedSprintsById[newSprintId]
# Move from closed sprint
if !sprint && @scope.closedSprintsById
sprint = @scope.closedSprintsById[oldSprintId]
movedFromClosedSprint = true if sprint
currentSprintId = if newSprintId != oldSprintId then newSprintId else oldSprintId
newSprint = @scope.sprintsById[newSprintId]
orderList = null
orderField = ""
# Move to closed sprint
if !newSprint && newSprintId
newSprint = @scope.closedSprintsById[newSprintId]
movedToClosedSprint = true if newSprint
if newSprintId != oldSprintId
if newSprintId == null # From sprint to backlog
for us, key in usList # delete from sprint userstories
_.remove sprint.user_stories, (it) -> it.id == us.id
# In the same sprint or in the backlog
if newSprintId == oldSprintId
items = null
userstories = null
orderField = "backlog_order"
orderList = @.backlogOrder
if newSprintId == null
userstories = @scope.userstories
else
userstories = newSprint.user_stories
beforeDestination = _.slice(@scope.userstories, 0, newUsIndex)
afterDestination = _.slice(@scope.userstories, newUsIndex)
@scope.$apply ->
for us, key in usList
r = userstories.indexOf(us)
userstories.splice(r, 1)
@scope.userstories = @scope.userstories.concat(usList)
else # From backlog to sprint
for us in usList # delete from sprint userstories
_.remove @scope.userstories, (it) -> it.id == us.id
args = [newUsIndex, 0].concat(usList)
Array.prototype.splice.apply(userstories, args)
orderField = "sprint_order"
orderList = @.milestonesOrder[newSprint.id]
# If in backlog
if newSprintId == null
# Rehash userstories order field
beforeDestination = _.slice(newSprint.user_stories, 0, newUsIndex)
afterDestination = _.slice(newSprint.user_stories, newUsIndex)
items = @.resortUserStories(userstories, "backlog_order")
data = @.prepareBulkUpdateData(items, "backlog_order")
# Persist in bulk all affected
# userstories with order change
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
@rootscope.$broadcast("sprint:us:moved")
# For sprint
else
# Rehash userstories order field
items = @.resortUserStories(userstories, "sprint_order")
data = @.prepareBulkUpdateData(items, "sprint_order")
# Persist in bulk all affected
# userstories with order change
@rs.userstories.bulkUpdateSprintOrder(project, data).then =>
@rootscope.$broadcast("sprint:us:moved")
return promise
# From sprint to backlog
if newSprintId == null
us.milestone = null for us in usList
@scope.$apply =>
# Add new us to backlog userstories list
# @scope.userstories.splice(newUsIndex, 0, us)
args = [newUsIndex, 0].concat(usList)
Array.prototype.splice.apply(@scope.userstories, args)
for us, key in usList
r = sprint.user_stories.indexOf(us)
sprint.user_stories.splice(r, 1)
# Persist the milestone change of userstory
promise = @repo.save(us)
# Rehash userstories order field
# and persist in bulk all changes.
promise = promise.then =>
items = @.resortUserStories(@scope.userstories, "backlog_order")
data = @.prepareBulkUpdateData(items, "backlog_order")
return @rs.userstories.bulkUpdateBacklogOrder(us.project, data).then =>
@rootscope.$broadcast("sprint:us:moved")
if movedFromClosedSprint
@rootscope.$broadcast("backlog:load-closed-sprints")
promise.then null, ->
console.log "FAIL" # TODO
return promise
# From backlog to sprint
if oldSprintId == null
us.milestone = newSprintId for us in usList
args = [newUsIndex, 0].concat(usList)
# Add moving us to sprint user stories list
Array.prototype.splice.apply(newSprint.user_stories, args)
# Remove moving us from backlog userstories lists.
for us, key in usList
r = @scope.userstories.indexOf(us)
@scope.userstories.splice(r, 1)
# From sprint to sprint
newSprint.user_stories = newSprint.user_stories.concat(usList)
else
us.milestone = newSprintId for us in usList
if oldSprintId == null # backlog
orderField = "backlog_order"
orderList = @.backlogOrder
@scope.$apply =>
args = [newUsIndex, 0].concat(usList)
list = _.filter @scope.userstories, (listIt) -> # Remove moved US from list
return !_.find usList, (moveIt) -> return listIt.id == moveIt.id
# Add new us to backlog userstories list
Array.prototype.splice.apply(newSprint.user_stories, args)
beforeDestination = _.slice(list, 0, newUsIndex)
afterDestination = _.slice(list, newUsIndex)
else # sprint
orderField = "sprint_order"
orderList = @.milestonesOrder[sprint.id]
# Remove the us from the sprint list.
for us in usList
r = sprint.user_stories.indexOf(us)
sprint.user_stories.splice(r, 1)
list = _.filter newSprint.user_stories, (listIt) -> # Remove moved US from list
return !_.find usList, (moveIt) -> return listIt.id == moveIt.id
#Persist the milestone change of userstory
promises = _.map usList, (us) => @repo.save(us)
beforeDestination = _.slice(list, 0, newUsIndex)
afterDestination = _.slice(list, newUsIndex)
#Rehash userstories order field
#and persist in bulk all changes.
promise = @q.all(promises).then =>
items = @.resortUserStories(newSprint.user_stories, "sprint_order")
data = @.prepareBulkUpdateData(items, "sprint_order")
# previous us
previous = beforeDestination[beforeDestination.length - 1]
@rs.userstories.bulkUpdateSprintOrder(project, data).then (result) =>
@rootscope.$broadcast("sprint:us:moved")
# this will store the previous us with the same position
setPreviousOrders = []
@rs.userstories.bulkUpdateBacklogOrder(project, data).then =>
@rootscope.$broadcast("sprint:us:moved")
if !previous
startIndex = 0
else if previous
startIndex = orderList[previous.id] + 1
if movedToClosedSprint || movedFromClosedSprint
@scope.$broadcast("backlog:load-closed-sprints")
previousWithTheSameOrder = _.filter(beforeDestination, (it) ->
it[orderField] == orderList[previous.id]
)
promise.then null, ->
console.log "FAIL" # TODO
# we must send the USs previous to the dropped USs to tell the backend
# which USs are before the dropped USs, if they have the same value to
# order, the backend doens't know after which one do you want to drop
# the USs
if previousWithTheSameOrder.length > 1
setPreviousOrders = _.map(previousWithTheSameOrder, (it) ->
{us_id: it.id, order: orderList[it.id]}
)
modifiedUs = []
for us, key in usList # update sprint and new position
us.milestone = currentSprintId
us[orderField] = startIndex + key
orderList[us.id] = us[orderField]
modifiedUs.push({us_id: us.id, order: us[orderField]})
startIndex = orderList[usList[usList.length - 1].id]
for it, key in afterDestination # increase position of the us after the dragged us's
orderList[it.id] = startIndex + key + 1
# refresh order
@scope.userstories = _.sortBy @scope.userstories, (it) => @.backlogOrder[it.id]
for sprint in @scope.sprints
sprint.user_stories = _.sortBy sprint.user_stories, (it) => @.milestonesOrder[sprint.id][it.id]
for sprint in @scope.closedSprints
sprint.user_stories = _.sortBy sprint.user_stories, (it) => @.milestonesOrder[sprint.id][it.id]
# saving
if usList.length > 1 && (newSprintId != oldSprintId) # drag multiple to sprint
data = modifiedUs.concat(setPreviousOrders)
promise = @rs.userstories.bulkUpdateMilestone(project, newSprintId, data)
else if usList.length > 1 # drag multiple in backlog
data = modifiedUs.concat(setPreviousOrders)
promise = @rs.userstories.bulkUpdateBacklogOrder(project, data)
else # drag single
setOrders = {}
for it in setPreviousOrders
setOrders[it.us_id] = it.order
options = {
headers: {
"set-orders": JSON.stringify(setOrders)
}
}
promise = @repo.save(usList[0], true, {}, options, true)
promise.then () =>
@rootscope.$broadcast("sprint:us:moved")
if @scope.closedSprintsById && @scope.closedSprintsById[oldSprintId]
@rootscope.$broadcast("backlog:load-closed-sprints")
return promise
isFilterSelected: (type, id) ->
if @searchdata[type]? and @searchdata[type][id]
return true
return false
setSearchDataFilters: () ->
urlfilters = @.getUrlFilters()
if urlfilters.q
@scope.filtersQ = @scope.filtersQ or urlfilters.q
@searchdata = {}
for name, value of urlfilters
if not @searchdata[name]?
@searchdata[name] = {}
for val in taiga.toString(value).split(",")
@searchdata[name][val] = true
getUrlFilters: ->
return _.pick(@location.search(), "status", "tags", "q")
generateFilters: ->
urlfilters = @.getUrlFilters()
@scope.filters = {}
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.q = urlfilters.q
loadFilters.milestone = 'null'
return @rs.userstories.filtersData(loadFilters).then (data) =>
choicesFiltersFormat = (choices, type, byIdObject) =>
_.map choices, (t) ->
t.type = type
return t
tagsFilterFormat = (tags) =>
return _.map tags, (t) ->
t.id = t.name
t.type = 'tags'
return t
# Build filters data structure
@scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.usStatusById)
@scope.filters.tags = tagsFilterFormat(data.tags)
selectedTags = _.filter(@scope.filters.tags, "selected")
selectedTags = _.map(selectedTags, "id")
selectedStatuses = _.filter(@scope.filters.status, "selected")
selectedStatuses = _.map(selectedStatuses, "id")
@.markSelectedFilters(@scope.filters, urlfilters)
#store query params
@rs.userstories.storeQueryParams(@scope.projectId, {
"status": selectedStatuses,
"tags": selectedTags,
"project": @scope.projectId
"milestone": null
})
markSelectedFilters: (filters, urlfilters) ->
# Build selected filters (from url) fast lookup data structure
searchdata = {}
for name, value of _.omit(urlfilters, "page", "orderBy")
if not searchdata[name]?
searchdata[name] = {}
for val in "#{value}".split(",")
searchdata[name][val] = true
isSelected = (type, id) ->
if searchdata[type]? and searchdata[type][id]
return true
return false
for key, value of filters
for obj in value
obj.selected = if isSelected(obj.type, obj.id) then true else undefined
## Template actions
updateUserStoryStatus: () ->
@.setSearchDataFilters()
@.generateFilters().then () =>
@rootscope.$broadcast("filters:update")
@.loadProjectStats()
@ -641,10 +531,10 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
currentDate = new Date().getTime()
return _.find @scope.sprints, (sprint) ->
start = moment(sprint.estimated_start, 'YYYY-MM-DD').format('x')
end = moment(sprint.estimated_finish, 'YYYY-MM-DD').format('x')
start = moment(sprint.estimated_start, 'YYYY-MM-DD').format('x')
end = moment(sprint.estimated_finish, 'YYYY-MM-DD').format('x')
return currentDate >= start && currentDate <= end
return currentDate >= start && currentDate <= end
module.controller("BacklogController", BacklogController)
@ -806,8 +696,15 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
text = $translate.instant("BACKLOG.TAGS.SHOW")
elm.text(text)
openFilterInit = ($scope, $el, $ctrl) ->
sidebar = $el.find("sidebar.backlog-filter")
sidebar.addClass("active")
$ctrl.activeFilters = true
showHideFilter = ($scope, $el, $ctrl) ->
sidebar = $el.find("sidebar.filters-bar")
sidebar = $el.find("sidebar.backlog-filter")
sidebar.one "transitionend", () ->
timeout 150, ->
$rootscope.$broadcast("resize")
@ -823,11 +720,6 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
toggleText(target, [hideText, showText])
if !sidebar.hasClass("active")
$ctrl.resetFilters()
else
$ctrl.restoreFilters()
$ctrl.toggleActiveFilters()
## Filters Link
@ -846,11 +738,13 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
linkFilters($scope, $el, $attrs, $ctrl)
linkDoomLine($scope, $el, $attrs, $ctrl)
filters = $ctrl.getUrlFilters()
filters = $ctrl.location.search()
if filters.status ||
filters.tags ||
filters.q
showHideFilter($scope, $el, $ctrl)
filters.q ||
filters.assigned_to ||
filters.owner
openFilterInit($scope, $el, $ctrl)
$scope.$on "showTags", () ->
showHideTags($ctrl)

View File

@ -23,16 +23,10 @@
###
taiga = @.taiga
mixOf = @.taiga.mixOf
toggleText = @.taiga.toggleText
scopeDefer = @.taiga.scopeDefer
bindOnce = @.taiga.bindOnce
groupBy = @.taiga.groupBy
module = angular.module("taigaBacklog")
#############################################################################
## Sortable Directive
#############################################################################
@ -42,7 +36,7 @@ deleteElement = (el) ->
$(el).off()
$(el).remove()
BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
BacklogSortableDirective = () ->
link = ($scope, $el, $attrs) ->
bindOnce $scope, "project", (project) ->
# If the user has not enough permissions we don't enable the sortable
@ -51,10 +45,6 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
initIsBacklog = false
filterError = ->
text = $translate.instant("BACKLOG.SORTABLE_FILTER_ERROR")
$tgConfirm.notify("error", text)
drake = dragula([$el[0], $('.empty-backlog')[0]], {
copySortSource: false,
copy: false,
@ -63,18 +53,11 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
if !$(item).hasClass('row')
return false
# it doesn't move is the filter is open
parent = $(item).parent()
initIsBacklog = parent.hasClass('backlog-table-body')
if initIsBacklog && $el.hasClass("active-filters")
filterError()
return false
return true
})
drake.on 'drag', (item, container) ->
# it doesn't move is the filter is open
parent = $(item).parent()
initIsBacklog = parent.hasClass('backlog-table-body')
@ -88,6 +71,8 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
$(item).addClass('backlog-us-mirror')
drake.on 'dragend', (item) ->
parent = $(item).parent()
$('.doom-line').remove()
parent = $(item).parent()
@ -102,8 +87,6 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
$(document.body).removeClass("drag-active")
items = $(item).parent().find('.row')
sprint = null
firstElement = if dragMultipleItems.length then dragMultipleItems[0] else item
@ -131,11 +114,7 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
usList = _.map dragMultipleItems, (item) ->
return item = $(item).scope().us
else
usList = _.map items, (item) ->
item = $(item)
itemUs = item.scope().us
return itemUs
usList = [$(item).scope().us]
$scope.$emit("sprint:us:move", usList, index, sprint)
@ -144,7 +123,7 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
pixels: 30,
scrollWhenOutside: true,
autoScroll: () ->
return this.down && drake.dragging;
return this.down && drake.dragging
})
$scope.$on "$destroy", ->
@ -153,11 +132,4 @@ BacklogSortableDirective = ($repo, $rs, $rootscope, $tgConfirm, $translate) ->
return {link: link}
module.directive("tgBacklogSortable", [
"$tgRepo",
"$tgResources",
"$rootScope",
"$tgConfirm",
"$translate",
BacklogSortableDirective
])
module.directive("tgBacklogSortable", BacklogSortableDirective)

View File

@ -73,13 +73,16 @@ urls = {
"project-taskboard": "/project/:project/taskboard/:sprint"
"project-kanban": "/project/:project/kanban"
"project-issues": "/project/:project/issues"
"project-epics": "/project/:project/epics"
"project-search": "/project/:project/search"
"project-epics-detail": "/project/:project/epic/:ref"
"project-userstories-detail": "/project/:project/us/:ref"
"project-tasks-detail": "/project/:project/task/:ref"
"project-issues-detail": "/project/:project/issue/:ref"
"project-wiki": "/project/:project/wiki"
"project-wiki-list": "/project/:project/wiki-list"
"project-wiki-page": "/project/:project/wiki/:slug"
# Team
@ -99,6 +102,7 @@ urls = {
"project-admin-project-values-severities": "/project/:project/admin/project-values/severities"
"project-admin-project-values-types": "/project/:project/admin/project-values/types"
"project-admin-project-values-custom-fields": "/project/:project/admin/project-values/custom-fields"
"project-admin-project-values-tags": "/project/:project/admin/project-values/tags"
"project-admin-memberships": "/project/:project/admin/memberships"
"project-admin-roles": "/project/:project/admin/roles"
@ -106,6 +110,7 @@ urls = {
"project-admin-third-parties-github": "/project/:project/admin/third-parties/github"
"project-admin-third-parties-gitlab": "/project/:project/admin/third-parties/gitlab"
"project-admin-third-parties-bitbucket": "/project/:project/admin/third-parties/bitbucket"
"project-admin-third-parties-gogs": "/project/:project/admin/third-parties/gogs"
"project-admin-contrib": "/project/:project/admin/contrib/:plugin"
# User settings

View File

@ -30,7 +30,7 @@ class HttpService extends taiga.Service
constructor: (@http, @q, @storage, @rootScope, @cacheFactory, @translate) ->
super()
@.cache = @cacheFactory("httpget");
@.cache = @cacheFactory("httpget")
headers: ->
headers = {}

View File

@ -41,7 +41,7 @@ class RepositoryService extends taiga.Service
defered = @q.defer()
url = @urls.resolve(name)
promise = @http.post(url, JSON.stringify(data))
promise = @http.post(url, JSON.stringify(data), extraParams)
promise.success (_data, _status) =>
defered.resolve(@model.make_model(name, _data, null, dataTypes))
@ -67,7 +67,7 @@ class RepositoryService extends taiga.Service
promises = _.map(models, (x) => @.save(x, true))
return @q.all(promises)
save: (model, patch=true) ->
save: (model, patch=true, params = {}, options, returnHeaders = false) ->
defered = @q.defer()
if not model.isModified() and patch
@ -75,20 +75,25 @@ class RepositoryService extends taiga.Service
return defered.promise
url = @.resolveUrlForModel(model)
data = JSON.stringify(model.getAttrs(patch))
if patch
promise = @http.patch(url, data)
promise = @http.patch(url, data, params, options)
else
promise = @http.put(url, data)
promise = @http.put(url, data, params, options)
promise.success (data, status) =>
promise.success (data, status, headers, response) =>
model._isModified = false
model._attrs = _.extend(model.getAttrs(), data)
model._modifiedAttrs = {}
model.applyCasts()
defered.resolve(model)
if returnHeaders
defered.resolve([model, headers()])
else
defered.resolve(model)
promise.error (data, status) ->
defered.reject(data)

View File

@ -398,3 +398,50 @@ Autofocus = ($timeout) ->
}
module.directive('tgAutofocus', ['$timeout', Autofocus])
module.directive 'tgPreloadImage', () ->
spinner = "<img class='loading-spinner' src='/" + window._version + "/svg/spinner-circle.svg' alt='loading...' />"
template = """
<div>
<ng-transclude></ng-transclude>
</div>
"""
preload = (src, onLoad) ->
image = new Image()
image.onload = onLoad
image.src = src
return image
return {
template: template,
transclude: true,
replace: true,
link: (scope, el, attrs) ->
image = el.find('img:last')
timeout = null
onLoad = () ->
el.find('.loading-spinner').remove()
image.show()
if timeout
clearTimeout(timeout)
timeout = null
attrs.$observe 'preloadSrc', (src) ->
if timeout
clearTimeout(timeout)
el.find('.loading-spinner').remove()
timeout = setTimeout () ->
el.prepend(spinner)
, 200
image.hide()
preload(src, onLoad)
}

View File

@ -126,7 +126,7 @@ module.directive("tgSprintProgressbar", SprintProgressBarDirective)
## Created-by display directive
#############################################################################
CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)->
CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls, avatarService)->
# Display the owner information (full name and photo) and the date of
# creation of an object (like USs, tasks and issues).
#
@ -141,11 +141,15 @@ CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)->
link = ($scope, $el, $attrs) ->
bindOnce $scope, $attrs.ngModel, (model) ->
if model?
avatar = avatarService.getAvatar(model.owner_extra_info)
$scope.owner = model.owner_extra_info or {
full_name_display: $translate.instant("COMMON.EXTERNAL_USER")
photo: "/" + window._version + "/images/user-noimage.png"
}
$scope.owner.avatar = avatar.url
$scope.owner.bg = avatar.bg
$scope.url = if $scope.owner?.is_active then $navUrls.resolve("user-profile", {username: $scope.owner.username}) else ""
@ -162,9 +166,45 @@ CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)->
templateUrl: "common/components/created-by.html"
}
module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls",
module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls", "tgAvatarService",
CreatedByDisplayDirective])
UserDisplayDirective = ($template, $compile, $translate, $navUrls, avatarService)->
# Display the user information (full name and photo).
#
# Example:
# div.creator(tg-user-display, tg-user-id="{{ user.id }}")
#
# Requirements:
# - scope.usersById object is required.
link = ($scope, $el, $attrs) ->
id = $attrs.tgUserId
$scope.user = $scope.usersById[id] or {
full_name_display: $translate.instant("COMMON.EXTERNAL_USER")
}
avatar = avatarService.getAvatar($scope.usersById[id] or null)
$scope.user.avatar = avatar.url
$scope.user.bg = avatar.bg
$scope.url = if $scope.user.is_active then $navUrls.resolve("user-profile", {username: $scope.user.username}) else ""
$scope.$on "$destroy", ->
$el.off()
return {
link: link
restrict: "EA"
scope: true,
templateUrl: "common/components/user-display.html"
}
module.directive("tgUserDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls", "tgAvatarService",
UserDisplayDirective])
#############################################################################
## Watchers directive
#############################################################################
@ -172,7 +212,6 @@ module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate",
WatchersDirective = ($rootscope, $confirm, $repo, $modelTransform, $template, $compile, $translate) ->
# You have to include a div with the tg-lb-watchers directive in the page
# where use this directive
template = $template.get("common/components/watchers.html", true)
link = ($scope, $el, $attrs, $model) ->
isEditable = ->
@ -209,13 +248,8 @@ WatchersDirective = ($rootscope, $confirm, $repo, $modelTransform, $template, $c
$confirm.notify("error")
renderWatchers = (watchers) ->
ctx = {
watchers: watchers
isEditable: isEditable()
}
html = $compile(template(ctx))($scope)
$el.html(html)
$scope.watchers = watchers
$scope.isEditable = isEditable()
$el.on "click", ".js-delete-watcher", (event) ->
event.preventDefault()
@ -244,12 +278,19 @@ WatchersDirective = ($rootscope, $confirm, $repo, $modelTransform, $template, $c
$scope.$watch $attrs.ngModel, (item) ->
return if not item?
watchers = _.map(item.watchers, (watcherId) -> $scope.usersById[watcherId])
watchers = _.filter watchers, (it) -> return !!it
renderWatchers(watchers)
$scope.$on "$destroy", ->
$el.off()
return {link:link, require:"ngModel"}
return {
scope: true,
templateUrl: "common/components/watchers.html",
link:link,
require:"ngModel"
}
module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQueueModelTransformation", "$tgTemplate", "$compile",
"$translate", WatchersDirective])
@ -259,7 +300,7 @@ module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQueue
## Assigned to directive
#############################################################################
AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $template, $translate, $compile, $currentUserService) ->
AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $template, $translate, $compile, $currentUserService, avatarService) ->
# You have to include a div with the tg-lb-assignedto directive in the page
# where use this directive
template = $template.get("common/components/assigned-to.html", true)
@ -293,20 +334,23 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $
return transform
renderAssignedTo = (assignedObject) ->
avatar = avatarService.getAvatar(assignedObject?.assigned_to_extra_info)
bg = null
if assignedObject?.assigned_to?
fullName = assignedObject.assigned_to_extra_info.full_name_display
photo = assignedObject.assigned_to_extra_info.photo
isUnassigned = false
bg = avatar.bg
else
fullName = $translate.instant("COMMON.ASSIGNED_TO.ASSIGN")
photo = "/#{window._version}/images/unnamed.png"
isUnassigned = true
isIocaine = assignedObject?.is_iocaine
ctx = {
fullName: fullName
photo: photo
avatar: avatar.url
bg: bg
isUnassigned: isUnassigned
isEditable: isEditable()
isIocaine: isIocaine
@ -353,7 +397,7 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $
require:"ngModel"
}
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQueueModelTransformation", "$tgTemplate", "$translate", "$compile","tgCurrentUserService",
module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQueueModelTransformation", "$tgTemplate", "$translate", "$compile","tgCurrentUserService", "tgAvatarService",
AssignedToDirective])
@ -448,93 +492,6 @@ DeleteButtonDirective = ($log, $repo, $confirm, $location, $template) ->
module.directive("tgDeleteButton", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "$tgTemplate", DeleteButtonDirective])
#############################################################################
## Editable subject directive
#############################################################################
EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $modelTransform, $template) ->
template = $template.get("common/components/editable-subject.html")
link = ($scope, $el, $attrs, $model) ->
$scope.$on "object:updated", () ->
$el.find('.edit-subject').hide()
$el.find('.view-subject').show()
isEditable = ->
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
save = (subject) ->
currentLoading = $loading()
.target($el.find('.save-container'))
.start()
transform = $modelTransform.save (item) ->
item.subject = subject
return item
transform.then =>
$confirm.notify("success")
$rootscope.$broadcast("object:updated")
$el.find('.edit-subject').hide()
$el.find('.view-subject').show()
transform.then null, ->
$confirm.notify("error")
transform.finally ->
currentLoading.finish()
return transform
$el.click ->
return if not isEditable()
$el.find('.edit-subject').show()
$el.find('.view-subject').hide()
$el.find('input').focus()
$el.on "click", ".save", (e) ->
e.preventDefault()
subject = $scope.item.subject
save(subject)
$el.on "keyup", "input", (event) ->
if event.keyCode == 13
subject = $scope.item.subject
save(subject)
else if event.keyCode == 27
$scope.$apply () => $model.$modelValue.revert()
$el.find('.edit-subject').hide()
$el.find('.view-subject').show()
$el.find('.edit-subject').hide()
$scope.$watch $attrs.ngModel, (value) ->
return if not value
$scope.item = value
if not isEditable()
$el.find('.view-subject .edit').remove()
$scope.$on "$destroy", ->
$el.off()
return {
link: link
restrict: "EA"
require: "ngModel"
template: template
}
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQueueModelTransformation",
"$tgTemplate", EditableSubjectDirective])
#############################################################################
## Editable description directive
#############################################################################
@ -656,7 +613,7 @@ EditableWysiwyg = (attachmentsService, attachmentsFullService) ->
link = ($scope, $el, $attrs, $model) ->
isInEditMode = ->
return $el.find('textarea').is(':visible')
return $el.find('textarea').is(':visible') and $model.$modelValue.id
uploadFile = (file, type) ->
@ -721,6 +678,16 @@ module.directive("tgEditableWysiwyg", ["tgAttachmentsService", "tgAttachmentsFul
## completely bindonce, they only serves for visualization of data.
#############################################################################
ListItemEpicStatusDirective = ->
link = ($scope, $el, $attrs) ->
epic = $scope.$eval($attrs.tgListitemEpicStatus)
bindOnce $scope, "epicStatusById", (epicStatusById) ->
$el.html(epicStatusById[epic.status].name)
return {link:link}
module.directive("tgListitemEpicStatus", ListItemEpicStatusDirective)
ListItemUsStatusDirective = ->
link = ($scope, $el, $attrs) ->
us = $scope.$eval($attrs.tgListitemUsStatus)
@ -743,7 +710,7 @@ ListItemTaskStatusDirective = ->
module.directive("tgListitemTaskStatus", ListItemTaskStatusDirective)
ListItemAssignedtoDirective = ($template, $translate) ->
ListItemAssignedtoDirective = ($template, $translate, avatarService) ->
template = $template.get("common/components/list-item-assigned-to-avatar.html", true)
link = ($scope, $el, $attrs) ->
@ -751,19 +718,22 @@ ListItemAssignedtoDirective = ($template, $translate) ->
item = $scope.$eval($attrs.tgListitemAssignedto)
ctx = {
name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"),
imgurl: "/#{window._version}/images/unnamed.png"
}
member = usersById[item.assigned_to]
avatar = avatarService.getAvatar(member)
ctx.imgurl = avatar.url
ctx.bg = avatar.bg
if member
ctx.imgurl = member.photo
ctx.name = member.full_name_display
$el.html(template(ctx))
return {link:link}
module.directive("tgListitemAssignedto", ["$tgTemplate", "$translate", ListItemAssignedtoDirective])
module.directive("tgListitemAssignedto", ["$tgTemplate", "$translate", "tgAvatarService", ListItemAssignedtoDirective])
ListItemIssueStatusDirective = ->

View File

@ -112,31 +112,23 @@ CustomAttributesValuesDirective = ($templates, $storage) ->
link = ($scope, $el, $attrs, $ctrls) ->
$ctrl = $ctrls[0]
$model = $ctrls[1]
hash = collapsedHash($attrs.type)
$scope.collapsed = $storage.get(hash) or false
bindOnce $scope, $attrs.ngModel, (value) ->
$ctrl.initialize($attrs.type, value.id)
$ctrl.loadCustomAttributesValues()
$el.on "click", ".custom-fields-header .collapse", ->
hash = collapsedHash($attrs.type)
collapsed = not($storage.get(hash) or false)
$storage.set(hash, collapsed)
if collapsed
$el.find(".custom-fields-header .icon").removeClass("open")
$el.find(".custom-fields-body").removeClass("open")
else
$el.find(".custom-fields-header .icon").addClass("open")
$el.find(".custom-fields-body").addClass("open")
$scope.toggleCollapse = () ->
$scope.collapsed = !$scope.collapsed
$storage.set(hash, $scope.collapsed)
$scope.$on "$destroy", ->
$el.off()
templateFn = ($el, $attrs) ->
collapsed = $storage.get(collapsedHash($attrs.type)) or false
return template({
requiredEditionPerm: $attrs.requiredEditionPerm
collapsed: collapsed
})
return {

View File

@ -57,6 +57,7 @@ LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $template,
totalPoints: @calculateTotalPoints()
roles: @calculateRoles()
editable: @isEditable
loading: false
}
mainTemplate = "common/estimation/us-estimation-points-per-role.html"
template = $template.get(mainTemplate, true)
@ -111,14 +112,19 @@ UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $template, $c
if us
estimationProcess = $tgEstimationsService.create($el, us, $scope.project)
estimationProcess.onSelectedPointForRole = (roleId, pointId, points) ->
estimationProcess.loading = roleId
estimationProcess.render()
save(points).then () ->
estimationProcess.loading = false
$rootScope.$broadcast("object:updated")
estimationProcess.render()
estimationProcess.render = () ->
ctx = {
totalPoints: @calculateTotalPoints()
roles: @calculateRoles()
editable: @isEditable
loading: estimationProcess.loading
}
mainTemplate = "common/estimation/us-estimation-points-per-role.html"
template = $template.get(mainTemplate, true)
@ -154,6 +160,7 @@ EstimationsService = ($template, $repo, $confirm, $q, $qqueue) ->
@isEditable = @project.my_permissions.indexOf("modify_us") != -1
@roles = @project.roles
@points = @project.points
@loading = false
@pointsById = groupBy(@points, (x) -> x.id)
@onSelectedPointForRole = (roleId, pointId) ->
@render = () ->
@ -163,6 +170,7 @@ EstimationsService = ($template, $repo, $confirm, $q, $qqueue) ->
$qqueue.add () =>
onSuccess = =>
deferred.resolve()
@render()
onError = =>
$confirm.notify("error")
@ -215,7 +223,6 @@ EstimationsService = ($template, $repo, $confirm, $q, $qqueue) ->
pointId = target.data("point-id")
@$el.find(".popover").popover().close()
points = _.clone(@us.points, true)
points[roleId] = pointId

View File

@ -74,3 +74,56 @@ sizeFormat = =>
return @.taiga.sizeFormat
module.filter("sizeFormat", sizeFormat)
toMutableFilter = ->
toMutable = (js) ->
return js.toJS()
memoizedMutable = _.memoize(toMutable)
return (input) ->
if input instanceof Immutable.List
return memoizedMutable(input)
return input
module.filter("toMutable", toMutableFilter)
byRefFilter = ($filterFilter)->
return (userstories, filter) ->
if filter?.startsWith("#")
cleanRef= filter.substr(1)
return _.filter(userstories, (us) => String(us.ref).startsWith(cleanRef))
return $filterFilter(userstories, filter)
module.filter("byRef", ["filterFilter", byRefFilter])
darkerFilter = ->
return (color, luminosity) ->
# validate hex string
color = new String(color).replace(/[^0-9a-f]/gi, '')
if color.length < 6
color = color[0]+ color[0]+ color[1]+ color[1]+ color[2]+ color[2];
luminosity = luminosity || 0
# convert to decimal and change luminosity
newColor = "#"
c = 0
i = 0
black = 0
white = 255
# for (i = 0; i < 3; i++)
for i in [0, 1, 2]
c = parseInt(color.substr(i*2,2), 16)
c = Math.round(Math.min(Math.max(black, c + (luminosity * white)), white)).toString(16)
newColor += ("00"+c).substr(c.length)
return newColor
module.filter("darker", darkerFilter)

View File

@ -1,507 +0,0 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/common/history.coffee
###
taiga = @.taiga
trim = @.taiga.trim
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
module = angular.module("taigaCommon")
IGNORED_FIELDS = {
"userstories.userstory": [
"watchers", "kanban_order", "backlog_order", "sprint_order", "finish_date", "tribe_gig"
],
"tasks.task": [
"watchers", "us_order", "taskboard_order"
],
"issues.issue": [
"watchers"
]
}
#############################################################################
## History Directive (Main)
#############################################################################
class HistoryController extends taiga.Controller
@.$inject = ["$scope", "$tgRepo", "$tgResources"]
constructor: (@scope, @repo, @rs) ->
initialize: (type, objectId) ->
@.type = type
@.objectId = objectId
loadHistory: (type, objectId) ->
return @rs.history.get(type, objectId).then (history) =>
for historyResult in history
# If description was modified take only the description_html field
if historyResult.values_diff.description_diff?
historyResult.values_diff.description = historyResult.values_diff.description_diff
delete historyResult.values_diff.description_html
delete historyResult.values_diff.description_diff
# If block note was modified take only the blocked_note_html field
if historyResult.values_diff.blocked_note_diff?
historyResult.values_diff.blocked_note = historyResult.values_diff.blocked_note_diff
delete historyResult.values_diff.blocked_note_html
delete historyResult.values_diff.blocked_note_diff
for historyEntry in history
changeModel = historyEntry.key.split(":")[0]
if IGNORED_FIELDS[changeModel]?
historyEntry.values_diff = _.removeKeys(historyEntry.values_diff, IGNORED_FIELDS[changeModel])
@scope.history = _.filter(history, (item) -> Object.keys(item.values_diff).length > 0)
@scope.comments = _.filter(history, (item) -> item.comment != "")
deleteComment: (type, objectId, activityId) ->
return @rs.history.deleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
undeleteComment: (type, objectId, activityId) ->
return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $compile, $navUrls, $rootScope, checkPermissionsService) ->
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)
templateChangeAttachment = $template.get("common/history/history-change-attachment.html", true)
templateChangeList = $template.get("common/history/history-change-list.html", true)
templateDeletedComment = $template.get("common/history/history-deleted-comment.html", true)
templateActivity = $template.get("common/history/history-activity.html", true)
templateBaseEntries = $template.get("common/history/history-base-entries.html", true)
templateBase = $template.get("common/history/history-base.html", true)
link = ($scope, $el, $attrs, $ctrl) ->
# Bootstraping
type = $attrs.type
objectId = null
showAllComments = false
showAllActivity = false
getPrettyDateFormat = ->
return $translate.instant("ACTIVITY.DATETIME")
bindOnce $scope, $attrs.ngModel, (model) ->
type = $attrs.type
objectId = model.id
$ctrl.initialize(type, objectId)
$ctrl.loadHistory(type, objectId)
# Helpers
getHumanizedFieldName = (field) ->
humanizedFieldNames = {
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")
}
return humanizedFieldNames[field] or field
countChanges = (comment) ->
return _.keys(comment.values_diff).length
formatChange = (change) ->
if _.isArray(change)
if change.length == 0
return $translate.instant("ACTIVITY.VALUES.EMPTY")
return change.join(", ")
if change == ""
return $translate.instant("ACTIVITY.VALUES.EMPTY")
if not change? or change == false
return $translate.instant("ACTIVITY.VALUES.NO")
if change == true
return $translate.instant("ACTIVITY.VALUES.YES")
return change
# Render into string (operations without mutability)
renderAttachmentEntry = (value) ->
attachments = _.map value, (changes, type) ->
if type == "new"
return _.map changes, (change) ->
return templateChangeDiff({
name: $translate.instant("ACTIVITY.NEW_ATTACHMENT"),
diff: change.filename
})
else if type == "deleted"
return _.map changes, (change) ->
return templateChangeDiff({
name: $translate.instant("ACTIVITY.DELETED_ATTACHMENT"),
diff: change.filename
})
else
return _.map changes, (change) ->
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})
return _.flatten(attachments).join("\n")
renderCustomAttributesEntry = (value) ->
customAttributes = _.map value, (changes, type) ->
if type == "new"
return _.map changes, (change) ->
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) ->
return templateChangeDiff({
name: $translate.instant("ACTIVITY.DELETED_CUSTOM_ATTRIBUTE")
diff: change.name
})
else
return _.map changes, (change) ->
customAttrsChanges = _.map change.changes, (values) ->
return templateChangeGeneric({
name: change.name
from: formatChange(values[0])
to: formatChange(values[1])
})
return _.flatten(customAttrsChanges).join("\n")
return _.flatten(customAttributes).join("\n")
renderChangeEntry = (field, value) ->
if field == "description"
return templateChangeDiff({name: getHumanizedFieldName("description"), diff: value[1]})
else if field == "blocked_note"
return templateChangeDiff({name: getHumanizedFieldName("blocked_note"), diff: value[1]})
else if field == "points"
html = templateChangePoints({points: value})
html = $compile(html)($scope)
return html[0].outerHTML
else if field == "attachments"
return renderAttachmentEntry(value)
else if field == "custom_attributes"
return renderCustomAttributesEntry(value)
else if field in ["tags", "watchers"]
name = getHumanizedFieldName(field)
removed = _.difference(value[0], value[1])
added = _.difference(value[1], value[0])
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 $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)
from = formatChange(value[0])
to = formatChange(value[1])
return templateChangeGeneric({name:name, from:from, to: to})
renderChangeEntries = (change) ->
return _.map(change.values_diff, (value, field) -> renderChangeEntry(field, value))
renderChangesHelperText = (change) ->
size = countChanges(change)
return $translate.instant("ACTIVITY.SIZE_CHANGE", {size: size}, 'messageformat')
renderComment = (comment) ->
if (comment.delete_comment_date or comment.delete_comment_user?.name)
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: ($scope.user and
(comment.delete_comment_user.pk == $scope.user.id or
$scope.project.my_permissions.indexOf("modify_project") > -1))
})
html = $compile(html)($scope)
return html[0].outerHTML
html = templateActivity({
avatar: comment.user.photo
userFullName: comment.user.name
userProfileUrl: if comment.user.is_active then $navUrls.resolve("user-profile", {username: comment.user.username}) else ""
creationDate: moment(comment.created_at).format(getPrettyDateFormat())
comment: comment.comment_html
changesText: renderChangesHelperText(comment)
changes: renderChangeEntries(comment)
mode: "comment"
deleteCommentActionTitle: $translate.instant("COMMENTS.DELETE")
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: change.user.photo
userFullName: change.user.name
userProfileUrl: if change.user.is_active then $navUrls.resolve("user-profile", {username: change.user.username}) else ""
creationDate: moment(change.created_at).format(getPrettyDateFormat())
comment: change.comment_html
changes: renderChangeEntries(change)
changesText: ""
mode: "activity"
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
})
renderHistory = (entries, totalEntries) ->
if entries.length == totalEntries
showMore = 0
else
showMore = totalEntries - entries.length
html = templateBaseEntries({entries: entries, showMore:showMore})
html = $compile(html)($scope)
return html
# Render into DOM (operations with dom mutability)
renderBase = ->
comments = $scope.comments or []
changes = $scope.history or []
historyVisible = !!changes.length
commentsVisible = (!!comments.length) || checkPermissionsService.check('modify_' + $attrs.type)
html = templateBase({
ngmodel: $attrs.ngModel,
type: $attrs.type,
mode: $attrs.mode,
historyVisible: historyVisible,
commentsVisible: commentsVisible
})
html = $compile(html)($scope)
$el.html(html)
rerender = ->
renderBase()
renderComments()
renderActivity()
renderComments = ->
comments = $scope.comments or []
totalComments = comments.length
if not showAllComments
comments = _.takeRight(comments, 4)
comments = _.map comments, (x) -> renderComment(x)
html = renderHistory(comments, totalComments)
$el.find(".comments-list").html(html)
renderActivity = ->
changes = $scope.history or []
totalChanges = changes.length
if not showAllActivity
changes = _.takeRight(changes, 4)
changes = _.map(changes, (x) -> renderChange(x))
html = renderHistory(changes, totalChanges)
$el.find(".changes-list").html(html)
save = $qqueue.bindAdd (target) =>
$scope.$broadcast("markdown-editor:submit")
$el.find(".comment-list").addClass("activeanimation")
currentLoading = $loading()
.target(target)
.start()
onSuccess = ->
$rootScope.$broadcast("comment:new")
$ctrl.loadHistory(type, objectId).finally ->
currentLoading.finish()
onError = ->
currentLoading.finish()
$confirm.notify("error")
model = $scope.$eval($attrs.ngModel)
$ctrl.repo.save(model).then(onSuccess, onError)
# Watchers
$scope.$watch("comments", rerender)
$scope.$watch("history", rerender)
$scope.$on("object:updated", -> $ctrl.loadHistory(type, objectId))
# Events
$el.on "click", ".add-comment .button-green", debounce 2000, (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
save(target)
$el.on "click", "a", (event) ->
target = angular.element(event.target)
href = target.attr('href')
if href && href.indexOf("#") == 0
event.preventDefault()
$('body').scrollTop($(href).offset().top)
$el.on "click", ".show-more", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
if target.parent().is(".changes-list")
showAllActivity = not showAllActivity
renderActivity()
else
showAllComments = not showAllComments
renderComments()
$el.on "click", ".show-deleted-comment", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
target.parents('.activity-single').find('.hide-deleted-comment').show()
target.parents('.activity-single').find('.show-deleted-comment').hide()
target.parents('.activity-single').find('.comment-body').show()
$el.on "click", ".hide-deleted-comment", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
target.parents('.activity-single').find('.hide-deleted-comment').hide()
target.parents('.activity-single').find('.show-deleted-comment').show()
target.parents('.activity-single').find('.comment-body').hide()
$el.on "click", ".changes-title", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
target.parent().find(".change-entry").toggleClass("active")
$el.on "focus", ".add-comment textarea", (event) ->
$(this).addClass('active')
$el.on "click", ".history-tabs a", (event) ->
target = angular.element(event.currentTarget)
$el.find(".history-tabs li").removeClass("active")
target.parent().addClass("active")
$el.find(".history section").addClass("hidden")
$el.find(".history section.#{target.data('section-class')}").removeClass("hidden")
$el.on "click", ".comment-delete", debounce 2000, (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
activityId = target.data('activity-id')
$ctrl.deleteComment(type, objectId, activityId)
$el.on "click", ".comment-restore", debounce 2000, (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
activityId = target.data('activity-id')
$ctrl.undeleteComment(type, objectId, activityId)
$scope.$on "$destroy", ->
$el.off()
renderBase()
return {
controller: HistoryController
restrict: "AE"
link: link
# require: ["ngModel", "tgHistory"]
}
module.directive("tgHistory", ["$log", "$tgLoading", "$tgQqueue", "$tgTemplate", "$tgConfirm", "$translate",
"$compile", "$tgNavUrls", "$rootScope", "tgCheckPermissionsService", HistoryDirective])

View File

@ -28,6 +28,7 @@ bindOnce = @.taiga.bindOnce
timeout = @.taiga.timeout
debounce = @.taiga.debounce
sizeFormat = @.taiga.sizeFormat
trim = @.taiga.trim
#############################################################################
## Common Lightbox Services
@ -35,9 +36,11 @@ sizeFormat = @.taiga.sizeFormat
# the lightboxContent hide/show doesn't have sense because is an IE hack
class LightboxService extends taiga.Service
constructor: (@animationFrame, @q) ->
constructor: (@animationFrame, @q, @rootScope) ->
open: ($el, onClose) ->
@.onClose = onClose
open: ($el) ->
if _.isString($el)
$el = $($el)
defered = @q.defer()
@ -70,25 +73,29 @@ class LightboxService extends taiga.Service
return defered.promise
close: ($el) ->
if _.isString($el)
$el = $($el)
docEl = angular.element(document)
docEl.off(".lightbox")
docEl.off(".keyboard-navigation") # Hack: to fix problems in the WYSIWYG textareas when press ENTER
return new Promise (resolve) =>
if _.isString($el)
$el = $($el)
docEl = angular.element(document)
docEl.off(".lightbox")
docEl.off(".keyboard-navigation") # Hack: to fix problems in the WYSIWYG textareas when press ENTER
@animationFrame.add ->
$el.addClass('close')
@animationFrame.add =>
$el.addClass('close')
$el.one "transitionend", =>
$el.removeAttr('style')
$el.removeClass("open").removeClass('close')
$el.one "transitionend", =>
$el.removeAttr('style')
$el.removeClass("open").removeClass('close')
if @.onClose
@rootScope.$apply(@.onClose)
resolve()
if $el.hasClass("remove-on-close")
scope = $el.data("scope")
scope.$destroy() if scope
$el.remove()
if $el.hasClass("remove-on-close")
scope = $el.data("scope")
scope.$destroy() if scope
$el.remove()
closeAll: ->
docEl = angular.element(document)
@ -96,7 +103,7 @@ class LightboxService extends taiga.Service
@.close($(lightboxEl))
module.service("lightboxService", ["animationFrame", "$q", LightboxService])
module.service("lightboxService", ["animationFrame", "$q", "$rootScope", LightboxService])
class LightboxKeyboardNavigationService extends taiga.Service
@ -292,7 +299,44 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
attachmentsToAdd = attachmentsToAdd.push(attachment)
$scope.deleteAttachment = (attachment) ->
attachmentsToDelete = attachmentsToDelete.push(attachment)
if attachment.get("id")
attachmentsToDelete = attachmentsToDelete.push(attachment)
$scope.addTag = (tag, color) ->
value = trim(tag.toLowerCase())
tags = $scope.project.tags
projectTags = $scope.project.tags_colors
tags = [] if not tags?
projectTags = {} if not projectTags?
if value not in tags
tags.push(value)
projectTags[tag] = color || null
$scope.project.tags = tags
itemtags = _.clone($scope.us.tags)
inserted = _.find itemtags, (it) -> it[0] == value
if !inserted
itemtags.push([tag , color])
$scope.us.tags = itemtags
$scope.deleteTag = (tag) ->
value = trim(tag[0].toLowerCase())
tags = $scope.project.tags
itemtags = _.clone($scope.us.tags)
_.remove itemtags, (tag) -> tag[0] == value
$scope.us.tags = itemtags
_.pull($scope.us.tags, value)
$scope.$on "usform:new", (ctx, projectId, status, statusList) ->
form.reset() if form
@ -320,7 +364,10 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
$el.find("label.team-requirement").removeClass("selected")
$el.find("label.client-requirement").removeClass("selected")
lightboxService.open($el)
$scope.createEditUsOpen = true
lightboxService.open $el, () ->
$scope.createEditUsOpen = false
$scope.$on "usform:edit", (ctx, us, attachments) ->
form.reset() if form
@ -353,7 +400,10 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
else
$el.find("label.client-requirement").removeClass("selected")
lightboxService.open($el)
$scope.createEditUsOpen = true
lightboxService.open $el, () ->
$scope.createEditUsOpen = false
createAttachments = (obj) ->
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
@ -378,22 +428,28 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
.target(submitButton)
.start()
params = {
include_attachments: true,
include_tasks: true
}
if $scope.isNew
promise = $repo.create("userstories", $scope.us)
broadcastEvent = "usform:new:success"
else
promise = $repo.save($scope.us)
promise = $repo.save($scope.us, true)
broadcastEvent = "usform:edit:success"
promise.then (data) ->
deleteAttachments(data).then () => createAttachments(data)
deleteAttachments(data)
.then () => createAttachments(data)
.then () =>
currentLoading.finish()
lightboxService.close($el)
return data
$rs.userstories.getByRef(data.project, data.ref, params).then (us) ->
$rootScope.$broadcast(broadcastEvent, us)
promise.then (data) ->
currentLoading.finish()
lightboxService.close($el)
$rootScope.$broadcast(broadcastEvent, data)
promise.then null, (data) ->
currentLoading.finish()
@ -407,8 +463,10 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
$el.on "click", ".close", (event) ->
event.preventDefault()
$scope.$apply ->
$scope.us.revert()
lightboxService.close($el)
$el.keydown (event) ->
@ -433,7 +491,7 @@ module.directive("tgLbCreateEditUserstory", [
"$translate",
"$tgConfirm",
"$q",
"tgAttachmentsService",
"tgAttachmentsService"
CreateEditUserstoryDirective
])
@ -442,7 +500,7 @@ module.directive("tgLbCreateEditUserstory", [
## Creare Bulk Userstories Lightbox Directive
#############################################################################
CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $loading) ->
CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $loading, $model) ->
link = ($scope, $el, attrs) ->
form = null
@ -469,6 +527,7 @@ CreateBulkUserstoriesDirective = ($repo, $rs, $rootscope, lightboxService, $load
promise = $rs.userstories.bulkCreate($scope.new.projectId, $scope.new.statusId, $scope.new.bulk)
promise.then (result) ->
result = _.map(result.data, (x) => $model.make_model('userstories', x))
currentLoading.finish()
$rootscope.$broadcast("usform:bulk:success", result)
lightboxService.close($el)
@ -494,6 +553,7 @@ module.directive("tgLbCreateBulkUserstories", [
"$rootScope",
"lightboxService",
"$tgLoading",
"$tgModel",
CreateBulkUserstoriesDirective
])
@ -502,7 +562,7 @@ module.directive("tgLbCreateBulkUserstories", [
## AssignedTo Lightbox Directive
#############################################################################
AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationService, $template, $compile) ->
AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationService, $template, $compile, avatarService) ->
link = ($scope, $el, $attrs) ->
selectedUser = null
selectedItem = null
@ -527,12 +587,21 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
render = (selected, text) ->
users = _.clone($scope.activeUsers, true)
users = _.reject(users, {"id": selected.id}) if selected?
users = _.sortBy(users, (o) -> if o.id is $scope.user.id then 0 else o.id)
users = _.filter(users, _.partial(filterUsers, text)) if text?
visibleUsers = _.slice(users, 0, 5)
visibleUsers = _.map visibleUsers, (user) ->
user.avatar = avatarService.getAvatar(user)
if selected
selected.avatar = avatarService.getAvatar(selected) if selected
ctx = {
selected: selected
users: _.slice(users, 0, 5)
showMore: users.length > 5
showMore: visibleUsers
}
html = usersTemplate(ctx)
@ -596,14 +665,14 @@ AssignedToLightboxDirective = (lightboxService, lightboxKeyboardNavigationServic
}
module.directive("tgLbAssignedto", ["lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", AssignedToLightboxDirective])
module.directive("tgLbAssignedto", ["lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", "tgAvatarService", AssignedToLightboxDirective])
#############################################################################
## Watchers Lightbox directive
#############################################################################
WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template, $compile) ->
WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template, $compile, avatarService) ->
link = ($scope, $el, $attrs) ->
selectedItem = null
usersTemplate = $template.get("common/lightbox/lightbox-assigned-to-users.html", true)
@ -625,9 +694,16 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
# Render the specific list of users.
render = (users) ->
visibleUsers = _.slice(users, 0, 5)
visibleUsers = _.map visibleUsers, (user) ->
user.avatar = avatarService.getAvatar(user)
return user
ctx = {
selected: false
users: _.slice(users, 0, 5)
users: visibleUsers
showMore: users.length > 5
}
@ -683,25 +759,9 @@ WatchersLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationS
link:link
}
module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", WatchersLightboxDirective])
module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", "tgAvatarService", WatchersLightboxDirective])
#############################################################################
## Attachment Preview Lighbox
#############################################################################
AttachmentPreviewLightboxDirective = (lightboxService, $template, $compile) ->
link = ($scope, $el, attrs) ->
lightboxService.open($el)
return {
templateUrl: 'common/lightbox/lightbox-attachment-preview.html',
link: link,
scope: true
}
module.directive("tgLbAttachmentPreview", ["lightboxService", "$tgTemplate", "$compile", AttachmentPreviewLightboxDirective])
LightboxLeaveProjectWarningDirective = (lightboxService, $template, $compile) ->
link = ($scope, $el, attrs) ->
lightboxService.open($el)

View File

@ -59,6 +59,7 @@ TgLoadingService = ($compile) ->
start: ->
target = service.settings.target
service.settings.classes.map (className) -> target.removeClass(className)
if not target.hasClass('loading') && !service.settings.template
@ -109,6 +110,7 @@ LoadingDirective = ($loading) ->
template = $el.html()
$scope.$watch attr.tgLoading, (showLoading) =>
if showLoading
currentLoading = $loading()
.target($el)
@ -120,6 +122,7 @@ LoadingDirective = ($loading) ->
currentLoading.finish()
return {
priority: 99999,
link:link
}

View File

@ -26,6 +26,7 @@ taiga = @.taiga
trim = @.taiga.trim
bindOnce = @.taiga.bindOnce
module = angular.module("taigaCommon")
# Directive that parses/format tags inputfield.
@ -61,28 +62,38 @@ ColorizeTagsDirective = ->
templates = {
backlog: _.template("""
<% _.each(tags, function(tag) { %>
<span class="tag" style="border-left: 5px solid <%- tag.color %>"><%- tag.name %></span>
<span class="tag"
<% if (tag[1] !== null) { %>
style="border-left: 5px solid <%- tag[1] %>"
<% } %>
title="<%- tag[0] %>"><%- tag[0] %></span>
<% }) %>
""")
kanban: _.template("""
<% _.each(tags, function(tag) { %>
<a class="kanban-tag" href="" style="border-color: <%- tag.color %>" title="<%- tag.name %>" />
<a class="kanban-tag"
href=""
<% if (tag[1] !== null) { %>
style="border-color: <%- tag[1] %>"
<% } %>
title="<%- tag[0] %>" />
<% }) %>
""")
taskboard: _.template("""
<% _.each(tags, function(tag) { %>
<a class="taskboard-tag" href="" style="border-color: <%- tag.color %>" title="<%- tag.name %>" />
<a class="taskboard-tag"
href=""
<% if (tag[1] !== null) { %>
style="border-color: <%- tag[1] %>"
<% } %>
title="<%- tag[0] %>" />
<% }) %>
""")
}
link = ($scope, $el, $attrs, $ctrl) ->
render = (srcTags) ->
render = (tags) ->
template = templates[$attrs.tgColorizeTagsType]
srcTags.sort()
tags = _.map srcTags, (tag) ->
color = $scope.project.tags_colors[tag]
return {name: tag, color: color}
html = template({tags: tags})
$el.html(html)
@ -111,15 +122,18 @@ LbTagLineDirective = ($rs, $template, $compile) ->
autocomplete = null
link = ($scope, $el, $attrs, $model) ->
## Render
renderTags = (tags, tagsColors) ->
ctx = {
tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]})
}
withoutColors = _.has($attrs, "withoutColors")
_.map ctx.tags, (tag) =>
if tag.color
tag.style = "border-left: 5px solid #{tag.color}"
## Render
renderTags = (tags, tagsColors = []) ->
color = if not withoutColors then tagsColors[t] else null
ctx = {
tags: _.map(tags, (t) -> {
name: t,
style: if color then "border-left: 5px solid #{color}" else ""
})
}
html = $compile(templateTags(ctx))($scope)
$el.find(".tags-container").html(html)
@ -196,7 +210,7 @@ LbTagLineDirective = ($rs, $template, $compile) ->
autocomplete = new Awesomplete(input[0], {
list: _.keys(project.tags_colors)
});
})
input.on "awesomplete-selectcomplete", () ->
addValue(input.val())
@ -216,189 +230,3 @@ LbTagLineDirective = ($rs, $template, $compile) ->
}
module.directive("tgLbTagLine", ["$tgResources", "$tgTemplate", "$compile", LbTagLineDirective])
#############################################################################
## TagLine Directive (for detail pages)
#############################################################################
TagLineDirective = ($rootScope, $repo, $rs, $confirm, $modelTransform, $template, $compile) ->
ENTER_KEY = 13
ESC_KEY = 27
COMMA_KEY = 188
templateTags = $template.get("common/tag/tags-line-tags.html", true)
link = ($scope, $el, $attrs, $model) ->
autocomplete = null
isEditable = ->
if $attrs.requiredPerm?
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
return true
## Render
renderTags = (tags, tagsColors) ->
ctx = {
tags: _.map(tags, (t) -> {name: t, color: tagsColors[t]})
isEditable: isEditable()
}
html = $compile(templateTags(ctx))($scope)
$el.find("div.tags-container").html(html)
renderInReadModeOnly = ->
$el.find(".add-tag").remove()
$el.find("input").remove()
$el.find(".save").remove()
showAddTagButton = -> $el.find(".add-tag").removeClass("hidden")
hideAddTagButton = -> $el.find(".add-tag").addClass("hidden")
showAddTagButtonText = -> $el.find(".add-tag-text").removeClass("hidden")
hideAddTagButtonText = -> $el.find(".add-tag-text").addClass("hidden")
showSaveButton = -> $el.find(".save").removeClass("hidden")
hideSaveButton = -> $el.find(".save").addClass("hidden")
showInput = -> $el.find("input").removeClass("hidden").focus()
hideInput = -> $el.find("input").addClass("hidden").blur()
resetInput = ->
$el.find("input").val("")
autocomplete.close()
## Aux methods
addValue = (value) ->
value = trim(value.toLowerCase())
return if value.length == 0
transform = $modelTransform.save (item) ->
if not item.tags
item.tags = []
tags = _.clone(item.tags)
tags.push(value) if value not in tags
item.tags = tags
return item
onSuccess = ->
$rootScope.$broadcast("object:updated")
onError = ->
$confirm.notify("error")
hideSaveButton()
return transform.then(onSuccess, onError)
deleteValue = (value) ->
value = trim(value.toLowerCase())
return if value.length == 0
transform = $modelTransform.save (item) ->
tags = _.clone(item.tags, false)
item.tags = _.pull(tags, value)
return item
onSuccess = ->
$rootScope.$broadcast("object:updated")
onError = ->
$confirm.notify("error")
return transform.then(onSuccess, onError)
saveInputTag = () ->
value = $el.find("input").val()
addValue(value)
resetInput()
## Events
$el.on "keypress", "input", (event) ->
target = angular.element(event.currentTarget)
if event.keyCode == ENTER_KEY
saveInputTag()
else if String.fromCharCode(event.keyCode) == ','
event.preventDefault()
saveInputTag()
else
if target.val().length
showSaveButton()
else
hideSaveButton()
$el.on "keyup", "input", (event) ->
if event.keyCode == ESC_KEY
resetInput()
hideInput()
hideSaveButton()
showAddTagButton()
$el.on "click", ".save", (event) ->
event.preventDefault()
saveInputTag()
$el.on "click", ".add-tag", (event) ->
event.preventDefault()
hideAddTagButton()
showInput()
$el.on "click", ".remove-tag", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
value = target.siblings(".tag-name").text()
deleteValue(value)
bindOnce $scope, "project.tags_colors", (tags_colors) ->
if not isEditable()
renderInReadModeOnly()
return
showAddTagButton()
input = $el.find("input")
autocomplete = new Awesomplete(input[0], {
list: _.keys(tags_colors)
});
input.on "awesomplete-selectcomplete", () ->
addValue(input.val())
input.val("")
$scope.$watchCollection () ->
return $model.$modelValue?.tags
, () ->
model = $model.$modelValue
return if not model
if model.tags?.length
hideAddTagButtonText()
else
showAddTagButtonText()
tagsColors = $scope.project?.tags_colors or []
renderTags(model.tags, tagsColors)
$scope.$on "$destroy", ->
$el.off()
return {
link:link,
require:"ngModel"
templateUrl: "common/tag/tag-line.html"
}
module.directive("tgTagLine", ["$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$tgQueueModelTransformation",
"$tgTemplate", "$compile", TagLineDirective])

View File

@ -83,7 +83,7 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
markdownDomNode = element.parents(".markdown")
markItUpDomNode = element.parents(".markItUp")
$rs.mdrender.render($scope.projectId, $model.$modelValue).then (data) ->
$rs.mdrender.render($scope.projectId || $scope.vm.projectId, $model.$modelValue).then (data) ->
html = previewTemplate({data: data.data})
html = $compile(html)($scope)
@ -374,7 +374,7 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
search: (term, callback) ->
term = taiga.slugify(term)
searchTypes = ['issues', 'tasks', 'userstories']
searchTypes = ['issues', 'tasks', 'userstories', 'epics']
searchProps = ['ref', 'subject']
filter = (item) =>
@ -384,8 +384,7 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
return false
cancelablePromise.abort() if cancelablePromise
cancelablePromise = $rs.search.do($scope.projectId, term)
cancelablePromise = $rs.search.do($scope.projectId || $scope.vm.projectId, term)
cancelablePromise.then (res) =>
# ignore wikipages if they're the only results. can't exclude them in search
@ -441,7 +440,7 @@ MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $trans
search: (term, callback) ->
term = taiga.slugify(term)
$rs.search.do($scope.projectId, term).then (res) =>
$rs.search.do($scope.projectId || $scope.vm.projectId, term).then (res) =>
if res.count < 1
callback([])

View File

@ -110,4 +110,201 @@ class FiltersMixin
location = if load then @location else @location.noreload(@scope)
location.search(name, value)
applyStoredFilters: (projectSlug, key) ->
if _.isEmpty(@location.search())
filters = @.getFilters(projectSlug, key)
if Object.keys(filters).length
@location.search(filters)
@location.replace()
return true
return false
storeFilters: (projectSlug, params, filtersHashSuffix) ->
ns = "#{projectSlug}:#{filtersHashSuffix}"
hash = taiga.generateHash([projectSlug, ns])
@storage.set(hash, params)
getFilters: (projectSlug, filtersHashSuffix) ->
ns = "#{projectSlug}:#{filtersHashSuffix}"
hash = taiga.generateHash([projectSlug, ns])
return @storage.get(hash) or {}
formatSelectedFilters: (type, list, urlIds) ->
selectedIds = urlIds.split(',')
selectedFilters = _.filter list, (it) ->
selectedIds.indexOf(_.toString(it.id)) != -1
return _.map selectedFilters, (it) ->
return {
id: it.id
key: type + ":" + it.id
dataType: type,
name: it.name
color: it.color
}
taiga.FiltersMixin = FiltersMixin
#############################################################################
## Us Filters Mixin
#############################################################################
class UsFiltersMixin
changeQ: (q) ->
@.replaceFilter("q", q)
@.filtersReloadContent()
@.generateFilters()
removeFilter: (filter) ->
@.unselectFilter(filter.dataType, filter.id)
@.filtersReloadContent()
@.generateFilters()
addFilter: (newFilter) ->
@.selectFilter(newFilter.category.dataType, newFilter.filter.id)
@.filtersReloadContent()
@.generateFilters()
selectCustomFilter: (customFilter) ->
@.replaceAllFilters(customFilter.filter)
@.filtersReloadContent()
@.generateFilters()
saveCustomFilter: (name) ->
filters = {}
urlfilters = @location.search()
filters.tags = urlfilters.tags
filters.status = urlfilters.status
filters.assigned_to = urlfilters.assigned_to
filters.owner = urlfilters.owner
@filterRemoteStorageService.getFilters(@scope.projectId, @.storeCustomFiltersName).then (userFilters) =>
userFilters[name] = filters
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, @.storeCustomFiltersName).then(@.generateFilters)
removeCustomFilter: (customFilter) ->
@filterRemoteStorageService.getFilters(@scope.projectId, @.storeCustomFiltersName).then (userFilters) =>
delete userFilters[customFilter.id]
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, @.storeCustomFiltersName).then(@.generateFilters)
@.generateFilters()
generateFilters: ->
@.storeFilters(@params.pslug, @location.search(), @.storeFiltersName)
urlfilters = @location.search()
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.assigned_to = urlfilters.assigned_to
loadFilters.owner = urlfilters.owner
loadFilters.epic = urlfilters.epic
loadFilters.q = urlfilters.q
return @q.all([
@rs.userstories.filtersData(loadFilters),
@filterRemoteStorageService.getFilters(@scope.projectId, @.storeCustomFiltersName)
]).then (result) =>
data = result[0]
customFiltersRaw = result[1]
statuses = _.map data.statuses, (it) ->
it.id = it.id.toString()
return it
tags = _.map data.tags, (it) ->
it.id = it.name
return it
tagsWithAtLeastOneElement = _.filter tags, (tag) ->
return tag.count > 0
assignedTo = _.map data.assigned_to, (it) ->
if it.id
it.id = it.id.toString()
else
it.id = "null"
it.name = it.full_name || "Unassigned"
return it
owner = _.map data.owners, (it) ->
it.id = it.id.toString()
it.name = it.full_name
return it
epic = _.map data.epics, (it) ->
if it.id
it.id = it.id.toString()
it.name = "##{it.ref} #{it.subject}"
else
it.id = "null"
it.name = "Not in an epic"
return it
@.selectedFilters = []
if loadFilters.status
selected = @.formatSelectedFilters("status", statuses, loadFilters.status)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.tags
selected = @.formatSelectedFilters("tags", tags, loadFilters.tags)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.assigned_to
selected = @.formatSelectedFilters("assigned_to", assignedTo, loadFilters.assigned_to)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.owner
selected = @.formatSelectedFilters("owner", owner, loadFilters.owner)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.epic
selected = @.formatSelectedFilters("epic", epic, loadFilters.epic)
@.selectedFilters = @.selectedFilters.concat(selected)
@.filterQ = loadFilters.q
@.filters = [
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.STATUS"),
dataType: "status",
content: statuses
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.TAGS"),
dataType: "tags",
content: tags,
hideEmpty: true,
totalTaggedElements: tagsWithAtLeastOneElement.length
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.ASSIGNED_TO"),
dataType: "assigned_to",
content: assignedTo
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.CREATED_BY"),
dataType: "owner",
content: owner
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.EPIC"),
dataType: "epic",
content: epic
}
]
@.customFilters = []
_.forOwn customFiltersRaw, (value, key) =>
@.customFilters.push({id: key, name: key, filter: value})
taiga.UsFiltersMixin = UsFiltersMixin

View File

@ -0,0 +1,25 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/epics.coffee
###
module = angular.module("taigaEpics", [])

View File

@ -0,0 +1,337 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/epics/detail.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
toString = @.taiga.toString
joinStr = @.taiga.joinStr
groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
bindMethods = @.taiga.bindMethods
module = angular.module("taigaEpics")
#############################################################################
## Epic Detail Controller
#############################################################################
class EpicDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"tgResources"
"$routeParams",
"$q",
"$tgLocation",
"$log",
"tgAppMetaService",
"$tgAnalytics",
"$tgNavUrls",
"$translate",
"$tgQueueModelTransformation",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @rs2, @params, @q, @location,
@log, @appMetaService, @analytics, @navUrls, @translate, @modelTransform, @errorHandlingService) ->
bindMethods(@)
@scope.epicRef = @params.epicref
@scope.sectionName = @translate.instant("EPIC.SECTION_NAME")
@.initializeEventHandlers()
promise = @.loadInitialData()
# On Success
promise.then =>
@._setMeta()
@.initializeOnDeleteGoToUrl()
# On Error
promise.then null, @.onInitialDataError.bind(@)
_setMeta: ->
title = @translate.instant("EPIC.PAGE_TITLE", {
epicRef: "##{@scope.epic.ref}"
epicSubject: @scope.epic.subject
projectName: @scope.project.name
})
description = @translate.instant("EPIC.PAGE_DESCRIPTION", {
epicStatus: @scope.statusById[@scope.epic.status]?.name or "--"
epicDescription: angular.element(@scope.epic.description_html or "").text()
})
@appMetaService.setAll(title, description)
initializeEventHandlers: ->
@scope.$on "attachment:create", =>
@analytics.trackEvent("attachment", "create", "create attachment on epic", 1)
@scope.$on "comment:new", =>
@.loadEpic()
@scope.$on "custom-attributes-values:edit", =>
@rootscope.$broadcast("object:updated")
initializeOnDeleteGoToUrl: ->
ctx = {project: @scope.project.slug}
@scope.onDeleteGoToUrl = @navUrls.resolve("project-epics", ctx)
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
@scope.projectId = project.id
@scope.project = project
@scope.immutableProject = Immutable.fromJS(project._attrs)
@scope.$emit('project:loaded', project)
@scope.statusList = project.epic_statuses
@scope.statusById = groupBy(project.epic_statuses, (x) -> x.id)
return project
loadEpic: ->
return @rs.epics.getByRef(@scope.projectId, @params.epicref).then (epic) =>
@scope.epic = epic
@scope.immutableEpic = Immutable.fromJS(epic._attrs)
@scope.epicId = epic.id
@scope.commentModel = epic
@modelTransform.setObject(@scope, 'epic')
if @scope.epic.neighbors.previous?.ref?
ctx = {
project: @scope.project.slug
ref: @scope.epic.neighbors.previous.ref
}
@scope.previousUrl = @navUrls.resolve("project-epics-detail", ctx)
if @scope.epic.neighbors.next?.ref?
ctx = {
project: @scope.project.slug
ref: @scope.epic.neighbors.next.ref
}
@scope.nextUrl = @navUrls.resolve("project-epics-detail", ctx)
loadUserstories: ->
return @rs2.userstories.listInEpic(@scope.epicId).then (data) =>
@scope.userstories = data
loadInitialData: ->
promise = @.loadProject()
return promise.then (project) =>
@.fillUsersAndRoles(project.members, project.roles)
@.loadEpic().then(=> @.loadUserstories())
###
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
# See app/modules/components/vote-button for more info
###
onUpvote: ->
onSuccess = =>
@.loadEpic()
@rootscope.$broadcast("object:updated")
onError = =>
@confirm.notify("error")
return @rs.epics.upvote(@scope.epicId).then(onSuccess, onError)
onDownvote: ->
onSuccess = =>
@.loadEpic()
@rootscope.$broadcast("object:updated")
onError = =>
@confirm.notify("error")
return @rs.epics.downvote(@scope.epicId).then(onSuccess, onError)
###
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
# See app/modules/components/watch-button for more info
###
onWatch: ->
onSuccess = =>
@.loadEpic()
@rootscope.$broadcast("object:updated")
onError = =>
@confirm.notify("error")
return @rs.epics.watch(@scope.epicId).then(onSuccess, onError)
onUnwatch: ->
onSuccess = =>
@.loadEpic()
@rootscope.$broadcast("object:updated")
onError = =>
@confirm.notify("error")
return @rs.epics.unwatch(@scope.epicId).then(onSuccess, onError)
onSelectColor: (color) ->
onSelectColorSuccess = () =>
@rootscope.$broadcast("object:updated")
@confirm.notify('success')
onSelectColorError = () =>
@confirm.notify('error')
transform = @modelTransform.save (epic) ->
epic.color = color
return epic
return transform.then(onSelectColorSuccess, onSelectColorError)
module.controller("EpicDetailController", EpicDetailController)
#############################################################################
## Epic status display directive
#############################################################################
EpicStatusDisplayDirective = ($template, $compile) ->
# Display if an epic is open or closed and its status.
#
# Example:
# tg-epic-status-display(ng-model="epic")
#
# Requirements:
# - Epic object (ng-model)
# - scope.statusById object
template = $template.get("common/components/status-display.html", true)
link = ($scope, $el, $attrs) ->
render = (epic) ->
status = $scope.statusById[epic.status]
html = template({
is_closed: status.is_closed
status: status
})
html = $compile(html)($scope)
$el.html(html)
$scope.$watch $attrs.ngModel, (epic) ->
render(epic) if epic?
$scope.$on "$destroy", ->
$el.off()
return {
link: link
restrict: "EA"
require: "ngModel"
}
module.directive("tgEpicStatusDisplay", ["$tgTemplate", "$compile", EpicStatusDisplayDirective])
#############################################################################
## Epic status button directive
#############################################################################
EpicStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTransform, $compile, $translate, $template) ->
# Display the status of epic and you can edit it.
#
# Example:
# tg-epic-status-button(ng-model="epic")
#
# Requirements:
# - Epic object (ng-model)
# - scope.statusById object
# - $scope.project.my_permissions
template = $template.get("common/components/status-button.html", true)
link = ($scope, $el, $attrs, $model) ->
isEditable = ->
return $scope.project.my_permissions.indexOf("modify_epic") != -1
render = (epic) =>
status = $scope.statusById[epic.status]
html = $compile(template({
status: status
statuses: $scope.statusList
editable: isEditable()
}))($scope)
$el.html(html)
save = (status) ->
currentLoading = $loading()
.target($el)
.start()
transform = $modelTransform.save (epic) ->
epic.status = status
return epic
onSuccess = ->
$rootScope.$broadcast("object:updated")
currentLoading.finish()
onError = ->
$confirm.notify("error")
currentLoading.finish()
transform.then(onSuccess, onError)
$el.on "click", ".js-edit-status", (event) ->
event.preventDefault()
event.stopPropagation()
return if not isEditable()
$el.find(".pop-status").popover().open()
$el.on "click", ".status", (event) ->
event.preventDefault()
event.stopPropagation()
return if not isEditable()
target = angular.element(event.currentTarget)
$.fn.popover().closeAll()
save(target.data("status-id"))
$scope.$watch () ->
return $model.$modelValue?.status
, () ->
epic = $model.$modelValue
render(epic) if epic
$scope.$on "$destroy", ->
$el.off()
return {
link: link
restrict: "EA"
require: "ngModel"
}
module.directive("tgEpicStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQueueModelTransformation",
"$compile", "$translate", "$tgTemplate", EpicStatusButtonDirective])

View File

@ -96,6 +96,7 @@ class EventsService
maxMissedHeartbeats = @config.get("eventsMaxMissedHeartbeats", 5)
heartbeatIntervalTime = @config.get("eventsHeartbeatIntervalTime", 60000)
reconnectTryInterval = @config.get("eventsReconnectTryInterval", 10000)
@.missedHeartbeats = 0
@.heartbeatInterval = setInterval(() =>
@ -108,7 +109,7 @@ class EventsService
@log.debug("HeartBeat send PING")
catch e
@log.error("HeartBeat error: " + e.message)
@.stopHeartBeatMessages()
@.setupConnection()
, heartbeatIntervalTime)
@log.debug("HeartBeat enabled")
@ -228,11 +229,13 @@ class EventsService
onError: (error) ->
@log.error("WebSocket error: #{error}")
@.error = true
setTimeout(@.setupConnection, @.reconnectTryInterval)
onClose: ->
@log.debug("WebSocket closed.")
@.connected = false
@.stopHeartBeatMessages()
setTimeout(@.setupConnection, @.reconnectTryInterval)
class EventsProvider

View File

@ -52,11 +52,12 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgAnalytics",
"$tgNavUrls",
"$translate",
"$tgQueueModelTransformation"
"$tgQueueModelTransformation",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appMetaService, @analytics, @navUrls, @translate, @modelTransform) ->
@log, @appMetaService, @analytics, @navUrls, @translate, @modelTransform, @errorHandlingService) ->
bindMethods(@)
@scope.issueRef = @params.issueref

View File

@ -25,6 +25,7 @@
taiga = @.taiga
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
trim = @.taiga.trim
module = angular.module("taigaIssues")
@ -44,8 +45,8 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading,
resetAttachments()
$el.find(".tag-input").val("")
lightboxService.open($el)
lightboxService.open $el, () ->
$scope.createIssueOpen = false
$scope.issue = {
project: project.id
@ -57,10 +58,11 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading,
tags: []
}
$scope.createIssueOpen = true
$scope.$on "$destroy", ->
$el.off()
createAttachments = (obj) ->
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
return attachmentsService.upload(attachment.file, obj.id, $scope.issue.project, 'issue')
@ -76,6 +78,42 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading,
$scope.addAttachment = (attachment) ->
attachmentsToAdd = attachmentsToAdd.push(attachment)
$scope.addTag = (tag, color) ->
value = trim(tag.toLowerCase())
tags = $scope.project.tags
projectTags = $scope.project.tags_colors
tags = [] if not tags?
projectTags = {} if not projectTags?
if value not in tags
tags.push(value)
projectTags[tag] = color || null
$scope.project.tags = tags
itemtags = _.clone($scope.issue.tags)
inserted = _.find itemtags, (it) -> it[0] == value
if !inserted
itemtags.push([tag , color])
$scope.issue.tags = itemtags
$scope.deleteTag = (tag) ->
value = trim(tag[0].toLowerCase())
tags = $scope.project.tags
itemtags = _.clone($scope.us.tags)
_.remove itemtags, (tag) -> tag[0] == value
$scope.us.tags = itemtags
_.pull($scope.issue.tags, value)
submit = debounce 2000, (event) =>
event.preventDefault()
@ -101,7 +139,6 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading,
currentLoading.finish()
$confirm.notify("error")
submitButton = $el.find(".submit-button")
$el.on "submit", "form", submit
@ -109,8 +146,8 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading,
return {link:link}
module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading", "$q", "tgAttachmentsService",
CreateIssueDirective])
module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading",
"$q", "tgAttachmentsService", CreateIssueDirective])
#############################################################################

View File

@ -32,6 +32,7 @@ groupBy = @.taiga.groupBy
bindOnce = @.taiga.bindOnce
debounceLeading = @.taiga.debounceLeading
startswith = @.taiga.startswith
bindMethods = @.taiga.bindMethods
module = angular.module("taigaIssues")
@ -54,20 +55,24 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"$translate"
"$translate",
"tgErrorHandlingService",
"$tgStorage",
"tgFilterRemoteStorageService"
]
filtersHashSuffix: "issues-filters"
myFiltersHashSuffix: "issues-my-filters"
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @urls, @params, @q, @location, @appMetaService,
@navUrls, @events, @analytics, @translate) ->
@navUrls, @events, @analytics, @translate, @errorHandlingService, @storage, @filterRemoteStorageService) ->
bindMethods(@)
@scope.sectionName = "Issues"
@scope.filters = {}
@.voting = false
if _.isEmpty(@location.search())
filters = @rs.issues.getFilters(@params.pslug)
filters.page = 1
@location.search(filters)
@location.replace()
return
return if @.applyStoredFilters(@params.pslug, @.filtersHashSuffix)
promise = @.loadInitialData()
@ -87,18 +92,207 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@analytics.trackEvent("issue", "create", "create issue on issues list", 1)
@.loadIssues()
changeQ: (q) ->
@.unselectFilter("page")
@.replaceFilter("q", q)
@.loadIssues()
@.generateFilters()
removeFilter: (filter) ->
@.unselectFilter("page")
@.unselectFilter(filter.dataType, filter.id)
@.loadIssues()
@.generateFilters()
addFilter: (newFilter) ->
@.unselectFilter("page")
@.selectFilter(newFilter.category.dataType, newFilter.filter.id)
@.loadIssues()
@.generateFilters()
selectCustomFilter: (customFilter) ->
orderBy = @location.search().order_by
if orderBy
customFilter.filter.order_by = orderBy
@.unselectFilter("page")
@.replaceAllFilters(customFilter.filter)
@.loadIssues()
@.generateFilters()
removeCustomFilter: (customFilter) ->
console.log "oooo"
@filterRemoteStorageService.getFilters(@scope.projectId, @.myFiltersHashSuffix).then (userFilters) =>
console.log userFilters[customFilter.id]
delete userFilters[customFilter.id]
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, @.myFiltersHashSuffix).then(@.generateFilters)
saveCustomFilter: (name) ->
filters = {}
urlfilters = @location.search()
filters.tags = urlfilters.tags
filters.status = urlfilters.status
filters.type = urlfilters.type
filters.severity = urlfilters.severity
filters.priority = urlfilters.priority
filters.assigned_to = urlfilters.assigned_to
filters.owner = urlfilters.owner
@filterRemoteStorageService.getFilters(@scope.projectId, @.myFiltersHashSuffix).then (userFilters) =>
userFilters[name] = filters
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, @.myFiltersHashSuffix).then(@.generateFilters)
generateFilters: ->
@.storeFilters(@params.pslug, @location.search(), @.filtersHashSuffix)
urlfilters = @location.search()
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.type = urlfilters.type
loadFilters.severity = urlfilters.severity
loadFilters.priority = urlfilters.priority
loadFilters.assigned_to = urlfilters.assigned_to
loadFilters.owner = urlfilters.owner
loadFilters.q = urlfilters.q
return @q.all([
@rs.issues.filtersData(loadFilters),
@filterRemoteStorageService.getFilters(@scope.projectId, @.myFiltersHashSuffix)
]).then (result) =>
data = result[0]
customFiltersRaw = result[1]
statuses = _.map data.statuses, (it) ->
it.id = it.id.toString()
return it
type = _.map data.types, (it) ->
it.id = it.id.toString()
return it
severity = _.map data.severities, (it) ->
it.id = it.id.toString()
return it
priority = _.map data.priorities, (it) ->
it.id = it.id.toString()
return it
tags = _.map data.tags, (it) ->
it.id = it.name
return it
tagsWithAtLeastOneElement = _.filter tags, (tag) ->
return tag.count > 0
assignedTo = _.map data.assigned_to, (it) ->
if it.id
it.id = it.id.toString()
else
it.id = "null"
it.name = it.full_name || "Unassigned"
return it
owner = _.map data.owners, (it) ->
it.id = it.id.toString()
it.name = it.full_name
return it
@.selectedFilters = []
if loadFilters.status
selected = @.formatSelectedFilters("status", statuses, loadFilters.status)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.tags
selected = @.formatSelectedFilters("tags", tags, loadFilters.tags)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.assigned_to
selected = @.formatSelectedFilters("assigned_to", assignedTo, loadFilters.assigned_to)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.owner
selected = @.formatSelectedFilters("owner", owner, loadFilters.owner)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.type
selected = @.formatSelectedFilters("type", type, loadFilters.type)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.severity
selected = @.formatSelectedFilters("severity", severity, loadFilters.severity)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.priority
selected = @.formatSelectedFilters("priority", priority, loadFilters.priority)
@.selectedFilters = @.selectedFilters.concat(selected)
@.filterQ = loadFilters.q
@.filters = [
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.TYPE"),
dataType: "type",
content: type
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.SEVERITY"),
dataType: "severity",
content: severity
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.PRIORITIES"),
dataType: "priority",
content: priority
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.STATUS"),
dataType: "status",
content: statuses
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.TAGS"),
dataType: "tags",
content: tags,
hideEmpty: true,
totalTaggedElements: tagsWithAtLeastOneElement.length
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.ASSIGNED_TO"),
dataType: "assigned_to",
content: assignedTo
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.CREATED_BY"),
dataType: "owner",
content: owner
}
]
@.customFilters = []
_.forOwn customFiltersRaw, (value, key) =>
@.customFilters.push({id: key, name: key, filter: value})
initializeSubscription: ->
routingKey = "changes.project.#{@scope.projectId}.issues"
@events.subscribe @scope, routingKey, (message) =>
@.loadIssues()
storeFilters: ->
@rs.issues.storeFilters(@params.pslug, @location.search())
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.is_issues_activated
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -115,160 +309,15 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
return project
getUrlFilters: ->
filters = _.pick(@location.search(), "page", "tags", "status", "types",
"q", "severities", "priorities",
"assignedTo", "createdBy", "orderBy")
filters.page = 1 if not filters.page
return filters
getUrlFilter: (name) ->
filters = _.pick(@location.search(), name)
return filters[name]
loadMyFilters: ->
return @rs.issues.getMyFilters(@scope.projectId).then (filters) =>
return _.map filters, (value, key) =>
return {id: key, name: key, type: "myFilters", selected: false}
removeNotExistingFiltersFromUrl: ->
currentSearch = @location.search()
urlfilters = @.getUrlFilters()
for filterName, filterValue of urlfilters
if filterName == "page" or filterName == "orderBy" or filterName == "q"
continue
if filterName == "tags"
splittedValues = _.map("#{filterValue}".split(","))
else
splittedValues = _.map("#{filterValue}".split(","), (x) -> if x == "null" then null else parseInt(x))
existingValues = _.intersection(splittedValues, _.map(@scope.filters[filterName], "id"))
if splittedValues.length != existingValues.length
@location.search(filterName, existingValues.join())
if currentSearch != @location.search()
@location.replace()
markSelectedFilters: (filters, urlfilters) ->
# Build selected filters (from url) fast lookup data structure
searchdata = {}
for name, value of _.omit(urlfilters, "page", "orderBy")
if not searchdata[name]?
searchdata[name] = {}
for val in "#{value}".split(",")
searchdata[name][val] = true
isSelected = (type, id) ->
if searchdata[type]? and searchdata[type][id]
return true
return false
for key, value of filters
for obj in value
obj.selected = if isSelected(obj.type, obj.id) then true else undefined
loadFilters: () ->
urlfilters = @.getUrlFilters()
if urlfilters.q
@scope.filtersQ = urlfilters.q
# Load My Filters
promise = @.loadMyFilters().then (myFilters) =>
@scope.filters.myFilters = myFilters
return myFilters
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.q = urlfilters.q
loadFilters.types = urlfilters.types
loadFilters.severities = urlfilters.severities
loadFilters.priorities = urlfilters.priorities
loadFilters.assigned_to = urlfilters.assignedTo
loadFilters.owner = urlfilters.createdBy
# Load default filters data
promise = promise.then =>
return @rs.issues.filtersData(loadFilters)
# Format filters and set them on scope
return promise.then (data) =>
usersFiltersFormat = (users, type, unknownOption) =>
reformatedUsers = _.map users, (t) =>
t.type = type
t.name = if t.full_name then t.full_name else unknownOption
return t
unknownItem = _.remove(reformatedUsers, (u) -> not u.id)
reformatedUsers = _.sortBy(reformatedUsers, (u) -> u.name.toUpperCase())
if unknownItem.length > 0
reformatedUsers.unshift(unknownItem[0])
return reformatedUsers
choicesFiltersFormat = (choices, type, byIdObject) =>
_.map choices, (t) ->
t.type = type
return t
tagsFilterFormat = (tags) =>
return _.map tags, (t) ->
t.id = t.name
t.type = 'tags'
return t
# Build filters data structure
@scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.issueStatusById)
@scope.filters.severities = choicesFiltersFormat(data.severities, "severities", @scope.severityById)
@scope.filters.priorities = choicesFiltersFormat(data.priorities, "priorities", @scope.priorityById)
@scope.filters.assignedTo = usersFiltersFormat(data.assigned_to, "assignedTo", "Unassigned")
@scope.filters.createdBy = usersFiltersFormat(data.owners, "createdBy", "Unknown")
@scope.filters.types = choicesFiltersFormat(data.types, "types", @scope.issueTypeById)
@scope.filters.tags = tagsFilterFormat(data.tags)
@.removeNotExistingFiltersFromUrl()
@.markSelectedFilters(@scope.filters, urlfilters)
@rootscope.$broadcast("filters:loaded", @scope.filters)
# We need to guarantee that the last petition done here is the finally used
# When searching by text loadIssues can be called fastly with different parameters and
# can be resolved in a different order than generated
# We count the requests made and only if the callback is for the last one data is updated
loadIssuesRequests: 0
loadIssues: =>
@scope.urlFilters = @.getUrlFilters()
params = @location.search()
# Convert stored filters to http parameters
# ready filters (the name difference exists
# because of some automatic lookups and is
# the simplest way todo it without adding
# additional complexity to code.
@scope.httpParams = {}
for name, values of @scope.urlFilters
if name == "severities"
name = "severity"
else if name == "orderBy"
name = "order_by"
else if name == "priorities"
name = "priority"
else if name == "assignedTo"
name = "assigned_to"
else if name == "createdBy"
name = "owner"
else if name == "status"
name = "status"
else if name == "types"
name = "type"
@scope.httpParams[name] = values
promise = @rs.issues.list(@scope.projectId, @scope.httpParams)
promise = @rs.issues.list(@scope.projectId, params)
@.loadIssuesRequests += 1
promise.index = @.loadIssuesRequests
promise.then (data) =>
@ -287,26 +336,10 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
return promise.then (project) =>
@.fillUsersAndRoles(project.members, project.roles)
@.initializeSubscription()
@.loadFilters()
@.generateFilters()
return @.loadIssues()
saveCurrentFiltersTo: (newFilter) ->
deferred = @q.defer()
@rs.issues.getMyFilters(@scope.projectId).then (filters) =>
filters[newFilter] = @location.search()
@rs.issues.storeMyFilters(@scope.projectId, filters).then =>
deferred.resolve()
return deferred.promise
deleteMyFilter: (filter) ->
deferred = @q.defer()
@rs.issues.getMyFilters(@scope.projectId).then (filters) =>
delete filters[filter]
@rs.issues.storeMyFilters(@scope.projectId, filters).then =>
deferred.resolve()
return deferred.promise
# Functions used from templates
addNewIssue: ->
@rootscope.$broadcast("issueform:new", @scope.project)
@ -314,6 +347,33 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
addIssuesInBulk: ->
@rootscope.$broadcast("issueform:bulk", @scope.projectId)
upVoteIssue: (issueId) ->
@.voting = issueId
onSuccess = =>
@.loadIssues()
@.voting = null
onError = =>
@confirm.notify("error")
@.voting = null
return @rs.issues.upvote(issueId).then(onSuccess, onError)
downVoteIssue: (issueId) ->
@.voting = issueId
onSuccess = =>
@.loadIssues()
@.voting = null
onError = =>
@confirm.notify("error")
@.voting = null
return @rs.issues.downvote(issueId).then(onSuccess, onError)
getOrderBy: ->
if _.isString(@location.search().order_by)
return @location.search().order_by
else
return "created_date"
module.controller("IssuesController", IssuesController)
@ -408,28 +468,40 @@ IssuesDirective = ($log, $location, $template, $compile) ->
## Issues Filters
linkOrdering = ($scope, $el, $attrs, $ctrl) ->
# Draw the arrow the first time
currentOrder = $ctrl.getUrlFilter("orderBy") or "created_date"
currentOrder = $ctrl.getOrderBy()
if currentOrder
icon = if startswith(currentOrder, "-") then "icon-arrow-up" else "icon-arrow-bottom"
icon = if startswith(currentOrder, "-") then "icon-arrow-up" else "icon-arrow-down"
colHeadElement = $el.find(".row.title > div[data-fieldname='#{trim(currentOrder, "-")}']")
colHeadElement.html("#{colHeadElement.html()}<span class='icon #{icon}'></span>")
svg = $("<tg-svg>").attr("svg-icon", icon)
colHeadElement.append(svg)
$compile(colHeadElement.contents())($scope)
$el.on "click", ".row.title > div", (event) ->
target = angular.element(event.currentTarget)
currentOrder = $ctrl.getUrlFilter("orderBy")
currentOrder = $ctrl.getOrderBy()
newOrder = target.data("fieldname")
finalOrder = if currentOrder == newOrder then "-#{newOrder}" else newOrder
$scope.$apply ->
$ctrl.replaceFilter("orderBy", finalOrder)
$ctrl.storeFilters()
$ctrl.replaceFilter("order_by", finalOrder)
$ctrl.storeFilters($ctrl.params.pslug, $location.search(), $ctrl.filtersHashSuffix)
$ctrl.loadIssues().then ->
# Update the arrow
$el.find(".row.title > div > span.icon").remove()
icon = if startswith(finalOrder, "-") then "icon-arrow-up" else "icon-arrow-bottom"
target.html("#{target.html()}<span class='icon #{icon}'></span>")
$el.find(".row.title > div > tg-svg").remove()
icon = if startswith(finalOrder, "-") then "icon-arrow-up" else "icon-arrow-down"
svg = $("<tg-svg>")
.attr("svg-icon", icon)
target.append(svg)
$compile(target.contents())($scope)
## Issues Link
link = ($scope, $el, $attrs) ->
@ -445,253 +517,6 @@ IssuesDirective = ($log, $location, $template, $compile) ->
module.directive("tgIssues", ["$log", "$tgLocation", "$tgTemplate", "$compile", IssuesDirective])
#############################################################################
## Issues Filters Directive
#############################################################################
IssuesFiltersDirective = ($q, $log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) ->
template = $template.get("issue/issues-filters.html", true)
templateSelected = $template.get("issue/issues-filters-selected.html", true)
link = ($scope, $el, $attrs) ->
$ctrl = $el.closest(".wrapper").controller()
selectedFilters = []
showFilters = (title, type) ->
$el.find(".filters-cats").hide()
$el.find(".filter-list").removeClass("hidden")
$el.find(".breadcrumb").removeClass("hidden")
$el.find("h2 .subfilter .title").html(title)
$el.find("h2 .subfilter .title").prop("data-type", type)
showCategories = ->
$el.find(".filters-cats").show()
$el.find(".filter-list").addClass("hidden")
$el.find(".breadcrumb").addClass("hidden")
initializeSelectedFilters = (filters) ->
selectedFilters = []
for name, values of filters
for val in values
selectedFilters.push(val) if val.selected
renderSelectedFilters(selectedFilters)
renderSelectedFilters = (selectedFilters) ->
_.filter selectedFilters, (f) =>
if f.color
f.style = "border-left: 3px solid #{f.color}"
html = templateSelected({filters:selectedFilters})
html = $compile(html)($scope)
$el.find(".filters-applied").html(html)
if $auth.isAuthenticated() && selectedFilters.length > 0
$el.find(".save-filters").show()
else
$el.find(".save-filters").hide()
renderFilters = (filters) ->
_.filter filters, (f) =>
if f.color
f.style = "border-left: 3px solid #{f.color}"
html = template({filters:filters})
html = $compile(html)($scope)
$el.find(".filter-list").html(html)
getFiltersType = () ->
return $el.find(".subfilter .title").prop('data-type')
reloadIssues = () ->
currentFiltersType = getFiltersType()
$q.all([$ctrl.loadIssues(), $ctrl.loadFilters()]).then () ->
filters = $scope.filters[currentFiltersType]
renderFilters(_.reject(filters, "selected"))
toggleFilterSelection = (type, id) ->
if type == "myFilters"
$rs.issues.getMyFilters($scope.projectId).then (data) ->
myFilters = data
filters = myFilters[id]
filters.page = 1
$ctrl.replaceAllFilters(filters)
$ctrl.storeFilters()
$ctrl.loadIssues()
$ctrl.markSelectedFilters($scope.filters, filters)
initializeSelectedFilters($scope.filters)
return null
filters = $scope.filters[type]
filterId = if type == 'tags' then taiga.toString(id) else id
filter = _.find(filters, {id: filterId})
filter.selected = (not filter.selected)
# Convert id to null as string for properly
# put null value on url parameters
id = "null" if id is null
if filter.selected
selectedFilters.push(filter)
$ctrl.selectFilter(type, id)
$ctrl.selectFilter("page", 1)
$ctrl.storeFilters()
else
selectedFilters = _.reject selectedFilters, (f) ->
return f.id == filter.id && f.type == filter.type
$ctrl.unselectFilter(type, id)
$ctrl.selectFilter("page", 1)
$ctrl.storeFilters()
reloadIssues()
renderSelectedFilters(selectedFilters)
currentFiltersType = getFiltersType()
if type == currentFiltersType
renderFilters(_.reject(filters, "selected"))
# Angular Watchers
$scope.$on "filters:loaded", (ctx, filters) ->
initializeSelectedFilters(filters)
$scope.$on "filters:issueupdate", (ctx, filters) ->
html = template({filters:filters.status})
html = $compile(html)($scope)
$el.find(".filter-list").html(html)
selectQFilter = debounceLeading 100, (value, oldValue) ->
return if value is undefined or value == oldValue
$ctrl.replaceFilter("page", null, true)
if value.length == 0
$ctrl.replaceFilter("q", null)
$ctrl.storeFilters()
else
$ctrl.replaceFilter("q", value)
$ctrl.storeFilters()
reloadIssues()
unwatchIssues = $scope.$watch "issues", (newValue) ->
if !_.isUndefined(newValue)
$scope.$watch("filtersQ", selectQFilter)
unwatchIssues()
# Dom Event Handlers
$el.on "click", ".filters-cat-single", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
tags = $scope.filters[target.data("type")]
renderFilters(_.reject(tags, "selected"))
showFilters(target.attr("title"), target.data("type"))
$el.on "click", ".back", (event) ->
event.preventDefault()
showCategories($el)
$el.on "click", ".filters-applied .remove-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget).parent()
id = target.data("id") or null
type = target.data("type")
toggleFilterSelection(type, id)
$el.on "click", ".filter-list .single-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
target.toggleClass("active")
id = target.data("id") or null
type = target.data("type")
# A saved filter can't be active
if type == "myFilters"
target.removeClass("active")
toggleFilterSelection(type, id)
$el.on "click", ".filter-list .remove-filter", (event) ->
event.preventDefault()
event.stopPropagation()
target = angular.element(event.currentTarget)
customFilterName = target.parent().data('id')
title = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.TITLE")
message = $translate.instant("ISSUES.FILTERS.CONFIRM_DELETE.MESSAGE", {customFilterName: customFilterName})
$confirm.askOnDelete(title, message).then (askResponse) ->
promise = $ctrl.deleteMyFilter(customFilterName)
promise.then ->
promise = $ctrl.loadMyFilters()
promise.then (filters) ->
askResponse.finish()
$scope.filters.myFilters = filters
renderFilters($scope.filters.myFilters)
promise.then null, ->
askResponse.finish()
promise.then null, ->
askResponse.finish(false)
$confirm.notify("error")
$el.on "click", ".save-filters", (event) ->
event.preventDefault()
renderFilters($scope.filters["myFilters"])
showFilters("My filters", "myFilters")
$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()
if event.keyCode == 13
target = angular.element(event.currentTarget)
newFilter = target.val()
currentLoading = $loading()
.target($el.find(".new"))
.start()
promise = $ctrl.saveCurrentFiltersTo(newFilter)
promise.then ->
loadPromise = $ctrl.loadMyFilters()
loadPromise.then (filters) ->
currentLoading.finish()
$scope.filters.myFilters = filters
currentfilterstype = $el.find("h2 .subfilter .title").prop('data-type')
if currentfilterstype == "myFilters"
renderFilters($scope.filters.myFilters)
$el.find('.my-filter-name').addClass("hidden")
$el.find('.save-filters').show()
loadPromise.then null, ->
currentLoading.finish()
$confirm.notify("error", "Error loading custom filters")
promise.then null, ->
currentLoading.finish()
$el.find(".my-filter-name").val(newFilter).focus().select()
$confirm.notify("error", "Filter not saved")
else if event.keyCode == 27
$el.find('.my-filter-name').val('')
$el.find('.my-filter-name').addClass("hidden")
$el.find('.save-filters').show()
return {link:link}
module.directive("tgIssuesFilters", ["$q", "$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
"$tgTemplate", "$translate", "$compile", "$tgAuth", IssuesFiltersDirective])
#############################################################################
## Issue status Directive (popover for change status)
#############################################################################
@ -778,9 +603,9 @@ module.directive("tgIssueStatusInlineEdition", ["$tgRepo", "$tgTemplate", "$root
## Issue assigned to Directive
#############################################################################
IssueAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
IssueAssignedToInlineEditionDirective = ($repo, $rootscope, $translate, avatarService) ->
template = _.template("""
<img src="<%- imgurl %>" alt="<%- name %>"/>
<img style="background-color: <%- bg %>" src="<%- imgurl %>" alt="<%- name %>"/>
<figcaption><%- name %></figcaption>
""")
@ -792,9 +617,14 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
}
member = $scope.usersById[issue.assigned_to]
avatar = avatarService.getAvatar(member)
ctx.imgurl = avatar.url
ctx.bg = null
if member
ctx.name = member.full_name_display
ctx.imgurl = member.photo
ctx.bg = avatar.bg
$el.find(".avatar").html(template(ctx))
$el.find(".issue-assignedto").attr('title', ctx.name)
@ -826,5 +656,5 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
return {link: link}
module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate"
module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate", "tgAvatarService",
IssueAssignedToInlineEditionDirective])

View File

@ -0,0 +1,188 @@
###
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
#
# 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: kanban-userstories.service.coffee
###
groupBy = @.taiga.groupBy
class KanbanUserstoriesService extends taiga.Service
@.$inject = []
constructor: () ->
@.reset()
reset: () ->
@.userstoriesRaw = []
@.archivedStatus = []
@.statusHide = []
@.foldStatusChanged = {}
@.usByStatus = Immutable.Map()
init: (project, usersById) ->
@.project = project
@.usersById = usersById
resetFolds: () ->
@.foldStatusChanged = {}
@.refresh()
toggleFold: (usId) ->
@.foldStatusChanged[usId] = !@.foldStatusChanged[usId]
@.refresh()
set: (userstories) ->
@.userstoriesRaw = userstories
@.refreshRawOrder()
@.refresh()
add: (us) ->
@.userstoriesRaw = @.userstoriesRaw.concat(us)
@.refreshRawOrder()
@.refresh()
addArchivedStatus: (statusId) ->
@.archivedStatus.push(statusId)
isUsInArchivedHiddenStatus: (usId) ->
us = @.getUsModel(usId)
return @.archivedStatus.indexOf(us.status) != -1 &&
@.statusHide.indexOf(us.status) != -1
hideStatus: (statusId) ->
@.deleteStatus(statusId)
@.statusHide.push(statusId)
showStatus: (statusId) ->
_.remove @.statusHide, (it) -> return it == statusId
getStatus: (statusId) ->
return _.filter @.userstoriesRaw, (us) -> return us.status == statusId
deleteStatus: (statusId) ->
toDelete = _.filter @.userstoriesRaw, (us) -> return us.status == statusId
toDelete = _.map (it) -> return it.id
@.archived = _.difference(@.archived, toDelete)
@.userstoriesRaw = _.filter @.userstoriesRaw, (us) -> return us.status != statusId
@.refresh()
refreshRawOrder: () ->
@.order = {}
@.order[it.id] = it.kanban_order for it in @.userstoriesRaw
assignOrders: (order) ->
order = _.invert(order)
@.order = _.assign(@.order, order)
@.refresh()
move: (id, statusId, index) ->
us = @.getUsModel(id)
usByStatus = _.filter @.userstoriesRaw, (it) =>
return it.status == statusId
usByStatus = _.sortBy usByStatus, (it) => @.order[it.id]
usByStatusWithoutMoved = _.filter usByStatus, (it) => it.id != id
beforeDestination = _.slice(usByStatusWithoutMoved, 0, index)
afterDestination = _.slice(usByStatusWithoutMoved, index)
setOrders = {}
previous = beforeDestination[beforeDestination.length - 1]
previousWithTheSameOrder = _.filter beforeDestination, (it) =>
@.order[it.id] == @.order[previous.id]
if previousWithTheSameOrder.length > 1
for it in previousWithTheSameOrder
setOrders[it.id] = @.order[it.id]
if !previous
@.order[us.id] = 0
else if previous
@.order[us.id] = @.order[previous.id] + 1
for it, key in afterDestination
@.order[it.id] = @.order[us.id] + key + 1
us.status = statusId
us.kanban_order = @.order[us.id]
@.refresh()
return {"us_id": us.id, "order": @.order[us.id], "set_orders": setOrders}
replace: (us) ->
@.usByStatus = @.usByStatus.map (status) ->
findedIndex = status.findIndex (usItem) ->
return usItem.get('id') == us.get('id')
if findedIndex != -1
status = status.set(findedIndex, us)
return status
replaceModel: (us) ->
@.userstoriesRaw = _.map @.userstoriesRaw, (usItem) ->
if us.id == usItem.id
return us
else
return usItem
@.refresh()
getUs: (id) ->
findedUs = null
@.usByStatus.forEach (status) ->
findedUs = status.find (us) -> return us.get('id') == id
return false if findedUs
return findedUs
getUsModel: (id) ->
return _.find @.userstoriesRaw, (us) -> return us.id == id
refresh: ->
@.userstoriesRaw = _.sortBy @.userstoriesRaw, (it) => @.order[it.id]
userstories = @.userstoriesRaw
userstories = _.map userstories, (usModel) =>
us = {}
us.foldStatusChanged = @.foldStatusChanged[usModel.id]
us.model = usModel.getAttrs()
us.images = _.filter usModel.attachments, (it) -> return !!it.thumbnail_card_url
us.id = usModel.id
us.assigned_to = @.usersById[usModel.assigned_to]
us.colorized_tags = _.map us.model.tags, (tag) =>
return {name: tag[0], color: tag[1]}
return us
usByStatus = _.groupBy userstories, (us) ->
return us.model.status
@.usByStatus = Immutable.fromJS(usByStatus)
angular.module("taigaKanban").service("tgKanbanUserstories", KanbanUserstoriesService)

View File

@ -34,26 +34,18 @@ bindMethods = @.taiga.bindMethods
module = angular.module("taigaKanban")
# Vars
defaultViewMode = "maximized"
viewModes = [
"maximized",
"minimized"
]
#############################################################################
## Kanban Controller
#############################################################################
class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin, taiga.UsFiltersMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"tgResources",
"$routeParams",
"$q",
"$tgLocation",
@ -61,16 +53,27 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
"$tgNavUrls",
"$tgEvents",
"$tgAnalytics",
"$translate"
"$translate",
"tgErrorHandlingService",
"$tgModel",
"tgKanbanUserstories",
"$tgStorage",
"tgFilterRemoteStorageService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@appMetaService, @navUrls, @events, @analytics, @translate) ->
storeCustomFiltersName: 'kanban-custom-filters'
storeFiltersName: 'kanban-filters'
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @rs2, @params, @q, @location,
@appMetaService, @navUrls, @events, @analytics, @translate, @errorHandlingService,
@model, @kanbanUserstoriesService, @storage, @filterRemoteStorageService) ->
bindMethods(@)
@kanbanUserstoriesService.reset()
@.openFilter = false
return if @.applyStoredFilters(@params.pslug, "kanban-filters")
@scope.sectionName = @translate.instant("KANBAN.SECTION_NAME")
@scope.statusViewModes = {}
@.initializeEventHandlers()
promise = @.loadInitialData()
@ -87,80 +90,109 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
# On Error
promise.then null, @.onInitialDataError.bind(@)
taiga.defineImmutableProperty @.scope, "usByStatus", () =>
return @kanbanUserstoriesService.usByStatus
setZoom: (zoomLevel, zoom) ->
if @.zoomLevel != zoomLevel
@kanbanUserstoriesService.resetFolds()
@.zoomLevel = zoomLevel
@.zoom = zoom
filtersReloadContent: () ->
@.loadUserstories().then () =>
openArchived = _.difference(@kanbanUserstoriesService.archivedStatus,
@kanbanUserstoriesService.statusHide)
if openArchived.length
for statusId in openArchived
@.loadUserStoriesForStatus({}, statusId)
initializeEventHandlers: ->
@scope.$on "usform:new:success", =>
@.loadUserstories()
@.refreshTagsColors()
@scope.$on "usform:new:success", (event, us) =>
@.refreshTagsColors().then () =>
@kanbanUserstoriesService.add(us)
@analytics.trackEvent("userstory", "create", "create userstory on kanban", 1)
@scope.$on "usform:bulk:success", =>
@.loadUserstories()
@scope.$on "usform:bulk:success", (event, uss) =>
@.refreshTagsColors().then () =>
@kanbanUserstoriesService.add(uss)
@analytics.trackEvent("userstory", "create", "bulk create userstory on kanban", 1)
@scope.$on "usform:edit:success", =>
@.loadUserstories()
@.refreshTagsColors()
@scope.$on "usform:edit:success", (event, us) =>
@.refreshTagsColors().then () =>
@kanbanUserstoriesService.replaceModel(us)
@scope.$on("assigned-to:added", @.onAssignedToChanged)
@scope.$on("kanban:us:move", @.moveUs)
@scope.$on("kanban:show-userstories-for-status", @.loadUserStoriesForStatus)
@scope.$on("kanban:hide-userstories-for-status", @.hideUserStoriesForStatus)
# Template actions
addNewUs: (type, statusId) ->
switch type
when "standard" then @rootscope.$broadcast("usform:new", @scope.projectId, statusId, @scope.usStatusList)
when "bulk" then @rootscope.$broadcast("usform:bulk", @scope.projectId, statusId)
when "standard" then @rootscope.$broadcast("usform:new",
@scope.projectId, statusId, @scope.usStatusList)
when "bulk" then @rootscope.$broadcast("usform:bulk",
@scope.projectId, statusId)
editUs: (id) ->
us = @kanbanUserstoriesService.getUs(id)
us = us.set('loading', true)
@kanbanUserstoriesService.replace(us)
@rs.userstories.getByRef(us.getIn(['model', 'project']), us.getIn(['model', 'ref']))
.then (editingUserStory) =>
@rs2.attachments.list("us", us.get('id'), us.getIn(['model', 'project'])).then (attachments) =>
@rootscope.$broadcast("usform:edit", editingUserStory, attachments.toJS())
us = us.set('loading', false)
@kanbanUserstoriesService.replace(us)
showPlaceHolder: (statusId) ->
if @scope.usStatusList[0].id == statusId &&
!@kanbanUserstoriesService.userstoriesRaw.length
return true
return false
toggleFold: (id) ->
@kanbanUserstoriesService.toggleFold(id)
isUsInArchivedHiddenStatus: (usId) ->
return @kanbanUserstoriesService.isUsInArchivedHiddenStatus(usId)
changeUsAssignedTo: (id) ->
us = @kanbanUserstoriesService.getUsModel(id)
changeUsAssignedTo: (us) ->
@rootscope.$broadcast("assigned-to:add", us)
# Scope Events Handlers
onAssignedToChanged: (ctx, userid, usModel) ->
usModel.assigned_to = userid
onAssignedToChanged: (ctx, userid, us) ->
us.assigned_to = userid
@kanbanUserstoriesService.replaceModel(usModel)
promise = @repo.save(us)
promise = @repo.save(usModel)
promise.then null, ->
console.log "FAIL" # TODO
# Load data methods
refreshTagsColors: ->
return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) =>
@scope.project.tags_colors = tags_colors
@scope.project.tags_colors = tags_colors._attrs
loadUserstories: ->
params = {
status__is_archived: false
status__is_archived: false,
include_attachments: true,
include_tasks: true
}
params = _.merge params, @location.search()
promise = @rs.userstories.listAll(@scope.projectId, params).then (userstories) =>
@scope.userstories = userstories
usByStatus = _.groupBy(userstories, "status")
us_archived = []
for status in @scope.usStatusList
if not usByStatus[status.id]?
usByStatus[status.id] = []
if @scope.usByStatus?
for us in @scope.usByStatus[status.id]
if us.status != status.id
us_archived.push(us)
# Must preserve the archived columns if loaded
if status.is_archived and @scope.usByStatus? and @scope.usByStatus[status.id].length != 0
for us in @scope.usByStatus[status.id].concat(us_archived)
if us.status == status.id
usByStatus[status.id].push(us)
usByStatus[status.id] = _.sortBy(usByStatus[status.id], "kanban_order")
if userstories.length == 0
status = @scope.usStatusList[0]
usByStatus[status.id].push({isPlaceholder: true})
@scope.usByStatus = usByStatus
@kanbanUserstoriesService.init(@scope.project, @scope.usersById)
@kanbanUserstoriesService.set(userstories)
# The broadcast must be executed when the DOM has been fully reloaded.
# We can't assure when this exactly happens so we need a defer
@ -174,14 +206,28 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
return promise
loadUserStoriesForStatus: (ctx, statusId) ->
params = { status: statusId }
filteredStatus = @location.search().status
# if there are filters applied the action doesn't end if the statusId is not in the url
if filteredStatus
filteredStatus = filteredStatus.split(",").map (it) -> parseInt(it, 10)
return if filteredStatus.indexOf(statusId) == -1
params = {
status: statusId
include_attachments: true,
include_tasks: true
}
params = _.merge params, @location.search()
return @rs.userstories.listAll(@scope.projectId, params).then (userstories) =>
@scope.usByStatus[statusId] = _.sortBy(userstories, "kanban_order")
@scope.$broadcast("kanban:shown-userstories-for-status", statusId, userstories)
return userstories
hideUserStoriesForStatus: (ctx, statusId) ->
@scope.usByStatus[statusId] = []
@scope.$broadcast("kanban:hidden-userstories-for-status", statusId)
loadKanban: ->
@ -193,7 +239,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.is_kanban_activated
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -203,8 +249,6 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id)
@scope.usStatusList = _.sortBy(project.us_statuses, "order")
@.generateStatusViewModes()
@scope.$emit("project:loaded", project)
return project
@ -219,82 +263,40 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@.fillUsersAndRoles(project.members, project.roles)
@.initializeSubscription()
@.loadKanban()
## View Mode methods
generateStatusViewModes: ->
storedStatusViewModes = @rs.kanban.getStatusViewModes(@scope.projectId)
@scope.statusViewModes = {}
for status in @scope.usStatusList
mode = storedStatusViewModes[status.id] || defaultViewMode
@scope.statusViewModes[status.id] = mode
@.storeStatusViewModes()
storeStatusViewModes: ->
@rs.kanban.storeStatusViewModes(@scope.projectId, @scope.statusViewModes)
updateStatusViewMode: (statusId, newViewMode) ->
@scope.statusViewModes[statusId] = newViewMode
@.storeStatusViewModes()
isMaximized: (statusId) ->
mode = @scope.statusViewModes[statusId] or defaultViewMode
return mode == 'maximized'
isMinimized: (statusId) ->
mode = @scope.statusViewModes[statusId] or defaultViewMode
return mode == 'minimized'
@.generateFilters()
# Utils methods
prepareBulkUpdateData: (uses, field="kanban_order") ->
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
resortUserStories: (uses) ->
items = []
for item, index in uses
item.kanban_order = index
if item.isModified()
items.push(item)
return items
moveUs: (ctx, us, oldStatusId, newStatusId, index) ->
if oldStatusId != newStatusId
# Remove us from old status column
r = @scope.usByStatus[oldStatusId].indexOf(us)
@scope.usByStatus[oldStatusId].splice(r, 1)
us = @kanbanUserstoriesService.getUsModel(us.get('id'))
# Add us to new status column.
@scope.usByStatus[newStatusId].splice(index, 0, us)
us.status = newStatusId
else
r = @scope.usByStatus[newStatusId].indexOf(us)
@scope.usByStatus[newStatusId].splice(r, 1)
@scope.usByStatus[newStatusId].splice(index, 0, us)
moveUpdateData = @kanbanUserstoriesService.move(us.id, newStatusId, index)
itemsToSave = @.resortUserStories(@scope.usByStatus[newStatusId])
@scope.usByStatus[newStatusId] = _.sortBy(@scope.usByStatus[newStatusId], "kanban_order")
params = {
include_attachments: true,
include_tasks: true
}
# Persist the userstory
promise = @repo.save(us)
options = {
headers: {
"set-orders": JSON.stringify(moveUpdateData.set_orders)
}
}
# Rehash userstories order field
# and persist in bulk all changes.
promise = promise.then =>
itemsToSave = _.reject(itemsToSave, {"id": us.id})
data = @.prepareBulkUpdateData(itemsToSave)
promise = @repo.save(us, true, params, options, true)
return @rs.userstories.bulkUpdateKanbanOrder(us.project, data).then =>
return itemsToSave
promise = promise.then (result) =>
headers = result[1]
if headers && headers['taiga-info-order-updated']
order = JSON.parse(headers['taiga-info-order-updated'])
@kanbanUserstoriesService.assignOrders(order)
return promise
module.controller("KanbanController", KanbanController)
#############################################################################
@ -321,7 +323,7 @@ module.directive("tgKanban", ["$tgRepo", "$rootScope", KanbanDirective])
## Kanban Archived Status Column Header Control
#############################################################################
KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
KanbanArchivedStatusHeaderDirective = ($rootscope, $translate, kanbanUserstoriesService) ->
showArchivedText = $translate.instant("KANBAN.ACTION_SHOW_ARCHIVED")
hideArchivedText = $translate.instant("KANBAN.ACTION_HIDE_ARCHIVED")
@ -329,6 +331,9 @@ KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
status = $scope.$eval($attrs.tgKanbanArchivedStatusHeader)
hidden = true
kanbanUserstoriesService.addArchivedStatus(status.id)
kanbanUserstoriesService.hideStatus(status.id)
$scope.class = "icon-watch"
$scope.title = showArchivedText
@ -341,24 +346,27 @@ KanbanArchivedStatusHeaderDirective = ($rootscope, $translate) ->
$scope.title = showArchivedText
$rootscope.$broadcast("kanban:hide-userstories-for-status", status.id)
kanbanUserstoriesService.hideStatus(status.id)
else
$scope.class = "icon-unwatch"
$scope.title = hideArchivedText
$rootscope.$broadcast("kanban:show-userstories-for-status", status.id)
kanbanUserstoriesService.showStatus(status.id)
$scope.$on "$destroy", ->
$el.off()
return {link:link}
module.directive("tgKanbanArchivedStatusHeader", [ "$rootScope", "$translate", KanbanArchivedStatusHeaderDirective])
module.directive("tgKanbanArchivedStatusHeader", [ "$rootScope", "$translate", "tgKanbanUserstories", KanbanArchivedStatusHeaderDirective])
#############################################################################
## Kanban Archived Status Column Intro Directive
#############################################################################
KanbanArchivedStatusIntroDirective = ($translate) ->
KanbanArchivedStatusIntroDirective = ($translate, kanbanUserstoriesService) ->
userStories = []
link = ($scope, $el, $attrs) ->
@ -366,105 +374,40 @@ KanbanArchivedStatusIntroDirective = ($translate) ->
status = $scope.$eval($attrs.tgKanbanArchivedStatusIntro)
$el.text(hiddenUserStoriexText)
updateIntroText = ->
if userStories.length > 0
updateIntroText = (hasArchived) ->
if hasArchived
$el.text("")
else
$el.text(hiddenUserStoriexText)
$scope.$on "kanban:us:move", (ctx, itemUs, oldStatusId, newStatusId, itemIndex) ->
# The destination columnd is this one
if status.id == newStatusId
# Reorder
if status.id == oldStatusId
r = userStories.indexOf(itemUs)
userStories.splice(r, 1)
userStories.splice(itemIndex, 0, itemUs)
# Archiving user story
else
itemUs.isArchived = true
userStories.splice(itemIndex, 0, itemUs)
# Unarchiving user story
else if status.id == oldStatusId
itemUs.isArchived = false
r = userStories.indexOf(itemUs)
userStories.splice(r, 1)
updateIntroText()
hasArchived = !!kanbanUserstoriesService.getStatus(newStatusId).length
updateIntroText(hasArchived)
$scope.$on "kanban:shown-userstories-for-status", (ctx, statusId, userStoriesLoaded) ->
if statusId == status.id
userStories = _.filter(userStoriesLoaded, (us) -> us.status == status.id)
updateIntroText()
kanbanUserstoriesService.deleteStatus(statusId)
kanbanUserstoriesService.add(userStoriesLoaded)
hasArchived = !!kanbanUserstoriesService.getStatus(statusId).length
updateIntroText(hasArchived)
$scope.$on "kanban:hidden-userstories-for-status", (ctx, statusId) ->
if statusId == status.id
userStories = []
updateIntroText()
updateIntroText(false)
$scope.$on "$destroy", ->
$el.off()
return {link:link}
module.directive("tgKanbanArchivedStatusIntro", ["$translate", KanbanArchivedStatusIntroDirective])
#############################################################################
## Kanban User Story Directive
#############################################################################
KanbanUserstoryDirective = ($rootscope, $loading, $rs, $rs2) ->
link = ($scope, $el, $attrs, $model) ->
$scope.$watch "us", (us) ->
if us.is_blocked and not $el.hasClass("blocked")
$el.addClass("blocked")
else if not us.is_blocked and $el.hasClass("blocked")
$el.removeClass("blocked")
$el.on 'click', '.edit-us', (event) ->
if $el.find(".icon-edit").hasClass("noclick")
return
target = $(event.target)
currentLoading = $loading()
.target(target)
.timeout(200)
.removeClasses("icon-edit")
.start()
us = $model.$modelValue
$rs.userstories.getByRef(us.project, us.ref).then (editingUserStory) =>
$rs2.attachments.list("us", us.id, us.project).then (attachments) =>
$rootscope.$broadcast("usform:edit", editingUserStory, attachments.toJS())
currentLoading.finish()
$scope.getTemplateUrl = () ->
if $scope.us.isPlaceholder
return "common/components/kanban-placeholder.html"
else
return "kanban/kanban-task.html"
$scope.$on "$destroy", ->
$el.off()
return {
template: '<ng-include src="getTemplateUrl()"/>',
link: link
require: "ngModel"
}
module.directive("tgKanbanUserstory", ["$rootScope", "$tgLoading", "$tgResources", "tgResources", KanbanUserstoryDirective])
module.directive("tgKanbanArchivedStatusIntro", ["$translate", "tgKanbanUserstories", KanbanArchivedStatusIntroDirective])
#############################################################################
## Kanban Squish Column Directive
#############################################################################
KanbanSquishColumnDirective = (rs) ->
link = ($scope, $el, $attrs) ->
$scope.$on "project:loaded", (event, project) ->
$scope.folds = rs.kanban.getStatusColumnModes(project.id)
@ -484,6 +427,7 @@ KanbanSquishColumnDirective = (rs) ->
return 310
totalWidth = _.reduce columnWidths, (total, width) ->
return total + width
$el.find('.kanban-table-inner').css("width", totalWidth)
return {link: link}
@ -501,7 +445,7 @@ KanbanWipLimitDirective = ->
redrawWipLimit = =>
$el.find(".kanban-wip-limit").remove()
timeout 200, =>
element = $el.find(".kanban-task")[status.wip_limit]
element = $el.find("tg-card")[status.wip_limit]
if element
angular.element(element).before("<div class='kanban-wip-limit'></div>")
@ -517,79 +461,3 @@ KanbanWipLimitDirective = ->
return {link: link}
module.directive("tgKanbanWipLimit", KanbanWipLimitDirective)
#############################################################################
## Kanban User Directive
#############################################################################
KanbanUserDirective = ($log, $compile, $translate) ->
template = _.template("""
<figure class="avatar">
<a href="#" title="{{'US.ASSIGN' | translate}}" <% if (!clickable) {%>class="not-clickable"<% } %>>
<img src="<%- imgurl %>" alt="<%- name %>" class="avatar">
</a>
</figure>
""")
clickable = false
link = ($scope, $el, $attrs, $model) ->
username_label = $el.parent().find("a.task-assigned")
username_label.addClass("not-clickable")
if not $attrs.tgKanbanUserAvatar
return $log.error "KanbanUserDirective: no attr is defined"
wtid = $scope.$watch $attrs.tgKanbanUserAvatar, (v) ->
if not $scope.usersById?
$log.error "KanbanUserDirective requires userById set in scope."
wtid()
else
user = $scope.usersById[v]
render(user)
render = (user) ->
if user is undefined
ctx = {
name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"),
imgurl: "/#{window._version}/images/unnamed.png",
clickable: clickable
}
else
ctx = {
name: user.full_name_display,
imgurl: user.photo,
clickable: clickable
}
html = $compile(template(ctx))($scope)
$el.html(html)
username_label.text(ctx.name)
bindOnce $scope, "project", (project) ->
if project.my_permissions.indexOf("modify_us") > -1
clickable = true
$el.on "click", (event) =>
if $el.find("a").hasClass("noclick")
return
us = $model.$modelValue
$ctrl = $el.controller()
$ctrl.changeUsAssignedTo(us)
username_label.removeClass("not-clickable")
username_label.on "click", (event) ->
if $el.find("a").hasClass("noclick")
return
us = $model.$modelValue
$ctrl = $el.controller()
$ctrl.changeUsAssignedTo(us)
$scope.$on "$destroy", ->
$el.off()
return {link: link, require:"ngModel"}
module.directive("tgKanbanUserAvatar", ["$log", "$compile", "$translate", KanbanUserDirective])

View File

@ -40,8 +40,12 @@ module = angular.module("taigaKanban")
KanbanSortableDirective = ($repo, $rs, $rootscope) ->
link = ($scope, $el, $attrs) ->
bindOnce $scope, "project", (project) ->
if not (project.my_permissions.indexOf("modify_us") > -1)
unwatch = $scope.$watch "usByStatus", (usByStatus) ->
return if !usByStatus || !usByStatus.size
unwatch()
if not ($scope.project.my_permissions.indexOf("modify_us") > -1)
return
oldParentScope = null
@ -63,7 +67,7 @@ KanbanSortableDirective = ($repo, $rs, $rootscope) ->
copy: false,
mirrorContainer: tdom[0],
moves: (item) ->
return $(item).hasClass('kanban-task')
return $(item).is('tg-card')
})
drake.on 'drag', (item) ->
@ -83,14 +87,14 @@ KanbanSortableDirective = ($repo, $rs, $rootscope) ->
deleteElement(itemEl)
$scope.$apply ->
$rootscope.$broadcast("kanban:us:move", itemUs, itemUs.status, newStatusId, itemIndex)
$rootscope.$broadcast("kanban:us:move", itemUs, itemUs.getIn(['model', 'status']), newStatusId, itemIndex)
scroll = autoScroll(containers, {
margin: 20,
margin: 100,
pixels: 30,
scrollWhenOutside: true,
autoScroll: () ->
return this.down && drake.dragging;
return this.down && drake.dragging
})
$scope.$on "$destroy", ->

View File

@ -168,6 +168,8 @@ RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading,
$scope.newTask = $tgmodel.make_model("tasks", newTask)
render = ->
return if $scope.openNewRelatedTask
$scope.openNewRelatedTask = true
$el.on "keyup", "input", (event)->
@ -229,7 +231,7 @@ RelatedTasksDirective = ($repo, $rs, $rootscope) ->
link = ($scope, $el, $attrs) ->
loadTasks = ->
return $rs.tasks.list($scope.projectId, null, $scope.usId).then (tasks) =>
$scope.tasks = _.sortBy(tasks, 'ref')
$scope.tasks = _.sortBy(tasks, (x) => [x.us_order, x.ref])
return tasks
_isVisible = ->
@ -267,9 +269,9 @@ RelatedTasksDirective = ($repo, $rs, $rootscope) ->
module.directive("tgRelatedTasks", ["$tgRepo", "$tgResources", "$rootScope", RelatedTasksDirective])
RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, $translate, avatarService) ->
template = _.template("""
<img src="<%- imgurl %>" alt="<%- name %>"/>
<img style="background-color: <%- bg %>" src="<%- imgurl %>" alt="<%- name %>"/>
<figcaption><%- name %></figcaption>
""")
@ -277,11 +279,15 @@ RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
updateRelatedTask = (task) ->
ctx = {
name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"),
imgurl: "/" + window._version + "/images/unnamed.png"
}
member = $scope.usersById[task.assigned_to]
avatar = avatarService.getAvatar(member)
ctx.imgurl = avatar.url
ctx.bg = avatar.bg
if member
ctx.imgurl = member.photo
ctx.name = member.full_name_display
$el.find(".avatar").html(template(ctx))
@ -320,5 +326,5 @@ RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) ->
return {link: link}
module.directive("tgRelatedTaskAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate",
module.directive("tgRelatedTaskAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate", "tgAvatarService",
RelatedTaskAssignedToInlineEditionDirective])

View File

@ -81,6 +81,7 @@ urls = {
"project-transfer-start": "/projects/%s/transfer_start"
# Project Values - Choises
"epic-statuses": "/epic-statuses"
"userstory-statuses": "/userstory-statuses"
"points": "/points"
"task-statuses": "/task-statuses"
@ -92,11 +93,21 @@ urls = {
# Milestones/Sprints
"milestones": "/milestones"
# Epics
"epics": "/epics"
"epic-upvote": "/epics/%s/upvote"
"epic-downvote": "/epics/%s/downvote"
"epic-watch": "/epics/%s/watch"
"epic-unwatch": "/epics/%s/unwatch"
"epic-related-userstories": "/epics/%s/related_userstories"
"epic-related-userstories-bulk-create": "/epics/%s/related_userstories/bulk_create"
# 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-milestone": "/userstories/bulk_update_milestone"
"bulk-update-us-miles-order": "/userstories/bulk_update_sprint_order"
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
"userstories-filters": "/userstories/filters_data"
"userstory-upvote": "/userstories/%s/upvote"
@ -112,6 +123,7 @@ urls = {
"task-downvote": "/tasks/%s/downvote"
"task-watch": "/tasks/%s/watch"
"task-unwatch": "/tasks/%s/unwatch"
"task-filters": "/tasks/filters_data"
# Issues
"issues": "/issues"
@ -128,26 +140,30 @@ urls = {
"wiki-links": "/wiki-links"
# History
"history/epic": "/history/epic"
"history/us": "/history/userstory"
"history/issue": "/history/issue"
"history/task": "/history/task"
"history/wiki": "/history/wiki"
"history/wiki": "/history/wiki/%s"
# Attachments
"attachments/epic": "/epics/attachments"
"attachments/us": "/userstories/attachments"
"attachments/issue": "/issues/attachments"
"attachments/task": "/tasks/attachments"
"attachments/wiki_page": "/wiki/attachments"
# Custom Attributess
"custom-attributes/epic": "/epic-custom-attributes"
"custom-attributes/userstory": "/userstory-custom-attributes"
"custom-attributes/issue": "/issue-custom-attributes"
"custom-attributes/task": "/task-custom-attributes"
"custom-attributes/issue": "/issue-custom-attributes"
# Custom Attributess - Values
"custom-attributes-values/epic": "/epics/custom-attributes-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"
"custom-attributes-values/issue": "/issues/custom-attributes-values"
# Webhooks
"webhooks": "/webhooks"
@ -156,6 +172,7 @@ urls = {
"webhooklogs-resend": "/webhooklogs/%s/resend"
# Reports - CSV
"epics-csv": "/epics/csv?uuid=%s"
"userstories-csv": "/userstories/csv?uuid=%s"
"tasks-csv": "/tasks/csv?uuid=%s"
"issues-csv": "/issues/csv?uuid=%s"
@ -217,6 +234,7 @@ module.run([
"$tgRolesResourcesProvider",
"$tgUserSettingsResourcesProvider",
"$tgSprintsResourcesProvider",
"$tgEpicsResourcesProvider",
"$tgUserstoriesResourcesProvider",
"$tgTasksResourcesProvider",
"$tgIssuesResourcesProvider",

View File

@ -29,6 +29,9 @@ resourceProvider = ($repo) ->
return $repo.queryOne(resource, objectId)
service = {
epic: {
get: (objectId) -> _get(objectId, "custom-attributes-values/epic")
}
userstory: {
get: (objectId) -> _get(objectId, "custom-attributes-values/userstory")
}

View File

@ -32,6 +32,9 @@ resourceProvider = ($repo) ->
return $repo.queryMany(resource, {project: projectId})
service = {
epic:{
list: (projectId) -> _list(projectId, "custom-attributes/epic")
}
userstory:{
list: (projectId) -> _list(projectId, "custom-attributes/userstory")
}

View File

@ -0,0 +1,77 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/epics.coffee
###
taiga = @.taiga
generateHash = taiga.generateHash
resourceProvider = ($repo, $http, $urls, $storage) ->
service = {}
hashSuffix = "epics-queryparams"
service.getByRef = (projectId, ref) ->
params = service.getQueryParams(projectId)
params.project = projectId
params.ref = ref
return $repo.queryOne("epics", "by_ref", params)
service.listValues = (projectId, type) ->
params = {"project": projectId}
service.storeQueryParams(projectId, params)
return $repo.queryMany(type, params)
service.storeQueryParams = (projectId, params) ->
ns = "#{projectId}:#{hashSuffix}"
hash = generateHash([projectId, ns])
$storage.set(hash, params)
service.getQueryParams = (projectId) ->
ns = "#{projectId}:#{hashSuffix}"
hash = generateHash([projectId, ns])
return $storage.get(hash) or {}
service.upvote = (epicId) ->
url = $urls.resolve("epic-upvote", epicId)
return $http.post(url)
service.downvote = (epicId) ->
url = $urls.resolve("epic-downvote", epicId)
return $http.post(url)
service.watch = (epicId) ->
url = $urls.resolve("epic-watch", epicId)
return $http.post(url)
service.unwatch = (epicId) ->
url = $urls.resolve("epic-unwatch", epicId)
return $http.post(url)
return (instance) ->
instance.epics = service
module = angular.module("taigaResources")
module.factory("$tgEpicsResourcesProvider", ["$tgRepo","$tgHttp", "$tgUrls", "$tgStorage", resourceProvider])

View File

@ -31,6 +31,25 @@ resourceProvider = ($repo, $http, $urls) ->
service.get = (type, objectId) ->
return $repo.queryOneRaw("history/#{type}", objectId)
service.editComment = (type, objectId, activityId, comment) ->
url = $urls.resolve("history/#{type}")
url = "#{url}/#{objectId}/edit_comment"
params = {
id: activityId
}
commentData = {
comment: comment
}
return $http.post(url, commentData, params).then (data) =>
return data.data
service.getCommentHistory = (type, objectId, activityId) ->
url = $urls.resolve("history/#{type}")
url = "#{url}/#{objectId}/comment_versions"
params = {id: activityId}
return $http.get(url, params).then (data) =>
return data.data
service.deleteComment = (type, objectId, activityId) ->
url = $urls.resolve("history/#{type}")
url = "#{url}/#{objectId}/delete_comment"

View File

@ -30,8 +30,6 @@ generateHash = taiga.generateHash
resourceProvider = ($repo, $http, $urls, $storage, $q) ->
service = {}
hashSuffix = "issues-queryparams"
filtersHashSuffix = "issues-filters"
myFiltersHashSuffix = "issues-my-filters"
service.get = (projectId, issueId) ->
params = service.getQueryParams(projectId)
@ -95,53 +93,6 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
hash = generateHash([projectId, ns])
return $storage.get(hash) or {}
service.storeFilters = (projectSlug, params) ->
ns = "#{projectSlug}:#{filtersHashSuffix}"
hash = generateHash([projectSlug, ns])
$storage.set(hash, params)
service.getFilters = (projectSlug) ->
ns = "#{projectSlug}:#{filtersHashSuffix}"
hash = generateHash([projectSlug, ns])
return $storage.get(hash) or {}
service.storeMyFilters = (projectId, myFilters) ->
deferred = $q.defer()
url = $urls.resolve("user-storage")
ns = "#{projectId}:#{myFiltersHashSuffix}"
hash = generateHash([projectId, ns])
if _.isEmpty(myFilters)
promise = $http.delete("#{url}/#{hash}", {key: hash, value:myFilters})
promise.then ->
deferred.resolve()
promise.then null, ->
deferred.reject()
else
promise = $http.put("#{url}/#{hash}", {key: hash, value:myFilters})
promise.then (data) ->
deferred.resolve()
promise.then null, (data) ->
innerPromise = $http.post("#{url}", {key: hash, value:myFilters})
innerPromise.then ->
deferred.resolve()
innerPromise.then null, ->
deferred.reject()
return deferred.promise
service.getMyFilters = (projectId) ->
deferred = $q.defer()
url = $urls.resolve("user-storage")
ns = "#{projectId}:#{myFiltersHashSuffix}"
hash = generateHash([projectId, ns])
promise = $http.get("#{url}/#{hash}")
promise.then (data) ->
deferred.resolve(data.data.value)
promise.then null, (data) ->
deferred.resolve({})
return deferred.promise
return (instance) ->
instance.issues = service

View File

@ -32,16 +32,6 @@ resourceProvider = ($storage) ->
hashSuffixStatusViewModes = "kanban-statusviewmodels"
hashSuffixStatusColumnModes = "kanban-statuscolumnmodels"
service.storeStatusViewModes = (projectId, params) ->
ns = "#{projectId}:#{hashSuffixStatusViewModes}"
hash = generateHash([projectId, ns])
$storage.set(hash, params)
service.getStatusViewModes = (projectId) ->
ns = "#{projectId}:#{hashSuffixStatusViewModes}"
hash = generateHash([projectId, ns])
return $storage.get(hash) or {}
service.storeStatusColumnModes = (projectId, params) ->
ns = "#{projectId}:#{hashSuffixStatusColumnModes}"
hash = generateHash([projectId, ns])

View File

@ -40,7 +40,7 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $translate) ->
return $repo.queryMany("projects")
service.listByMember = (memberId) ->
params = {"member": memberId, "order_by": "memberships__user_order"}
params = {"member": memberId, "order_by": "user_order"}
return $repo.queryMany("projects", params)
service.templates = ->
@ -61,18 +61,22 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $translate) ->
url = $urls.resolve("bulk-update-projects-order")
return $http.post(url, bulkData)
service.regenerate_epics_csv_uuid = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_epics_csv_uuid"
return $http.post(url)
service.regenerate_userstories_csv_uuid = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_userstories_csv_uuid"
return $http.post(url)
service.regenerate_issues_csv_uuid = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_issues_csv_uuid"
return $http.post(url)
service.regenerate_tasks_csv_uuid = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_tasks_csv_uuid"
return $http.post(url)
service.regenerate_issues_csv_uuid = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/regenerate_issues_csv_uuid"
return $http.post(url)
service.leave = (projectId) ->
url = "#{$urls.resolve("projects")}/#{projectId}/leave"
return $http.post(url)
@ -83,6 +87,34 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $translate) ->
service.tagsColors = (projectId) ->
return $repo.queryOne("projects", "#{projectId}/tags_colors")
service.deleteTag = (projectId, tag) ->
url = "#{$urls.resolve("projects")}/#{projectId}/delete_tag"
return $http.post(url, {tag: tag})
service.createTag = (projectId, tag, color) ->
url = "#{$urls.resolve("projects")}/#{projectId}/create_tag"
data = {}
data.tag = tag
data.color = null
if color
data.color = color
return $http.post(url, data)
service.editTag = (projectId, from_tag, to_tag, color) ->
url = "#{$urls.resolve("projects")}/#{projectId}/edit_tag"
data = {}
data.from_tag = from_tag
if to_tag
data.to_tag = to_tag
data.color = null
if color
data.color = color
return $http.post(url, data)
service.mixTags = (projectId, to_tag, from_tags) ->
url = "#{$urls.resolve("projects")}/#{projectId}/mix_tags"
return $http.post(url, {to_tag: to_tag, from_tags: from_tags})
service.export = (projectId) ->
url = "#{$urls.resolve("exporter")}/#{projectId}"
return $http.get(url)

View File

@ -38,17 +38,23 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
params.project = projectId
return $repo.queryOne("tasks", taskId, params)
service.getByRef = (projectId, ref) ->
service.getByRef = (projectId, ref, extraParams) ->
params = service.getQueryParams(projectId)
params.project = projectId
params.ref = ref
params = _.extend({}, params, extraParams)
return $repo.queryOne("tasks", "by_ref", params)
service.listInAllProjects = (filters) ->
return $repo.queryMany("tasks", filters)
service.list = (projectId, sprintId=null, userStoryId=null) ->
params = {project: projectId}
service.filtersData = (params) ->
return $repo.queryOneRaw("task-filters", null, params)
service.list = (projectId, sprintId=null, userStoryId=null, params) ->
params = _.merge(params, {project: projectId})
params.milestone = sprintId if sprintId
params.user_story = userStoryId if userStoryId
service.storeQueryParams(projectId, params)
@ -56,7 +62,7 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
service.bulkCreate = (projectId, sprintId, usId, data) ->
url = $urls.resolve("bulk-create-tasks")
params = {project_id: projectId, sprint_id: sprintId, us_id: usId, bulk_tasks: data}
params = {project_id: projectId, milestone_id: sprintId, us_id: usId, bulk_tasks: data}
return $http.post(url, params).then (result) ->
return result.data

View File

@ -26,7 +26,7 @@ taiga = @.taiga
generateHash = taiga.generateHash
resourceProvider = ($repo, $http, $urls, $storage) ->
resourceProvider = ($repo, $http, $urls, $storage, $q) ->
service = {}
hashSuffix = "userstories-queryparams"
@ -35,10 +35,12 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
params.project = projectId
return $repo.queryOne("userstories", usId, params)
service.getByRef = (projectId, ref) ->
service.getByRef = (projectId, ref, extraParams = {}) ->
params = service.getQueryParams(projectId)
params.project = projectId
params.ref = ref
params = _.extend({}, params, extraParams)
return $repo.queryOne("userstories", "by_ref", params)
service.listInAllProjects = (filters) ->
@ -96,9 +98,9 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
params = {project_id: projectId, bulk_stories: data}
return $http.post(url, params)
service.bulkUpdateSprintOrder = (projectId, data) ->
url = $urls.resolve("bulk-update-us-sprint-order")
params = {project_id: projectId, bulk_stories: data}
service.bulkUpdateMilestone = (projectId, milestoneId, data) ->
url = $urls.resolve("bulk-update-us-milestone")
params = {project_id: projectId, milestone_id: milestoneId, bulk_stories: data}
return $http.post(url, params)
service.bulkUpdateKanbanOrder = (projectId, data) ->
@ -133,4 +135,4 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
instance.userstories = service
module = angular.module("taigaResources")
module.factory("$tgUserstoriesResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", "$tgStorage", resourceProvider])
module.factory("$tgUserstoriesResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", "$tgStorage", "$q", resourceProvider])

View File

@ -34,6 +34,9 @@ resourceProvider = ($repo, $http, $urls) ->
service.getBySlug = (projectId, slug) ->
return $repo.queryOne("wiki", "by_slug?project=#{projectId}&slug=#{slug}")
service.list = (projectId) ->
return $repo.queryMany("wiki", {project: projectId})
service.listLinks = (projectId) ->
return $repo.queryMany("wiki-links", {project: projectId})

View File

@ -48,10 +48,11 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgLocation",
"tgAppMetaService",
"$tgNavUrls",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @repo, @rs, @params, @q, @location, @appMetaService, @navUrls, @translate) ->
constructor: (@scope, @repo, @rs, @params, @q, @location, @appMetaService, @navUrls, @translate, @errorHandlingService) ->
@scope.sectionName = "Search"
promise = @.loadInitialData()
@ -87,6 +88,8 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin)
return @rs.projects.getBySlug(@params.pslug).then (project) =>
@scope.project = project
@scope.$emit('project:loaded', project)
@scope.epicStatusById = groupBy(project.epic_statuses, (x) -> x.id)
@scope.issueStatusById = groupBy(project.issue_statuses, (x) -> x.id)
@scope.taskStatusById = groupBy(project.task_statuses, (x) -> x.id)
@scope.severityById = groupBy(project.severities, (x) -> x.id)
@ -193,7 +196,7 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
return selectedSection
if data
for name in ["userstories", "issues", "tasks", "wikipages"]
for name in ["userstories", "epics", "issues", "tasks", "wikipages"]
value = data[name]
if value.length > maxVal
@ -221,6 +224,7 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) ->
activeSectionName = section.name
templates = {
epics: $templatecache.get("search-epics")
issues: $templatecache.get("search-issues")
tasks: $templatecache.get("search-tasks")
userstories: $templatecache.get("search-userstories")

View File

@ -25,6 +25,7 @@
taiga = @.taiga
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
trim = @.taiga.trim
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService, $translate, $q, attachmentsService) ->
link = ($scope, $el, attrs) ->
@ -41,7 +42,8 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
attachmentsToAdd = attachmentsToAdd.push(attachment)
$scope.deleteAttachment = (attachment) ->
attachmentsToDelete = attachmentsToDelete.push(attachment)
if attachment.get("id")
attachmentsToDelete = attachmentsToDelete.push(attachment)
createAttachments = (obj) ->
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
@ -55,6 +57,45 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
return $q.all(promises)
tagsToAdd = []
$scope.addTag = (tag, color) ->
value = trim(tag.toLowerCase())
tags = $scope.project.tags
projectTags = $scope.project.tags_colors
tags = [] if not tags?
projectTags = {} if not projectTags?
if value not in tags
tags.push(value)
projectTags[tag] = color || null
$scope.project.tags = tags
itemtags = _.clone($scope.task.tags)
inserted = _.find itemtags, (it) -> it[0] == value
if !inserted
itemtags.push([tag , color])
$scope.task.tags = itemtags
$scope.deleteTag = (tag) ->
value = trim(tag[0].toLowerCase())
tags = $scope.project.tags
itemtags = _.clone($scope.task.tags)
_.remove itemtags, (tag) -> tag[0] == value
$scope.task.tags = itemtags
_.pull($scope.task.tags, value)
$scope.$on "taskform:new", (ctx, sprintId, usId) ->
$scope.task = {
project: $scope.projectId
@ -78,7 +119,10 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
$el.find(".title").html(newTask + " ")
$el.find(".tag-input").val("")
lightboxService.open($el)
lightboxService.open $el, () ->
$scope.createEditTaskOpen = false
$scope.createEditTaskOpen = true
$scope.$on "taskform:edit", (ctx, task, attachments) ->
$scope.task = task
@ -96,7 +140,10 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
$el.find(".title").html(edit + " ")
$el.find(".tag-input").val("")
lightboxService.open($el)
lightboxService.open $el, () ->
$scope.createEditTaskOpen = false
$scope.createEditTaskOpen = true
submitButton = $el.find(".submit-button")
@ -108,6 +155,11 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
if not form.validate()
return
params = {
include_attachments: true,
include_tasks: true
}
if $scope.isNew
promise = $repo.create("tasks", $scope.task)
broadcastEvent = "taskform:new:success"
@ -116,20 +168,22 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
broadcastEvent = "taskform:edit:success"
promise.then (data) ->
createAttachments(data)
deleteAttachments(data)
.then () => createAttachments(data)
.then () =>
currentLoading.finish()
lightboxService.close($el)
return data
$rs.tasks.getByRef(data.project, data.ref, params).then (task) ->
$rootscope.$broadcast(broadcastEvent, task)
currentLoading = $loading()
.target(submitButton)
.start()
# FIXME: error handling?
promise.then (data) ->
currentLoading.finish()
lightboxService.close($el)
$rootscope.$broadcast(broadcastEvent, data)
$el.on "submit", "form", submit
@ -139,7 +193,7 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer
return {link: link}
CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService) ->
CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService, $model) ->
link = ($scope, $el, attrs) ->
$scope.form = {data: "", usId: null}
@ -161,6 +215,7 @@ CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService) -
promise = $rs.tasks.bulkCreate(projectId, sprintId, usId, data)
promise.then (result) ->
result = _.map(result, (x) => $model.make_model('userstories', x))
currentLoading.finish()
$rootscope.$broadcast("taskform:bulk:success", result)
lightboxService.close($el)
@ -205,5 +260,6 @@ module.directive("tgLbCreateBulkTasks", [
"$rootScope",
"$tgLoading",
"lightboxService",
"$tgModel",
CreateBulkTasksDirective
])

View File

@ -38,13 +38,14 @@ module = angular.module("taigaTaskboard")
## Taskboard Controller
#############################################################################
class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"tgResources"
"$routeParams",
"$q",
"tgAppMetaService",
@ -52,12 +53,21 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgNavUrls"
"$tgEvents"
"$tgAnalytics",
"$translate"
"$translate",
"tgErrorHandlingService",
"tgTaskboardTasks",
"$tgStorage",
"tgFilterRemoteStorageService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appMetaService, @location, @navUrls,
@events, @analytics, @translate) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @rs2, @params, @q, @appMetaService, @location, @navUrls,
@events, @analytics, @translate, @errorHandlingService, @taskboardTasksService, @storage, @filterRemoteStorageService) ->
bindMethods(@)
@taskboardTasksService.reset()
@scope.userstories = []
@.openFilter = false
return if @.applyStoredFilters(@params.pslug, "tasks-filters")
@scope.sectionName = @translate.instant("TASKBOARD.SECTION_NAME")
@.initializeEventHandlers()
@ -69,6 +79,155 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
# On Error
promise.then null, @.onInitialDataError.bind(@)
taiga.defineImmutableProperty @.scope, "usTasks", () =>
return @taskboardTasksService.usTasks
setZoom: (zoomLevel, zoom) ->
if @.zoomLevel != zoomLevel
@taskboardTasksService.resetFolds()
@.zoomLevel = zoomLevel
@.zoom = zoom
if @.zoomLevel == '0'
@rootscope.$broadcast("sprint:zoom0")
changeQ: (q) ->
@.replaceFilter("q", q)
@.loadTasks()
@.generateFilters()
removeFilter: (filter) ->
@.unselectFilter(filter.dataType, filter.id)
@.loadTasks()
@.generateFilters()
addFilter: (newFilter) ->
@.selectFilter(newFilter.category.dataType, newFilter.filter.id)
@.loadTasks()
@.generateFilters()
selectCustomFilter: (customFilter) ->
@.replaceAllFilters(customFilter.filter)
@.loadTasks()
@.generateFilters()
removeCustomFilter: (customFilter) ->
@filterRemoteStorageService.getFilters(@scope.projectId, 'tasks-custom-filters').then (userFilters) =>
delete userFilters[customFilter.id]
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, 'tasks-custom-filters').then(@.generateFilters)
saveCustomFilter: (name) ->
filters = {}
urlfilters = @location.search()
filters.tags = urlfilters.tags
filters.status = urlfilters.status
filters.assigned_to = urlfilters.assigned_to
filters.owner = urlfilters.owner
@filterRemoteStorageService.getFilters(@scope.projectId, 'tasks-custom-filters').then (userFilters) =>
userFilters[name] = filters
@filterRemoteStorageService.storeFilters(@scope.projectId, userFilters, 'tasks-custom-filters').then(@.generateFilters)
generateFilters: ->
@.storeFilters(@params.pslug, @location.search(), "tasks-filters")
urlfilters = @location.search()
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.milestone = @scope.sprintId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.assigned_to = urlfilters.assigned_to
loadFilters.owner = urlfilters.owner
loadFilters.q = urlfilters.q
return @q.all([
@rs.tasks.filtersData(loadFilters),
@filterRemoteStorageService.getFilters(@scope.projectId, 'tasks-custom-filters')
]).then (result) =>
data = result[0]
customFiltersRaw = result[1]
statuses = _.map data.statuses, (it) ->
it.id = it.id.toString()
return it
tags = _.map data.tags, (it) ->
it.id = it.name
return it
tagsWithAtLeastOneElement = _.filter tags, (tag) ->
return tag.count > 0
assignedTo = _.map data.assigned_to, (it) ->
if it.id
it.id = it.id.toString()
else
it.id = "null"
it.name = it.full_name || "Unassigned"
return it
owner = _.map data.owners, (it) ->
it.id = it.id.toString()
it.name = it.full_name
return it
@.selectedFilters = []
if loadFilters.status
selected = @.formatSelectedFilters("status", statuses, loadFilters.status)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.tags
selected = @.formatSelectedFilters("tags", tags, loadFilters.tags)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.assigned_to
selected = @.formatSelectedFilters("assigned_to", assignedTo, loadFilters.assigned_to)
@.selectedFilters = @.selectedFilters.concat(selected)
if loadFilters.owner
selected = @.formatSelectedFilters("owner", owner, loadFilters.owner)
@.selectedFilters = @.selectedFilters.concat(selected)
@.filterQ = loadFilters.q
@.filters = [
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.STATUS"),
dataType: "status",
content: statuses
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.TAGS"),
dataType: "tags",
content: tags,
hideEmpty: true,
totalTaggedElements: tagsWithAtLeastOneElement.length
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.ASSIGNED_TO"),
dataType: "assigned_to",
content: assignedTo
},
{
title: @translate.instant("COMMON.FILTERS.CATEGORIES.CREATED_BY"),
dataType: "owner",
content: owner
}
]
@.customFilters = []
_.forOwn customFiltersRaw, (value, key) =>
@.customFilters.push({id: key, name: key, filter: value})
_setMeta: ->
prettyDate = @translate.instant("BACKLOG.SPRINTS.DATE")
@ -91,24 +250,33 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
@appMetaService.setAll(title, description)
initializeEventHandlers: ->
# TODO: Reload entire taskboard after create/edit tasks seems
# a big overhead. It should be optimized in near future.
@scope.$on "taskform:bulk:success", =>
@.loadTaskboard()
@scope.$on "taskform:bulk:success", (event, tasks) =>
@.refreshTagsColors().then () =>
@taskboardTasksService.add(tasks)
@analytics.trackEvent("task", "create", "bulk create task on taskboard", 1)
@scope.$on "taskform:new:success", =>
@.loadTaskboard()
@scope.$on "taskform:new:success", (event, task) =>
@.refreshTagsColors().then () =>
@taskboardTasksService.add(task)
@analytics.trackEvent("task", "create", "create task on taskboard", 1)
@scope.$on("taskform:edit:success", => @.loadTaskboard())
@scope.$on("taskboard:task:move", @.taskMove)
@scope.$on "taskform:edit:success", (event, task) =>
@.refreshTagsColors().then () =>
@taskboardTasksService.replaceModel(task)
@scope.$on "assigned-to:added", (ctx, userId, task) =>
task.assigned_to = userId
promise = @repo.save(task)
promise.then null, ->
console.log "FAIL" # TODO
@scope.$on("taskboard:task:move", @.taskMove)
@scope.$on("assigned-to:added", @.onAssignedToChanged)
onAssignedToChanged: (ctx, userid, taskModel) ->
taskModel.assigned_to = userid
@taskboardTasksService.replaceModel(taskModel)
promise = @repo.save(taskModel)
promise.then null, ->
console.log "FAIL" # TODO
initializeSubscription: ->
routingKey = "changes.project.#{@scope.projectId}.tasks"
@ -124,12 +292,11 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>
if not project.is_backlog_activated
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.project = project
# Not used at this momment
@scope.pointsList = _.sortBy(project.points, "order")
# @scope.roleList = _.sortBy(project.roles, "order")
@scope.pointsById = groupBy(project.points, (e) -> e.id)
@scope.roleById = groupBy(project.roles, (e) -> e.id)
@scope.taskStatusList = _.sortBy(project.task_statuses, "order")
@ -163,40 +330,27 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
refreshTagsColors: ->
return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) =>
@scope.project.tags_colors = tags_colors
@scope.project.tags_colors = tags_colors._attrs
loadSprint: ->
return @rs.sprints.get(@scope.projectId, @scope.sprintId).then (sprint) =>
@scope.sprint = sprint
@scope.userstories = _.sortBy(sprint.user_stories, "sprint_order")
@taskboardTasksService.setUserstories(@scope.userstories)
return sprint
loadTasks: ->
return @rs.tasks.list(@scope.projectId, @scope.sprintId).then (tasks) =>
@scope.tasks = _.sortBy(tasks, 'taskboard_order')
@scope.usTasks = {}
params = {
include_attachments: true,
}
# Iterate over all userstories and
# null userstory for unassigned tasks
for us in _.union(@scope.userstories, [{id:null}])
@scope.usTasks[us.id] = {}
for status in @scope.taskStatusList
@scope.usTasks[us.id][status.id] = []
params = _.merge params, @location.search()
for task in @scope.tasks
if @scope.usTasks[task.user_story]? and @scope.usTasks[task.user_story][task.status]?
@scope.usTasks[task.user_story][task.status].push(task)
if tasks.length == 0
if @scope.userstories.length > 0
usId = @scope.userstories[0].id
else
usId = null
@scope.usTasks[usId][@scope.taskStatusList[0].id].push({isPlaceholder: true})
return tasks
return @rs.tasks.list(@scope.projectId, @scope.sprintId, null, params).then (tasks) =>
@taskboardTasksService.init(@scope.project, @scope.usersById)
@taskboardTasksService.set(tasks)
loadTaskboard: ->
return @q.all([
@ -218,60 +372,97 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
return data
return promise.then(=> @.loadProject())
.then(=> @.loadTaskboard())
.then =>
@.generateFilters()
refreshTasksOrder: (tasks) ->
items = @.resortTasks(tasks)
data = @.prepareBulkUpdateData(items)
return @.loadTaskboard().then(=> @.setRolePoints())
return @rs.tasks.bulkUpdateTaskTaskboardOrder(@scope.project.id, data)
showPlaceHolder: (statusId, usId) ->
if !@taskboardTasksService.tasksRaw.length
if @scope.taskStatusList[0].id == statusId &&
(!@scope.userstories.length || @scope.userstories[0].id == usId)
return true
resortTasks: (tasks) ->
items = []
return false
for item, index in tasks
item["taskboard_order"] = index
if item.isModified()
items.push(item)
editTask: (id) ->
task = @.taskboardTasksService.getTask(id)
return items
task = task.set('loading', true)
@taskboardTasksService.replace(task)
prepareBulkUpdateData: (uses) ->
return _.map(uses, (x) -> {"task_id": x.id, "order": x["taskboard_order"]})
@rs.tasks.getByRef(task.getIn(['model', 'project']), task.getIn(['model', 'ref'])).then (editingTask) =>
@rs2.attachments.list("task", task.get('id'), task.getIn(['model', 'project'])).then (attachments) =>
@rootscope.$broadcast("taskform:edit", editingTask, attachments.toJS())
task = task.set('loading', false)
@taskboardTasksService.replace(task)
taskMove: (ctx, task, usId, statusId, order) ->
# Remove task from old position
r = @scope.usTasks[task.user_story][task.status].indexOf(task)
@scope.usTasks[task.user_story][task.status].splice(r, 1)
taskMove: (ctx, task, oldStatusId, usId, statusId, order) ->
task = @taskboardTasksService.getTaskModel(task.get('id'))
# Add task to new position
tasks = @scope.usTasks[usId][statusId]
tasks.splice(order, 0, task)
moveUpdateData = @taskboardTasksService.move(task.id, usId, statusId, order)
task.user_story = usId
task.status = statusId
task.taskboard_order = order
params = {
status__is_archived: false,
include_attachments: true,
}
promise = @repo.save(task)
options = {
headers: {
"set-orders": JSON.stringify(moveUpdateData.set_orders)
}
}
@rootscope.$broadcast("sprint:task:moved", task)
promise = @repo.save(task, true, params, options, true).then (result) =>
headers = result[1]
if headers && headers['taiga-info-order-updated']
order = JSON.parse(headers['taiga-info-order-updated'])
@taskboardTasksService.assignOrders(order)
promise.then =>
@.refreshTasksOrder(tasks)
@.loadSprintStats()
promise.then null, =>
console.log "FAIL TASK SAVE"
## Template actions
addNewTask: (type, us) ->
switch type
when "standard" then @rootscope.$broadcast("taskform:new", @scope.sprintId, us?.id)
when "bulk" then @rootscope.$broadcast("taskform:bulk", @scope.sprintId, us?.id)
editTaskAssignedTo: (task) ->
toggleFold: (id) ->
@taskboardTasksService.toggleFold(id)
changeTaskAssignedTo: (id) ->
task = @taskboardTasksService.getTaskModel(id)
@rootscope.$broadcast("assigned-to:add", task)
setRolePoints: () ->
computableRoles = _.filter(@scope.project.roles, "computable")
getRole = (roleId) =>
roleId = parseInt(roleId, 10)
return _.find computableRoles, (role) -> role.id == roleId
getPoint = (pointId) =>
poitnId = parseInt(pointId, 10)
return _.find @scope.project.points, (point) -> point.id == pointId
pointsByRole = _.reduce @scope.userstories, (result, us, key) =>
_.forOwn us.points, (pointId, roleId) ->
role = getRole(roleId)
point = getPoint(pointId)
if !result[role.id]
result[role.id] = role
result[role.id].points = 0
result[role.id].points += point.value
return result
, {}
@scope.pointsByRole = Object.keys(pointsByRole).map (key) -> return pointsByRole[key]
module.controller("TaskboardController", TaskboardController)
@ -302,43 +493,6 @@ TaskboardDirective = ($rootscope) ->
module.directive("tgTaskboard", ["$rootScope", TaskboardDirective])
#############################################################################
## Taskboard Task Directive
#############################################################################
TaskboardTaskDirective = ($rootscope, $loading, $rs, $rs2) ->
link = ($scope, $el, $attrs, $model) ->
$scope.$watch "task", (task) ->
if task.is_blocked and not $el.hasClass("blocked")
$el.addClass("blocked")
else if not task.is_blocked and $el.hasClass("blocked")
$el.removeClass("blocked")
$el.find(".edit-task").on "click", (event) ->
if $el.find('.edit-task').hasClass('noclick')
return
$scope.$apply ->
target = $(event.target)
currentLoading = $loading()
.target(target)
.timeout(200)
.start()
task = $scope.task
$rs.tasks.getByRef(task.project, task.ref).then (editingTask) =>
$rs2.attachments.list("task", editingTask.id, editingTask.project).then (attachments) =>
$rootscope.$broadcast("taskform:edit", editingTask, attachments.toJS())
currentLoading.finish()
return {link:link}
module.directive("tgTaskboardTask", ["$rootScope", "$tgLoading", "$tgResources", "tgResources", TaskboardTaskDirective])
#############################################################################
## Taskboard Squish Column Directive
#############################################################################
@ -348,14 +502,18 @@ TaskboardSquishColumnDirective = (rs) ->
maxColumnWidth = 300
link = ($scope, $el, $attrs) ->
$scope.$on "sprint:zoom0", () =>
recalculateTaskboardWidth()
$scope.$on "sprint:task:moved", () =>
recalculateTaskboardWidth()
bindOnce $scope, "usTasks", (project) ->
$scope.statusesFolded = rs.tasks.getStatusColumnModes($scope.project.id)
$scope.usFolded = rs.tasks.getUsRowModes($scope.project.id, $scope.sprintId)
$scope.$watch "usTasks", () ->
if $scope.project
$scope.statusesFolded = rs.tasks.getStatusColumnModes($scope.project.id)
$scope.usFolded = rs.tasks.getUsRowModes($scope.project.id, $scope.sprintId)
recalculateTaskboardWidth()
recalculateTaskboardWidth()
$scope.foldStatus = (status) ->
$scope.statusesFolded[status.id] = !!!$scope.statusesFolded[status.id]
@ -374,7 +532,10 @@ TaskboardSquishColumnDirective = (rs) ->
recalculateTaskboardWidth()
getCeilWidth = (usId, statusId) =>
tasks = $scope.usTasks[usId][statusId].length
if usId
tasks = $scope.usTasks.getIn([usId.toString(), statusId.toString()]).size
else
tasks = $scope.usTasks.getIn(['null', statusId.toString()]).size
if $scope.statusesFolded[statusId]
if tasks and $scope.usFolded[usId]
@ -393,7 +554,10 @@ TaskboardSquishColumnDirective = (rs) ->
if width
column.css('max-width', width)
else
column.css("max-width", maxColumnWidth)
if $scope.ctrl.zoomLevel == '0'
column.css("max-width", 148)
else
column.css("max-width", maxColumnWidth)
refreshTaskboardTableWidth = () =>
columnWidths = []
@ -429,65 +593,3 @@ TaskboardSquishColumnDirective = (rs) ->
return {link: link}
module.directive("tgTaskboardSquishColumn", ["$tgResources", TaskboardSquishColumnDirective])
#############################################################################
## Taskboard User Directive
#############################################################################
TaskboardUserDirective = ($log, $translate) ->
clickable = false
link = ($scope, $el, $attrs) ->
username_label = $el.parent().find("a.task-assigned")
username_label.addClass("not-clickable")
$scope.$watch 'task.assigned_to', (assigned_to) ->
user = $scope.usersById[assigned_to]
if user is undefined
_.assign($scope, {
name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"),
imgurl: "/#{window._version}/images/unnamed.png",
clickable: clickable
})
else
_.assign($scope, {
name: user.full_name_display,
imgurl: user.photo,
clickable: clickable
})
username_label.text($scope.name)
bindOnce $scope, "project", (project) ->
if project.my_permissions.indexOf("modify_task") > -1
clickable = true
$el.find(".avatar-assigned-to").on "click", (event) =>
if $el.find('a').hasClass('noclick')
return
$ctrl = $el.controller()
$ctrl.editTaskAssignedTo($scope.task)
username_label.removeClass("not-clickable")
username_label.on "click", (event) ->
if $el.find('a').hasClass('noclick')
return
$ctrl = $el.controller()
$ctrl.editTaskAssignedTo($scope.task)
return {
link: link,
templateUrl: "taskboard/taskboard-user.html",
scope: {
"usersById": "=users",
"project": "=",
"task": "=",
}
}
module.directive("tgTaskboardUserAvatar", ["$log", "$translate", TaskboardUserDirective])

View File

@ -37,11 +37,14 @@ module = angular.module("taigaBacklog")
## Sortable Directive
#############################################################################
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
TaskboardSortableDirective = ($repo, $rs, $rootscope, $translate) ->
link = ($scope, $el, $attrs) ->
bindOnce $scope, "tasks", (xx) ->
# If the user has not enough permissions we don't enable the sortable
if not ($scope.project.my_permissions.indexOf("modify_us") > -1)
unwatch = $scope.$watch "usTasks", (usTasks) ->
return if !usTasks || !usTasks.size
unwatch()
if not ($scope.project.my_permissions.indexOf("modify_task") > -1)
return
oldParentScope = null
@ -49,6 +52,10 @@ TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
itemEl = null
tdom = $el
filterError = ->
text = $translate.instant("BACKLOG.SORTABLE_FILTER_ERROR")
$tgConfirm.notify("error", text)
deleteElement = (itemEl) ->
# Completelly remove item and its scope from dom
itemEl.scope().$destroy()
@ -62,12 +69,23 @@ TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
copySortSource: false,
copy: false,
mirrorContainer: $el[0],
moves: (item) -> return $(item).hasClass('taskboard-task')
accepts: (el, target) -> return !$(target).hasClass('taskboard-userstory-box')
moves: (item) ->
return $(item).is('tg-card')
})
drake.on 'drag', (item) ->
oldParentScope = $(item).parent().scope()
if $el.hasClass("active-filters")
filterError()
setTimeout (() ->
drake.cancel(true)
), 0
return false
drake.on 'dragend', (item) ->
parentEl = $(item).parent()
itemEl = $(item)
@ -84,14 +102,15 @@ TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
deleteElement(itemEl)
$scope.$apply ->
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
$rootscope.$broadcast("taskboard:task:move", itemTask, itemTask.getIn(['model', 'status']), newUsId, newStatusId, itemIndex)
scroll = autoScroll([$('.taskboard-table-body')[0]], {
margin: 20,
margin: 100,
pixels: 30,
scrollWhenOutside: true,
autoScroll: () ->
return this.down && drake.dragging;
return this.down && drake.dragging
})
$scope.$on "$destroy", ->
@ -105,5 +124,6 @@ module.directive("tgTaskboardSortable", [
"$tgRepo",
"$tgResources",
"$rootScope",
"$translate",
TaskboardSortableDirective
])

View File

@ -0,0 +1,172 @@
###
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
#
# 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: home.service.coffee
###
groupBy = @.taiga.groupBy
class TaskboardTasksService extends taiga.Service
@.$inject = []
constructor: () ->
@.reset()
reset: () ->
@.tasksRaw = []
@.foldStatusChanged = {}
@.usTasks = Immutable.Map()
init: (project, usersById) ->
@.project = project
@.usersById = usersById
resetFolds: () ->
@.foldStatusChanged = {}
@.refresh()
toggleFold: (taskId) ->
@.foldStatusChanged[taskId] = !@.foldStatusChanged[taskId]
@.refresh()
add: (task) ->
@.tasksRaw = @.tasksRaw.concat(task)
@.refresh()
set: (tasks) ->
@.tasksRaw = tasks
@.refreshRawOrder()
@.refresh()
setUserstories: (userstories) ->
@.userstories = userstories
refreshRawOrder: () ->
@.order = {}
@.order[task.id] = task.taskboard_order for task in @.tasksRaw
assignOrders: (order) ->
order = _.invert(order)
@.order = _.assign(@.order, order)
@.refresh()
getTask: (id) ->
findedTask = null
@.usTasks.forEach (us) ->
us.forEach (status) ->
findedTask = status.find (task) -> return task.get('id') == id
return false if findedTask
return false if findedTask
return findedTask
replace: (task) ->
@.usTasks = @.usTasks.map (us) ->
return us.map (status) ->
findedIndex = status.findIndex (usItem) ->
return usItem.get('id') == us.get('id')
if findedIndex != -1
status = status.set(findedIndex, task)
return status
getTaskModel: (id) ->
return _.find @.tasksRaw, (task) -> return task.id == id
replaceModel: (task) ->
@.tasksRaw = _.map @.tasksRaw, (it) ->
if task.id == it.id
return task
else
return it
@.refresh()
move: (id, usId, statusId, index) ->
task = @.getTaskModel(id)
taskByUsStatus = _.filter @.tasksRaw, (task) =>
return task.status == statusId && task.user_story == usId
taskByUsStatus = _.sortBy taskByUsStatus, (it) => @.order[it.id]
taksWithoutMoved = _.filter taskByUsStatus, (it) => it.id != id
beforeDestination = _.slice(taksWithoutMoved, 0, index)
afterDestination = _.slice(taksWithoutMoved, index)
setOrders = {}
previous = beforeDestination[beforeDestination.length - 1]
previousWithTheSameOrder = _.filter beforeDestination, (it) =>
@.order[it.id] == @.order[previous.id]
if previousWithTheSameOrder.length > 1
for it in previousWithTheSameOrder
setOrders[it.id] = @.order[it.id]
if !previous
@.order[task.id] = 0
else if previous
@.order[task.id] = @.order[previous.id] + 1
for it, key in afterDestination
@.order[it.id] = @.order[task.id] + key + 1
task.status = statusId
task.user_story = usId
task.taskboard_order = @.order[task.id]
@.refresh()
return {"task_id": task.id, "order": @.order[task.id], "set_orders": setOrders}
refresh: ->
@.tasksRaw = _.sortBy @.tasksRaw, (it) => @.order[it.id]
tasks = @.tasksRaw
taskStatusList = _.sortBy(@.project.task_statuses, "order")
usTasks = {}
# Iterate over all userstories and
# null userstory for unassigned tasks
for us in _.union(@.userstories, [{id:null}])
usTasks[us.id] = {}
for status in taskStatusList
usTasks[us.id][status.id] = []
for taskModel in tasks
if usTasks[taskModel.user_story]? and usTasks[taskModel.user_story][taskModel.status]?
task = {}
task.foldStatusChanged = @.foldStatusChanged[taskModel.id]
task.model = taskModel.getAttrs()
task.images = _.filter taskModel.attachments, (it) -> return !!it.thumbnail_card_url
task.id = taskModel.id
task.assigned_to = @.usersById[taskModel.assigned_to]
task.colorized_tags = _.map task.model.tags, (tag) =>
return {name: tag[0], color: tag[1]}
usTasks[taskModel.user_story][taskModel.status].push(task)
@.usTasks = Immutable.fromJS(usTasks)
angular.module("taigaKanban").service("tgTaskboardTasks", TaskboardTasksService)

View File

@ -50,11 +50,12 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgNavUrls",
"$tgAnalytics",
"$translate",
"$tgQueueModelTransformation"
"$tgQueueModelTransformation",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appMetaService, @navUrls, @analytics, @translate, @modelTransform) ->
@log, @appMetaService, @navUrls, @analytics, @translate, @modelTransform, @errorHandlingService) ->
bindMethods(@)
@scope.taskRef = @params.taskref

View File

@ -45,11 +45,12 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
"tgAppMetaService",
"$tgAuth",
"$translate",
"tgProjectService"
"tgProjectService",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appMetaService, @auth,
@translate, @projectService) ->
@translate, @projectService, @errorHandlingService) ->
@scope.sectionName = "TEAM.SECTION_NAME"
promise = @.loadInitialData()
@ -80,6 +81,8 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
for member in @scope.activeUsers
@scope.totals[member.id] = 0
console.log @scope.activeUsers
# Get current user
@scope.currentUser = _.find(@scope.activeUsers, {id: user?.id})

View File

@ -45,19 +45,19 @@ class UserSettingsController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgLocation",
"$tgNavUrls",
"$tgAuth",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @config, @repo, @confirm, @rs, @params, @q, @location, @navUrls,
@auth, @translate) ->
@auth, @translate, @errorHandlingService) ->
@scope.sectionName = "USER_SETTINGS.MENU.SECTION_TITLE"
@scope.project = {}
@scope.user = @auth.getUser()
if !@scope.user
@location.path(@navUrls.resolve("permission-denied"))
@location.replace()
@errorHandlingService.permissionDenied()
@scope.lang = @getLan()
@scope.theme = @getTheme()

View File

@ -44,10 +44,11 @@ class UserNotificationsController extends mixOf(taiga.Controller, taiga.PageMixi
"$q",
"$tgLocation",
"$tgNavUrls",
"$tgAuth"
"$tgAuth",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @auth, @errorHandlingService) ->
@scope.sectionName = "USER_SETTINGS.NOTIFICATIONS.SECTION_NAME"
@scope.user = @auth.getUser()
promise = @.loadInitialData()

View File

@ -50,12 +50,13 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgNavUrls",
"$tgAnalytics",
"$translate",
"$tgConfig",
"$tgQueueModelTransformation"
"$tgQueueModelTransformation",
"tgErrorHandlingService",
"$tgConfig"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @log, @appMetaService,
@navUrls, @analytics, @translate, @configService, @modelTransform) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
@log, @appMetaService, @navUrls, @analytics, @translate, @modelTransform, @errorHandlingService, @configService) ->
bindMethods(@)
@scope.usRef = @params.usref
@ -165,20 +166,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@modelTransform.setObject(@scope, 'us')
if @scope.us.neighbors.previous?.ref?
ctx = {
project: @scope.project.slug
ref: @scope.us.neighbors.previous.ref
}
@scope.previousUrl = @navUrls.resolve("project-userstories-detail", ctx)
if @scope.us.neighbors.next?.ref?
ctx = {
project: @scope.project.slug
ref: @scope.us.neighbors.next.ref
}
@scope.nextUrl = @navUrls.resolve("project-userstories-detail", ctx)
return us
loadSprint: ->
@ -330,7 +317,7 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTransfor
$el.html(html)
$compile($el.contents())($scope);
$compile($el.contents())($scope)
save = (status) =>
$el.find(".pop-status").popover().close()

View File

@ -51,11 +51,13 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
"tgAppMetaService",
"$tgNavUrls",
"$tgAnalytics",
"$translate"
"$translate",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location,
@filter, @log, @appMetaService, @navUrls, @analytics, @translate) ->
@filter, @log, @appMetaService, @navUrls, @analytics, @translate, @errorHandlingService) ->
@scope.$on("wiki:links:move", @.moveLink)
@scope.projectSlug = @params.pslug
@scope.wikiSlug = @params.slug
@scope.wikiTitle = @scope.wikiSlug
@ -86,7 +88,7 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.is_wiki_activated
@location.path(@navUrls.resolve("permission-denied"))
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@ -155,6 +157,16 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@repo.remove(@scope.wiki).then onSuccess, onError
moveLink: (ctx, item, itemIndex) =>
values = @scope.wikiLinks
r = values.indexOf(item)
values.splice(r, 1)
values.splice(itemIndex, 0, item)
_.each values, (value, index) ->
value.order = index
@repo.saveAll(values)
module.controller("WikiDetailController", WikiDetailController)
@ -162,7 +174,7 @@ module.controller("WikiDetailController", WikiDetailController)
## Wiki Summary Directive
#############################################################################
WikiSummaryDirective = ($log, $template, $compile, $translate) ->
WikiSummaryDirective = ($log, $template, $compile, $translate, avatarService) ->
template = $template.get("wiki/wiki-summary.html", true)
link = ($scope, $el, $attrs, $model) ->
@ -172,10 +184,12 @@ WikiSummaryDirective = ($log, $template, $compile, $translate) ->
else
user = $scope.usersById[wiki.last_modifier]
avatar = avatarService.getAvatar(user)
if user is undefined
user = {name: "unknown", imgUrl: "/" + window._version + "/images/user-noimage.png"}
user = {name: "unknown", avatar: avatar}
else
user = {name: user.full_name_display, imgUrl: user.photo}
user = {name: user.full_name_display, avatar: avatar}
ctx = {
totalEditions: wiki.editions
@ -199,14 +213,15 @@ WikiSummaryDirective = ($log, $template, $compile, $translate) ->
require: "ngModel"
}
module.directive("tgWikiSummary", ["$log", "$tgTemplate", "$compile", "$translate", WikiSummaryDirective])
module.directive("tgWikiSummary", ["$log", "$tgTemplate", "$compile", "$translate", "tgAvatarService", WikiSummaryDirective])
#############################################################################
## Editable Wiki Content Directive
#############################################################################
EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $analytics, $qqueue, $translate) ->
EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $analytics, $qqueue, $translate,
$wikiHistoryService) ->
link = ($scope, $el, $attrs, $model) ->
isEditable = ->
return $scope.project.my_permissions.indexOf("modify_wiki_page") != -1
@ -228,7 +243,6 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
return if not $model.$modelValue.id
$model.$modelValue.revert()
switchToReadMode()
getSelectedText = ->
@ -245,6 +259,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
$model.$setViewValue wikiPage.clone()
$wikiHistoryService.loadHistoryEntries()
$confirm.notify("success")
switchToReadMode()
@ -252,8 +267,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
$confirm.notify("error")
currentLoading = $loading()
.removeClasses("icon-floppy")
.target($el.find('.icon-floppy'))
.target($el.find('.save'))
.start()
if wiki.id?
@ -322,4 +336,5 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
}
module.directive("tgEditableWikiContent", ["$window", "$document", "$tgRepo", "$tgConfirm", "$tgLoading",
"$tgAnalytics", "$tgQqueue", "$translate", EditableWikiContentDirective])
"$tgAnalytics", "$tgQqueue", "$translate", "tgWikiHistoryService",
EditableWikiContentDirective])

View File

@ -38,12 +38,16 @@ module = angular.module("taigaWiki")
WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $template,
$compile, $translate) ->
template = $template.get("wiki/wiki-nav.html", true)
link = ($scope, $el, $attrs) ->
linkWikiLinks = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
if not $attrs.ngModel?
return $log.error "WikiNavDirective: no ng-model attr is defined"
addWikiLinkPermission = $scope.project.my_permissions.indexOf("add_wiki_link") > -1
drake = null
render = (wikiLinks) ->
addWikiLinkPermission = $scope.project.my_permissions.indexOf("add_wiki_link") > -1
deleteWikiLinkPermission = $scope.project.my_permissions.indexOf("delete_wiki_link") > -1
@ -58,8 +62,37 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $t
html = $compile(html)($scope)
$el.off()
if addWikiLinkPermission and drake
drake.destroy()
$el.html(html)
if addWikiLinkPermission
itemEl = null
tdom = $el.find(".sortable")
drake = dragula([tdom[0]], {
direction: 'vertical',
copySortSource: false,
copy: false,
mirrorContainer: tdom[0],
moves: (item) -> return $(item).is('li')
})
drake.on 'dragend', (item) ->
itemEl = $(item)
item = itemEl.scope().link
itemIndex = itemEl.index()
$scope.$emit("wiki:links:move", item, itemIndex)
scroll = autoScroll(window, {
margin: 20,
pixels: 30,
scrollWhenOutside: true,
autoScroll: () ->
return this.down && drake.dragging
})
$el.on "click", ".add-button", (event) ->
event.preventDefault()
$el.find(".new").removeClass("hidden")
@ -130,9 +163,14 @@ WikiNavDirective = ($tgrepo, $log, $location, $confirm, $analytics, $loading, $t
$el.find(".new input").val('')
$el.find(".add-button").show()
bindOnce($scope, $attrs.ngModel, render)
link = ($scope, $el, $attrs) ->
linkWikiLinks($scope, $el, $attrs)
$scope.$on "$destroy", ->
$el.off()
return {link:link}
module.directive("tgWikiNav", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgAnalytics",

View File

@ -0,0 +1,100 @@
###
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán Merino <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
# Copyright (C) 2014-2016 Xavi Julian <xavier.julian@kaleidos.net>
#
# 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/wiki/pages-list.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
module = angular.module("taigaWiki")
#############################################################################
## Wiki Pages List Controller
#############################################################################
class WikiPagesListController extends mixOf(taiga.Controller, taiga.PageMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgModel",
"$tgConfirm",
"$tgResources",
"$routeParams",
"$q",
"$tgNavUrls",
"tgErrorHandlingService"
]
constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q,
@navUrls, @errorHandlingService) ->
@scope.projectSlug = @params.pslug
@scope.wikiSlug = @params.slug
@scope.wikiTitle = @scope.wikiSlug
@scope.sectionName = "Wiki"
@scope.linksVisible = false
promise = @.loadInitialData()
# On Error
promise.then null, @.onInitialDataError.bind(@)
loadProject: ->
return @rs.projects.getBySlug(@params.pslug).then (project) =>
if not project.is_wiki_activated
@errorHandlingService.permissionDenied()
@scope.projectId = project.id
@scope.project = project
@scope.$emit('project:loaded', project)
return project
loadWikiPages: ->
promise = @rs.wiki.list(@scope.projectId).then (wikipages) =>
@scope.wikipages = wikipages
loadWikiLinks: ->
return @rs.wiki.listLinks(@scope.projectId).then (wikiLinks) =>
@scope.wikiLinks = wikiLinks
for link in @scope.wikiLinks
link.url = @navUrls.resolve("project-wiki-page", {
project: @scope.projectSlug
slug: link.href
})
selectedWikiLink = _.find(wikiLinks, {href: @scope.wikiSlug})
@scope.wikiTitle = selectedWikiLink.title if selectedWikiLink?
loadInitialData: ->
promise = @.loadProject()
return promise.then (project) =>
@.fillUsersAndRoles(project.members, project.roles)
@q.all([@.loadWikiLinks(), @.loadWikiPages()]).then @.checkLinksPerms.bind(this)
checkLinksPerms: ->
if @scope.project.my_permissions.indexOf("add_wiki_link") != -1 ||
(@scope.project.my_permissions.indexOf("view_wiki_links") != -1 && @scope.wikiLinks.length)
@scope.linksVisible = true
module.controller("WikiPagesListController", WikiPagesListController)

View File

@ -28,21 +28,24 @@ addClass = (el, className) ->
else
el.className += ' ' + className
nl2br = (str) =>
breakTag = '<br />'
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2')
bindMethods = (object) =>
dependencies = _.keys(object)
methods = []
_.forIn object, (value, key) =>
if key not in dependencies
if key not in dependencies && _.isFunction(value)
methods.push(key)
_.bindAll(object, methods)
bindOnce = (scope, attr, continuation) =>
val = scope.$eval(attr)
if val != undefined
@ -75,6 +78,7 @@ slugify = (data) ->
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
unslugify = (data) ->
if data
return _.capitalize(data.replace(/-/g, ' '))
@ -165,6 +169,7 @@ sizeFormat = (input, precision=1) ->
size = (input / Math.pow(1024, number)).toFixed(precision)
return "#{size} #{units[number]}"
stripTags = (str, exception) ->
if exception
pattern = new RegExp('<(?!' + exception + '\s*\/?)[^>]+>', 'gi')
@ -172,6 +177,7 @@ stripTags = (str, exception) ->
else
return String(str).replace(/<\/?[^>]+>/g, '')
replaceTags = (str, tags, replace) ->
# open tag
pattern = new RegExp('<(' + tags + ')>', 'gi')
@ -183,6 +189,7 @@ replaceTags = (str, tags, replace) ->
return str
defineImmutableProperty = (obj, name, fn) =>
Object.defineProperty obj, name, {
get: () =>
@ -197,6 +204,7 @@ defineImmutableProperty = (obj, name, fn) =>
return fn_result
}
_.mixin
removeKeys: (obj, keys) ->
_.chain([keys]).flatten().reduce(
@ -211,10 +219,14 @@ _.mixin
, [ [] ])
isImage = (name) ->
return name.match(/\.(jpe?g|png|gif|gifv|webm)/i) != null
isPdf = (name) ->
return name.match(/\.(pdf)/i) != null
patch = (oldImmutable, newImmutable) ->
pathObj = {}
@ -227,6 +239,18 @@ patch = (oldImmutable, newImmutable) ->
return pathObj
DEFAULT_COLOR_LIST = [
'#fce94f', '#edd400', '#c4a000', '#8ae234', '#73d216', '#4e9a06', '#d3d7cf',
'#fcaf3e', '#f57900', '#ce5c00', '#729fcf', '#3465a4', '#204a87', '#888a85',
'#ad7fa8', '#75507b', '#5c3566', '#ef2929', '#cc0000', '#a40000', '#222222'
]
getRandomDefaultColor = () ->
return _.sample(DEFAULT_COLOR_LIST)
getDefaulColorList = () ->
return _.clone(DEFAULT_COLOR_LIST)
taiga = @.taiga
taiga.addClass = addClass
taiga.nl2br = nl2br
@ -252,4 +276,7 @@ taiga.stripTags = stripTags
taiga.replaceTags = replaceTags
taiga.defineImmutableProperty = defineImmutableProperty
taiga.isImage = isImage
taiga.isPdf = isPdf
taiga.patch = patch
taiga.getRandomDefaultColor = getRandomDefaultColor
taiga.getDefaulColorList = getDefaulColorList

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
app/images/epics-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -20,9 +20,14 @@ html(lang="en")
window.prerenderReady = false;
body(tg-main)
div(tg-navigation-bar)
div(tg-navigation-bar, ng-if="!errorHandling.showingError")
div(ng-if="!errorHandling.showingError")
div.master(ng-view)
div.master(ng-view)
div(ng-if="errorHandling.notfound", ng-include="'error/not-found.html'")
div(ng-if="errorHandling.error", ng-include="'error/error.html'")
div(ng-if="errorHandling.permissionDenied", ng-include="'error/permission-denied.html'")
div(ng-if="errorHandling.blocked", ng-include="'projects/project/blocked-project.html'")
div.lightbox.lightbox-generic-ask
include partials/includes/modules/lightbox-generic-ask

View File

@ -464,13 +464,70 @@
};
var createPointCB = function createPointCB(object){
// A persistent object (as opposed to returned object) is used to save memory
// This is good to prevent layout thrashing, or for games, and such
// NOTE
// This uses IE fixes which should be OK to remove some day. :)
// Some speed will be gained by removal of these.
// pointCB should be saved in a variable on return
// This allows the usage of element.removeEventListener
return function pointCB(event){
event = event || window.event; // IE-ism
object.target = event.target || event.srcElement || event.originalTarget;
object.element = this;
object.type = event.type;
// Support touch
// http://www.creativebloq.com/javascript/make-your-site-work-touch-devices-51411644
if(event.targetTouches){
object.x = event.targetTouches[0].clientX;
object.y = event.targetTouches[0].clientY;
object.pageX = event.pageX;
object.pageY = event.pageY;
}else{
// If pageX/Y aren't available and clientX/Y are,
// calculate pageX/Y - logic taken from jQuery.
// (This is to support old IE)
// NOTE Hopefully this can be removed soon.
if (event.pageX === null && event.clientX !== null) {
var eventDoc = (event.target && event.target.ownerDocument) || document;
var doc = eventDoc.documentElement;
var body = eventDoc.body;
object.pageX = event.clientX +
(doc && doc.scrollLeft || body && body.scrollLeft || 0) -
(doc && doc.clientLeft || body && body.clientLeft || 0);
object.pageY = event.clientY +
(doc && doc.scrollTop || body && body.scrollTop || 0) -
(doc && doc.clientTop || body && body.clientTop || 0 );
}else{
object.pageX = event.pageX;
object.pageY = event.pageY;
}
// pageX, and pageY change with page scroll
// so we're not going to use those for x, and y.
// NOTE Most browsers also alias clientX/Y with x/y
// so that's something to consider down the road.
object.x = event.clientX;
object.y = event.clientY;
}
};
//NOTE Remember accessibility, Aria roles, and labels.
};
// Autscroller
function AutoScrollerFactory(element, options){
return new AutoScroller(element, options);
}
function AutoScroller(elements, options){
var self = this, pixels = 2;
options = options || {};
@ -479,7 +536,10 @@
this.scrolling = false;
this.scrollWhenOutside = options.scrollWhenOutside || false;
this.point = pointer(elements);
var point = {}, pointCB = createPointCB(point), down = false;
window.addEventListener('mousemove', pointCB, false);
window.addEventListener('touchmove', pointCB, false);
if(!isNaN(options.pixels)){
pixels = options.pixels;
@ -494,12 +554,30 @@
}
this.destroy = function() {
this.point.destroy();
window.removeEventListener('mousemove', pointCB, false);
window.removeEventListener('touchmove', pointCB, false);
window.removeEventListener('mousedown', onDown, false);
window.removeEventListener('touchstart', onDown, false);
window.removeEventListener('mouseup', onUp, false);
window.removeEventListener('touchend', onUp, false);
};
var hasWindow = null, temp = [];
for(var i=0; i<elements.length; i++){
if(elements[i] === window){
hasWindow = window;
break;
}else{
temp.push(elements[i])
}
}
elements = temp;
temp = null;
Object.defineProperties(this, {
down: {
get: function(){ return self.point.down; }
get: function(){ return down; }
},
interval: {
get: function(){ return 1/pixels * 1000; }
@ -510,48 +588,105 @@
}
});
this.point.on('move', function(el, rect){
window.addEventListener('mousedown', onDown, false);
window.addEventListener('touchstart', onDown, false);
window.addEventListener('mouseup', onUp, false);
window.addEventListener('touchend', onUp, false);
function onDown(){
down = true;
}
function onUp(){
down = false;
}
var n = 0, current;
window.addEventListener('mousemove', onMove, false);
window.addEventListener('touchmove', onMove, false);
function onMove(event){
if(!el) return;
if(!self.autoScroll()) return;
if(!self.scrollWhenOutside && this.outside(el)) return;
if(!event.target) return;
var target = event.target, last;
if(self.point.y < rect.top + self.margin){
if(!current || !inside(point, current)){
if(!current && target){
current = null;
while(target = target.parentNode){
for(var i=0; i<elements.length; i++){
if(elements[i] === target && inside(point, elements[i])){
current = elements[i];
break;
}
}
}
}else{
last = current;
current = null;
for(var i=0; i<elements.length; i++){
if(elements[i] !== last && inside(point, elements[i])){
current = elements[i];
}
}
}
}
if(hasWindow){
autoScroll(hasWindow);
}
if(!current) return;
autoScroll(current);
}
function autoScroll(el){
var rect = getRect(el);
if(point.y < rect.top + self.margin){
autoScrollV(el, -1, rect);
}else if(self.point.y > rect.bottom - self.margin){
}else if(point.y > rect.bottom - self.margin){
autoScrollV(el, 1, rect);
}
if(self.point.x < rect.left + self.margin){
if(point.x < rect.left + self.margin){
autoScrollH(el, -1, rect);
}else if(self.point.x > rect.right - self.margin){
}else if(point.x > rect.right - self.margin){
autoScrollH(el, 1, rect);
}
});
}
function autoScrollV(el, amount, rect){
//if(!self.down) return;
if(!self.autoScroll()) return;
if(!self.scrollWhenOutside && self.point.outside(el)) return;
if(!self.scrollWhenOutside && !inside(point, el, rect)) return;
if(el === window){
window.scrollTo(el.pageXOffset, el.pageYOffset + amount);
}else{
el.scrollTop = el.scrollTop + amount;
}
setTimeout(function(){
if(self.point.y < rect.top + self.margin){
if(point.y < rect.top + self.margin){
autoScrollV(el, amount, rect);
}else if(self.point.y > rect.bottom - self.margin){
}else if(point.y > rect.bottom - self.margin){
autoScrollV(el, amount, rect);
}
}, self.interval);
}
function autoScrollH(el, amount, rect){
//if(!self.down) return;
if(!self.autoScroll()) return;
if(!self.scrollWhenOutside && self.point.outside(el)) return;
if(!self.scrollWhenOutside && !inside(point, el, rect)) return;
if(el === window){
window.scrollTo(el.pageXOffset + amount, el.pageYOffset);
}else{
@ -559,9 +694,9 @@
}
setTimeout(function(){
if(self.point.x < rect.left + self.margin){
if(point.x < rect.left + self.margin){
autoScrollH(el, amount, rect);
}else if(self.point.x > rect.right - self.margin){
}else if(point.x > rect.right - self.margin){
autoScrollH(el, amount, rect);
}
}, self.interval);
@ -569,5 +704,36 @@
}
function getRect(el){
if(el === window){
return {
top: 0,
left: 0,
right: window.innerWidth,
bottom: window.innerHeight,
width: window.innerWidth,
height: window.innerHeight
};
}else{
try{
return el.getBoundingClientRect();
}catch(e){
throw new TypeError("Can't call getBoundingClientRect on "+el);
}
}
}
function inside(point, el, rect){
rect = rect || getRect(el);
return (point.y > rect.top && point.y < rect.bottom &&
point.x > rect.left && point.x < rect.right);
}
function AutoScrollerFactory(element, options){
return new AutoScroller(element, options);
}
window.autoScroll = AutoScrollerFactory;
}());

View File

@ -2,6 +2,7 @@
var multipleSortableClass = 'ui-multisortable-multiple';
var mainClass = 'main-drag-item';
var inProgress = false;
var removeEventFn = null;
var reset = function(elm) {
$(elm)
@ -59,7 +60,7 @@
var current = dragMultiple.items.elm;
var container = dragMultiple.items.container;
$(window).off('mousemove.dragmultiple');
document.documentElement.removeEventListener('mousemove', removeEventFn);
// reset
dragMultiple.items = {};
@ -199,12 +200,14 @@
dragMultiple.start = function(item, container) {
if (isMultiple(item, container)) {
$(window).on('mousemove.dragmultiple', function() {
document.documentElement.addEventListener('mousemove', function() {
if (!inProgress) {
dragMultiple.prepare(item, container);
}
drag();
removeEventFn = arguments.callee;
});
}
};

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "In item per línia",
"NEW_BULK": "Nova inserció en grup",
"RELATED_TASKS": "Tasques relacionades",
"PREVIOUS": "Previous",
"NEXT": "Següent",
"LOGOUT": "Surt",
"EXTERNAL_USER": "un usuari extern",
"GENERIC_ERROR": "Un Oompa Loompas diu {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Aquest valor pareix invàlid.",
"TYPE_EMAIL": "Deu ser un correu vàlid.",
@ -115,8 +122,9 @@
"USER_STORY": "Història d'usuari",
"TASK": "Tasca",
"ISSUE": "incidència",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Afegir tag",
"PLACEHOLDER": "Enter tag",
"DELETE": "Elimina l'etiqueta",
"ADD": "Afegeix l'etiqueta"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "filtres",
"TITLE": "Filtres",
"INPUT_PLACEHOLDER": "Descripció o referència",
"TITLE_ACTION_FILTER_BUTTON": "cerca",
"BREADCRUMB_TITLE": "tornar a categories",
"BREADCRUMB_FILTERS": "Filtres",
"BREADCRUMB_STATUS": "estats"
"INPUT_SEARCH_PLACEHOLDER": "Descripció o ref",
"TITLE_ACTION_SEARCH": "Cerca",
"ACTION_SAVE_CUSTOM_FILTER": "Guarda com a filtre",
"PLACEHOLDER_FILTER_NAME": "Escriu el filtre i pressiona Intro",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Tipus",
"STATUS": "Estats",
"SEVERITY": "Severitat",
"PRIORITIES": "Prioritats",
"TAGS": "Etiquetes",
"ASSIGNED_TO": "Assignat a",
"CREATED_BY": "Creat per",
"CUSTOM_FILTERS": "Filtres personalitzats",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Esborrar filtre",
"MESSAGE": "el filtre '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Capçcalera de primer nivel",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Previsualitzar",
"EDIT_BUTTON": "Editar",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Ajuda de Markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Vore sprints",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Vore istòries d'usuari",
"ADD_USER_STORIES": "Afegir històries d'usuari",
"MODIFY_USER_STORIES": "Editar història d'usuari",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Esborrar històries d'usuari"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Vore tasca",
"ADD_TASKS": "Afegit tasques",
"MODIFY_TASKS": "Modificar tasques",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Esborrar tasques"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Vore incidències",
"ADD_ISSUES": "Afegeix incidències",
"MODIFY_ISSUES": "Modifica incidències",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Elimina incidències"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Observant",
"DASHBOARD": "Panell principal"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Sense assignar"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Vots",
"NAME": "Nom",
"PROJECT": "Projecte",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Estats",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Bloquejat",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "Els meus projectes - Taiga",
"PAGE_DESCRIPTION": "Una llista de tots els teus projects, que pots reordenar o crear nous.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Editar valor",
"TITLE_ACTION_DELETE_VALUE": "Borrar valor"
"TITLE_ACTION_DELETE_VALUE": "Borrar valor",
"TITLE_ACTION_DELETE_TAG": "Elimina l'etiqueta"
},
"HELP": "Necessites ajuda? Mira la nosta pàgina de suport!",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Mòdules",
"ENABLE": "Activa",
"DISABLE": "Desactiva",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Organitza les històries d'usuari per a mantindre una vista organitzada i prioritzada del treball.",
"NUMBER_SPRINTS": "Expected number of sprints",
@ -471,9 +546,9 @@
"PRIVATE_PROJECT": "Projecte privat",
"PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?",
"DELETE": "Esborra aquest projecte",
"LOGO_HELP": "The image will be scaled to 80x80px.",
"LOGO_HELP": "S'escalarà la imatge a 80x80px.",
"CHANGE_LOGO": "Change logo",
"ACTION_USE_DEFAULT_LOGO": "Use default image",
"ACTION_USE_DEFAULT_LOGO": "Utilitza la imatge per defecte",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects allowed by your current plan",
"MAX_PRIVATE_PROJECTS_MEMBERS": "The maximum number of members for private projects has been exceeded",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects allowed by your current plan",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Vas a canviar la URL d'accés al CSV. La URL previa no funcionarà. Estàs segur?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "informes d'històries d'usuari",
"SECTION_TITLE_TASK": "infome de tasques",
"SECTION_TITLE_ISSUE": "informe d'incidències",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Camps personalitzats",
"SUBTITLE": "Especifiqueu els camps personalitzats del les vostres històries d'usuari, tasques i incidències",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Camps personalitzats d'històries d'usuari",
"US_ADD": "Afegeix camps personalitzats en històries d'usuari",
"TASK_DESCRIPTION": "Camps personalitzats de tasques",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Estat",
"SUBTITLE": "Especifica els estats de les vostres històries d'usuari, tasques i incidències",
"US_TITLE": "Estats d'US",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Estats de tasques",
"ISSUE_TITLE": "Estats d'incidències"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Tipus d'incidències",
"ACTION_ADD": "Afegir now {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Etiquetes",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Afegeix l'etiqueta",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Rols - {{projectName}}",
"WARNING_NO_ROLE": "Ves amb compte, cap rol en el teu projecte pot estimar punts per a les històries d'usuari",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "la invitació a '{{email}}'."
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Valor per defecte per a selector de punts",
"LABEL_US": "Valor per defecte per a selector d'estats d'US",
"LABEL_TASK_STATUS": "Valor per defecte per a selector d'estats de tasques",
"LABEL_PRIORITY": "Valor per defecte per a selector de prioritat",
"LABEL_SEVERITY": "Valor per defecte per a selector de severitat",
"LABEL_ISSUE_TYPE": "Valor per defecte per a selector de tipus",
"LABEL_ISSUE_STATUS": "Valor per defecte per a selector de estats"
"LABEL_ISSUE_STATUS": "Valor per defecte per a selector de estats",
"LABEL_PRIORITY": "Valor per defecte per a selector de prioritat",
"LABEL_SEVERITY": "Valor per defecte per a selector de severitat"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Escriu un nom per a nou estat"
@ -681,7 +776,8 @@
"PRIORITIES": "Prioritats",
"SEVERITIES": "severitats",
"TYPES": "Tipus",
"CUSTOM_FIELDS": "Camps personalitzats"
"CUSTOM_FIELDS": "Camps personalitzats",
"TAGS": "Etiquetes"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Perfil de projecte"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Mostrar tot",
"FILTER_TYPE_PROJECTS": "Projectes",
"FILTER_TYPE_PROJECT_TITLES": "Mostra només projectes",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Históries",
"FILTER_TYPE_USER_STORIES_TITLES": "Veure només històries d'usuari",
"FILTER_TYPE_TASKS": "Tasques",
@ -821,7 +919,7 @@
"CHANGE_PASSWORD": "Canvi de contrasenya",
"DASHBOARD_TITLE": "Tauler",
"DISCOVER_TITLE": "Discover trending projects",
"NEW_ITEM": "New",
"NEW_ITEM": "Nova",
"DISCOVER": "Descobreix",
"ACTION_REORDER": "Arrossega els elements per endreçar"
},
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Opcional) Afegix un text personalizat a la invitació. Dis-li algo divertit als nous membres. ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Escriu un correu",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1068,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nova història d'usuari",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Descripció",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - Història d'Usuari {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estat: {{userStoryStatus }}. Completat {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} de {{userStoryTotalTasks}} tasques tancades). Punts: {{userStoryPoints}}. Descripció: {{userStoryDescription}}",
"SECTION_NAME": "Detalls de la història d'usuari",
"SECTION_NAME": "Història d'usuari",
"LINK_TASKBOARD": "Panell de tasques",
"TITLE_LINK_TASKBOARD": "Anar a panell de tasques",
"TOTAL_POINTS": "punts totals",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Aquesta US ha sigut creada desde",
"GO_TO_EXTERNAL_REFERENCE": "Anar a l'orige",
"BLOCKED": "Aquest història d'usuari està bloquejada",
"PREVIOUS": "previa història d'usuari",
"NEXT": "Pròxima història d'usuari",
"TITLE_DELETE_ACTION": "Esborra història d'usuari",
"LIGHTBOX_TITLE_BLOKING_US": "Bloquejant US",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tasques completades",
"ASSIGN": "Assigna història d'usuari",
"NOT_ESTIMATED": "Sense estimar",
"TOTAL_US_POINTS": "Punts totals d'US",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "More info",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Edit link",
"CLOSE": "Close",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Requeriment d'equip",
"CLIENT_REQUIREMENT": "Requeriment de client",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Comentari esborrat per {{user}} el {{date}}",
"DELETED_INFO": "Comment deleted by {{user}}",
"TITLE": "Comentaris",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Recent first",
"COMMENT": "Comentar",
"EDIT_COMMENT": "Edit comment",
"EDITED_COMMENT": "Edited:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Escriu un nou comentari ací",
"SHOW_DELETED": "Mostra el comentari esborrat.",
"HIDE_DELETED": "Amaga el comentari esborrat",
"DELETE": "Esborrar comentari",
"RESTORE": "Resturar comentari."
"RESTORE": "Resturar comentari.",
"HISTORY": {
"TITLE": "Activitat"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Mostrar activitat",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Mostrar activitat anterior ({{showMore}} més)",
"TITLE": "Activitat",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "Borrat",
"ADDED": "Afegit",
"US_POINTS": "Punts d'US ({{name}})",
"NEW_ATTACHMENT": "Nou adjunt",
"DELETED_ATTACHMENT": "Adjunts esborrats",
"UPDATED_ATTACHMENT": "Actualitzat adjunt {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "Esborrar camps personalitzat",
"TAGS_ADDED": "tags added:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "new attachment:",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Fet {size, plural, one{un canvi} other{# changes}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Requeriment d'equip",
"CLIENT_REQUIREMENT": "Requeriment de client",
"BLOCKED": "Bloquejat",
"VALUES": {
"YES": "si",
"NO": "no",
@ -1052,12 +1198,14 @@
"TAGS": "Etiquetes",
"ATTACHMENTS": "adjunts",
"IS_DEPRECATED": "és obsolet",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "ordre",
"BACKLOG_ORDER": "ordre de backlog",
"SPRINT_ORDER": "ordre d'sprint",
"KANBAN_ORDER": "ordre de kanban",
"TASKBOARD_ORDER": "ordre de panell de tasques",
"US_ORDER": "ordre d'US"
"US_ORDER": "ordre d'US",
"COLOR": "color"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "tasques<br />tancades",
"IOCAINE_DOSES": "dosis<br />iocaína",
"SHOW_STATISTICS_TITLE": "Mostrar estadístiques",
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph"
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "punts<br/> projecte",
@ -1122,9 +1271,7 @@
"TITLE": "Filtres",
"REMOVE": "Esborra filtres",
"HIDE": "Amaga filtres",
"SHOW": "Mostra filtres",
"FILTER_CATEGORY_STATUS": "Estats",
"FILTER_CATEGORY_TAGS": "Etiquetes"
"SHOW": "Mostra filtres"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Tasca {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estat: {{taskStatus }}. Descripció: {{taskDescription}}",
"SECTION_NAME": "Detalls de la tasca",
"SECTION_NAME": "Tasca",
"LINK_TASKBOARD": "Panell de tasques",
"TITLE_LINK_TASKBOARD": "Anar a panell de tasques",
"PLACEHOLDER_SUBJECT": "Afegix la descripció de la tasca",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Aquesta tasca ha sigut creada desde",
"TITLE_LINK_GO_ORIGIN": "Anar a història d'usuari",
"BLOCKED": "Aquesta tasca està bloquejada",
"PREVIOUS": "tasca prèvia",
"NEXT": "pròxima tasca",
"TITLE_DELETE_ACTION": "Esborrar tasca",
"LIGHTBOX_TITLE_BLOKING_TASK": "Bloquejant tasca",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Incidències - {{projectName}}",
"PAGE_DESCRIPTION": "El panell d'incidències de {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Incidències",
"SECTION_NAME": "Detalls d'incidència",
"SECTION_NAME": "incidència",
"ACTION_NEW_ISSUE": "+ NOVA INCIDÈNCIA",
"ACTION_PROMOTE_TO_US": "Promocionar història d'usuari",
"PLACEHOLDER_FILTER_NAME": "Escriu el filtre i pressiona Intro",
"PROMOTED": "Esta incidència ha sigut promcionada a US:",
"EXTERNAL_REFERENCE": "Esta incidència ha sigut creada desde",
"GO_TO_EXTERNAL_REFERENCE": "Anar a l'orige",
"BLOCKED": "Aquesta incidència està bloquejada",
"TITLE_PREVIOUS_ISSUE": "incidència prèvia",
"TITLE_NEXT_ISSUE": "pròxima incidència",
"ACTION_DELETE": "Esborrar incidència",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Bloquejant incidència",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Promociona aquesta incidència a història d'usuari",
"MESSAGE": "Segur que vols crear una nova US desde aquesta incidència"
},
"FILTERS": {
"TITLE": "Filtres",
"INPUT_SEARCH_PLACEHOLDER": "Descripció o ref",
"TITLE_ACTION_SEARCH": "Busca",
"ACTION_SAVE_CUSTOM_FILTER": "Guarda com a filtre",
"BREADCRUMB": "Filtres",
"TITLE_BREADCRUMB": "Filtres",
"CATEGORIES": {
"TYPE": "Tipus",
"STATUS": "Estats",
"SEVERITY": "Severitat",
"PRIORITIES": "Prioritats",
"TAGS": "Etiquetes",
"ASSIGNED_TO": "Assignat a",
"CREATED_BY": "Creat per",
"CUSTOM_FILTERS": "Filtres personalitzats"
},
"CONFIRM_DELETE": {
"TITLE": "Esborrar filtre",
"MESSAGE": "el filtre '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Tipus",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Cerca - {{projectName}}",
"PAGE_DESCRIPTION": "Busca qualsevol cosa al projecte {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "Històries d'usuari",
"FILTER_ISSUES": "Incidències",
"FILTER_TASKS": "Tasca",
@ -1375,9 +1496,9 @@
}
},
"USER_PROFILE": {
"IMAGE_HELP": "The image will be scaled to 80x80px.",
"IMAGE_HELP": "S'escalarà la imatge a 80x80px.",
"ACTION_CHANGE_IMAGE": "Canviar",
"ACTION_USE_GRAVATAR": "Use default image",
"ACTION_USE_GRAVATAR": "Utilitza la imatge per defecte",
"ACTION_DELETE_ACCOUNT": "Esborrar compte de Taiga",
"CHANGE_EMAIL_SUCCESS": "<strong>Mira el teu correu!</strong><br />Hem enviat un correu al teu conter<br /> amb les instrucciones per a escriure una nova adreça de correu",
"CHANGE_PHOTO": "Canviar foto",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Esborrar pàgina de Wiki",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Enllaços",
"ACTION_ADD_LINK": "Afegir link"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "voltes <br />editat",
"LAST_EDIT": "última <br />edició",
"LAST_MODIFICATION": "última modificació"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Creat",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} ha creat una nova tasca {{obj_name}} a {{project_name}} provinent de US {{us_name}}",
"WIKI_CREATED": "{{username}} has created a new wiki page {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} has created a new sprint {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} ha creat el projecte {{project_name}}",
"MILESTONE_UPDATED": "{{username}} has updated the sprint {{obj_name}}",
"US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} ha actualitzat l'atribut \"{{field_name}}\" de la tasca {{obj_name}} de la història d'usuari {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} ha actualitzat l'atribut \"{{field_name}}\" de la tasca {{obj_name}} de la història d'usuari {{us_name}} amb el valor {{new_value}}",
"WIKI_UPDATED": "{{username}} ha actualitzat la pàgina wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} ha comentat la història d'usuari {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} ha comentat la incidència {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} ha comentat la tasca {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} te un nou membre",
"US_ADDED_MILESTONE": "{{username}} ha afegit la història d'usuari {{obj_name}} a {{sprint_name}}",
"US_MOVED": "{{username}} ha mogut la història d'usuari {{obj_name}}",

View File

@ -5,8 +5,8 @@
"OR": "oder",
"LOADING": "Wird geladen...",
"LOADING_PROJECT": "Projekt wird geladen...",
"DATE": "DD MMM YYYY",
"DATETIME": "DD MMM YYYY HH:mm",
"DATE": "DD. MMM YYYY",
"DATETIME": "DD. MMM YYYY HH:mm",
"SAVE": "Speichern",
"CANCEL": "Abbrechen",
"ACCEPT": "Akzeptieren",
@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Ein Eintrag pro Zeile...",
"NEW_BULK": "Neue Massenerstellung",
"RELATED_TASKS": "Verbundene Aufgaben",
"PREVIOUS": "Previous",
"NEXT": "Weiter",
"LOGOUT": "Ausloggen",
"EXTERNAL_USER": "ein externer Benutzer",
"GENERIC_ERROR": "Eins unserer Helferlein sagt {{error}}.",
@ -43,8 +45,13 @@
"TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client",
"OWNER": "Projekteigentümer",
"CAPSLOCK_WARNING": "Achtung! Sie verwenden Großbuchstaben in einem Eingabefeld, dass Groß- und Kleinschreibung berücksichtigt.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Sind Sie sicher, dass Sie den Bearbeitungsmodus beenden möchten?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Beachten Sie, dass alle Änderungen verloren gehen, wenn Sie den Bearbeitungsmodus schließen, ohne vorher zu speichern.",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Dieser Wert scheint ungültig zu sein.",
"TYPE_EMAIL": "Dieser Wert sollte eine gültige E-Mail Adresse enthalten.",
@ -73,7 +80,7 @@
"PIKADAY": "Ungültiges Datumsformat. Bitte nutze DD MMM YYYY (etwa 23 März 1984)"
},
"PICKERDATE": {
"FORMAT": "DD MMM YYYY",
"FORMAT": "DD. MMM YYYY",
"IS_RTL": "falsch",
"FIRST_DAY_OF_WEEK": "1",
"PREV_MONTH": "Vorheriger Monat",
@ -115,8 +122,9 @@
"USER_STORY": "User-Story",
"TASK": "Aufgabe",
"ISSUE": "Ticket",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Schlagwort...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Schlagwort löschen",
"ADD": "Schlagwort hinzufügen"
},
@ -196,9 +204,26 @@
"TITLE": "Filter",
"INPUT_PLACEHOLDER": "Betreff oder Verweis",
"TITLE_ACTION_FILTER_BUTTON": "suche",
"BREADCRUMB_TITLE": "zurück zu den Kategorien",
"BREADCRUMB_FILTERS": "Filter",
"BREADCRUMB_STATUS": "Status"
"INPUT_SEARCH_PLACEHOLDER": "Thema oder ref",
"TITLE_ACTION_SEARCH": "Suche",
"ACTION_SAVE_CUSTOM_FILTER": "Als Benutzerfilter speichern",
"PLACEHOLDER_FILTER_NAME": "Benennen Sie den Filter und drücken Sie die Eingabetaste",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Arten",
"STATUS": "Status",
"SEVERITY": "Gewichtung",
"PRIORITIES": "Prioritäten",
"TAGS": "Schlagwörter",
"ASSIGNED_TO": "Zugeordnet zu",
"CREATED_BY": "Erstellt durch",
"CUSTOM_FILTERS": "Benutzerfilter",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Benutzerfilter löschen",
"MESSAGE": "der Benutzerfilter '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Überschrift 1",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Vorschau",
"EDIT_BUTTON": "Bearbeiten",
"ATTACH_FILE_HELP": "Dateien per Drag & Drop auf das obere Textfeld anhängen.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Markdown syntax Hilfe"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Sprints ansehen",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "User-Stories ansehen",
"ADD_USER_STORIES": "User-Stories hinzufügen",
"MODIFY_USER_STORIES": "User-Stories modifizieren",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "User-Stories löschen"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Aufgaben ansehen",
"ADD_TASKS": "Aufgaben hinzufügen",
"MODIFY_TASKS": "Aufgaben ändern",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Aufgaben löschen"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Tickets ansehen",
"ADD_ISSUES": "Tickets hinzufügen",
"MODIFY_ISSUES": "Tickets ändern",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Tickets löschen"
},
"WIKI": {
@ -289,7 +326,7 @@
"HEADER": "Ich bin bereits bei Taiga angemeldet",
"PLACEHOLDER_AUTH_NAME": "Benutzername oder E-Mail-Adresse",
"LINK_FORGOT_PASSWORD": "Haben Sie es vergessen?",
"TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?",
"TITLE_LINK_FORGOT_PASSWORD": "Haben Sie Ihr Passwort vergessen?",
"ACTION_ENTER": "Eingabe",
"ACTION_SIGN_IN": "Login",
"PLACEHOLDER_AUTH_PASSWORD": "Passwort"
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Beobachtet",
"DASHBOARD": "ProjeKte Dashboard"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ EPIC HINZUFÜGEN",
"UNASSIGNED": "Nicht zugeordnet"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Erfahren Sie mehr über Epics"
},
"TABLE": {
"VOTES": "Stimmen",
"NAME": "Name",
"PROJECT": "Projekt",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Zugewiesen",
"STATUS": "Status",
"PROGRESS": "Fortschritt",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "Neues Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team-Anforderung",
"CLIENT_REQUIREMENT": "Kunden-Anforderung",
"BLOCKED": "Blockiert",
"BLOCKED_NOTE_PLACEHOLDER": "Warum ist dieses Epic geblockt?",
"CREATE_EPIC": "Epic erzeugen"
}
},
"PROJECTS": {
"PAGE_TITLE": "Meine Projekte - Taiga",
"PAGE_DESCRIPTION": "Eine Liste mit all Deinen Projekten. Du kannst sie ordnen oder ein Neues anlegen.",
@ -385,7 +457,7 @@
"HIDE_DEPRECATED": "- verworfene Anhänge verbergen",
"COUNT_DEPRECATED": "({{ counter }} verworfen)",
"MAX_UPLOAD_SIZE": "Die maximale Dateigröße beträgt {{maxFileSize}}",
"DATE": "DD MMM YYYY [um] hh:mm",
"DATE": "DD. MMM YYYY [um] hh:mm",
"ERROR_UPLOAD_ATTACHMENT": "Das Hochladen war uns nicht möglich '{{fileName}}'. {{errorMessage}}",
"TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Anhang löschen...",
"MSG_LIGHTBOX_DELETE_ATTACHMENT": "der Anhang '{{fileName}}'",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Wert bearbeiten",
"TITLE_ACTION_DELETE_VALUE": "Wert löschen"
"TITLE_ACTION_DELETE_VALUE": "Wert löschen",
"TITLE_ACTION_DELETE_TAG": "Schlagwort löschen"
},
"HELP": "Wenn Sie Hilfe benötigen, besuchen Sie unsere Support-Seite!",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Module",
"ENABLE": "Aktivieren",
"DISABLE": "Deaktivieren",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualisieren und verwalten Sie den strategischsten Teil Ihres Projektes",
"BACKLOG": "Auftragsliste",
"BACKLOG_DESCRIPTION": "Verwalten Sie Ihre User-Stories, um einen organisierten Überblick der anstehenden und priorisierten Aufgaben zu erhalten.",
"NUMBER_SPRINTS": "Erwartete Anzahl an Sprints",
@ -452,9 +527,9 @@
"SELECT_VIDEOCONFERENCE": "Wählen Sie ein Videokonferenzsystem",
"SALT_CHAT_ROOM": "Fügen Sie ein Präfix für den Chatraum-Namen hinzu",
"JITSI_CHAT_ROOM": "Jitsi",
"APPEARIN_CHAT_ROOM": "Erscheint in",
"TALKY_CHAT_ROOM": "Gesprächig",
"CUSTOM_CHAT_ROOM": "Kunde",
"APPEARIN_CHAT_ROOM": "Appear.in",
"TALKY_CHAT_ROOM": "Talky.io",
"CUSTOM_CHAT_ROOM": "Benutzerdefiniert",
"URL_CHAT_ROOM": "URL Ihres Chatrooms"
},
"PROJECT_PROFILE": {
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Sie sind im Begriff, die CSV data access URL zu ändern. Die vorherige URL wird deaktiviert. Sind Sie sicher?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "User-Stories Berichte",
"SECTION_TITLE_TASK": "Aufgabenberichte",
"SECTION_TITLE_ISSUE": "Ticket Berichte",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Benutzerfelder",
"SUBTITLE": "Spezifizieren Sie die Benutzerfelder für Ihre User-Stories, Aufgaben und Tickets.",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Benutzerdefinierte Felder der User-Story",
"US_ADD": "Benutzerdefiniertes Feld bei User-Stories hinzufügen",
"TASK_DESCRIPTION": "Aufgaben benutzerdefinierte Felder",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Status",
"SUBTITLE": "Spezifizieren Sie die Status, die Ihre User-Stories, Aufgaben und Tickets durchlaufen werden.",
"US_TITLE": "User-Story Status",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Aufgaben-Status",
"ISSUE_TITLE": "Ticket-Status"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Ticketarten",
"ACTION_ADD": "Neu hinzufügen {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Schlagwörter",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "Es sieht so aus, als konnte zu Ihren Suchkriterien nichts passendes gefunden werden.",
"ACTION_ADD": "Schlagwort hinzufügen",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Rollen - {{projectName}}",
"WARNING_NO_ROLE": "Beachten Sie, keine Rolle in Ihrem Projekt wird in der Lage sein, die Punktevergabe für User-Stories einzuschätzen.",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -607,7 +701,7 @@
"HEADERS": "Überschriften",
"PAYLOAD": "Ladung",
"RESPONSE": "Rückmeldung",
"DATE": "DD MMM YYYY [um] hh:mm:ss",
"DATE": "DD. MMM YYYY [um] hh:mm:ss",
"ACTION_HIDE_HISTORY": "(Chronik verbergen)",
"ACTION_HIDE_HISTORY_TITLE": "Chronik Details verbergen",
"ACTION_SHOW_HISTORY": "(Chronik anzeigen)",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "die Einladung an {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Vorgegebener Wert für Punkteauswahl",
"LABEL_US": "Vorgegebener Wert für User-Story-Status Auswahl",
"LABEL_TASK_STATUS": "Vorgegebene Auswahl für den Aufgaben-Status",
"LABEL_PRIORITY": "Vorgegebener Wert für Prioritätsauswahl",
"LABEL_SEVERITY": "Vorgegebener Wert für Gewichtungsauswahl",
"LABEL_ISSUE_TYPE": "Vorgegebener Wert für Ticketartauswahl",
"LABEL_ISSUE_STATUS": "Vorgegebene Auswahl für den Ticket-Status"
"LABEL_ISSUE_STATUS": "Vorgegebene Auswahl für den Ticket-Status",
"LABEL_PRIORITY": "Vorgegebener Wert für Prioritätsauswahl",
"LABEL_SEVERITY": "Vorgegebener Wert für Gewichtungsauswahl"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Benennen Sie den neuen Status"
@ -681,7 +776,8 @@
"PRIORITIES": "Prioritäten",
"SEVERITIES": "Schweregrade",
"TYPES": "Typen",
"CUSTOM_FIELDS": "Benutzerdefinierte Felder"
"CUSTOM_FIELDS": "Benutzerdefinierte Felder",
"TAGS": "Schlagwörter"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Projektprofil"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Alle anzeigen",
"FILTER_TYPE_PROJECTS": "Projekte",
"FILTER_TYPE_PROJECT_TITLES": "Nur Projekte anzeigen",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Stories",
"FILTER_TYPE_USER_STORIES_TITLES": "Nur User-Stories anzeigen",
"FILTER_TYPE_TASKS": "Aufgaben",
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Optional) Fügen Sie einen persönlichen Text zur Einladung hinzu. Erzählen Sie Ihren neuen Mitgliedern etwas Schönes. ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Geben Sie eine E-Mail ein",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Leider kann dieses Projekt nicht mehr als <strong>{{maxMembers}}</strong> Mitglieder haben. Wenn Sie die derzeitige Grenze erhöhen möchten, kontaktieren Sie den Administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Leider kann dieses Projekt nicht mehr als <strong>{{maxMembers}}</strong> Mitglieder haben."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Das Projekt kann nicht ohne einen Projektleiter existieren.",
@ -970,10 +1068,30 @@
"BUTTON": "Fragen Sie dieses Projektmitglied, um Projektleiter zu werden"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Neue User-Story",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Thema",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - User-Story {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Abgeschlossen {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} von {{userStoryTotalTasks}} Aufgaben geschlossen). Punkte: {{userStoryPoints}}. Beschreibung: {{userStoryDescription}}",
"SECTION_NAME": "User-Story Details",
"SECTION_NAME": "User-Story",
"LINK_TASKBOARD": "Taskboard",
"TITLE_LINK_TASKBOARD": "Zu Taskboard wechseln",
"TOTAL_POINTS": "Gesamtpunkte",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Dies User-Story wurde angelegt von",
"GO_TO_EXTERNAL_REFERENCE": "Zur Quelle wechseln",
"BLOCKED": "Diese User-Story wird blockiert",
"PREVIOUS": "Vorherige User-Story",
"NEXT": "nächste User-Story",
"TITLE_DELETE_ACTION": "User-Story löschen",
"LIGHTBOX_TITLE_BLOKING_US": "Blockiert uns",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} Aufgaben fertiggestellt",
"ASSIGN": "Zugeordnete User-Story",
"NOT_ESTIMATED": "Nicht eingeschätzt",
"TOTAL_US_POINTS": "User-Story-Punkte insgesamt",
"TRIBE": {
"PUBLISH": "Als Gig in Taiga Tribe veröffentlichen",
"PUBLISH_INFO": "Weitere Infos",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story veröffentlicht als Gig in Taiga Tribe",
"EDIT_LINK": "Link bearbeiten",
"CLOSE": "Schließen",
"SYNCHRONIZE_LINK": "mit Taiga Tribe synchronisieren",
"PUBLISH_MORE_INFO_TITLE": "Brauchen Sie jemanden für diese Aufgabe?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Team Anforderung",
"CLIENT_REQUIREMENT": "Kundenanforderung",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Kommentar gelöscht von {{user}} am {{date}}",
"DELETED_INFO": "Kommentar gelöscht von {{user}}",
"TITLE": "Kommentare",
"COMMENTS_COUNT": "{{comments}} Kommentare",
"ORDER": "Reihenfolge",
"OLDER_FIRST": "Ältere zuerst",
"RECENT_FIRST": "Letzte zuerst",
"COMMENT": "Kommentieren",
"EDIT_COMMENT": "Kommentar bearbeiten",
"EDITED_COMMENT": "Bearbeitet:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Geben Sie hier einen neuen Kommentar ein",
"SHOW_DELETED": "Gelöschten Kommentar anzeigen",
"HIDE_DELETED": "Gelöschten Kommentar ausblenden",
"DELETE": "Kommentar löschen",
"RESTORE": "Kommentar wiederherstellen"
"RESTORE": "Kommentar wiederherstellen",
"HISTORY": {
"TITLE": "Aktivität"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Aktivitäten zeigen",
"DATETIME": "DD MMM YYYY HH:mm",
"DATETIME": "DD. MMM YYYY HH:mm",
"SHOW_MORE": "+ Vorherige Einträge zeigen ({{showMore}} vorhanden)",
"TITLE": "Aktivität",
"ACTIVITIES_COUNT": "{{activities}} Aktivitäten",
"REMOVED": "entfernt",
"ADDED": "hinzugefügt",
"US_POINTS": "User-Story Punkte ({{name}})",
"NEW_ATTACHMENT": "Neuer Anhang",
"DELETED_ATTACHMENT": "Gelöschter Anhang",
"UPDATED_ATTACHMENT": "aktualisierter Anhang {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "gelöschtes Kundenattribut",
"TAGS_ADDED": "Tags hinzugefügt:",
"TAGS_REMOVED": "Tags entfernt:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "neuer Anhang:",
"DELETED_ATTACHMENT": "gelöschter Anhang:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Machte {size, plural, one{eine Änderung} other{# Änderungen}}",
"BECAME_DEPRECATED": "ist veraltet",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Team Anforderung",
"CLIENT_REQUIREMENT": "Kundenanforderung",
"BLOCKED": "Blockiert",
"VALUES": {
"YES": "ja",
"NO": "nein",
@ -1052,12 +1198,14 @@
"TAGS": "Schlagwörter",
"ATTACHMENTS": "Anhänge",
"IS_DEPRECATED": "ist veraltet",
"IS_NOT_DEPRECATED": "ist nicht verworfen",
"ORDER": "Befehl",
"BACKLOG_ORDER": "Backlog Befehl",
"SPRINT_ORDER": "Sprint Befehl",
"KANBAN_ORDER": "Kanban Befehl",
"TASKBOARD_ORDER": "Taskboard Befehl",
"US_ORDER": "User-Story Befehl"
"US_ORDER": "User-Story Befehl",
"COLOR": "Farbe"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "geschlossene<br />Aufgaben",
"IOCAINE_DOSES": "Iocaine<br />Dosen",
"SHOW_STATISTICS_TITLE": "Statistik anzeigen",
"TOGGLE_BAKLOG_GRAPH": "Zeige/Verstecke Burndowngraph"
"TOGGLE_BAKLOG_GRAPH": "Zeige/Verstecke Burndowngraph",
"POINTS_PER_ROLE": "Points pro Rolle"
},
"SUMMARY": {
"PROJECT_POINTS": "Projekt<br />Punkte",
@ -1122,13 +1271,11 @@
"TITLE": "Filter",
"REMOVE": "Filter entfernen",
"HIDE": "Filter verbergen",
"SHOW": "Filter anzeigen",
"FILTER_CATEGORY_STATUS": "Status",
"FILTER_CATEGORY_TAGS": "Schlagwörter"
"SHOW": "Filter anzeigen"
},
"SPRINTS": {
"TITLE": "SPRINTS",
"DATE": "DD MMM YYYY",
"DATE": "DD. MMM YYYY",
"LINK_TASKBOARD": "Sprint Taskboard",
"TITLE_LINK_TASKBOARD": "Gehe zu Taskboard von \"{{name}}\"",
"NUMBER_SPRINTS": "<br/>Sprints",
@ -1173,13 +1320,13 @@
"YAXIS_LABEL": "Punkte",
"OPTIMAL": "Optimale unerledigte Punkte für Tag {{formattedDate}} sollten sein {{roundedValue}}",
"REAL": "Tatsächliche Anzahl unerledigter Punkte für Tag {{formattedDate}} ist {{roundedValue}}",
"DATE": "DD MMMM YYYY"
"DATE": "DD. MMMM YYYY"
}
},
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Aufgabe {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{taskStatus }}. Beschreibung: {{taskDescription}}",
"SECTION_NAME": "Aufgabendetails",
"SECTION_NAME": "Aufgabe",
"LINK_TASKBOARD": "Taskboard",
"TITLE_LINK_TASKBOARD": "Zu Taskboard wechseln",
"PLACEHOLDER_SUBJECT": "Betreff...",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Diese Aufgabe wurde erstellt durch",
"TITLE_LINK_GO_ORIGIN": "Zu User-Story wechseln",
"BLOCKED": "Diese Aufgabe wird blockiert",
"PREVIOUS": "vorherige Aufgabe",
"NEXT": "nächste Aufgabe",
"TITLE_DELETE_ACTION": "Aufgabe löschen",
"LIGHTBOX_TITLE_BLOKING_TASK": "Blockierende Aufgabe",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Tickets - {{projectName}}",
"PAGE_DESCRIPTION": "Das Ticket-Listen Panel des Projekts {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Tickets",
"SECTION_NAME": "Ticket Details",
"SECTION_NAME": "Ticket",
"ACTION_NEW_ISSUE": "+ NEUES TICKET",
"ACTION_PROMOTE_TO_US": "Zur User-Story aufwerten",
"PLACEHOLDER_FILTER_NAME": "Benennen Sie den Filter und drücken Sie die Eingabetaste",
"PROMOTED": "Dieses Ticket wurde aufgewertet zu User-Story:",
"EXTERNAL_REFERENCE": "Dieses Ticket wurde erstellt durch",
"GO_TO_EXTERNAL_REFERENCE": "Zur Quelle wechseln",
"BLOCKED": "Dieses Ticket wird blockiert",
"TITLE_PREVIOUS_ISSUE": "vorheriges Ticket",
"TITLE_NEXT_ISSUE": "nächstes Ticket",
"ACTION_DELETE": "Ticket löschen",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Blockierendes Ticket",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Dieses Problem zur User-Story aufwerten",
"MESSAGE": "Sind Sie sicher, dass Sie aus diesem Ticket eine neue User-Story erstellen möchten?"
},
"FILTERS": {
"TITLE": "Filter",
"INPUT_SEARCH_PLACEHOLDER": "Thema oder ref",
"TITLE_ACTION_SEARCH": "Suche",
"ACTION_SAVE_CUSTOM_FILTER": "Als Benutzerfilter speichern",
"BREADCRUMB": "Filter",
"TITLE_BREADCRUMB": "Filter",
"CATEGORIES": {
"TYPE": "Arten",
"STATUS": "Status",
"SEVERITY": "Gewichtung",
"PRIORITIES": "Prioritäten",
"TAGS": "Schlagwörter",
"ASSIGNED_TO": "Zugeordnet",
"CREATED_BY": "Erstellt durch",
"CUSTOM_FILTERS": "Benutzerfilter"
},
"CONFIRM_DELETE": {
"TITLE": "Benutzerfilter löschen",
"MESSAGE": "der Benutzerfilter '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Arten",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Suche - {{projectName}}",
"PAGE_DESCRIPTION": "Suchen Sie User-Stories, Tickets, Aufgaben oder Wiki Seiten im Projekt {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "User-Stories",
"FILTER_ISSUES": "Tickets",
"FILTER_TASKS": "Aufgaben",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Wiki Seite löschen",
"DELETE_LINK_TITLE": "Entferne Wiki Link",
"NAVIGATION": {
"SECTION_NAME": "Links",
"ACTION_ADD_LINK": "Link hinzufügen"
"HOME": "Hauptseite",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Bookmark hinzufügen",
"ALL_PAGES": "Alle Wiki-Seiten"
},
"SUMMARY": {
"TIMES_EDITED": "Zeiten <br />bearbeitet",
"TIMES_EDITED": "mal <br />bearbeitet",
"LAST_EDIT": "letzte <br />Bearbeitung",
"LAST_MODIFICATION": "letzte Änderung"
},
"SECTION_PAGES_LIST": "Alle Seiten",
"PAGES_LIST_COLUMNS": {
"TITLE": "Titel",
"EDITIONS": "Editions",
"CREATED": "Erstellt",
"MODIFIED": "Geändert",
"CREATOR": "Ersteller",
"LAST_MODIFIER": "Letzter Bearbeiter"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} erstellte die neue Aufgabe {{obj_name}} in {{project_name}}, die zur User-Story {{us_name}} gehört",
"WIKI_CREATED": "{{username}} erstellte die neue Wiki Seite {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} erstellte den neuen Sprint {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} erstellte das Projekt {{project_name}}",
"MILESTONE_UPDATED": "{{username}} aktualisierte den Sprint {{obj_name}}",
"US_UPDATED": "{{username}} aktualisierte das Attribut \"{{field_name}}\" der User-Story {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} aktualisierte das Attribut \"{{field_name}}\" der Aufgabe {{obj_name}} von User-Story {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} aktualisierte das Attribut \"{{field_name}}\" der Aufgabe {{obj_name}} die zu der User-Story gehört {{us_name}} zu {{new_value}}",
"WIKI_UPDATED": "{{username}} aktualisierte die WIKI Seite {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} schrieb einen Kommentar in der User-Story {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} schrieb einen Kommentar im Ticket {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} schrieb einen Kommentar in der Aufgabe {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} hat ein neues Mitglied",
"US_ADDED_MILESTONE": "{{username}} fügte dem Sprint {{sprint_name}} die User-Story {{obj_name}} hinzu",
"US_MOVED": "{{username}} wurde in die Story {{obj_name}} verschoben",

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "One item per line...",
"NEW_BULK": "New bulk insert",
"RELATED_TASKS": "Related tasks",
"PREVIOUS": "Previous",
"NEXT": "Next",
"LOGOUT": "Logout",
"EXTERNAL_USER": "an external user",
"GENERIC_ERROR": "One of our Oompa Loompas says {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "This value seems to be invalid.",
"TYPE_EMAIL": "This value should be a valid email.",
@ -115,8 +122,9 @@
"USER_STORY": "User story",
"TASK": "Task",
"ISSUE": "Issue",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "I'm it! Tag me...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Delete tag",
"ADD": "Add tag"
},
@ -196,9 +204,27 @@
"TITLE": "filters",
"INPUT_PLACEHOLDER": "Subject or reference",
"TITLE_ACTION_FILTER_BUTTON": "search",
"BREADCRUMB_TITLE": "back to categories",
"BREADCRUMB_FILTERS": "Filters",
"BREADCRUMB_STATUS": "status"
"TITLE": "Filters",
"INPUT_SEARCH_PLACEHOLDER": "Subject or ref",
"TITLE_ACTION_SEARCH": "Search",
"ACTION_SAVE_CUSTOM_FILTER": "save as custom filter",
"PLACEHOLDER_FILTER_NAME": "Write the filter name and press enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Status",
"SEVERITY": "Severity",
"PRIORITIES": "Priorities",
"TAGS": "Tags",
"ASSIGNED_TO": "Assigned to",
"CREATED_BY": "Created by",
"CUSTOM_FILTERS": "Custom filters",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Delete custom filter",
"MESSAGE": "the custom filter '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "First Level Heading",
@ -228,9 +254,18 @@
"PREVIEW_BUTTON": "Preview",
"EDIT_BUTTON": "Edit",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Markdown syntax help"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "View sprints",
@ -243,6 +278,7 @@
"VIEW_USER_STORIES": "View user stories",
"ADD_USER_STORIES": "Add user stories",
"MODIFY_USER_STORIES": "Modify user stories",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Delete user stories"
},
"TASKS": {
@ -250,6 +286,7 @@
"VIEW_TASKS": "View tasks",
"ADD_TASKS": "Add tasks",
"MODIFY_TASKS": "Modify tasks",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Delete tasks"
},
"ISSUES": {
@ -257,6 +294,7 @@
"VIEW_ISSUES": "View issues",
"ADD_ISSUES": "Add issues",
"MODIFY_ISSUES": "Modify issues",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Delete issues"
},
"WIKI": {
@ -366,6 +404,41 @@
"WATCHING_SECTION": "Watching",
"DASHBOARD": "Projects Dashboard"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Unassigned"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Votes",
"NAME": "Name",
"PROJECT": "Project",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Status",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Blocked",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "My projects - Taiga",
"PAGE_DESCRIPTION": "A list with all your projects, you can reorder or create a new one.",
@ -402,7 +475,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Edit value",
"TITLE_ACTION_DELETE_VALUE": "Delete value"
"TITLE_ACTION_DELETE_VALUE": "Delete value",
"TITLE_ACTION_DELETE_TAG": "Delete tag"
},
"HELP": "Do you need help? Check out our support page!",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +509,8 @@
"TITLE": "Modules",
"ENABLE": "Enable",
"DISABLE": "Disable",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Manage your user stories to maintain an organized view of upcoming and prioritized work.",
"NUMBER_SPRINTS": "Expected number of sprints",
@ -497,6 +573,7 @@
"REGENERATE_SUBTITLE": "You going to change the CSV data access url. The previous url will be disabled. Are you sure?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "user stories reports",
"SECTION_TITLE_TASK": "tasks reports",
"SECTION_TITLE_ISSUE": "issues reports",
@ -509,6 +586,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Custom Fields",
"SUBTITLE": "Specify the custom fields for your user stories, tasks and issues",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "User stories custom fields",
"US_ADD": "Add a custom field in user stories",
"TASK_DESCRIPTION": "Tasks custom fields",
@ -546,7 +625,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Status",
"SUBTITLE": "Specify the statuses your user stories, tasks and issues will go through",
"US_TITLE": "US Statuses",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Task Statuses",
"ISSUE_TITLE": "Issue Statuses"
},
@ -556,6 +636,17 @@
"ISSUE_TITLE": "Issues types",
"ACTION_ADD": "Add new {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Tags",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Add tag",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Roles - {{projectName}}",
"WARNING_NO_ROLE": "Be careful, no role in your project will be able to estimate the point value for user stories",
@ -588,6 +679,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +738,14 @@
"DEFAULT_DELETE_MESSAGE": "the invitation to {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Default value for points selector",
"LABEL_US": "Default value for US status selector",
"LABEL_TASK_STATUS": "Default value for task status selector",
"LABEL_PRIORITY": "Default value for priority selector",
"LABEL_SEVERITY": "Default value for severity selector",
"LABEL_ISSUE_TYPE": "Default value for issue type selector",
"LABEL_ISSUE_STATUS": "Default value for issue status selector"
"LABEL_ISSUE_STATUS": "Default value for issue status selector",
"LABEL_PRIORITY": "Default value for priority selector",
"LABEL_SEVERITY": "Default value for severity selector"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Write a name for the new status"
@ -681,7 +777,8 @@
"PRIORITIES": "Priorities",
"SEVERITIES": "Severities",
"TYPES": "Types",
"CUSTOM_FIELDS": "Custom fields"
"CUSTOM_FIELDS": "Custom fields",
"TAGS": "Tags"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Project Profile"
@ -751,6 +848,8 @@
"FILTER_TYPE_ALL_TITLE": "Show all",
"FILTER_TYPE_PROJECTS": "Projects",
"FILTER_TYPE_PROJECT_TITLES": "Show only projects",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Stories",
"FILTER_TYPE_USER_STORIES_TITLES": "Show only user stories",
"FILTER_TYPE_TASKS": "Tasks",
@ -950,8 +1049,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Optional) Add a personalized text to the invitation. Tell something lovely to your new members ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Type an Email",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1069,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "New user story",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Subject",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - User Story {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Completed {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} of {{userStoryTotalTasks}} tasks closed). Points: {{userStoryPoints}}. Description: {{userStoryDescription}}",
"SECTION_NAME": "User story details",
"SECTION_NAME": "User story",
"LINK_TASKBOARD": "Taskboard",
"TITLE_LINK_TASKBOARD": "Go to the taskboard",
"TOTAL_POINTS": "total points",
@ -984,8 +1103,6 @@
"EXTERNAL_REFERENCE": "This US has been created from",
"GO_TO_EXTERNAL_REFERENCE": "Go to origin",
"BLOCKED": "This user story is blocked",
"PREVIOUS": "previous user story",
"NEXT": "next user story",
"TITLE_DELETE_ACTION": "Delete User Story",
"LIGHTBOX_TITLE_BLOKING_US": "Blocking us",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tasks completed",
@ -1010,28 +1127,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Comment deleted by {{user}} on {{date}}",
"DELETED_INFO": "Comment deleted by {{user}}",
"TITLE": "Comments",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Recent first",
"COMMENT": "Comment",
"EDIT_COMMENT": "Edit comment",
"EDITED_COMMENT": "Edited:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Type a new comment here",
"SHOW_DELETED": "Show deleted comment",
"HIDE_DELETED": "Hide deleted comment",
"DELETE": "Delete comment",
"RESTORE": "Restore comment"
"RESTORE": "Restore comment",
"HISTORY": {
"TITLE": "Activity"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Show activity",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Show previous entries ({{showMore}} more)",
"TITLE": "Activity",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "removed",
"ADDED": "added",
"US_POINTS": "US points ({{name}})",
"NEW_ATTACHMENT": "new attachment",
"DELETED_ATTACHMENT": "deleted attachment",
"UPDATED_ATTACHMENT": "updated attachment {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "deleted custom attribute",
"TAGS_ADDED": "tags added:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "new attachment:",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}): ",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Made {size, plural, one{one change} other{# changes}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Team Requirement",
"CLIENT_REQUIREMENT": "Client Requirement",
"BLOCKED": "Blocked",
"VALUES": {
"YES": "yes",
"NO": "no",
@ -1063,12 +1199,14 @@
"TAGS": "tags",
"ATTACHMENTS": "attachments",
"IS_DEPRECATED": "is deprecated",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "order",
"BACKLOG_ORDER": "backlog order",
"SPRINT_ORDER": "sprint order",
"KANBAN_ORDER": "kanban order",
"TASKBOARD_ORDER": "taskboard order",
"US_ORDER": "us order"
"US_ORDER": "us order",
"COLOR": "color"
}
},
"BACKLOG": {
@ -1120,7 +1258,8 @@
"CLOSED_TASKS": "closed<br />tasks",
"IOCAINE_DOSES": "iocaine<br />doses",
"SHOW_STATISTICS_TITLE": "Show statistics",
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph"
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "project<br />points",
@ -1133,9 +1272,7 @@
"TITLE": "Filters",
"REMOVE": "Remove Filters",
"HIDE": "Hide Filters",
"SHOW": "Show Filters",
"FILTER_CATEGORY_STATUS": "Status",
"FILTER_CATEGORY_TAGS": "Tags"
"SHOW": "Show Filters"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1190,7 +1327,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Task {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{taskStatus }}. Description: {{taskDescription}}",
"SECTION_NAME": "Task details",
"SECTION_NAME": "Task",
"LINK_TASKBOARD": "Taskboard",
"TITLE_LINK_TASKBOARD": "Go to the taskboard",
"PLACEHOLDER_SUBJECT": "Type the new task subject",
@ -1200,8 +1337,6 @@
"ORIGIN_US": "This task has been created from",
"TITLE_LINK_GO_ORIGIN": "Go to user story",
"BLOCKED": "This task is blocked",
"PREVIOUS": "previous task",
"NEXT": "next task",
"TITLE_DELETE_ACTION": "Delete Task",
"LIGHTBOX_TITLE_BLOKING_TASK": "Blocking task",
"FIELDS": {
@ -1239,16 +1374,13 @@
"PAGE_TITLE": "Issues - {{projectName}}",
"PAGE_DESCRIPTION": "The issues list panel of the project {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Issues",
"SECTION_NAME": "Issue details",
"SECTION_NAME": "Issue",
"ACTION_NEW_ISSUE": "+ NEW ISSUE",
"ACTION_PROMOTE_TO_US": "Promote to User Story",
"PLACEHOLDER_FILTER_NAME": "Write the filter name and press enter",
"PROMOTED": "This issue has been promoted to US:",
"EXTERNAL_REFERENCE": "This issue has been created from",
"GO_TO_EXTERNAL_REFERENCE": "Go to origin",
"BLOCKED": "This issue is blocked",
"TITLE_PREVIOUS_ISSUE": "previous issue",
"TITLE_NEXT_ISSUE": "next issue",
"ACTION_DELETE": "Delete issue",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Blocking issue",
"FIELDS": {
@ -1260,28 +1392,6 @@
"TITLE": "Promote this issue to a new user story",
"MESSAGE": "Are you sure you want to create a new US from this Issue?"
},
"FILTERS": {
"TITLE": "Filters",
"INPUT_SEARCH_PLACEHOLDER": "Subject or ref",
"TITLE_ACTION_SEARCH": "Search",
"ACTION_SAVE_CUSTOM_FILTER": "save as custom filter",
"BREADCRUMB": "Filters",
"TITLE_BREADCRUMB": "Filters",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Status",
"SEVERITY": "Severity",
"PRIORITIES": "Priorities",
"TAGS": "Tags",
"ASSIGNED_TO": "Assigned to",
"CREATED_BY": "Created by",
"CUSTOM_FILTERS": "Custom filters"
},
"CONFIRM_DELETE": {
"TITLE": "Delete custom filter",
"MESSAGE": "the custom filter '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Type",
@ -1327,6 +1437,7 @@
"SEARCH": {
"PAGE_TITLE": "Search - {{projectName}}",
"PAGE_DESCRIPTION": "Search anything, user stories, issues, tasks or wiki pages, in the project {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "User Stories",
"FILTER_ISSUES": "Issues",
"FILTER_TASKS": "Tasks",
@ -1428,13 +1539,24 @@
"DELETE_LIGHTBOX_TITLE": "Delete Wiki Page",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Links",
"ACTION_ADD_LINK": "Add link"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "times <br />edited",
"LAST_EDIT": "last <br />edit",
"LAST_MODIFICATION": "last modification"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Created",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1458,6 +1580,8 @@
"TASK_CREATED_WITH_US": "{{username}} has created a new task {{obj_name}} in {{project_name}} which belongs to the US {{us_name}}",
"WIKI_CREATED": "{{username}} has created a new wiki page {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} has created a new sprint {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} created the project {{project_name}}",
"MILESTONE_UPDATED": "{{username}} has updated the sprint {{obj_name}}",
"US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}",
@ -1470,9 +1594,13 @@
"TASK_UPDATED_WITH_US": "{{username}} has updated the attribute \"{{field_name}}\" of the task {{obj_name}} which belongs to the US {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the task {{obj_name}} which belongs to the US {{us_name}} to {{new_value}}",
"WIKI_UPDATED": "{{username}} has updated the wiki page {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} has commented in the US {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} has commented in the issue {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} has commented in the task {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} has a new member",
"US_ADDED_MILESTONE": "{{username}} has added the US {{obj_name}} to {{sprint_name}}",
"US_MOVED": "{{username}} has moved the US {{obj_name}}",

View File

@ -35,16 +35,23 @@
"ONE_ITEM_LINE": "Un elemento por línea...",
"NEW_BULK": "Nueva inserción en bloque",
"RELATED_TASKS": "Tareas relacionadas",
"PREVIOUS": "Previous",
"NEXT": "Siguiente",
"LOGOUT": "Cerrar sesión",
"EXTERNAL_USER": "un usuario externo",
"GENERIC_ERROR": "Uno de nuestros Oompa Loompas dice {{error}}.",
"IOCAINE_TEXT": "¿Te sientes fuera de tu zona de confort en una tarea? Asegúrate de que los demás están al tanto de ello, marca el check de la Iocaína al editar una tarea. Igual eu era posible llegar a ser inmune a este veneno mortal a base de consumir pequeñas dosis a lo largo del tiempo, es posible conseguir mejor en lo que estás haciendo si afrontas de vez en cuando esta clase de retos!",
"CLIENT_REQUIREMENT": "Requerimiento de cliente es un nuevo requisito que no se esperaba y es necesario que forme parte del proyecto.",
"TEAM_REQUIREMENT": "Requerimiento del equipo es un nuevo requisito que debe existir en el proyecto pero que no conllevará ningún coste para el cliente.",
"OWNER": "Project Owner",
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"OWNER": "Dueño del proyecto",
"CAPSLOCK_WARNING": "¡Cuidado!. Esta usando mayusculas en un campo sensible a mayusculas",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "¿Seguro que desea cerrar el modo de edición?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Recuerde que si cierra el modo de edicion sin guardar todos los cambios se perderán",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Este valor parece inválido.",
"TYPE_EMAIL": "El valor debe ser un email.",
@ -69,8 +76,8 @@
"MAX_CHECK": "Debes seleccionar %s o menos.",
"RANGE_CHECK": "Debes seleccionar de %s a %s.",
"EQUAL_TO": "Este valor debe ser el mismo.",
"LINEWIDTH": "One or more lines is perhaps too long. Try to keep under %s characters.",
"PIKADAY": "Invalid date format, please use DD MMM YYYY (like 23 Mar 1984)"
"LINEWIDTH": "Una o más líneas es tal vez demasiado tiempo. Trate de mantener bajo %s caracteres.",
"PIKADAY": "Formato de fecha no válida, por favor utilice DD MMM AAAA (como 23 Mar 1984)"
},
"PICKERDATE": {
"FORMAT": "DD MMM YYYY",
@ -115,8 +122,9 @@
"USER_STORY": "Historia de usuario",
"TASK": "Tarea",
"ISSUE": "Petición",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "¿Qué soy? Etiquétame...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Borrar etiqueta",
"ADD": "Añadir etiqueta"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Se borrarán todos los valores de este atributo personalizado. \n¿Estás seguro de que quieres continuar?"
},
"FILTERS": {
"TITLE": "filtros",
"TITLE": "Filtros",
"INPUT_PLACEHOLDER": "Asunto o referencia",
"TITLE_ACTION_FILTER_BUTTON": "busqueda",
"BREADCRUMB_TITLE": "Regresar a categorias",
"BREADCRUMB_FILTERS": "Filtros",
"BREADCRUMB_STATUS": "estado"
"INPUT_SEARCH_PLACEHOLDER": "Asunto o referencia",
"TITLE_ACTION_SEARCH": "Buscar",
"ACTION_SAVE_CUSTOM_FILTER": "guardar como filtro personalizado",
"PLACEHOLDER_FILTER_NAME": "Escribe un nombre para el filtro y pulsa enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Estado",
"SEVERITY": "Gravedad",
"PRIORITIES": "Prioridades",
"TAGS": "Etiquetas",
"ASSIGNED_TO": "Asignado a",
"CREATED_BY": "Creada por",
"CUSTOM_FILTERS": "Filtros personalizados",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Eliminar filtros personalizados",
"MESSAGE": "el filtro personalizado '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Título de primer nivel",
@ -227,10 +252,19 @@
"CODE_BLOCK_SAMPLE_TEXT": "Tu texto aquí...",
"PREVIEW_BUTTON": "Previsualizar",
"EDIT_BUTTON": "Editar",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP": "Adjunte archivos arrastrando y soltando dentro del area de texto",
"ATTACH_FILE_HELP_SAVE_FIRST": "Si desea guardar adjuntos guarde primero, luego arrastre y suelte los archivos en el area de texto mas arriba",
"MARKDOWN_HELP": "Ayuda de sintaxis Markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Épicas",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Ver sprints",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Ver historias de usuario",
"ADD_USER_STORIES": "Crear historias de usuario",
"MODIFY_USER_STORIES": "Editar historias de usuario",
"COMMENT_USER_STORIES": "Comentar historias de usuario",
"DELETE_USER_STORIES": "Borrar historias de usuario"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Ver tareas",
"ADD_TASKS": "Crear tareas",
"MODIFY_TASKS": "Editar tareas",
"COMMENT_TASKS": "Comentar tareas",
"DELETE_TASKS": "Borrar tareas"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Ver peticiones",
"ADD_ISSUES": "Crear peticiones",
"MODIFY_ISSUES": "Editar peticiones",
"COMMENT_ISSUES": "Comentar problemas",
"DELETE_ISSUES": "Borrar peticiones"
},
"WIKI": {
@ -322,8 +359,8 @@
"PLACEHOLDER_FIELD": "Nombre de usuario o email",
"ACTION_RESET_PASSWORD": "Restablecer Contraseña",
"LINK_CANCEL": "Nah, llévame de vuelta, creo que lo recordé.",
"SUCCESS_TITLE": "Check your inbox!",
"SUCCESS_TEXT": "We sent you an email with the instructions to set a new password",
"SUCCESS_TITLE": "¡Revisa tu bandeja de entrada!",
"SUCCESS_TEXT": "Te hemos enviado un correo con las instrucciones para restablecer tu contraseña",
"ERROR": "Según nuestros Oompa Loompas tú no estás registrado"
},
"CHANGE_PASSWORD": {
@ -359,13 +396,48 @@
"HOME": {
"PAGE_TITLE": "Inicio - Taiga",
"PAGE_DESCRIPTION": "Página de inicio de Taiga, con tus proyectos principales y tus historias de usuario, tareas y peticiones en progreso asignadas y las que observas.",
"EMPTY_WORKING_ON": "<strong>It feels empty, doesn't it?</strong> Start working with Taiga and you'll see here the stories, tasks and issues you are working on.",
"EMPTY_WORKING_ON": "<strong>Parece vacío, ¿no?</strong> Empieza a trabajar con Taiga y verás aquí las historias, tareas e incidentes en los que estás trabajando.",
"EMPTY_WATCHING": "<strong>Sigue</strong> Historias de Usuario, Tareas y Peticiones en tus proyectos y se te notificará sobre sus cambios :)",
"EMPTY_PROJECT_LIST": "Todavía no tienes ningún proyecto",
"WORKING_ON_SECTION": "Trabajando en",
"WATCHING_SECTION": "Observando",
"DASHBOARD": "Dashboard de proyecto"
},
"EPICS": {
"TITLE": "ÉPICAS",
"SECTION_NAME": "Épicas",
"EPIC": "Épica",
"PAGE_TITLE": "Épicas - {{projectName}}",
"PAGE_DESCRIPTION": "El listado de épicas del proyecto {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ AÑADIR ÉPICA",
"UNASSIGNED": "No asignado"
},
"EMPTY": {
"TITLE": "Parece que todavía no hay épicas.",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Aprende más sobre Épicas"
},
"TABLE": {
"VOTES": "Votos",
"NAME": "Nombre",
"PROJECT": "Proyecto",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Estado",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "Nueva Épica",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Bloqueada",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "Mis proyectos - Taiga",
"PAGE_DESCRIPTION": "Una lista con todos tus proyectos, puedes reordenarla o crear un proyecto nuevo.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Editar valor",
"TITLE_ACTION_DELETE_VALUE": "Eliminar valor"
"TITLE_ACTION_DELETE_VALUE": "Eliminar valor",
"TITLE_ACTION_DELETE_TAG": "Borrar etiqueta"
},
"HELP": "¿Necesitas ayuda? ¡Revisa nuestra pagina de soporte! ",
"PROJECT_DEFAULT_VALUES": {
@ -414,8 +487,8 @@
"PAGE_TITLE": "Miembros - {{projectName}}",
"ADD_BUTTON": "+ Nuevo miembro",
"ADD_BUTTON_TITLE": "Añadir un nuevo miembro",
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Unfortunately, this project has reached its limit of <strong>({{members}})</strong> allowed members.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "This project has reached its limit of <strong>({{members}})</strong> allowed members. If you would like to increase that limit please contact the administrator."
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Desafortunadamente, este proyecto ha alcanzado su límite de <strong>({{members}})</strong> miembros permitidos.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Este proyecto ha llegado a su límite de <strong>({{members}})</strong> miembros permitidos. Si se desea aumentar ese límite póngase en contacto con el administrador."
},
"PROJECT_EXPORT": {
"TITLE": "Exportar",
@ -435,12 +508,14 @@
"TITLE": "Módulos",
"ENABLE": "Activado",
"DISABLE": "Desactivado",
"EPICS": "Épicas",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Gestiona tus historias de usuario para mantener una vista organizada y priorizada de los próximos trabajos que deberás afrontar. ",
"NUMBER_SPRINTS": "Expected number of sprints",
"NUMBER_SPRINTS_HELP": "0 for an undetermined number",
"NUMBER_US_POINTS": "Expected total of story points",
"NUMBER_US_POINTS_HELP": "0 for an undetermined number",
"NUMBER_SPRINTS": "Numero esperado de sprints",
"NUMBER_SPRINTS_HELP": "0 para un numero indeterminado",
"NUMBER_US_POINTS": "Total esperado de puntos historicos",
"NUMBER_US_POINTS_HELP": "0 para un numero indeterminado",
"KANBAN": "Kanban",
"KANBAN_DESCRIPTION": "Organiza tus proyectos de una manera flexible con este panel.",
"ISSUES": "Peticiones",
@ -448,9 +523,9 @@
"WIKI": "Wiki",
"WIKI_DESCRIPTION": "Añade, modifica o borra contenido en colaboración con otros miembros. Este es el lugar adecuado para la documentación de tu proyecto.",
"MEETUP": "Meet Up",
"MEETUP_DESCRIPTION": "Choose your videoconference system.",
"MEETUP_DESCRIPTION": "Elegir su sistema de videoconferencia.",
"SELECT_VIDEOCONFERENCE": "Elige un sistema de videoconferencia",
"SALT_CHAT_ROOM": "Add a prefix to the chatroom name",
"SALT_CHAT_ROOM": "Agregar prefijo al nombre de la sala de chat",
"JITSI_CHAT_ROOM": "Jitsi",
"APPEARIN_CHAT_ROOM": "AppearIn",
"TALKY_CHAT_ROOM": "Talky",
@ -474,19 +549,19 @@
"LOGO_HELP": "La imagen se escalará a 80x80px.",
"CHANGE_LOGO": "Cambia el logo",
"ACTION_USE_DEFAULT_LOGO": "Usar imagen por defecto",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects allowed by your current plan",
"MAX_PRIVATE_PROJECTS_MEMBERS": "The maximum number of members for private projects has been exceeded",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects allowed by your current plan",
"MAX_PUBLIC_PROJECTS_MEMBERS": "The project exceeds your maximum number of members for public projects",
"PROJECT_OWNER": "Project owner",
"REQUEST_OWNERSHIP": "Request ownership",
"REQUEST_OWNERSHIP_CONFIRMATION_TITLE": "Do you want to become the new project owner?",
"REQUEST_OWNERSHIP_DESC": "Request that current project owner {{name}} transfer ownership of this project to you.",
"MAX_PRIVATE_PROJECTS": "Has alcanzado el número máximo de proyectos privados permitidos por su actual plan",
"MAX_PRIVATE_PROJECTS_MEMBERS": "El numero máximo de miembros para proyectos privados se ha excedido",
"MAX_PUBLIC_PROJECTS": "Desafortunadamente, usted ha alcanzado el número máximo de proyectos públicos permitidos por su plan actual",
"MAX_PUBLIC_PROJECTS_MEMBERS": "El proyecto excede el numero maximo de usuarios para proyectos publicos",
"PROJECT_OWNER": "Dueño del proyecto",
"REQUEST_OWNERSHIP": "Solicitar de dueño",
"REQUEST_OWNERSHIP_CONFIRMATION_TITLE": "¿Desea convertirse en el nuevo dueño del proyecto?",
"REQUEST_OWNERSHIP_DESC": "Solicitar que el actual project owner {{name}} te transfiera la propiedad de este proyecto a ti.",
"REQUEST_OWNERSHIP_BUTTON": "Solicitud",
"REQUEST_OWNERSHIP_SUCCESS": "We'll notify the project owner",
"CHANGE_OWNER": "Change owner",
"CHANGE_OWNER_SUCCESS_TITLE": "Ok, your request has been sent!",
"CHANGE_OWNER_SUCCESS_DESC": "We will notify you by email if the project ownership request is accepted or declined"
"REQUEST_OWNERSHIP_SUCCESS": "Notificaremos al dueño del proyecto",
"CHANGE_OWNER": "Cambiar dueño",
"CHANGE_OWNER_SUCCESS_TITLE": "Ok, su solicitud ha sido enviado!",
"CHANGE_OWNER_SUCCESS_DESC": "Le notificaremos por correo si la solicitud para ser dueño del proyecto fue aceptada o rechazada"
},
"REPORTS": {
"TITLE": "Informes",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Vas a cambiar la url de acceso a los datos en formato CSV. La url anterior se deshabilitará. ¿Estás seguro?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "informes de historias de usuario",
"SECTION_TITLE_TASK": "Informes de tareas",
"SECTION_TITLE_ISSUE": "informes de peticiones",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Atributos personalizados",
"SUBTITLE": "Especifica los atributos personalizados para las historias de usuario, tareas y peticiones",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Atributos personalizados de historias de usuario",
"US_ADD": "Añadir un atributo personalizado en las historias de usuario",
"TASK_DESCRIPTION": "Atributos personalizados de tareas",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Estado",
"SUBTITLE": "Especifica los estado que atravesarán tus historias de usuario, tareas y peticiones",
"US_TITLE": "Estados de historias",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Estados de Tarea",
"ISSUE_TITLE": "Estados de la petición"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Tipos de la petición",
"ACTION_ADD": "Añadir nuevo {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Etiquetas",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Actualmente no hay etiquetas",
"EMPTY_SEARCH": "Parece que no se encontro nada con este criterio de busqueda",
"ACTION_ADD": "Añadir etiqueta",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Roles - {{projectName}}",
"WARNING_NO_ROLE": "Ojo, ningún rol en tu proyecto podrá estimar historias de usuario",
@ -565,7 +655,7 @@
"COUNT_MEMBERS": "{{ role.members_count }} miembros con este rol",
"TITLE_DELETE_ROLE": "Borrar Rol",
"REPLACEMENT_ROLE": "Todos los usuarios con este rol serán movidos a",
"WARNING_DELETE_ROLE": "Be careful! All role estimations will be removed",
"WARNING_DELETE_ROLE": "¡Ten cuidado! Todas las estimaciones de roles serán eliminados",
"ERROR_DELETE_ALL": "No puedes eliminar todos los valores",
"EXTERNAL_USER": "Usuario externo"
},
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "la invitación enviada a"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Valor por defecto para el selector de puntos",
"LABEL_US": "Valor por defecto para el selector de estado de historia",
"LABEL_TASK_STATUS": "Valor por defecto para el selector de estado de tarea",
"LABEL_PRIORITY": "Valor por defecto para el selector de prioridad",
"LABEL_SEVERITY": "Valor por defecto para el selector de gravedad",
"LABEL_ISSUE_TYPE": "Valor por defecto para el selector de tipo de la petición",
"LABEL_ISSUE_STATUS": "Valor por defecto para el selector de estado de petición"
"LABEL_ISSUE_STATUS": "Valor por defecto para el selector de estado de petición",
"LABEL_PRIORITY": "Valor por defecto para el selector de prioridad",
"LABEL_SEVERITY": "Valor por defecto para el selector de gravedad"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Escribe un nombre para el nuevo estado"
@ -681,7 +776,8 @@
"PRIORITIES": "Prioridades",
"SEVERITIES": "Gravedades",
"TYPES": "Tipos",
"CUSTOM_FIELDS": "Atributos personalizados"
"CUSTOM_FIELDS": "Atributos personalizados",
"TAGS": "Etiquetas"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Perfil de proyecto"
@ -695,21 +791,21 @@
"TITLE": "Servicios"
},
"PROJECT_TRANSFER": {
"DO_YOU_ACCEPT_PROJECT_OWNERNSHIP": "Would you like to become the new project owner?",
"PRIVATE": "Private",
"ACCEPTED_PROJECT_OWNERNSHIP": "Congratulations! You're now the new project owner.",
"REJECTED_PROJECT_OWNERNSHIP": "OK. We'll contact the current project owner",
"DO_YOU_ACCEPT_PROJECT_OWNERNSHIP": "¿Te gustaría ser el nuevo project owner?",
"PRIVATE": "Privado",
"ACCEPTED_PROJECT_OWNERNSHIP": "¡Felicitaciones! Usted es ahora el nuevo propietario del proyecto.",
"REJECTED_PROJECT_OWNERNSHIP": "Ok. Nos pondremos en contacto con el propietario actual del proyecto",
"ACCEPT": "Aceptar",
"REJECT": "Reject",
"PROPOSE_OWNERSHIP": "<strong>{{owner}}</strong>, the current owner of the project <strong>{{project}}</strong> has asked that you become the new project owner.",
"ADD_COMMENT": "Would you like to add a comment for the project owner?",
"UNLIMITED_PROJECTS": "Unlimited",
"REJECT": "Rechazar",
"PROPOSE_OWNERSHIP": "<strong>{{owner}}</strong>, el dueño del proyecto <strong>{{project}}</strong> pregunta si es el nuevo dueño del proyecto.",
"ADD_COMMENT": "¿Te gustaría añadir un comentario para el project owner?",
"UNLIMITED_PROJECTS": "Sin límite",
"OWNER_MESSAGE": {
"PRIVATE": "Please remember that you can own up to <strong>{{maxProjects}}</strong> private projects. You currently own <strong>{{currentProjects}}</strong> private projects",
"PUBLIC": "Please remember that you can own up to <strong>{{maxProjects}}</strong> public projects. You currently own <strong>{{currentProjects}}</strong> public projects"
"PRIVATE": "Por favor recuerde que puede ser dueño de maximo <strong>{{maxProjects}}</strong> proyectos privados. Usted tiene actualmente <strong>{{currentProjects}}</strong> proyectos privados bajo su poder",
"PUBLIC": "Pro favor recuerde que solo puede ser dueño de maximo <strong>{{maxProjects}}</strong> proyectos publicos. Actualmente tiene <strong>{{currentProjects}}</strong> proyectos publicos bajo su poder"
},
"CANT_BE_OWNED": "At the moment you cannot become an owner of a project of this type. If you would like to become the owner of this project, please contact the administrator so they change your account settings to enable project ownership.",
"CHANGE_MY_PLAN": "Change my plan"
"CANT_BE_OWNED": "en el momento no puede ser el dueño de un proyecto de este tipo. Si desea ser el dueño de este proyecto, por favor contacte al administrador para cambiar la configuracion que le permita ser dueño del proyecto",
"CHANGE_MY_PLAN": "Cambiar mi plan"
}
},
"USER": {
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Mostrar todos",
"FILTER_TYPE_PROJECTS": "Proyectos",
"FILTER_TYPE_PROJECT_TITLES": "Mostrar sólo proyectos",
"FILTER_TYPE_EPICS": "Épicas",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Historias",
"FILTER_TYPE_USER_STORIES_TITLES": "Mostrar sólo historias de usuario",
"FILTER_TYPE_TASKS": "Tareas",
@ -771,9 +869,9 @@
"WATCHERS_COUNTER_TITLE": "{total, plural, one{un observador} other{# observadores}}",
"MEMBERS_COUNTER_TITLE": "{total, plural, one{un miembro} other{# miembros}}",
"BLOCKED_PROJECT": {
"BLOCKED": "Blocked project",
"THIS_PROJECT_IS_BLOCKED": "This project is temporarily blocked",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "In order to unblock your projects, contact the administrator."
"BLOCKED": "Proyecto bloqueado",
"THIS_PROJECT_IS_BLOCKED": "Este proyecto esta temporalmente bloqueado",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "Para desbloquear sus proyectos, contacte al administrador."
},
"STATS": {
"PROJECT": "puntos<br/> proyecto",
@ -838,28 +936,28 @@
"ERROR_MAX_SIZE_EXCEEDED": "El fichero '{{fileName}}' ({{fileSize}}) es demasiado pesado para nuestros Oompa Loompas, prueba con uno de menos de ({{maxFileSize}}).",
"SYNC_SUCCESS": "Tu proyecto se ha importado con éxito.",
"PROJECT_RESTRICTIONS": {
"PROJECT_MEMBERS_DESC": "The project you are trying to import has {{members}} members, unfortunately, your current plan allows for a maximum of {{max_memberships}} members per project. If you would like to increase that limit please contact the administrator.",
"PROJECT_MEMBERS_DESC": "El proyecto que esta tratando de importar tiene {{members}} miembros, desafortunadamente, su plna actual solo le permite un maximo de {{max_memberships}} miembros por proyecto. si desea aumentar este limite por favor contacte al administrador.",
"PRIVATE_PROJECTS_SPACE": {
"TITLE": "Unfortunately, your current plan does not allow for additional private projects",
"DESC": "The project you are trying to import is private. Unfortunately, your current plan does not allow for additional private projects."
"TITLE": "Desafortunadamente, su plan actual no permite a los proyectos privados adicionales",
"DESC": "El proyecto que trata de importar es privado. Desafortunadamente, su plan actual no le permite adicionar mas proyectos privados"
},
"PUBLIC_PROJECTS_SPACE": {
"TITLE": "Unfortunately, your current plan does not allow for additional public projects",
"DESC": "The project you are trying to import is public. Unfortunately, your current plan does not allow additional public projects."
"TITLE": "Desafortunadamente, su plan actual no permite adicionar mas proyectos publicos",
"DESC": "El proyecto que estás intento importar es público. Desafortunadamente, tu plan actual no permite proyectos públicos adicionales."
},
"PRIVATE_PROJECTS_MEMBERS": {
"TITLE": "Your current plan allows for a maximum of {{max_memberships}} members per private project"
"TITLE": "Su plan actual solo permite un numero maximo de {{max_memberships}} miembros por proyecto privado"
},
"PUBLIC_PROJECTS_MEMBERS": {
"TITLE": "Your current plan allows for a maximum of {{max_memberships}} members per public project."
"TITLE": "Su plan actual solo permite un maximo de {{max_memberships}} miembros por proyecto publico."
},
"PRIVATE_PROJECTS_SPACE_MEMBERS": {
"TITLE": "Unfortunately your current plan doesn't allow additional private projects or an increase of more than {{max_memberships}} members per private project",
"DESC": "The project that you are trying to import is private and has {{members}} members."
"TITLE": "Desafortunadamente tu plan actual no permite proyectos privados adicionales o un incremento de más de {{max_memberships}} miembros por proyecto privado",
"DESC": "El proyecto que estás intentando importar es privado y tiene {{members}} miembros."
},
"PUBLIC_PROJECTS_SPACE_MEMBERS": {
"TITLE": "Unfortunately your current plan doesn't allow additional public projects or an increase of more than {{max_memberships}} members per public project",
"DESC": "The project that you are trying to import is public and has more than {{members}} members."
"TITLE": "Desafortunadamente su plan actual no le permite adicionar proyectos publicos o un aumento de {{max_memberships}} miembros por proyecto publico",
"DESC": "El proyecto que estás intentando importar es público y tiene más de {{members}} miembros."
}
}
},
@ -890,10 +988,10 @@
"SECTION_NAME": "Eliminar cuenta de Taiga",
"CONFIRM": "¿Está seguro que deseas eliminar tu cuenta de Taiga?",
"NEWSLETTER_LABEL_TEXT": "No quiero recibir la newsletter nunca más.",
"CANCEL": "Back to settings",
"ACCEPT": "Delete account",
"BLOCK_PROJECT": "Note that all the projects you own projects will be <strong>blocked</strong> after you delete your account. If you do want a project blocked, transfer ownership to another member of each project prior to deleting your account.",
"SUBTITLE": "Sorry to see you go. We'll be here if you should ever consider us again! :("
"CANCEL": "Volver a los ajustes",
"ACCEPT": "Eliminar cuenta",
"BLOCK_PROJECT": "Recuerde que todos los proyectos de los cuales usted es dueño seran <strong>bloqueados</strong> despues de eliminar su cuenta. si desea mantener un proyecto bloqueado, transfiera el dominio a otro usuario en cada proyecto antes de eliminar la cuenta.",
"SUBTITLE": "Sentimos mucho verte ir. Estaremos aquí por si alguna vez nos consideras de nuevo! :("
},
"DELETE_PROJECT": {
"TITLE": "Borrar proyecto",
@ -950,30 +1048,50 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Opcional) Añade un texto personalizado a la invitación. Dile algo encantador a tus nuevos miembros ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Escribe un email",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
"TITLE": "Por desgracia, este proyecto no puede ser dejado sin dueño",
"CURRENT_USER_OWNER": {
"DESC": "You are the current owner of this project. Before leaving, please transfer ownership to someone else.",
"BUTTON": "Change the project owner"
"DESC": "Usted es el dueño actual de este proyecto. Antes de salir, tranfiera el dominio de su proyecto a alguien mas.",
"BUTTON": "Cambiar el dueño del proyecto"
},
"OTHER_USER_OWNER": {
"DESC": "Unfortunately, you can't delete a member who is also the current project owner. First, please assign a new project owner.",
"BUTTON": "Request project owner change"
"DESC": "Desafortunadamente, usted no puede eliminar un miembro que es a su vez el dueño actual del proyecto. Primero, por favor asigne un nuevo dueño del proyecto.",
"BUTTON": "Solicitud del cambio del dueño del proyecto"
}
},
"CHANGE_OWNER": {
"TITLE": "Who do you want to be the new project owner?",
"ADD_COMMENT": "Add comment",
"BUTTON": "Ask this project member to become the new project owner"
"TITLE": "¿A quién quiere ser el nuevo dueño del proyecto?",
"ADD_COMMENT": "Añadir comentario",
"BUTTON": "Pregunte a este usuario para convertirlo en el nuero dueño del proyecto"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Épica {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nueva historia de usuario",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Asunto",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - Historia de Usuario {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estado: {{userStoryStatus }}. Completado el {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} de {{userStoryTotalTasks}} tareas cerradas). Puntos: {{userStoryPoints}}. Descripción: {{userStoryDescription}}",
"SECTION_NAME": "Detalles de historia de usuario",
"SECTION_NAME": "Historia de usuario",
"LINK_TASKBOARD": "Panel de tareas",
"TITLE_LINK_TASKBOARD": "Ir al panel de tareas",
"TOTAL_POINTS": "puntos totales",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Esta historia ha sido creada desde",
"GO_TO_EXTERNAL_REFERENCE": "Ir al origen",
"BLOCKED": "Esta historia de usuario está bloqueada",
"PREVIOUS": "anterior historia de usuario",
"NEXT": "siguiente historia de usuario",
"TITLE_DELETE_ACTION": "Borrar Historia de Usuario",
"LIGHTBOX_TITLE_BLOKING_US": "Historia bloqueada",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tareas completadas",
"ASSIGN": "Asignar Historia de Usuario",
"NOT_ESTIMATED": "No estimada",
"TOTAL_US_POINTS": "Total puntos de historia",
"TRIBE": {
"PUBLISH": "Publicar como Gig en la Tribu Taiga",
"PUBLISH_INFO": "Mas información",
"PUBLISH_TITLE": "Mas informacion para publicar en la Tribu Taiga",
"PUBLISHED_AS_GIG": "Historia publicada como Gig en la Tribu Taiga",
"EDIT_LINK": "Editar link",
"CLOSE": "Cerrar",
"SYNCHRONIZE_LINK": "sincronizar con la Tribu Taiga",
"PUBLISH_MORE_INFO_TITLE": "Necesita a alguien para esta tarea?",
"PUBLISH_MORE_INFO_TEXT": "<p>SI necesita ayuda con una parte especifica del trabajo puede crear facilmente gigs en<a href='taigatribe.com' title='Taiga Tribe'> la Tribu Taiga </a> y recibir ayuda de todo el mundo. Tendra la habilidad de manejar y controlar su gig disfrutando de una comunidad dispuesta a colaborar.</p><p><a href='taigatribe.com' title='Tribu Taiga'> la Tribu Taiga </a> nacio como una rama de Taiga. Ambas plataformas viven separadas Pero creemos que hay mucho poder al usarlas combinadamente y asi aseguramos que la integracion funciona muy bien.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Requerido por el Equipo",
"CLIENT_REQUIREMENT": "Requerido por el Cliente",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Comentario borrado por {{user}} el {{date}}",
"DELETED_INFO": "Comentario borrado por {{user}}",
"TITLE": "Comentarios",
"COMMENTS_COUNT": "{{comments}} Comentarios",
"ORDER": "Orden",
"OLDER_FIRST": "Mas antiguo primero",
"RECENT_FIRST": "Mas reciente primero",
"COMMENT": "Comentar",
"EDIT_COMMENT": "Editar comentario",
"EDITED_COMMENT": "Editado:",
"SHOW_HISTORY": "Ver histórico",
"TYPE_NEW_COMMENT": "Escribe un nuevo comentario aquí",
"SHOW_DELETED": "Mostrar comentarios eliminados",
"HIDE_DELETED": "Ocultar comentarios eliminados",
"DELETE": "Borrar comentario",
"RESTORE": "Restaurar comentario"
"RESTORE": "Restaurar comentario",
"HISTORY": {
"TITLE": "Actividad"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Mostrar actividad",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Ver entradas anteriores ({{showMore}} más)",
"TITLE": "Actividad",
"ACTIVITIES_COUNT": "{{activities}} Actividades",
"REMOVED": "borrado",
"ADDED": "agregado",
"US_POINTS": "Puntos de historia ({{name}})",
"TAGS_ADDED": "etiquetas añadidas",
"TAGS_REMOVED": "Etiquetas borradas:",
"US_POINTS": "{{role}} puntos",
"NEW_ATTACHMENT": "nuevo adjunto",
"DELETED_ATTACHMENT": "adjunto eliminado",
"UPDATED_ATTACHMENT": "Adjunto {{filename}} actualizado",
"DELETED_CUSTOM_ATTRIBUTE": "eliminar atributos personalizados",
"DELETED_ATTACHMENT": "adjunto borrado",
"UPDATED_ATTACHMENT": "actualizar adjunto ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "atributo personalizado creado",
"UPDATED_CUSTOM_ATTRIBUTE": "atributo personalizado actualizado",
"SIZE_CHANGE": "{size, plural, one{Un cambio realizado} other{# cambios realizados}}",
"BECAME_DEPRECATED": "esta obsoleto",
"BECAME_UNDEPRECATED": "no esta obsoleto",
"TEAM_REQUIREMENT": "Requerido por el Equipo",
"CLIENT_REQUIREMENT": "Requerido por el Cliente",
"BLOCKED": "Bloqueada",
"VALUES": {
"YES": "sí",
"NO": "no",
@ -1052,12 +1198,14 @@
"TAGS": "etiquetas",
"ATTACHMENTS": "adjuntos",
"IS_DEPRECATED": "está desactualizado",
"IS_NOT_DEPRECATED": "No es obsoleto",
"ORDER": "orden",
"BACKLOG_ORDER": "orden en backlog",
"SPRINT_ORDER": "orden en sprint",
"KANBAN_ORDER": "orden en kanban",
"TASKBOARD_ORDER": "orden en panel de tareas",
"US_ORDER": "orden en historia"
"US_ORDER": "orden en historia",
"COLOR": "color"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "tareas<br />cerradas",
"IOCAINE_DOSES": "dosis de<br >iocaína",
"SHOW_STATISTICS_TITLE": "Ver estadísticas",
"TOGGLE_BAKLOG_GRAPH": "Ver/Ocultar gráfica de burndown"
"TOGGLE_BAKLOG_GRAPH": "Ver/Ocultar gráfica de burndown",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "puntos<br /> proyecto",
@ -1122,9 +1271,7 @@
"TITLE": "Filtros",
"REMOVE": "Borrar Filtros",
"HIDE": "Ocultar filtros",
"SHOW": "Ver Filtros",
"FILTER_CATEGORY_STATUS": "Estado",
"FILTER_CATEGORY_TAGS": "Etiquetas"
"SHOW": "Ver Filtros"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Tarea {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estado: {{taskStatus }}. Descripción: {{taskDescription}}",
"SECTION_NAME": "Detalles de tarea",
"SECTION_NAME": "Tarea",
"LINK_TASKBOARD": "Panel de tareas",
"TITLE_LINK_TASKBOARD": "Ir al panel de tareas",
"PLACEHOLDER_SUBJECT": "Escribe el asunto de la nueva tarea",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Esta tarea pertenece a ",
"TITLE_LINK_GO_ORIGIN": "Ir a historia de usuario",
"BLOCKED": "Esta tarea está bloqueada",
"PREVIOUS": "tarea anterior",
"NEXT": "tarea siguiente",
"TITLE_DELETE_ACTION": "Eliminar Tarea",
"LIGHTBOX_TITLE_BLOKING_TASK": "Tarea bloqueada",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Peticiones - {{projectName}}",
"PAGE_DESCRIPTION": "El panel de peticiones del proyecto {{projectName}}: {{projectDescription}}\n",
"LIST_SECTION_NAME": "Peticiones",
"SECTION_NAME": "Detalles de petición",
"SECTION_NAME": "Petición",
"ACTION_NEW_ISSUE": "+ NUEVA PETICIÓN",
"ACTION_PROMOTE_TO_US": "Promover a Historia de Usuario",
"PLACEHOLDER_FILTER_NAME": "Escribe un nombre para el filtro y pulsa enter",
"PROMOTED": "Esta petición ha sido promovida a la historia:",
"EXTERNAL_REFERENCE": "Esta petición ha sido creada a partir de ",
"GO_TO_EXTERNAL_REFERENCE": "Ir al origen",
"BLOCKED": "La petición está bloqueada",
"TITLE_PREVIOUS_ISSUE": "petición anterior",
"TITLE_NEXT_ISSUE": "petición siguiente",
"ACTION_DELETE": "Borrar petición",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Petición bloqueada",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Promover esta petición a una nueva historia de usuario",
"MESSAGE": "¿Está seguro de que desea crear una nueva Historia de Usuario a partir de esta Petición?"
},
"FILTERS": {
"TITLE": "Filtros",
"INPUT_SEARCH_PLACEHOLDER": "Asunto o referencia",
"TITLE_ACTION_SEARCH": "Buscar",
"ACTION_SAVE_CUSTOM_FILTER": "guardar como filtro personalizado",
"BREADCRUMB": "Filtros",
"TITLE_BREADCRUMB": "Filtros",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Estado",
"SEVERITY": "Gravedad",
"PRIORITIES": "Prioridad",
"TAGS": "Etiquetas",
"ASSIGNED_TO": "Asignado a",
"CREATED_BY": "Creada por",
"CUSTOM_FILTERS": "Filtros personalizados"
},
"CONFIRM_DELETE": {
"TITLE": "Eliminar filtros personalizados",
"MESSAGE": "el filtro personalizado '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Tipo",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Buscar - {{projectName}}",
"PAGE_DESCRIPTION": "Busca cualquier cosa: historias de usuario, peticiones, tareas o páginas del wiki en el proyecto {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Épicas",
"FILTER_USER_STORIES": "Historias de Usuario",
"FILTER_ISSUES": "Peticiones",
"FILTER_TASKS": "Tareas",
@ -1397,16 +1518,16 @@
"WIZARD": {
"SECTION_TITLE_CREATE_PROJECT": "Crear Proyecto",
"CREATE_PROJECT_TEXT": "Fresco y claro. ¡Es emocionante!",
"CHOOSE_TEMPLATE": "Which template fits your project best?",
"CHOOSE_TEMPLATE_TITLE": "More info about project templates",
"CHOOSE_TEMPLATE_INFO": "More info",
"PROJECT_DETAILS": "Project Details",
"PUBLIC_PROJECT": "Public Project",
"PRIVATE_PROJECT": "Private Project",
"CHOOSE_TEMPLATE": "¿Que plantilla se ajusta mejor con tu proyecto?",
"CHOOSE_TEMPLATE_TITLE": "Mas informacion acerca de la plantillas del proyecto",
"CHOOSE_TEMPLATE_INFO": "Mas información",
"PROJECT_DETAILS": "Detalles del proyecto",
"PUBLIC_PROJECT": "Proyecto público",
"PRIVATE_PROJECT": "Proyecto privado",
"CREATE_PROJECT": "Crear proyecto",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects",
"CHANGE_PLANS": "change plans"
"MAX_PRIVATE_PROJECTS": "Has alcanzado el número máximo de proyectos privados",
"MAX_PUBLIC_PROJECTS": "Desafortunadamente, has alcanzado el número máximo de proyectos públicos",
"CHANGE_PLANS": "cambiar planes"
},
"WIKI": {
"PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}",
@ -1415,15 +1536,26 @@
"PLACEHOLDER_PAGE": "Escribe el contenido de tu página",
"REMOVE": "Eliminar esta página del wiki",
"DELETE_LIGHTBOX_TITLE": "Eliminar Página del Wiki",
"DELETE_LINK_TITLE": "Delete Wiki link",
"DELETE_LINK_TITLE": "Eliminar enlace de la Wiki",
"NAVIGATION": {
"SECTION_NAME": "Enlaces",
"ACTION_ADD_LINK": "Añadir enlace"
"HOME": "Principal",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "veces <br />editada",
"LAST_EDIT": "última <br />edición",
"LAST_MODIFICATION": "ultima modificación"
},
"SECTION_PAGES_LIST": "Todas",
"PAGES_LIST_COLUMNS": {
"TITLE": "Título",
"EDITIONS": "Ediciones",
"CREATED": "Creado",
"MODIFIED": "Modificado",
"CREATOR": "Creador",
"LAST_MODIFIER": "Ultimo modificador"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} ha creado una nueva tarea {{obj_name}} en {{project_name}} que proviene de la historia {{us_name}}",
"WIKI_CREATED": "{{username}} ha creado una nueva página de wiki {{obj_name}} en {{project_name}}\n",
"MILESTONE_CREATED": "{{username}} ha creado un nuevo sprint {{obj_name}} en {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} creó el proyecto {{project_name}}",
"MILESTONE_UPDATED": "{{username}} ha actualizado el sprint {{obj_name}}",
"US_UPDATED": "{{username}} ha actualizado el atributo \"{{field_name}}\" de la historia {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} ha actualizado el atributo \"{{field_name}}\" de la tarea {{obj_name}} que proviene de la historia {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} ha actualizado el atributo \"{{field_name}}\" de la tarea {{obj_name}} que pertenece a la historia {{us_name}} a {{new_value}}",
"WIKI_UPDATED": "{{username}} ha actualizado la página del wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} ha añadido un comentado en la historia {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} ha añadido un comentado en la petición {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} ha añadido un comentado en la tarea {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} tiene un nuevo miembro",
"US_ADDED_MILESTONE": "{{username}} ha añadido la historia {{obj_name}} a {{sprint_name}}",
"US_MOVED": "{{username}} ha movido la historia {{obj_name}}",

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Yksi riviä kohti...",
"NEW_BULK": "Lisää monta",
"RELATED_TASKS": "Liittyvät tehtävät",
"PREVIOUS": "Previous",
"NEXT": "Seuraava",
"LOGOUT": "Kirjaudu ulos",
"EXTERNAL_USER": "ulkoinen käyttäjä",
"GENERIC_ERROR": "Oompa Loompas havaitsivat virheen {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Tämä arvo vaikuttaa virheelliseltä.",
"TYPE_EMAIL": "Tämän pitäisi olla toimiva sähköpostiosoite.",
@ -115,8 +122,9 @@
"USER_STORY": "Käyttäjätarina",
"TASK": "Task",
"ISSUE": "Issue",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Anna avainsana...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Poista avainsana",
"ADD": "Lisää avainsana"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "suodattimet",
"TITLE": "Suodattimet",
"INPUT_PLACEHOLDER": "Aihe tai viittaus",
"TITLE_ACTION_FILTER_BUTTON": "hae",
"BREADCRUMB_TITLE": "takaisin kategorioihin",
"BREADCRUMB_FILTERS": "Suodattimet",
"BREADCRUMB_STATUS": "tila"
"INPUT_SEARCH_PLACEHOLDER": "Otsikko tai viittaus",
"TITLE_ACTION_SEARCH": "Hae",
"ACTION_SAVE_CUSTOM_FILTER": "tallenna omaksi suodattimeksi",
"PLACEHOLDER_FILTER_NAME": "Anna suodattimen nimi ja paina enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Tyyppi",
"STATUS": "Tila",
"SEVERITY": "Vakavuus",
"PRIORITIES": "Kiireellisyydet",
"TAGS": "Avainsanat",
"ASSIGNED_TO": "Tekijä",
"CREATED_BY": "Luoja",
"CUSTOM_FILTERS": "Omat suodattimet",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Poista oma suodatin",
"MESSAGE": "oma suodatin '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Päätason otsikko",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Esikatselu",
"EDIT_BUTTON": "Muokkaa",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Merkintätavan ohjeet"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Kierrokset",
"VIEW_SPRINTS": "Katso kierroksia",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Katso käyttäjätarinoita",
"ADD_USER_STORIES": "Lisää käyttäjätarinoita",
"MODIFY_USER_STORIES": "Muokkaa käyttäjätarinoita",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Poista käyttäjätarinoita"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Katsot tehtäviä",
"ADD_TASKS": "Lisää tehtäviä",
"MODIFY_TASKS": "Muokkaa tehtäviä",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Poista tehtäviä"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Katso pyyntöjä",
"ADD_ISSUES": "Lisää pyyntöjä",
"MODIFY_ISSUES": "Muokkaa pyyntöjä",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Poista pyyntöjä"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Watching",
"DASHBOARD": "Projects Dashboard"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Tekijä puuttuu"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Ääniä",
"NAME": "Nimi",
"PROJECT": "Projekti",
"SPRINT": "Kierros",
"ASSIGNED_TO": "Assigned",
"STATUS": "Tila",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Suljettu",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "My projects - Taiga",
"PAGE_DESCRIPTION": "A list with all your projects, you can reorder or create a new one.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Muokkaa arvoa",
"TITLE_ACTION_DELETE_VALUE": "Poista arvo"
"TITLE_ACTION_DELETE_VALUE": "Poista arvo",
"TITLE_ACTION_DELETE_TAG": "Poista avainsana"
},
"HELP": "Tarvitsetko apua? Katso tukisivuilta.",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Modulit",
"ENABLE": "Aktivoi",
"DISABLE": "Passivoi",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Odottavat",
"BACKLOG_DESCRIPTION": "Hallinnoi käyttäjätarinoita: järjestele ja priorisoi työtä.",
"NUMBER_SPRINTS": "Expected number of sprints",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Jos muutata CSV-datan URLia, edellien lakkaa toimimasta. Oletko varma?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "käyttäjätarinoiden raportit",
"SECTION_TITLE_TASK": "tehtävien raportit",
"SECTION_TITLE_ISSUE": "pyyntöjen raportit",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Omat kentät",
"SUBTITLE": "Määritele omia kenttiä käyttäjätarinoihin, tehtäviin ja pyytöihin",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Käyttäjätarinoiden omat kentät",
"US_ADD": "Lisää käyttäjätarinoihin oma kenttä",
"TASK_DESCRIPTION": "Tehtävien omat kentät",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Tila",
"SUBTITLE": "Määrittele tilat joiden kautta käyttäjätarinasi, tehtäväsi ja pyyntösi kulkevat",
"US_TITLE": "Kt tilat",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Tehtävien tilat",
"ISSUE_TITLE": "Pyyntöjen tilat"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Pyyntöjen tyypit",
"ACTION_ADD": "Lisää uusi {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Avainsanat",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Lisää avainsana",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Roles - {{projectName}}",
"WARNING_NO_ROLE": "Ole varovainen, yksikään rooli projektissasi ei voi arvioida käyttäjätarinoidesi kokoa",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhookit",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "kutsu sähköpostiin {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Oletukset pisteiden valintaan",
"LABEL_US": "Oletukset käyttäjätarinoiden tiloiksi",
"LABEL_TASK_STATUS": "Oletukset tehtävien tilaksi",
"LABEL_PRIORITY": "Oletus arvo tärkeyden valiintaan",
"LABEL_SEVERITY": "Oletukset vakavuudeksi",
"LABEL_ISSUE_TYPE": "Oletukset pyyntöjen tyypeiksi",
"LABEL_ISSUE_STATUS": "Oletukset pyyntöjen statuksiksi"
"LABEL_ISSUE_STATUS": "Oletukset pyyntöjen statuksiksi",
"LABEL_PRIORITY": "Oletus arvo tärkeyden valiintaan",
"LABEL_SEVERITY": "Oletukset vakavuudeksi"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Anna uuden tilan nimi"
@ -681,7 +776,8 @@
"PRIORITIES": "Tärkeydet",
"SEVERITIES": "Vakavuudet",
"TYPES": "Tyypit",
"CUSTOM_FIELDS": "Omat kentät"
"CUSTOM_FIELDS": "Omat kentät",
"TAGS": "Avainsanat"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Projektin profiili"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Show all",
"FILTER_TYPE_PROJECTS": "Projektit",
"FILTER_TYPE_PROJECT_TITLES": "Show only projects",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Stories",
"FILTER_TYPE_USER_STORIES_TITLES": "Show only user stories",
"FILTER_TYPE_TASKS": "Tehtävät",
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Vapaaehtoinen) Lisää oma kuvaus kutsuusi uusille jäsenille ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Anna sähköposti",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1068,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Uusi käyttäjätarina",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Aihe",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - User Story {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Completed {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} of {{userStoryTotalTasks}} tasks closed). Points: {{userStoryPoints}}. Description: {{userStoryDescription}}",
"SECTION_NAME": "Käyttäjätarinan tiedot",
"SECTION_NAME": "Käyttäjätarina",
"LINK_TASKBOARD": "Tehtävätaulu",
"TITLE_LINK_TASKBOARD": "Siirry tehtävätauluun",
"TOTAL_POINTS": "total points",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Tämä Kt oon luotu täältä: ",
"GO_TO_EXTERNAL_REFERENCE": "Palaa alkuun",
"BLOCKED": "Tämä käyttäjätarina on suljettu",
"PREVIOUS": "edellinen käyttäjätarina",
"NEXT": "seuraava käyttäjätarina",
"TITLE_DELETE_ACTION": "Poista käyttäjätarina",
"LIGHTBOX_TITLE_BLOKING_US": "Meitä estää",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tehtyä tehtävää",
"ASSIGN": "Käyttäjätarinan tekijä",
"NOT_ESTIMATED": "Ei arvioitu",
"TOTAL_US_POINTS": "Kt pisteet yhteensä",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "More info",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Edit link",
"CLOSE": "Close",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Tiimin vaatimus",
"CLIENT_REQUIREMENT": "Asiakkaan vaatimus",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "{{user}} poisti kommentin {{date}}",
"DELETED_INFO": "Comment deleted by {{user}}",
"TITLE": "Kommentit",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Recent first",
"COMMENT": "Kommentti",
"EDIT_COMMENT": "Edit comment",
"EDITED_COMMENT": "Edited:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Lisää uusi kommentti tässä",
"SHOW_DELETED": "Näytä poistettu kommentti",
"HIDE_DELETED": "Piilota poistettu kommentti",
"DELETE": "Delete comment",
"RESTORE": "Palauta kommentti"
"RESTORE": "Palauta kommentti",
"HISTORY": {
"TITLE": "Aktiivisuus"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Näytä tapahtumat",
"DATETIME": "DD.MM.YY - HH:mm",
"SHOW_MORE": "+ Näytä edelliset rivit ({{showMore}} lisää)",
"TITLE": "Aktiivisuus",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "poistettu",
"ADDED": "lisätty",
"US_POINTS": "Kt pisteet ({{name}})",
"NEW_ATTACHMENT": "uusi liite",
"DELETED_ATTACHMENT": "poistettu liite",
"UPDATED_ATTACHMENT": "päivitetty liite {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "poista oma attribuutti",
"TAGS_ADDED": "tags added:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "new attachment:",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Tehty {size, plural, one{muutos} other{# muutosta}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Tiimin vaatimus",
"CLIENT_REQUIREMENT": "Asiakkaan vaatimus",
"BLOCKED": "Suljettu",
"VALUES": {
"YES": "Kyllä",
"NO": "ei",
@ -1052,12 +1198,14 @@
"TAGS": "avainsanat",
"ATTACHMENTS": "liitteet",
"IS_DEPRECATED": "on vanhentunut",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "järjestys",
"BACKLOG_ORDER": "odottavien järjestys",
"SPRINT_ORDER": "kierroksen järjestys",
"KANBAN_ORDER": "kanban järjestys",
"TASKBOARD_ORDER": "Tehtävätaulun järjestys",
"US_ORDER": "kt järjestys"
"US_ORDER": "kt järjestys",
"COLOR": "väri"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "suljettu<br/>tehtävää",
"IOCAINE_DOSES": "myrkkye-<br/>annosta",
"SHOW_STATISTICS_TITLE": "Näytä tilastot",
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph"
"TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "projekti<br/>pistettä",
@ -1122,9 +1271,7 @@
"TITLE": "Suodattimet",
"REMOVE": "Poista suodattimet",
"HIDE": "Piilota suodattimet",
"SHOW": "Näytä suodattimet",
"FILTER_CATEGORY_STATUS": "Tila",
"FILTER_CATEGORY_TAGS": "Avainsanat"
"SHOW": "Näytä suodattimet"
},
"SPRINTS": {
"TITLE": "KIERROKSET",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Task {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{taskStatus }}. Description: {{taskDescription}}",
"SECTION_NAME": "Tehtävän tiedot",
"SECTION_NAME": "Task",
"LINK_TASKBOARD": "Tehtävätaulu",
"TITLE_LINK_TASKBOARD": "Siirry tehtävätauluun",
"PLACEHOLDER_SUBJECT": "Anna tehtävän aihe",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Tämä tehtävä on luotu",
"TITLE_LINK_GO_ORIGIN": "Siirry käyttäjätarinaan",
"BLOCKED": "Tämä tehtävä on suljettu",
"PREVIOUS": "edellinen tehtävä",
"NEXT": "seuraava tehtävä",
"TITLE_DELETE_ACTION": "Poista tehtävä",
"LIGHTBOX_TITLE_BLOKING_TASK": "Estävä tehtävä",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Issues - {{projectName}}",
"PAGE_DESCRIPTION": "The issues list panel of the project {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Pyynnöt",
"SECTION_NAME": "Pyynnön tiedot",
"SECTION_NAME": "Issue",
"ACTION_NEW_ISSUE": "+ UUSI PYYNTÖ",
"ACTION_PROMOTE_TO_US": "Liitä käyttäjätarinaan",
"PLACEHOLDER_FILTER_NAME": "Anna suodattimen nimi ja paina enter",
"PROMOTED": "Tämä pyyntö on liitetty Kthen:",
"EXTERNAL_REFERENCE": "Tämä pyyntö on luotu täältä:",
"GO_TO_EXTERNAL_REFERENCE": "Palaa alkuun",
"BLOCKED": "Tämä pyyntö on estetty",
"TITLE_PREVIOUS_ISSUE": "edellinen pyyntö",
"TITLE_NEXT_ISSUE": "seuraava pyyntö",
"ACTION_DELETE": "Poista pyyntö",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Estävä pyyntö",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Liitä tämä pyyntö uuteen käyttäjätarinaan",
"MESSAGE": "Haluatko varmasti lisätä uuden käyttäjätarinan tästä pyynnöstä?"
},
"FILTERS": {
"TITLE": "Suodattimet",
"INPUT_SEARCH_PLACEHOLDER": "Otsikko tai viittaus",
"TITLE_ACTION_SEARCH": "Hae",
"ACTION_SAVE_CUSTOM_FILTER": "tallenna omaksi suodattimeksi",
"BREADCRUMB": "Suodattimet",
"TITLE_BREADCRUMB": "Suodattimet",
"CATEGORIES": {
"TYPE": "Tyyppi",
"STATUS": "Tila",
"SEVERITY": "Vakavuus",
"PRIORITIES": "Tärkeydet",
"TAGS": "Avainsanat",
"ASSIGNED_TO": "Tekijä",
"CREATED_BY": "Luoja",
"CUSTOM_FILTERS": "Omat suodattimet"
},
"CONFIRM_DELETE": {
"TITLE": "Poista oma suodatin",
"MESSAGE": "oma suodatin '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Tyyppi",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Search - {{projectName}}",
"PAGE_DESCRIPTION": "Search anything, user stories, issues, tasks or wiki pages, in the project {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "Käyttäjätarinat",
"FILTER_ISSUES": "Pyynnöt",
"FILTER_TASKS": "Tehtävät",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Poista wiki-sivu",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Linkit",
"ACTION_ADD_LINK": "Lisää linkki"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "kertaa<br/>muokattu",
"LAST_EDIT": "viimeinen<br/>muokkaus",
"LAST_MODIFICATION": "viimeinen muokkaus"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Luotu",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} has created a new task {{obj_name}} in {{project_name}} which belongs to the US {{us_name}}",
"WIKI_CREATED": "{{username}} has created a new wiki page {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} has created a new sprint {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} created the project {{project_name}}",
"MILESTONE_UPDATED": "{{username}} has updated the sprint {{obj_name}}",
"US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} has updated the attribute \"{{field_name}}\" of the task {{obj_name}} which belongs to the US {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the task {{obj_name}} which belongs to the US {{us_name}} to {{new_value}}",
"WIKI_UPDATED": "{{username}} has updated the wiki page {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} has commented in the US {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} has commented in the issue {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} has commented in the task {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} has a new member",
"US_ADDED_MILESTONE": "{{username}} has added the US {{obj_name}} to {{sprint_name}}",
"US_MOVED": "{{username}} has moved the US {{obj_name}}",

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Un élément par ligne...",
"NEW_BULK": "Nouvel ajout en bloc",
"RELATED_TASKS": "Tâches associées",
"PREVIOUS": "Précédent",
"NEXT": "Suivant",
"LOGOUT": "Déconnexion",
"EXTERNAL_USER": "un utilisateur externe",
"GENERIC_ERROR": "L'un de nos Oompa Loompas dit {{error}}.",
@ -43,8 +45,13 @@
"TEAM_REQUIREMENT": "Un besoin projet est un besoin qui est nécessaire au projet mais qui ne doit avoir aucun impact pour le client",
"OWNER": "Propriétaire du Projet",
"CAPSLOCK_WARNING": "Attention ! Vous utilisez des majuscules dans un champ qui est sensible à la casse.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Êtes-vous sûr de vouloir fermer le mode Édition ?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Souvenez-vous que si vous fermez le mode édition sans enregistrer, toutes vos modifications seront perdues",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Affecter à",
"EDIT": "Modifier la carte"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Cette valeur semble être invalide.",
"TYPE_EMAIL": "Cette valeur devrait être une adresse courriel valide.",
@ -115,8 +122,9 @@
"USER_STORY": "Récit utilisateur",
"TASK": "Tâche",
"ISSUE": "Ticket",
"EPIC": "Épopée",
"TAGS": {
"PLACEHOLDER": "Taggez moi !",
"PLACEHOLDER": "Enter tag",
"DELETE": "Supprimer le mot-clé",
"ADD": "Ajouter un mot-clé"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Souvenez-vous que toutes les valeurs de ce champ personnalisé vont être effacées.\nEtes-vous sûr de vouloir continuer ?"
},
"FILTERS": {
"TITLE": "filtres",
"TITLE": "Filtres",
"INPUT_PLACEHOLDER": "Objet ou référence",
"TITLE_ACTION_FILTER_BUTTON": "recherche",
"BREADCRUMB_TITLE": "retour aux catégories",
"BREADCRUMB_FILTERS": "Filtres",
"BREADCRUMB_STATUS": "état"
"INPUT_SEARCH_PLACEHOLDER": "Objet ou réf.",
"TITLE_ACTION_SEARCH": "Rechercher",
"ACTION_SAVE_CUSTOM_FILTER": "sauvegarder en tant que filtre personnalisé",
"PLACEHOLDER_FILTER_NAME": "Écrivez le nom du filtre et appuyez sur \"Entrée\"",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Statut",
"SEVERITY": "Gravité",
"PRIORITIES": "Priorités",
"TAGS": "Mots-clés",
"ASSIGNED_TO": "Affecté à",
"CREATED_BY": "Créé par",
"CUSTOM_FILTERS": "Filtres personnalisés",
"EPIC": "Épopée"
},
"CONFIRM_DELETE": {
"TITLE": "Supprime le filtre personnalisé",
"MESSAGE": "le filtre personnalisé '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Premier niveau de titre",
@ -227,10 +252,19 @@
"CODE_BLOCK_SAMPLE_TEXT": "Votre texte ici…",
"PREVIEW_BUTTON": "Aperçu",
"EDIT_BUTTON": "Modifier",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP": "Joindre des fichiers en glissant et déposant ceux-ci sur la zone de texte ci-dessus.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Enregistrez d'abord si vous voulez joindre des fichiers en glissant et déposant ceux-ci sur la zone de texte ci-dessus.",
"MARKDOWN_HELP": "Aide sur la syntaxe Markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Épopées",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Voir les sprints",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Afficher les récits utilisateur",
"ADD_USER_STORIES": "Ajouter des récits utilisateur",
"MODIFY_USER_STORIES": "Modifier les récits utilisateur",
"COMMENT_USER_STORIES": "Commenter les histoires utilisateur",
"DELETE_USER_STORIES": "Supprimer des récits utilisateur"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Voir les tâches",
"ADD_TASKS": "Ajouter des tâches",
"MODIFY_TASKS": "Modifier des tâches",
"COMMENT_TASKS": "Commenter les tâches",
"DELETE_TASKS": "Supprimer des tâches"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Voir les tickets",
"ADD_ISSUES": "Ajouter des tickets",
"MODIFY_ISSUES": "Modifier des tickets",
"COMMENT_ISSUES": "Commenter les tickets",
"DELETE_ISSUES": "Supprimer des tickets"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Suivi",
"DASHBOARD": "Tableau de bord des projets"
},
"EPICS": {
"TITLE": "ÉPOPÉES",
"SECTION_NAME": "Épopées",
"EPIC": "ÉPOPÉE",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ AJOUTER ÉPOPÉE",
"UNASSIGNED": "Non affecté"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "En savoir plus sur les épopées"
},
"TABLE": {
"VOTES": "Votes",
"NAME": "Nom",
"PROJECT": "Projet",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Affecté",
"STATUS": "Statut",
"PROGRESS": "Avancement",
"VIEW_OPTIONS": "Voir les options"
},
"CREATE": {
"TITLE": "Nouvelle épopée",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Besoin projet",
"CLIENT_REQUIREMENT": "Besoin client",
"BLOCKED": "Bloqué",
"BLOCKED_NOTE_PLACEHOLDER": "Pourquoi cette épopée est-elle bloquée ?",
"CREATE_EPIC": "Créer une épopée"
}
},
"PROJECTS": {
"PAGE_TITLE": "Mes projets - Taiga",
"PAGE_DESCRIPTION": "Une liste de tous vos projets, vous pouvez la trier ou en créer une nouvelle.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Modifier la valeur",
"TITLE_ACTION_DELETE_VALUE": "Supprimer"
"TITLE_ACTION_DELETE_VALUE": "Supprimer",
"TITLE_ACTION_DELETE_TAG": "Supprimer le mot-clé"
},
"HELP": "Avez-vous besoin d'aide ? Consultez notre page d'assistance !",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Modules",
"ENABLE": "Activer",
"DISABLE": "Désactiver",
"EPICS": "Épopées",
"EPICS_DESCRIPTION": "Visualiser et gérer les aspects les plus stratégiques de votre projet",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Gérez votre récits utilisateur pour garder une vue organisée des travaux à venir et priorisés.",
"NUMBER_SPRINTS": "Nombre prévu de sprints",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Vous êtes sur le point de changer l'url d'accès aux données CSV. L'url précédente sera désactivée. Êtes-vous sûr ?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "rapports des récits utilisateur",
"SECTION_TITLE_TASK": "rapports des tâches",
"SECTION_TITLE_ISSUE": "Rapports des tickets",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Champs personnalisés",
"SUBTITLE": "Spécifiez les champs personnalisés de vos récits utilisateur, tâches et tickets",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Champs personnalisés des récits utilisateur",
"US_ADD": "Ajouter un champ personnalisé dans les récits utilisateur",
"TASK_DESCRIPTION": "Champs personnalisés de tâches",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Statut",
"SUBTITLE": "Spécifiez les statuts que vont prendre vos récits utilisateur, tâches et tickets",
"US_TITLE": "Statuts des RU",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Statuts des tâches",
"ISSUE_TITLE": "Statuts des Tickets"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Types de tickets",
"ACTION_ADD": "Ajouter un nouveau {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Mots-clés",
"SUBTITLE": "Voir et modifier la couleur de vos mots-clés",
"EMPTY": "Il n'y a pas de mots-clés pour l'instant",
"EMPTY_SEARCH": "Il semble qu'aucun résultat ne correspond à vos critères de recherche",
"ACTION_ADD": "Ajouter un mot-clé",
"NEW_TAG": "Nouveau mot-clé",
"MIXING_HELP_TEXT": "Sélectionnez les mots-clés que vous voulez fusionner",
"MIXING_MERGE": "Fusionner des mots-clés",
"SELECTED": "Sélectionné"
},
"ROLES": {
"PAGE_TITLE": "Rôles - {{projectName}}",
"WARNING_NO_ROLE": "Attention, aucun rôle dans votre projet ne pourra estimer la valeur du point pour les récits utilisateurs",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "l'invitation à {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Valeur par défaut pour la sélection des points",
"LABEL_US": "Valeur par défaut pour la sélection du récit utilisateur",
"LABEL_TASK_STATUS": "Valeur par défaut pour la sélection de l'état des tâches",
"LABEL_PRIORITY": "Valeur par défaut de la sélection des priorités",
"LABEL_SEVERITY": "Valeur par défaut pour le sélecteur de gravité",
"LABEL_ISSUE_TYPE": "Valeur par défaut pour le sélecteur de type",
"LABEL_ISSUE_STATUS": "Valeur par défaut pour le sélecteur de statut de bug"
"LABEL_ISSUE_STATUS": "Valeur par défaut pour le sélecteur de statut de bug",
"LABEL_PRIORITY": "Valeur par défaut de la sélection des priorités",
"LABEL_SEVERITY": "Valeur par défaut pour le sélecteur de gravité"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Entrez le nom du nouvel état"
@ -681,7 +776,8 @@
"PRIORITIES": "Priorités",
"SEVERITIES": "Gravité",
"TYPES": "Types",
"CUSTOM_FIELDS": "Champs personnalisés"
"CUSTOM_FIELDS": "Champs personnalisés",
"TAGS": "Mots-clés"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Profil projet"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Voir tous",
"FILTER_TYPE_PROJECTS": "Projets",
"FILTER_TYPE_PROJECT_TITLES": "Voir uniquement les projets",
"FILTER_TYPE_EPICS": "Épopées",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Récits",
"FILTER_TYPE_USER_STORIES_TITLES": "Voir uniquement les user stories",
"FILTER_TYPE_TASKS": "Tâches",
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Optionnel) Ajoutez un texte personnalisé à l'invitation. Dites quelque chose de gentil à vos nouveaux membres ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Saisissez une adresse courriel",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Désolé, ce projet ne peut avoir plus de <strong>{{maxMembers}}</strong> membres. <br>Si vous désirez augmenter cette limite, merci de contacter l'administrateur.",
"LIMIT_USERS_WARNING_MESSAGE": "Désolé, ce projet ne peut avoir plus de <strong>{{maxMembers}}</strong> membres."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Malheureusement, ce projet ne peut pas être laissé sans propriétaire",
@ -970,10 +1068,30 @@
"BUTTON": "Demander à ce membre du projet de devenir le nouveau propriétaire"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Épopée",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nouveau récit utilisateur",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Objet",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - Récit utilisateur {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "État : {{userStoryStatus }}. Achevé {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} sur {{userStoryTotalTasks}} tâches fermées). Points : {{userStoryPoints}}. Description : {{userStoryDescription}}",
"SECTION_NAME": "Détails du récit utilisateur",
"SECTION_NAME": "Récit utilisateur",
"LINK_TASKBOARD": "Tableau des tâches",
"TITLE_LINK_TASKBOARD": "Aller au tableau des tâches",
"TOTAL_POINTS": "total des points",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Ce récit utilisateur a été créé depuis",
"GO_TO_EXTERNAL_REFERENCE": "Allez à l'origine",
"BLOCKED": "Ce récit utilisateur est bloqué",
"PREVIOUS": "récit utilisateur précédent",
"NEXT": "récit utilisateur suivant",
"TITLE_DELETE_ACTION": "Supprimer le récit utilisateur",
"LIGHTBOX_TITLE_BLOKING_US": "Bloque le RU",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tâches complétées",
"ASSIGN": "Affecter le récit utilisateur",
"NOT_ESTIMATED": "Non estimé",
"TOTAL_US_POINTS": "Total des points RU",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "Plus d'informations",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Modifier le lien",
"CLOSE": "Fermer",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Avez-vous besoin de quelqu'un pour cette tâche ?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Besoin projet",
"CLIENT_REQUIREMENT": "Besoin client",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Commentaire supprimé par {{user}} le {{date}}",
"DELETED_INFO": "Commentaire supprimé par {{user}}",
"TITLE": "Commentaires",
"COMMENTS_COUNT": "{{comments}} commentaires",
"ORDER": "Trier",
"OLDER_FIRST": "Plus ancien d'abord",
"RECENT_FIRST": "Plus récent d'abord",
"COMMENT": "Commentaire",
"EDIT_COMMENT": "Modifier le commentaire",
"EDITED_COMMENT": "Modifié :",
"SHOW_HISTORY": "Voir l'historique",
"TYPE_NEW_COMMENT": "Entrez un nouveau commentaire ici",
"SHOW_DELETED": "Afficher le commentaire supprimé",
"HIDE_DELETED": "Cacher le commentaire supprimé",
"DELETE": "Supprimer le commentaire",
"RESTORE": "Restaurer le commentaire"
"RESTORE": "Restaurer le commentaire",
"HISTORY": {
"TITLE": "Activité"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Afficher l'activité",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Montrer les entrées précédentes ({{showMore}} plus)",
"TITLE": "Activité",
"ACTIVITIES_COUNT": "{{activities}} activités",
"REMOVED": "supprimé",
"ADDED": "ajouté",
"US_POINTS": "Points du récit utilisateur ({{name}})",
"NEW_ATTACHMENT": "nouvelle pièce jointe",
"DELETED_ATTACHMENT": "Pièce jointe supprimée",
"UPDATED_ATTACHMENT": "Pièce jointe {{filename}} modifiée",
"DELETED_CUSTOM_ATTRIBUTE": "Attribut personnalisé supprimé",
"TAGS_ADDED": "Mots-clés ajoutés :",
"TAGS_REMOVED": "Mots-clés supprimés",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "Nouvelle pièce jointe",
"DELETED_ATTACHMENT": "Pièces jointes supprimées :",
"UPDATED_ATTACHMENT": "Pièces jointes mises à jour ({{filename}}) :",
"CREATED_CUSTOM_ATTRIBUTE": "Attribut personnalisé créé",
"UPDATED_CUSTOM_ATTRIBUTE": "Attribut personnalisé mis à jour",
"SIZE_CHANGE": "A fait {size, plural, one{une modification} other{# modifications}}",
"BECAME_DEPRECATED": "devenu obsolète",
"BECAME_UNDEPRECATED": "n'est plus obsolète",
"TEAM_REQUIREMENT": "Besoin projet",
"CLIENT_REQUIREMENT": "Besoin client",
"BLOCKED": "Bloqué",
"VALUES": {
"YES": "oui",
"NO": "no",
@ -1052,12 +1198,14 @@
"TAGS": "mots-clés",
"ATTACHMENTS": "pièces jointes",
"IS_DEPRECATED": "est obsolète",
"IS_NOT_DEPRECATED": "n'est pas obsolète",
"ORDER": "classement",
"BACKLOG_ORDER": "classement du backlog",
"SPRINT_ORDER": "classement du sprint",
"KANBAN_ORDER": "Classement du Kanban",
"TASKBOARD_ORDER": "trier le tableau des tâches",
"US_ORDER": "classement des récits utilisateur"
"US_ORDER": "classement des récits utilisateur",
"COLOR": "couleur"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "tâches<br />fermées",
"IOCAINE_DOSES": "doses<br />de iocaine",
"SHOW_STATISTICS_TITLE": "Afficher les statistiques",
"TOGGLE_BAKLOG_GRAPH": "Afficher/masquer le graphique d'avancement"
"TOGGLE_BAKLOG_GRAPH": "Afficher/masquer le graphique d'avancement",
"POINTS_PER_ROLE": "Points par rôle"
},
"SUMMARY": {
"PROJECT_POINTS": "projet<br />points",
@ -1122,9 +1271,7 @@
"TITLE": "Filtres",
"REMOVE": "Supprimer les filtres",
"HIDE": "Cacher les filtres",
"SHOW": "Afficher les filtres",
"FILTER_CATEGORY_STATUS": "Etat",
"FILTER_CATEGORY_TAGS": "Mots-clés"
"SHOW": "Afficher les filtres"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Tâche {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "État : {{taskStatus }}. Description : {{taskDescription}}",
"SECTION_NAME": "Détails de la tâche",
"SECTION_NAME": "Tâche",
"LINK_TASKBOARD": "Tableau des tâches",
"TITLE_LINK_TASKBOARD": "Aller au tableau des tâches",
"PLACEHOLDER_SUBJECT": "Entrez l'objet de la nouvelle tâche",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Cette tâche a été créée par",
"TITLE_LINK_GO_ORIGIN": "Aller au récit utilisateur",
"BLOCKED": "Cette tâche est bloquée",
"PREVIOUS": "tâche précédente",
"NEXT": "tâche suivante",
"TITLE_DELETE_ACTION": "Supprimer une tâche",
"LIGHTBOX_TITLE_BLOKING_TASK": "Tâche bloquante",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Tickets - {{projectName}}",
"PAGE_DESCRIPTION": "Le panneau de la liste des tickets du projet {{projectName}} : {{projectDescription}}",
"LIST_SECTION_NAME": "Tickets",
"SECTION_NAME": "Détails du ticket",
"SECTION_NAME": "Ticket",
"ACTION_NEW_ISSUE": "+ NOUVEAU TICKET",
"ACTION_PROMOTE_TO_US": "Promouvoir en récit utilisateur",
"PLACEHOLDER_FILTER_NAME": "Écrivez le nom du filtre et appuyez sur \"Entrée\"",
"PROMOTED": "Le ticket a été promu en récit utilisateur",
"EXTERNAL_REFERENCE": "Ce ticket a été créé à partir de",
"GO_TO_EXTERNAL_REFERENCE": "Aller à l'origine",
"BLOCKED": "Ce bug est bloqué",
"TITLE_PREVIOUS_ISSUE": "ticket précédent",
"TITLE_NEXT_ISSUE": "ticket suivant",
"ACTION_DELETE": "Supprimer le ticket",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Ticket bloquant",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Promouvoir ce ticket en nouveau récit utilisateur",
"MESSAGE": "Êtes-vous sure de vouloir créer un nouveau récit utilisateur à partir de ce ticket ?"
},
"FILTERS": {
"TITLE": "Filtres",
"INPUT_SEARCH_PLACEHOLDER": "Objet ou réf.",
"TITLE_ACTION_SEARCH": "Rechercher",
"ACTION_SAVE_CUSTOM_FILTER": "sauvegarder en tant que filtre personnalisé",
"BREADCRUMB": "Filtres",
"TITLE_BREADCRUMB": "Filtres",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Statut",
"SEVERITY": "Gravité",
"PRIORITIES": "Priorités",
"TAGS": "Mots-clés",
"ASSIGNED_TO": "Affecté à",
"CREATED_BY": "Créé par",
"CUSTOM_FILTERS": "Filtres personnalisés"
},
"CONFIRM_DELETE": {
"TITLE": "Supprime le filtre personnalisé",
"MESSAGE": "le filtre personnalisé '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Type",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Chercher - {{projectName}}",
"PAGE_DESCRIPTION": "Chercher tout, récits utilisateurs, tickets, tâches ou pages de wiki, dans le projet {{projectName}} : {{projectDescription}}",
"FILTER_EPICS": "Épopées",
"FILTER_USER_STORIES": "Récits utilisateur",
"FILTER_ISSUES": "Tickets",
"FILTER_TASKS": "Tâches",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Supprimer la page Wiki",
"DELETE_LINK_TITLE": "Supprimer un lien Wiki",
"NAVIGATION": {
"SECTION_NAME": "Liens",
"ACTION_ADD_LINK": "Ajouter un lien"
"HOME": "Page principale",
"SECTION_NAME": "SIGNETS",
"ACTION_ADD_LINK": "Ajouter un signet",
"ALL_PAGES": "Toutes les pages wiki"
},
"SUMMARY": {
"TIMES_EDITED": "modifications",
"LAST_EDIT": "dernière <br />modification",
"LAST_MODIFICATION": "dernière modification"
},
"SECTION_PAGES_LIST": "Toutes les pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Titre",
"EDITIONS": "Modifications",
"CREATED": "Créé le",
"MODIFIED": "Modifié",
"CREATOR": "Créateur",
"LAST_MODIFIER": "Dernier modificateur"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} a créé une nouvelle tâche {{obj_name}} dans le projet {{project_name}} pour le récit utilisateur {{us_name}}",
"WIKI_CREATED": "{{username}} a créé une nouvelle page wiki {{obj_name}} dans {{project_name}}",
"MILESTONE_CREATED": "{{username}} a créé un nouveau sprint {{obj_name}} dans {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} a créé le projet {{project_name}}",
"MILESTONE_UPDATED": "{{username}} a mis à jour le sprint {{obj_name}}",
"US_UPDATED": "{{username}} a mis à jour l'attribut «{{field_name}}» du récit utilisateur {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} a mis à jour l'attribut «{{field_name}}» de la tâche {{obj_name}} qui appartient au récit utilisateur {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} a mis l'attribut \"{{field_name}}\" à {{new_value}} pour la tâche {{obj_name}} appartenant au récit utilisateur {{us_name}}",
"WIKI_UPDATED": "{{username}} a mis à jour la page wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} a commenté le récit utilisateur {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} a commenté le ticket {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} a commenté la tâche {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} a un nouveau membre",
"US_ADDED_MILESTONE": "{{username}} a ajouté le récit utilisateur {{obj_name}} à {{sprint_name}}",
"US_MOVED": "{{username}} a déplacé le RU {{obj_name}}",

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Un elemento per riga...",
"NEW_BULK": "Nuovo inserimento nel carico",
"RELATED_TASKS": "Compiti correlati",
"PREVIOUS": "Previous",
"NEXT": "Successivo",
"LOGOUT": "Esci",
"EXTERNAL_USER": "un utente esterno",
"GENERIC_ERROR": "C'é uno dei nostri Oompa Loompa che dice {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Questo valore non è valido.",
"TYPE_EMAIL": "Questo valore dovrebbe corrispondere ad una mail valida",
@ -115,8 +122,9 @@
"USER_STORY": "Storia utente",
"TASK": "Compito",
"ISSUE": "Problema",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Eccomi! taggami",
"PLACEHOLDER": "Enter tag",
"DELETE": "Elimina tag",
"ADD": "Aggiungi un tag"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Ricorda che tutti i valori in questo campo custom andranno persi.\nSei sicuro di voler continuare?"
},
"FILTERS": {
"TITLE": "filtri",
"TITLE": "Filtri",
"INPUT_PLACEHOLDER": "Soggetto o referenza",
"TITLE_ACTION_FILTER_BUTTON": "cerca",
"BREADCRUMB_TITLE": "Indietro alle categorie",
"BREADCRUMB_FILTERS": "Filtri",
"BREADCRUMB_STATUS": "stato"
"INPUT_SEARCH_PLACEHOLDER": "Soggetto o referenza",
"TITLE_ACTION_SEARCH": "Cerca",
"ACTION_SAVE_CUSTOM_FILTER": "salva come filtro personalizzato",
"PLACEHOLDER_FILTER_NAME": "Scrivi il nome del filtro e premi invio",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Stato",
"SEVERITY": "Gravità",
"PRIORITIES": "Priorità",
"TAGS": "Tag",
"ASSIGNED_TO": "Assegnato a",
"CREATED_BY": "Creato da",
"CUSTOM_FILTERS": "Filtri personalizzati",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Elimina il filtro personalizzato",
"MESSAGE": "Il filtro personalizzato '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Intestazione di primo livello",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Anteprima",
"EDIT_BUTTON": "Modifica",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Aiuto per la sintassi Markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Vedi gli sprint",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Vai alle storie utente",
"ADD_USER_STORIES": "Aggiungi le storie utente",
"MODIFY_USER_STORIES": "Modifica le storie utente",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Elimina le storie utente"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Guarda i compiti",
"ADD_TASKS": "Aggiungi i compiti",
"MODIFY_TASKS": "Modifica i compiti",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Elimina i compiti"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Guarda i problemi",
"ADD_ISSUES": "Aggiungi un problema",
"MODIFY_ISSUES": "Modifica i problemi",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Elimina i problemi"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Osservando",
"DASHBOARD": "Dashboard Progetti"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Non assegnato"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Voti",
"NAME": "Nome",
"PROJECT": "Progetto",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Stato",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Bloccato",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "I miei progetti - Taiga",
"PAGE_DESCRIPTION": "Una lista di tutti i tuoi progetti, la puoi riordinare o crearne una nuova.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Modifica valore",
"TITLE_ACTION_DELETE_VALUE": "Elimina valore"
"TITLE_ACTION_DELETE_VALUE": "Elimina valore",
"TITLE_ACTION_DELETE_TAG": "Elimina tag"
},
"HELP": "Hai bisogno di aiuto? Controlla la nostra pagina di supporto!",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Moduli",
"ENABLE": "Abilita",
"DISABLE": "Disabilita",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Amministra le storie degli utenti per mantenere una visione organizzata dei lavori in arrivo e di quelli ad alta priorità ",
"NUMBER_SPRINTS": "Expected number of sprints",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Stai per modificare l'url di accesso al CSV. il precedente url verrá disabilitato. Sei sicuro?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "Report delle storie utente",
"SECTION_TITLE_TASK": "Analisi dei compiti",
"SECTION_TITLE_ISSUE": "Report criticitá",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Campi Personalizzati",
"SUBTITLE": "Specifica i campi personalizzati per le tue Storie Utente, compiti e problemi",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Campi personalizzati delle storie utente",
"US_ADD": "Aggiungi un campo personalizzato nelle storie utente",
"TASK_DESCRIPTION": "Campi personalizzati dei Compiti",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Stato",
"SUBTITLE": "Specifica lo stato delle storie utente, i compiti e i problemi saranno affrontati",
"US_TITLE": "Stato delle storie utente",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Stato dei compiti",
"ISSUE_TITLE": "Stato dei problemi"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Tipi di criticitá",
"ACTION_ADD": "Aggiungi {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Tag",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Aggiungi un tag",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Ruoli - {{projectName}}",
"WARNING_NO_ROLE": "Attento, nessun ruolo, all'interno del tuo progetto, potrà stimare i punti valore per le storie utente",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "L'invito a {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Valore standard per punti di selezione",
"LABEL_US": "Valore predefinito per la selezione di stati delle storie utente",
"LABEL_TASK_STATUS": "Valore predefinito per la selezione degli stati del compito",
"LABEL_PRIORITY": "Valore predefinito per la selezione prioritaria",
"LABEL_SEVERITY": "Valore predefinito per la selezione di rigore",
"LABEL_ISSUE_TYPE": "Valore predefinito per il tipo di selezione del problema",
"LABEL_ISSUE_STATUS": "Valore predefinito per la selezione di stato del problema"
"LABEL_ISSUE_STATUS": "Valore predefinito per la selezione di stato del problema",
"LABEL_PRIORITY": "Valore predefinito per la selezione prioritaria",
"LABEL_SEVERITY": "Valore predefinito per la selezione di rigore"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Scrivi un nome per il nuovo status"
@ -681,7 +776,8 @@
"PRIORITIES": "priorità",
"SEVERITIES": "Severitá",
"TYPES": "Tipi",
"CUSTOM_FIELDS": "Campi personalizzati"
"CUSTOM_FIELDS": "Campi personalizzati",
"TAGS": "Tag"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Profilo progetto"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Mostra tutto",
"FILTER_TYPE_PROJECTS": "Progetti",
"FILTER_TYPE_PROJECT_TITLES": "Mostra solo i progetti",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Resoconti",
"FILTER_TYPE_USER_STORIES_TITLES": "Mostra solo resoconti utente",
"FILTER_TYPE_TASKS": "Compiti",
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(facoltativo) aggiungi un testo personalizzato all'invito. Di qualcosa di simpatico ai tuoi nuovi membri ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Scrivi una mail",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1068,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nuova storia utente",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Oggetto",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - User Story {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Completata per il {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} di {{userStoryTotalTasks}} tasks closed). Punti: {{userStoryPoints}}. Descrizione: {{userStoryDescription}}",
"SECTION_NAME": "Dettagli della storia utente",
"SECTION_NAME": "Storia utente",
"LINK_TASKBOARD": "Pannello dei compiti",
"TITLE_LINK_TASKBOARD": "Vai al pannello dei compiti",
"TOTAL_POINTS": "totale punti",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Questo US é stato creato da",
"GO_TO_EXTERNAL_REFERENCE": "Ritorna all'inizio",
"BLOCKED": "Questa storia utente è bloccata",
"PREVIOUS": "Storia utente precedente ",
"NEXT": "Prossima storia utente",
"TITLE_DELETE_ACTION": "Elimina la storia utente",
"LIGHTBOX_TITLE_BLOKING_US": "Blocco la storia utente",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} compiti completati",
"ASSIGN": "Assegna la storia utente",
"NOT_ESTIMATED": "Non stimato",
"TOTAL_US_POINTS": "Totale punti della storia utente",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "More info",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Edit link",
"CLOSE": "Close",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Requisito del team",
"CLIENT_REQUIREMENT": "Requisito del client",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Commento cancellato da {{user}} il {{date}}",
"DELETED_INFO": "Comment deleted by {{user}}",
"TITLE": "Commenti",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Recent first",
"COMMENT": "Commento",
"EDIT_COMMENT": "Edit comment",
"EDITED_COMMENT": "Edited:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Scrivi un nuovo commento qui",
"SHOW_DELETED": "Visualizza commento cancellato",
"HIDE_DELETED": "Nascondi commento cancellato",
"DELETE": "Cancella commento",
"RESTORE": "Ripristina commento"
"RESTORE": "Ripristina commento",
"HISTORY": {
"TITLE": "Attività"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Mostra attività",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "Mostra gli inserimenti precedenti ({{showMore}} more)",
"TITLE": "Attività",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "rimosso",
"ADDED": "aggiunto",
"US_POINTS": "punti storia utente ({{name}})",
"NEW_ATTACHMENT": "nuovo allegato",
"DELETED_ATTACHMENT": "allegato eliminato",
"UPDATED_ATTACHMENT": "Aggiornato l'allegato {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "elimina un attributo personalizzato",
"TAGS_ADDED": "tags added:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "new attachment:",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Fatto {size, plural, one{un cambiamento} other{# cambiamenti}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Requisito del team",
"CLIENT_REQUIREMENT": "Requisito del client",
"BLOCKED": "Bloccato",
"VALUES": {
"YES": "si",
"NO": "no",
@ -1052,12 +1198,14 @@
"TAGS": "tag",
"ATTACHMENTS": "allegati",
"IS_DEPRECATED": "è deprecato",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "ordine",
"BACKLOG_ORDER": "Ordine di backlog",
"SPRINT_ORDER": "Ordine dello sprint",
"KANBAN_ORDER": "ordina kanban",
"TASKBOARD_ORDER": "Ordine del pannello dei compiti",
"US_ORDER": "Ordine delle storie utente"
"US_ORDER": "Ordine delle storie utente",
"COLOR": "colore"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "<br /> compiti chiusi",
"IOCAINE_DOSES": "<br /> pasticche di aspirina",
"SHOW_STATISTICS_TITLE": "Mostra statistiche",
"TOGGLE_BAKLOG_GRAPH": "Mostra/nascondi i grafici burndown"
"TOGGLE_BAKLOG_GRAPH": "Mostra/nascondi i grafici burndown",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "<br /> punti di progetto",
@ -1122,9 +1271,7 @@
"TITLE": "Filtri",
"REMOVE": "Rimuovi filtri",
"HIDE": "Nascondi Filtri",
"SHOW": "Mostra Filtri",
"FILTER_CATEGORY_STATUS": "Stato",
"FILTER_CATEGORY_TAGS": "Tag"
"SHOW": "Mostra Filtri"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Compiti {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Stato: {{taskStatus }}. Descrizione: {{taskDescription}}",
"SECTION_NAME": "Dettagli del compito",
"SECTION_NAME": "Compito",
"LINK_TASKBOARD": "Pannello dei compiti",
"TITLE_LINK_TASKBOARD": "Vai al pannello dei compiti",
"PLACEHOLDER_SUBJECT": "Inserisci il soggetto del nuovo compito",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Questo compito è stato creato da",
"TITLE_LINK_GO_ORIGIN": "Vai alla storia utente",
"BLOCKED": "Questo compito è bloccato",
"PREVIOUS": "Compito precedente",
"NEXT": "Prossimo compito",
"TITLE_DELETE_ACTION": "Rimuovi compito",
"LIGHTBOX_TITLE_BLOKING_TASK": "Sto bloccando il compito",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Criticitá - {{projectName}}",
"PAGE_DESCRIPTION": "Il pannello con la lista dei problemi del progetto {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "problemi",
"SECTION_NAME": "Dettagli della criticitá",
"SECTION_NAME": "Problema",
"ACTION_NEW_ISSUE": "+ NUOVA CRITICITÁ",
"ACTION_PROMOTE_TO_US": "Promuovi la storia utente",
"PLACEHOLDER_FILTER_NAME": "Scrivi il nome del filtro e premi invio",
"PROMOTED": "Il problema è stato promosso a storia utente",
"EXTERNAL_REFERENCE": "Questo problema è stato creato da ",
"GO_TO_EXTERNAL_REFERENCE": "Ritorna all'inizio",
"BLOCKED": "Questo problema è bloccato",
"TITLE_PREVIOUS_ISSUE": "problema precedente",
"TITLE_NEXT_ISSUE": "Problema successivo",
"ACTION_DELETE": "Elimina problema",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Issue bloccante",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Promuovi questo problema come nuova storia utente",
"MESSAGE": "Sei sicuro di voler creare una nuova storia utente da questo problema?"
},
"FILTERS": {
"TITLE": "Filtri",
"INPUT_SEARCH_PLACEHOLDER": "Soggetto o referenza",
"TITLE_ACTION_SEARCH": "Cerca",
"ACTION_SAVE_CUSTOM_FILTER": "salva come filtro personalizzato",
"BREADCRUMB": "Filtri",
"TITLE_BREADCRUMB": "Filtri",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Stato",
"SEVERITY": "Gravità",
"PRIORITIES": "priorità",
"TAGS": "Tag",
"ASSIGNED_TO": "Assegna a",
"CREATED_BY": "Creato da",
"CUSTOM_FILTERS": "Filtri personalizzati"
},
"CONFIRM_DELETE": {
"TITLE": "Elimina il filtro personalizzato",
"MESSAGE": "Il filtro personalizzato '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Tipo",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Cerca - {{projectName}}",
"PAGE_DESCRIPTION": "Cerca storie utenti, problemi, compiti o pagine wiki, all'interno del progetto {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "Storie Utente",
"FILTER_ISSUES": "problemi",
"FILTER_TASKS": "Compiti",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Elimina Pagina Wiki",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Link",
"ACTION_ADD_LINK": "Aggiungi link"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "tempo <br />modificato",
"LAST_EDIT": "<br /> ultima modifica",
"LAST_MODIFICATION": "ultima modifica"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Creato",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} ha creato un nuovo compito {{obj_name}} in {{project_name}} che appartiene alla storia utente {{us_name}}",
"WIKI_CREATED": "{{username}} ha creato una nuova pagina wiki {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} ha creato un nuovo sprint {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} ha creato il progetto {{project_name}}",
"MILESTONE_UPDATED": "{{username}} ha aggiornato lo sprint {{obj_name}}",
"US_UPDATED": "{{username}} ha aggiornato l'attributo \"{{field_name}}\" alla storia utente {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} ha aggiornato l'attributo \"{{field_name}}\" del compito {{obj_name}} che appartiene alla storia utente {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} ha aggiornato l'attributo \"{{field_name}}\" del compito {{obj_name}} che appartiene alla storia utente {{us_name}} a {{new_value}}",
"WIKI_UPDATED": "{{username}} ha aggiornato la pagina wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} ha commentato nella storia utente {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} ha commentato nel problema {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} ha commentato nel compito {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} ha un nuovo membro",
"US_ADDED_MILESTONE": "{{username}} ha aggiunto la storia utente {{obj_name}} a {{sprint_name}}",
"US_MOVED": "{{username}} ha spostato la storia utente {{obj_name}}",

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Eén item per regel...",
"NEW_BULK": "Nieuwe bulk toevoeging",
"RELATED_TASKS": "Gerelateerde taken",
"PREVIOUS": "Previous",
"NEXT": "Volgende",
"LOGOUT": "Afmelden",
"EXTERNAL_USER": "een extern gebruiker",
"GENERIC_ERROR": "Een van onze Oempa Loempa's zegt {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Deze waarde lijkt ongeldig te zijn",
"TYPE_EMAIL": "Deze waarde moet een geldig emailadres bevatten",
@ -115,8 +122,9 @@
"USER_STORY": "User story",
"TASK": "Taak",
"ISSUE": "Issue",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Ik ben 'm! Tag me...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Verwijder tag",
"ADD": "Tag tovoegen"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "filters",
"TITLE": "Filters",
"INPUT_PLACEHOLDER": "Onderwerp of referentie",
"TITLE_ACTION_FILTER_BUTTON": "zoek",
"BREADCRUMB_TITLE": "terug naar categorieën",
"BREADCRUMB_FILTERS": "Filters",
"BREADCRUMB_STATUS": "status"
"INPUT_SEARCH_PLACEHOLDER": "Onderwerp of ref.",
"TITLE_ACTION_SEARCH": "Zoek",
"ACTION_SAVE_CUSTOM_FILTER": "Als eigen filter opslaan",
"PLACEHOLDER_FILTER_NAME": "Geef de filternaam in en druk op enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Status",
"SEVERITY": "Ernst",
"PRIORITIES": "Prioriteit",
"TAGS": "Tags",
"ASSIGNED_TO": "Toegewezen aan",
"CREATED_BY": "Aangemaakt door",
"CUSTOM_FILTERS": "Eigen filters",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Verwijder eigen filter",
"MESSAGE": "de eigen filter '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Eerste niveau heading",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Voorbeeld",
"EDIT_BUTTON": "Bewerk",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Markdown syntax help"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Sprints bekijken",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Bekijk user stories",
"ADD_USER_STORIES": "User stories toevoegen",
"MODIFY_USER_STORIES": "user stories bewerken",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Verwijderd user stories"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Bekijk taken",
"ADD_TASKS": "Taak toevoegen",
"MODIFY_TASKS": "Bewerk taken",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Verwijder taken"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Bekijk issues",
"ADD_ISSUES": "Issues toevoegen",
"MODIFY_ISSUES": "Bewerk issues",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Issues verwijderen"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Volgers",
"DASHBOARD": "Projects Dashboard"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Niet toegewezen"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Stemmen",
"NAME": "Naam",
"PROJECT": "Project",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Status",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Geblokkeerd",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "Mijn projecten - Taiga",
"PAGE_DESCRIPTION": "Een lijst met al jouw projecten, je kunt deze herodenen of een nieuwe aanmaken.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Bewerk waarde",
"TITLE_ACTION_DELETE_VALUE": "Verwijder waarde"
"TITLE_ACTION_DELETE_VALUE": "Verwijder waarde",
"TITLE_ACTION_DELETE_TAG": "Verwijder tag"
},
"HELP": "Help je hulp nodig? Bekijk onze support pagina!",
"PROJECT_DEFAULT_VALUES": {
@ -435,6 +508,8 @@
"TITLE": "Modules",
"ENABLE": "Inschakelen",
"DISABLE": "Uitschakelen",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Organiseer je user stories om een duidelijk overzicht van aankomend en geprioritiseerd werk te behouden.",
"NUMBER_SPRINTS": "Expected number of sprints",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Je staat op het punt de CSV data toegang url te veranderen. De vorige url zal worden uitgeschakeld. Ben je zeker dat je ermee door wil gaan?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "user stories rapporten",
"SECTION_TITLE_TASK": "taak rapporten",
"SECTION_TITLE_ISSUE": "Issues rapport",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Eigen velden",
"SUBTITLE": "Specifieer de aangepaste velden voor je user stories, taken en issues",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Eigen velden user stories",
"US_ADD": "Voeg eigen velden toe in user stories",
"TASK_DESCRIPTION": "Eigen velden taken",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Status",
"SUBTITLE": "Specifieer de statussen waar je user stories, taken en issues door zullen gaan",
"US_TITLE": "US statussen",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Taak statussen",
"ISSUE_TITLE": "Issue statussen"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Issues types",
"ACTION_ADD": "Voeg nieuwe {{objName}} toe"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Tags",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Tag tovoegen",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Rollen - {{projectName}}",
"WARNING_NO_ROLE": "Wees voorzichtig, geen enkele rol in je project zal de puntenwaarde van een user story kunnen estimeren",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "de uitnodiging naar {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Standaard waarde voor punten selectie",
"LABEL_US": "Standaard waarde voor US status selectie",
"LABEL_TASK_STATUS": "Standaard waarde voor taak status selectie",
"LABEL_PRIORITY": "Standaard waarde voor prioriteit selectie",
"LABEL_SEVERITY": "Standaard waarde voor ernst selectie",
"LABEL_ISSUE_TYPE": "Standaard waarde voor issue type selectie",
"LABEL_ISSUE_STATUS": "Standaard waarde voor issue status selectie"
"LABEL_ISSUE_STATUS": "Standaard waarde voor issue status selectie",
"LABEL_PRIORITY": "Standaard waarde voor prioriteit selectie",
"LABEL_SEVERITY": "Standaard waarde voor ernst selectie"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Geef een naam voor de nieuwe status"
@ -681,7 +776,8 @@
"PRIORITIES": "Prioriteiten",
"SEVERITIES": "Ernst",
"TYPES": "Types",
"CUSTOM_FIELDS": "Eigen velden"
"CUSTOM_FIELDS": "Eigen velden",
"TAGS": "Tags"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Project profiel"
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Alles weergeven",
"FILTER_TYPE_PROJECTS": "Projecten",
"FILTER_TYPE_PROJECT_TITLES": "Enkel projecten weergeven",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Verhalen",
"FILTER_TYPE_USER_STORIES_TITLES": "Enkel verhalen van gebruikers weergeven",
"FILTER_TYPE_TASKS": "Taken",
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Optioneel) Voeg een gepersonaliseerd bericht toe aan je uitnodiging. Vertel iets leuks aan je nieuwe leden ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Type en E-mail",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1068,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nieuwe user story",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Onderwerp",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - User Story {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Voltooid {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} van {{userStoryTotalTasks}} taken gesloten). Punten: {{userStoryPoints}}. Omschrijving: {{userStoryDescription}}",
"SECTION_NAME": "User story details",
"SECTION_NAME": "User story",
"LINK_TASKBOARD": "Taakbord",
"TITLE_LINK_TASKBOARD": "Ga naar het dashboard",
"TOTAL_POINTS": "totaal aantal punten",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Deze US is aangemaakt vanaf",
"GO_TO_EXTERNAL_REFERENCE": "Ga naar bron",
"BLOCKED": "Deze user story is geblokkeerd",
"PREVIOUS": "Vorige user story",
"NEXT": "volgende user story",
"TITLE_DELETE_ACTION": "Verwijder user story",
"LIGHTBOX_TITLE_BLOKING_US": "User story blokkeren",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} taken afgewerkt",
"ASSIGN": "User story toewijzen",
"NOT_ESTIMATED": "Niet ingeschat",
"TOTAL_US_POINTS": "Totaal US punten",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "More info",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Edit link",
"CLOSE": "Close",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Eisen team",
"CLIENT_REQUIREMENT": "Requirement van de klant",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Opmerking verwijderd door {{user}} op {{date}}",
"DELETED_INFO": "Comment deleted by {{user}}",
"TITLE": "Reacties",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Recent first",
"COMMENT": "Reageer",
"EDIT_COMMENT": "Edit comment",
"EDITED_COMMENT": "Edited:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Type hier nieuw commentaar",
"SHOW_DELETED": "Toon verwijderd commentaar",
"HIDE_DELETED": "Verberg verwijderde opmerkingen",
"DELETE": "Delete comment",
"RESTORE": "Opmerking herstellen"
"RESTORE": "Opmerking herstellen",
"HISTORY": {
"TITLE": "Activiteit"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Toon activiteit",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Toon vorige items ({{showMore}} meer)",
"TITLE": "Activiteit",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "verwijderd",
"ADDED": "toegevoegd",
"US_POINTS": "US punten ({{name}})",
"NEW_ATTACHMENT": "Nieuwe bijlage",
"DELETED_ATTACHMENT": "verwijderd bijlage",
"UPDATED_ATTACHMENT": "bijlage {{filename}} bijgewerkt",
"DELETED_CUSTOM_ATTRIBUTE": "eigen attribuut verwijderen",
"TAGS_ADDED": "tags added:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "new attachment:",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "{size, plural, one{één verandering} other{# veranderingen}} gemaakt",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Eisen team",
"CLIENT_REQUIREMENT": "Requirement van de klant",
"BLOCKED": "Geblokkeerd",
"VALUES": {
"YES": "ja",
"NO": "nee",
@ -1052,12 +1198,14 @@
"TAGS": "tags",
"ATTACHMENTS": "bijlagen",
"IS_DEPRECATED": "is verourderd",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "volgorde",
"BACKLOG_ORDER": "backlog volgorde",
"SPRINT_ORDER": "sprint volgorde",
"KANBAN_ORDER": "kanban volgorde",
"TASKBOARD_ORDER": "taakbord volgorde",
"US_ORDER": "us volgorde"
"US_ORDER": "us volgorde",
"COLOR": "kleur"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "gesloten<br/>taken",
"IOCAINE_DOSES": "iocaine<br />dosissen",
"SHOW_STATISTICS_TITLE": "Toon statistieken",
"TOGGLE_BAKLOG_GRAPH": "Toon/Verstop burndown grafiek"
"TOGGLE_BAKLOG_GRAPH": "Toon/Verstop burndown grafiek",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "project<br/>punten",
@ -1122,9 +1271,7 @@
"TITLE": "Filters",
"REMOVE": "Filters verwijderd",
"HIDE": "Filters verbergen",
"SHOW": "Toon filters",
"FILTER_CATEGORY_STATUS": "Status",
"FILTER_CATEGORY_TAGS": "Tags"
"SHOW": "Toon filters"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Taak {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{taskStatus }}. Omschrijving: {{taskDescription}}",
"SECTION_NAME": "Taak details",
"SECTION_NAME": "Taak",
"LINK_TASKBOARD": "Taakbord",
"TITLE_LINK_TASKBOARD": "Ga naar het taakbord",
"PLACEHOLDER_SUBJECT": "Type het nieuwe onderwerp voor de taak",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Deze taak werd aangemaakt vanaf",
"TITLE_LINK_GO_ORIGIN": "Ga naar user story",
"BLOCKED": "Deze taak is geblokkeerd",
"PREVIOUS": "vorige taak",
"NEXT": "volgende taak",
"TITLE_DELETE_ACTION": "Verwijder taak",
"LIGHTBOX_TITLE_BLOKING_TASK": "Blokkerende taak",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Issues - {{projectName}}",
"PAGE_DESCRIPTION": "Het issue lijst overzicht van het project {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Issues",
"SECTION_NAME": "Issue details",
"SECTION_NAME": "Issue",
"ACTION_NEW_ISSUE": "+ nieuw probleem",
"ACTION_PROMOTE_TO_US": "Promoveer tot User Story",
"PLACEHOLDER_FILTER_NAME": "Geef de filternaam in en druk op enter",
"PROMOTED": "Dit issue is gepromoveerd tot US:",
"EXTERNAL_REFERENCE": "Dit issue is aangemaakt vanaf",
"GO_TO_EXTERNAL_REFERENCE": "Ga naar bron",
"BLOCKED": "Dit issue is geblokkeerd",
"TITLE_PREVIOUS_ISSUE": "vorig issue",
"TITLE_NEXT_ISSUE": "volgend issue",
"ACTION_DELETE": "Verwijderd issue",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Blokkerend issue",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Bevorder dit issue tot een nieuwe user story",
"MESSAGE": "Weet je zeker dat je een nieuw US van dit issue wilt maken?"
},
"FILTERS": {
"TITLE": "Filters",
"INPUT_SEARCH_PLACEHOLDER": "Onderwerp of ref.",
"TITLE_ACTION_SEARCH": "Zoek",
"ACTION_SAVE_CUSTOM_FILTER": "Als eigen filter opslaan",
"BREADCRUMB": "Filters",
"TITLE_BREADCRUMB": "Filters",
"CATEGORIES": {
"TYPE": "Type",
"STATUS": "Status",
"SEVERITY": "Ernst",
"PRIORITIES": "Prioriteiten",
"TAGS": "Tags",
"ASSIGNED_TO": "Toegewezen aan",
"CREATED_BY": "Aangemaakt door",
"CUSTOM_FILTERS": "Eigen filters"
},
"CONFIRM_DELETE": {
"TITLE": "Verwijder eigen filter",
"MESSAGE": "de eigen filter '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Type",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Zoek - {{projectName}}",
"PAGE_DESCRIPTION": "Zoek op alles, user stories, issues, taken, wiki pagina's, in het project {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "User Stories",
"FILTER_ISSUES": "Issues",
"FILTER_TASKS": "Taken",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Verwijderd wiki pagina",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Links",
"ACTION_ADD_LINK": "Link toevoegen"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "keer <br/>bewerkt",
"LAST_EDIT": "laatst <br />bewerkt",
"LAST_MODIFICATION": "laatste wijziging"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Aangemaakt",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} heeft de nieuwe taak {{obj_name}} aangemakt in {{project_name}} die hoort bij de US {{us_name}}",
"WIKI_CREATED": "{{username}} heeft een nieuwe Wiki-pagina aangemaakt {{obj_name}} in {{project_name}}",
"MILESTONE_CREATED": "{{username}} heeft een nieuwe sprint aangemaakt {{obj_name}} in {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} heeft een nieuw project aangemaakt {{project_name}}",
"MILESTONE_UPDATED": "{{username}} heeft de sprint {{obj_name}} bijgewerkt",
"US_UPDATED": "{{username}} heeft de eigenschap \"{{field_name}}\" van de US {{obj_name}} bijgewerkt",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} heeft de eigenschap \"{{field_name}}\" van de taak {{obj_name}} die behoort tot de US {{us_name}} bijgewerkt",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} heeft de eigenschap \"{{field_name}}\" van de taak {{obj_name}} die behoort tot de US {{us_name}} gewijzigd naar {{new_value}}",
"WIKI_UPDATED": "{{username}} heeft de wiki pagina {{obj_name}} bijgewerkt",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} heeft gereageerd op de US {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} heeft gereageerd op het issue {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} heeft gereageerd op de taak {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} heeft een nieuw lid",
"US_ADDED_MILESTONE": "{{username}} heeft de US {{obj_name}} toegevoegd aan {{sprint_name}}",
"US_MOVED": "{{username}} heeft de user story {{obj_name}} verplaatst",

View File

@ -19,11 +19,11 @@
"TAG_LINE": "Twoje zwinne, wolne, otwartoźródłowe narzędzie do zarządzania projektem",
"TAG_LINE_2": "Pokochaj swój projekt!",
"BLOCK": "Blokuj",
"BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied",
"BLOCK_TITLE": "Zablokuj to np. jeżeli posiada zależności, które nie mogą być zrealizowane",
"BLOCKED": "Zablokowane",
"UNBLOCK": "Odblokuj",
"UNBLOCK_TITLE": "Unblock this item",
"BLOCKED_NOTE": "Why is this blocked?",
"UNBLOCK_TITLE": "Odblokuj",
"BLOCKED_NOTE": "Dlaczego jest zabokowane?",
"BLOCKED_REASON": "Wyjaśnij powód",
"CREATED_BY": "Utworzone przez {{fullDisplayName}}",
"FROM": "od",
@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Jedna pozycja na wiersz...",
"NEW_BULK": "Nowe zbiorcze dodawanie",
"RELATED_TASKS": "Zadania pokrewne",
"PREVIOUS": "Previous",
"NEXT": "Następny",
"LOGOUT": "Wyloguj",
"EXTERNAL_USER": "zewnętrzny użytkownik",
"GENERIC_ERROR": "Umpa Lumpa mówi {{error}}.",
@ -45,6 +47,11 @@
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Nieprawidłowa wartość",
"TYPE_EMAIL": "Podaj prawidłowy adres email.",
@ -115,8 +122,9 @@
"USER_STORY": "Historyjka użytkownika",
"TASK": "Zadania",
"ISSUE": "Zgłoszenie",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Otaguj mnie!...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Usuń tag",
"ADD": "Dodaj tag"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "filtry",
"TITLE": "Filtry",
"INPUT_PLACEHOLDER": "Temat lub odniesienie",
"TITLE_ACTION_FILTER_BUTTON": "szukaj",
"BREADCRUMB_TITLE": "wróć do kategorii",
"BREADCRUMB_FILTERS": "Filtry",
"BREADCRUMB_STATUS": "status"
"INPUT_SEARCH_PLACEHOLDER": "Temat lub referencja",
"TITLE_ACTION_SEARCH": "Szukaj",
"ACTION_SAVE_CUSTOM_FILTER": "zapisz jako filtr niestandardowy",
"PLACEHOLDER_FILTER_NAME": "Wpisz nazwę filtru i kliknij enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Typ",
"STATUS": "Statusy",
"SEVERITY": "Ważność",
"PRIORITIES": "Priorytety",
"TAGS": "Tagi",
"ASSIGNED_TO": "Przypisane do",
"CREATED_BY": "Stworzona przez",
"CUSTOM_FILTERS": "Filtry niestandardowe",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Usuń filtr niestandardowy",
"MESSAGE": "filtr niestandardowy '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Nagłówek pierwszego poziomu",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Podgląd",
"EDIT_BUTTON": "Edycja",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Składnia Markdown pomoc"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprinty",
"VIEW_SPRINTS": "Przeglądaj Sprinty",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Przeglądaj historyjki użytkownika",
"ADD_USER_STORIES": "Dodawaj historyjki użytkownika",
"MODIFY_USER_STORIES": "Modyfikuj historyjki użytkownika",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Usuwaj historyjki użytkownika"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Przeglądaj zadania",
"ADD_TASKS": "Dodawaj zadania",
"MODIFY_TASKS": "Modyfikuj zadania",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Usuwaj zadania"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Przeglądaj zgłoszenia",
"ADD_ISSUES": "Dodawaj zgłoszenia",
"MODIFY_ISSUES": "Modyfikuj zgłoszenia",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Usuwaj zgłoszenia"
},
"WIKI": {
@ -359,13 +396,48 @@
"HOME": {
"PAGE_TITLE": "Strona główna - Taiga",
"PAGE_DESCRIPTION": "Główna strona Taiga, z Twoimi głównymi projektami i wszystkimi przypisanymi Tobie i obserwowanymi historyjkami użytkownika, zadaniami i zgłoszeniami.",
"EMPTY_WORKING_ON": "<strong>It feels empty, doesn't it?</strong> Start working with Taiga and you'll see here the stories, tasks and issues you are working on.",
"EMPTY_WORKING_ON": "<strong>Trochę pusto, nieprawdaż?</strong> Zacznij pracować z Taiga a tutaj pojawią się historie, zadania i zgłoszenia nad którymi pracujesz.",
"EMPTY_WATCHING": "<strong>Follow</strong> User Stories, Tasks, Issues in your projects and be notified about its changes :)",
"EMPTY_PROJECT_LIST": "Nie masz jeszcze żadnych projektów",
"WORKING_ON_SECTION": "Pracujesz nad",
"WATCHING_SECTION": "Obserwujesz",
"DASHBOARD": "Projects Dashboard"
},
"EPICS": {
"TITLE": "EPIKI",
"SECTION_NAME": "Epics",
"EPIC": "EPIK",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+DODAJ EPIK",
"UNASSIGNED": "Nieprzypisane"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Głosy",
"NAME": "Nazwa",
"PROJECT": "Projekt",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Statusy",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "Nowy epik",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Wymaganie zespołu",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Zablokowane",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "Moje projekty - Taiga",
"PAGE_DESCRIPTION": "Lista wszystkich Twoich projektów, możesz zmieniać ich kolejność lub tworzyć nowe.",
@ -374,13 +446,13 @@
"ATTACHMENT": {
"SECTION_NAME": "załączniki",
"TITLE": "{{ plik }} załadowany dnia {{ data }}",
"LIST_VIEW_MODE": "List view mode",
"GALLERY_VIEW_MODE": "Gallery view mode",
"LIST_VIEW_MODE": "Tryb listy",
"GALLERY_VIEW_MODE": "Tryb galerii",
"DESCRIPTION": "Wpisz krótki opis",
"DEPRECATED": "(przestarzały)",
"DEPRECATED_FILE": "Przestarzałe?",
"ADD": "Dodaj nowy załącznik. {{maxFileSizeMsg}}",
"DROP": "Drop attachments here!",
"DROP": "Upuść załączniki tutaj",
"SHOW_DEPRECATED": "+ pokaż przestarzałe załączniki",
"HIDE_DEPRECATED": "- ukryj przestarzałe załączniki",
"COUNT_DEPRECATED": "({{ counter }} przestarzałych",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Edytuj wartość",
"TITLE_ACTION_DELETE_VALUE": "Usuń wartość"
"TITLE_ACTION_DELETE_VALUE": "Usuń wartość",
"TITLE_ACTION_DELETE_TAG": "Usuń tag"
},
"HELP": "Potrzebujesz pomocy? Sprawdź naszą stronę wsparcia!",
"PROJECT_DEFAULT_VALUES": {
@ -414,7 +487,7 @@
"PAGE_TITLE": "Członkostwa - {{projectName}}",
"ADD_BUTTON": "+ Nowy członek",
"ADD_BUTTON_TITLE": "Dodaj nowego członka",
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Unfortunately, this project has reached its limit of <strong>({{members}})</strong> allowed members.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Niestety ten projekt osiągnął maksymalną liczbę <strong>{{{members}}}</strong> dozwolonych użytkowników.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "This project has reached its limit of <strong>({{members}})</strong> allowed members. If you would like to increase that limit please contact the administrator."
},
"PROJECT_EXPORT": {
@ -435,12 +508,14 @@
"TITLE": "Moduły",
"ENABLE": "Włącz",
"DISABLE": "Wyłącz",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Dziennik",
"BACKLOG_DESCRIPTION": "Zarządzaj swoimi historyjkami użytkownika aby utrzymać zorganizowany widok i priorytety zadań",
"NUMBER_SPRINTS": "Expected number of sprints",
"NUMBER_SPRINTS_HELP": "0 for an undetermined number",
"NUMBER_SPRINTS_HELP": "0 dla nieokreślonej liczby",
"NUMBER_US_POINTS": "Expected total of story points",
"NUMBER_US_POINTS_HELP": "0 for an undetermined number",
"NUMBER_US_POINTS_HELP": "0 dla nieokreślonej liczby",
"KANBAN": "Kanban",
"KANBAN_DESCRIPTION": "Organizuj swój projekt przy użyciu metody lean.",
"ISSUES": "Zgłoszenia",
@ -464,8 +539,8 @@
"PROJECT_SLUG": "Szczegóły projektu",
"TAGS": "Tagi",
"DESCRIPTION": "Opis",
"RECRUITING": "Is this project looking for people?",
"RECRUITING_MESSAGE": "Who are you looking for?",
"RECRUITING": "Czy ten pojekt szuka uczestników?",
"RECRUITING_MESSAGE": "Kogo szukasz?",
"RECRUITING_PLACEHOLDER": "Define the profiles you are looking for",
"PUBLIC_PROJECT": "Projekt publiczny",
"PRIVATE_PROJECT": "Projekt prywatny",
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Zamierzasz zmienić link dostępu do danych CSV. Poprzedni link będzie niedostępny. Czy jesteś pewien?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "raporty historii użytkownika",
"SECTION_TITLE_TASK": "Raporty zadań",
"SECTION_TITLE_ISSUE": "raporty zgłoszeń",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Własne Pola",
"SUBTITLE": "Zdefiniuj własne dodatkowe pola dla historyjek użytkownika, zadań i zgłoszeń.",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Własne pola dla historyjek użytkownika",
"US_ADD": "Dodaj własne pole dla historyjek użytkownika",
"TASK_DESCRIPTION": "Własne pola dla zadań",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Status",
"SUBTITLE": "Zdefiniuj statusy dla historyjek użytkownika, zadań i zgłoszeń.",
"US_TITLE": "Statusy",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Statusy zadań",
"ISSUE_TITLE": "Statusy zgłoszeń"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Typy zgłoszeń",
"ACTION_ADD": "Dodaj nowy {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Tagi",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Currently there are no tags",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Dodaj tag",
"NEW_TAG": "New tag",
"MIXING_HELP_TEXT": "Select the tags that you want to merge",
"MIXING_MERGE": "Merge Tags",
"SELECTED": "Selected"
},
"ROLES": {
"PAGE_TITLE": "Role - {{projectName}}",
"WARNING_NO_ROLE": "Bez przydzielenia ról w projekcie nie ma możliwości oceniania historyjek użytkownika. Umpa Lumpy nie będą wiedziały komu wolno to zrobić :)",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "zaproszenie do {{e-mail}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Domyślna wartość dla selektora punktów",
"LABEL_US": "Domyślna wartość dla selektora statusu historyjek użytkownika",
"LABEL_TASK_STATUS": "Domyśla wartość dla selektora statusu zadań",
"LABEL_PRIORITY": "Domyślna wartość dla selektora priorytetu",
"LABEL_SEVERITY": "Domyślna wartość dla selektora ważności",
"LABEL_ISSUE_TYPE": "Domyślna wartość dla selektora typu zgłoszenia",
"LABEL_ISSUE_STATUS": "Domyślna wartość dla selektora statusu zgłoszenia"
"LABEL_ISSUE_STATUS": "Domyślna wartość dla selektora statusu zgłoszenia",
"LABEL_PRIORITY": "Domyślna wartość dla selektora priorytetu",
"LABEL_SEVERITY": "Domyślna wartość dla selektora ważności"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Wpisz nazwę nowego statusu"
@ -681,7 +776,8 @@
"PRIORITIES": "Priorytety",
"SEVERITIES": "Ważność",
"TYPES": "Typy",
"CUSTOM_FIELDS": "Niestandardowe pola"
"CUSTOM_FIELDS": "Niestandardowe pola",
"TAGS": "Tagi"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Profil projektu"
@ -727,9 +823,9 @@
"REPORT": "Zgłoś naruszenie",
"TABS": {
"ACTIVITY_TAB": "Oś czasu",
"ACTIVITY_TAB_TITLE": "Show all the activity of this user",
"ACTIVITY_TAB_TITLE": "Wyświetl całą aktywność użytkownika",
"PROJECTS_TAB": "Projekty",
"PROJECTS_TAB_TITLE": "List of all projects in which the user is a member",
"PROJECTS_TAB_TITLE": "Lista wszystkich projektów, do których należy użytkownik",
"LIKES_TAB": "Likes",
"LIKES_TAB_TITLE": "List all likes made by this user",
"VOTES_TAB": "Głosy",
@ -743,7 +839,7 @@
"PROFILE_SIDEBAR": {
"TITLE": "Twój profil",
"DESCRIPTION": "People can see everything you do and what you are working on. Add a nice bio to give an enhanced version of your information.",
"ADD_INFO": "Edit bio"
"ADD_INFO": "Edytuj biografię"
},
"PROFILE_FAVS": {
"FILTER_INPUT_PLACEHOLDER": "Type something...",
@ -751,13 +847,15 @@
"FILTER_TYPE_ALL_TITLE": "Show all",
"FILTER_TYPE_PROJECTS": "Projekty",
"FILTER_TYPE_PROJECT_TITLES": "Show only projects",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Stories",
"FILTER_TYPE_USER_STORIES_TITLES": "Show only user stories",
"FILTER_TYPE_TASKS": "Zadania",
"FILTER_TYPE_TASK_TITLES": "Show only tasks",
"FILTER_TYPE_ISSUES": "Zgłoszenia",
"FILTER_TYPE_ISSUES_TITLE": "Show only issues",
"EMPTY_TITLE": "It looks like there's nothing to show here."
"EMPTY_TITLE": "Wygląda na to, że nie ma niczego do wyświetlenia tutaj."
}
},
"PROJECT": {
@ -766,14 +864,14 @@
"SECTION_PROJECTS": "Projekty",
"HELP": "Ustal kolejność Twoich projektów tak, aby na górze znalazły się te najważniejsze. <br/> Pierwsze 10 projektów pojawi się w liście projektów na górnym pasku nawigacji.",
"PRIVATE": "Projekt prywatny",
"LOOKING_FOR_PEOPLE": "This project is looking for people",
"LOOKING_FOR_PEOPLE": "Ten projekt szuka uczestników",
"FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}",
"WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}",
"MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}",
"BLOCKED_PROJECT": {
"BLOCKED": "Blocked project",
"THIS_PROJECT_IS_BLOCKED": "This project is temporarily blocked",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "In order to unblock your projects, contact the administrator."
"BLOCKED": "Projekt zablokowany",
"THIS_PROJECT_IS_BLOCKED": "Ten projekt jest tymczasowo zablokowany",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "Aby odblokować swój projekt, skontaktuj się z administacją"
},
"STATS": {
"PROJECT": "projekt<br/> punkty",
@ -890,8 +988,8 @@
"SECTION_NAME": "Usuń konto z Taiga",
"CONFIRM": "Czy na pewno chcesz usunąć swoje konto z Taiga?",
"NEWSLETTER_LABEL_TEXT": "Nie chcę więcej otrzymywać waszego newslettera",
"CANCEL": "Back to settings",
"ACCEPT": "Delete account",
"CANCEL": "Powrót do ustawień",
"ACCEPT": "Usuń konto",
"BLOCK_PROJECT": "Note that all the projects you own projects will be <strong>blocked</strong> after you delete your account. If you do want a project blocked, transfer ownership to another member of each project prior to deleting your account.",
"SUBTITLE": "Sorry to see you go. We'll be here if you should ever consider us again! :("
},
@ -950,8 +1048,8 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Opcjonalne) Dodaj spersonalizowany tekst do zaproszenia. Napisz coś słodziachnego do nowego członka zespołu :)",
"PLACEHOLDER_TYPE_EMAIL": "Wpisz Email",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
@ -970,10 +1068,30 @@
"BUTTON": "Ask this project member to become the new project owner"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nowa historyjka użytkownika",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Temat",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - Historyjka użytkownika {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. Zakończono {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} z {{userStoryTotalTasks}} zadań). Punktów: {{userStoryPoints}}. Opis: {{userStoryDescription}}",
"SECTION_NAME": "Szczegóły historyjki użytkownika",
"SECTION_NAME": "Historyjka użytkownika",
"LINK_TASKBOARD": "Tablica zadań",
"TITLE_LINK_TASKBOARD": "Idź do listy zadań",
"TOTAL_POINTS": "total points",
@ -984,14 +1102,23 @@
"EXTERNAL_REFERENCE": "Ta historyjka została utworzona z",
"GO_TO_EXTERNAL_REFERENCE": "Idź do źródła",
"BLOCKED": "Ta historia użytkownika jest zablokowana",
"PREVIOUS": "poprzednia historia użytkownika",
"NEXT": "następna historia użytkownika",
"TITLE_DELETE_ACTION": "Usuń historyjkę użytkownika",
"LIGHTBOX_TITLE_BLOKING_US": "Blokuje nas",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} zadanie zakończone",
"ASSIGN": "Przypisz historyjkę użytkownika",
"NOT_ESTIMATED": "Nie oszacowane",
"TOTAL_US_POINTS": "Łącznie punktów",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "More info",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Edit link",
"CLOSE": "Close",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Wymaganie zespołu",
"CLIENT_REQUIREMENT": "Wymaganie klienta",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Komentarz usunięty przez {{user}} w dniu {{date}}",
"DELETED_INFO": "Komentarz usunięty przez {{user}}",
"TITLE": "Komentarze",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Order",
"OLDER_FIRST": "Older first",
"RECENT_FIRST": "Ostatnie najpierw",
"COMMENT": "Komentarz",
"EDIT_COMMENT": "Edytuj komentarz",
"EDITED_COMMENT": "Edytowano",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Tutaj wpisz nowy komentarz",
"SHOW_DELETED": "Pokaż usunięty komentarz",
"HIDE_DELETED": "Ukryj skasowane komentarze",
"DELETE": "Delete comment",
"RESTORE": "Przywróć komentarz"
"RESTORE": "Przywróć komentarz",
"HISTORY": {
"TITLE": "Aktywność"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Pokaż aktywność",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Pokaż poprzednie wpisy ({{showMore}} więcej)",
"TITLE": "Aktywność",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "usunięty",
"ADDED": "dodany",
"US_POINTS": "Punkty HU ({{name}})",
"TAGS_ADDED": "dodano klucz",
"TAGS_REMOVED": "usunięto tag:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "nowy załącznik",
"DELETED_ATTACHMENT": "Usunięty załącznik",
"UPDATED_ATTACHMENT": "Zaktualizowany załącznik {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "Usunięty niestandardowy atrybut",
"DELETED_ATTACHMENT": "deleted attachment:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Dokonano {size, plural, one{one change} other{# changes}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Wymaganie zespołu",
"CLIENT_REQUIREMENT": "Wymaganie klienta",
"BLOCKED": "Zablokowane",
"VALUES": {
"YES": "tak",
"NO": "nie",
@ -1052,12 +1198,14 @@
"TAGS": "tagi",
"ATTACHMENTS": "załączniki",
"IS_DEPRECATED": "jest przedawniony",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "kolejność",
"BACKLOG_ORDER": "kolejność backlogu",
"SPRINT_ORDER": "kolejność sprintów",
"KANBAN_ORDER": "kolejność kanban",
"TASKBOARD_ORDER": "kolejność tablicy zadań",
"US_ORDER": "Kolejność HU"
"US_ORDER": "Kolejność HU",
"COLOR": "kolor"
}
},
"BACKLOG": {
@ -1069,7 +1217,7 @@
"CUSTOMIZE_GRAPH_ADMIN": "Admin",
"CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin",
"MOVE_US_TO_CURRENT_SPRINT": "Przejdź do bieżącego sprintu",
"MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint",
"MOVE_US_TO_LATEST_SPRINT": "Przejdź do ostatniego sprintu",
"SHOW_FILTERS": "Pokaż filtry",
"SHOW_TAGS": "Pokaż tagi",
"EMPTY": "The backlog is empty!",
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "zamkniętych<br />zadań",
"IOCAINE_DOSES": "dawek<br />Iokainy",
"SHOW_STATISTICS_TITLE": "Pokaż statystyki",
"TOGGLE_BAKLOG_GRAPH": "Pokaż/Ukryj wykres spalania"
"TOGGLE_BAKLOG_GRAPH": "Pokaż/Ukryj wykres spalania",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "punktów w<br />projekcie",
@ -1122,9 +1271,7 @@
"TITLE": "Filtry",
"REMOVE": "Usuń filtry",
"HIDE": "Ukryj filtry",
"SHOW": "Pokaż filtry",
"FILTER_CATEGORY_STATUS": "Status",
"FILTER_CATEGORY_TAGS": "Tagi"
"SHOW": "Pokaż filtry"
},
"SPRINTS": {
"TITLE": "SPRINTY",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Zadanie {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{taskStatus }}. Opis: {{taskDescription}}",
"SECTION_NAME": "Szczegóły zadania",
"SECTION_NAME": "Zadania",
"LINK_TASKBOARD": "Tablica zadań",
"TITLE_LINK_TASKBOARD": "Idź do listy zadań",
"PLACEHOLDER_SUBJECT": "Wpisz temat zadania",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Źródło tego zadania to",
"TITLE_LINK_GO_ORIGIN": "Idź do historyjki użytkownika",
"BLOCKED": "To zadanie jest zablokowane",
"PREVIOUS": "poprzednie zadanie",
"NEXT": "następne zadanie",
"TITLE_DELETE_ACTION": "Usuń zadanie",
"LIGHTBOX_TITLE_BLOKING_TASK": "Blokowanie zadania",
"FIELDS": {
@ -1228,16 +1373,13 @@
"PAGE_TITLE": "Zgłoszenia - {{projectName}}",
"PAGE_DESCRIPTION": "Lista zgłoszeń w projekcie {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Zgłoszenia",
"SECTION_NAME": "Szczegóły zgłoszenia",
"SECTION_NAME": "Zgłoszenie",
"ACTION_NEW_ISSUE": "+ NOWE ZGŁOSZENIE",
"ACTION_PROMOTE_TO_US": "Awansuj na historyjkę użytkownika",
"PLACEHOLDER_FILTER_NAME": "Wpisz nazwę filtru i kliknij enter",
"PROMOTED": "To zgłoszenie zostało wypromowane na HU:",
"EXTERNAL_REFERENCE": "Źródło zgłoszenia",
"GO_TO_EXTERNAL_REFERENCE": "Idź do źródła",
"BLOCKED": "To zgłoszenie jest zablokowane",
"TITLE_PREVIOUS_ISSUE": "poprzednie zgłoszenie",
"TITLE_NEXT_ISSUE": "następne zgłoszenie",
"ACTION_DELETE": "Usuń zgłoszenie",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Blokowanie zgłoszenia",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Awansuj to zgłoszenie na historyjkę użytkownika",
"MESSAGE": "Jesteś pewny, że chcesz wypromować to zgłoszenie na historyjkę użytkownika?"
},
"FILTERS": {
"TITLE": "Filtry",
"INPUT_SEARCH_PLACEHOLDER": "Temat lub referencja",
"TITLE_ACTION_SEARCH": "Szukaj",
"ACTION_SAVE_CUSTOM_FILTER": "zapisz jako filtr niestandardowy",
"BREADCRUMB": "Filtry",
"TITLE_BREADCRUMB": "Filtry",
"CATEGORIES": {
"TYPE": "Typy",
"STATUS": "Statusy",
"SEVERITY": "Ważność",
"PRIORITIES": "Priorytety",
"TAGS": "Tagi",
"ASSIGNED_TO": "Przypisane do",
"CREATED_BY": "Stworzona przez",
"CUSTOM_FILTERS": "Filtry niestandardowe"
},
"CONFIRM_DELETE": {
"TITLE": "Usuń filtr niestandardowy",
"MESSAGE": "filtr niestandardowy '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Typ",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Szukaj - {{projectName}}",
"PAGE_DESCRIPTION": "Możesz przeszukiwać wszystko, historyjki użytkownika, zgłoszenia, zadania oraz strony Wiki w projekcie {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "Historyjki użytkownika",
"FILTER_ISSUES": "Zgłoszenia",
"FILTER_TASKS": "Zadania",
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Usuń tą stronę Wiki",
"DELETE_LINK_TITLE": "Delete Wiki link",
"NAVIGATION": {
"SECTION_NAME": "Linki",
"ACTION_ADD_LINK": "Dodaj link"
"HOME": "Main Page",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "razy <br />edytowano",
"LAST_EDIT": "ostatnia <br />edycja",
"LAST_MODIFICATION": "ostatnia modyfikacja"
},
"SECTION_PAGES_LIST": "All pages",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Utworzone",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "Użytkownik {{username}} utworzył nowe zadanie {{obj_name}} w projekcie {{project_name}} należące do HU {{us_name}}",
"WIKI_CREATED": "Użytkownik {{username}} utworzył nową stronę Wiki {{obj_name}} w projekcie {{project_name}}",
"MILESTONE_CREATED": "Użytkownik {{username}} utworzył nowy sprint {{obj_name}} w projekcie {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "Użytkownik {{username}} utworzył projekt {{project_name}}",
"MILESTONE_UPDATED": "Użytkownik {{username}} zaktualizował sprint {{obj_name}}",
"US_UPDATED": "Użytkownik {{username}} zaktualizował atrybut {{field_name}} historyjki użytkownika {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "Użytkownik {{username}} zaktualizował atrybut {{field_name}} zadania {{obj_name}} należącego do HU {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "Użytkownik {{username}} zaktualizował atrybut {{field_name}} zadania {{obj_name}} należącego do HU {{us_name}} na {{new_value}}",
"WIKI_UPDATED": "Użytkownik {{username}} zaktualizował stronę Wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "Użytkownik {{username}} skomentował historyjkę użytkownika {{obj_name}}",
"NEW_COMMENT_ISSUE": "Użytkownik {{username}} skomentował zgłoszenie {{obj_name}}",
"NEW_COMMENT_TASK": "Użytkownik {{username}} skomentował zadanie {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "Projekt {{project_name}} ma nowego członka",
"US_ADDED_MILESTONE": "Użytkownik{{username}} dodał HU {{obj_name}} do {{sprint_name}}",
"US_MOVED": "{{username}} przeniósł historyjkę użytkownika {{obj_name}}",
@ -1553,7 +1691,7 @@
"MOST_LIKED": "Most liked",
"MOST_LIKED_EMPTY": "There are no LIKED projects yet",
"VIEW_MORE": "View more",
"RECRUITING": "This project is looking for people",
"RECRUITING": "Ten projekt szuka ludzi",
"FEATURED": "Featured Projects",
"EMPTY": "There are no projects to show with this search criteria.<br> Try again!",
"FILTERS": {

View File

@ -35,6 +35,8 @@
"ONE_ITEM_LINE": "Um item por linha...",
"NEW_BULK": "Nova inserção em lote",
"RELATED_TASKS": "Tarefas relacionadas",
"PREVIOUS": "Anterior",
"NEXT": "Próximo",
"LOGOUT": "Sair",
"EXTERNAL_USER": "um usuário externo",
"GENERIC_ERROR": "Um Oompa Loompas disse {{error}}.",
@ -43,8 +45,13 @@
"TEAM_REQUIREMENT": "Requisito de time é um requisito que deve existir no projeto, mas que não deve ter nenhum custo para o cliente.",
"OWNER": "Dono do Projeto",
"CAPSLOCK_WARNING": "Seja cuidadoso! Você está escrevendo em letras maiúsculas e esse campo é case sensitive, ou seja, trata com distinção as letras maiúsculas das minúsculas.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Você tem certeza que quer fechar o modo de edição?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Lembre-se que se você fechar o modo de edição sem salvar, todas as mudanças serão perdidas",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Este valor parece ser inválido.",
"TYPE_EMAIL": "Este valor deve ser um e-mail válido.",
@ -69,7 +76,7 @@
"MAX_CHECK": "Você deve selecionar %s escolhas ou menos.",
"RANGE_CHECK": "Você deve selecionar entre %s e %s escolhas.",
"EQUAL_TO": "Esse valor deveria ser o mesmo.",
"LINEWIDTH": "One or more lines is perhaps too long. Try to keep under %s characters.",
"LINEWIDTH": "Talvez uma ou mais linhas estejam muito grandes. Tente usar menos de %s caracteres.",
"PIKADAY": "Formato de data inválido, por favor, use DD MMM YYYY (exemplo: 23 Mar 1984)"
},
"PICKERDATE": {
@ -115,8 +122,9 @@
"USER_STORY": "História de usuário",
"TASK": "Tarefa",
"ISSUE": "Problema",
"EPIC": "Épico",
"TAGS": {
"PLACEHOLDER": "Adicionar tags...",
"PLACEHOLDER": "Enter tag",
"DELETE": "Apagar tag",
"ADD": "Adicionar tag"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "filtros",
"TITLE": "Filtros",
"INPUT_PLACEHOLDER": "Assunto ou referência",
"TITLE_ACTION_FILTER_BUTTON": "procurar",
"BREADCRUMB_TITLE": "voltar para categorias",
"BREADCRUMB_FILTERS": "Filtros",
"BREADCRUMB_STATUS": "status"
"INPUT_SEARCH_PLACEHOLDER": "Assunto ou ref",
"TITLE_ACTION_SEARCH": "Procurar",
"ACTION_SAVE_CUSTOM_FILTER": "salve como filtro personalizado",
"PLACEHOLDER_FILTER_NAME": "Digite o nome do filtro e pressione Enter",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Status",
"SEVERITY": "Gravidade",
"PRIORITIES": "Prioridades",
"TAGS": "Tags",
"ASSIGNED_TO": "Atribuído a",
"CREATED_BY": "Criado por",
"CUSTOM_FILTERS": "Filtros personalizados",
"EPIC": "Épico"
},
"CONFIRM_DELETE": {
"TITLE": "Apagar filtro personalizado",
"MESSAGE": "O filtro personalizado '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Primeira caixa de cabeçalho",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Pré Visualizar",
"EDIT_BUTTON": "Editar",
"ATTACH_FILE_HELP": "Anexe arquivos arrastando e soltando na área de texto acima.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Ajuda de sintaxe markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Épicos",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Sprints",
"VIEW_SPRINTS": "Ver sprints",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Ver histórias de usuários",
"ADD_USER_STORIES": "Adicionar histórias de usuários",
"MODIFY_USER_STORIES": "Modificar histórias de usuários",
"COMMENT_USER_STORIES": "Comentar histórias de usuário",
"DELETE_USER_STORIES": "Apagar histórias de usuários"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Ver tarefas",
"ADD_TASKS": "Adicionar uma nova Tarefa",
"MODIFY_TASKS": "Modificar tarefa",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Apagar tarefas"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Ver problemas",
"ADD_ISSUES": "Adicionar problemas",
"MODIFY_ISSUES": "Modificar problemas",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Apagar problemas"
},
"WIKI": {
@ -366,6 +403,41 @@
"WATCHING_SECTION": "Observando",
"DASHBOARD": "Painel de Projetos"
},
"EPICS": {
"TITLE": "ÉPICOS",
"SECTION_NAME": "Épicos",
"EPIC": "ÉPICO",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADICIONAR ÉPICO",
"UNASSIGNED": "Não-atribuído"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Saiba mais sobre épicos"
},
"TABLE": {
"VOTES": "Votos",
"NAME": "Nome",
"PROJECT": "Projeto",
"SPRINT": "Sprint",
"ASSIGNED_TO": "Assigned",
"STATUS": "Status",
"PROGRESS": "Progresso",
"VIEW_OPTIONS": "Ver opções"
},
"CREATE": {
"TITLE": "Novo Épico",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Bloqueado",
"BLOCKED_NOTE_PLACEHOLDER": "Por que esse épico está bloqueado?",
"CREATE_EPIC": "Criar épico"
}
},
"PROJECTS": {
"PAGE_TITLE": "Meus projetos - Taiga",
"PAGE_DESCRIPTION": "Uma lista com todos os seus projetos, você pode reorganizá-los ou criar um novo.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Editar valor",
"TITLE_ACTION_DELETE_VALUE": "Apagar valor"
"TITLE_ACTION_DELETE_VALUE": "Apagar valor",
"TITLE_ACTION_DELETE_TAG": "Apagar tag"
},
"HELP": "Você precisa de ajuda? Verifique nossa pagina de suporte!",
"PROJECT_DEFAULT_VALUES": {
@ -414,8 +487,8 @@
"PAGE_TITLE": "Filiados - {{projectName}}",
"ADD_BUTTON": "+ Novo Membro",
"ADD_BUTTON_TITLE": "Adicionar novo membro",
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Unfortunately, this project has reached its limit of <strong>({{members}})</strong> allowed members.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "This project has reached its limit of <strong>({{members}})</strong> allowed members. If you would like to increase that limit please contact the administrator."
"LIMIT_USERS_WARNING_MESSAGE_FOR_ADMIN": "Infelizmente, este projeto atingiu o número máximo de <strong>({{membros}})</strong> membros permitidos.",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Este projeto atingiu o limite de <strong>({{members}})</strong> membros permitidos. Se você deseja aumentar este limite entre em contato com o administrador."
},
"PROJECT_EXPORT": {
"TITLE": "Exportar",
@ -435,6 +508,8 @@
"TITLE": "Modulos",
"ENABLE": "Habilitar",
"DISABLE": "Desabilitar",
"EPICS": "Épicos",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Backlog",
"BACKLOG_DESCRIPTION": "Gerencie suas histórias de usuários para manter uma visualização organizada de trabalhos futuros e priorizados.",
"NUMBER_SPRINTS": "Número de sprints esperadas",
@ -450,7 +525,7 @@
"MEETUP": "Reunião",
"MEETUP_DESCRIPTION": "Selecione seu sistema de videoconferência.",
"SELECT_VIDEOCONFERENCE": "Selecione um sistema de videoconferência",
"SALT_CHAT_ROOM": "Add a prefix to the chatroom name",
"SALT_CHAT_ROOM": "Adicionar um prefixo ao nome da sala de chat",
"JITSI_CHAT_ROOM": "Jitsi",
"APPEARIN_CHAT_ROOM": "AppearIn",
"TALKY_CHAT_ROOM": "Talky",
@ -474,18 +549,18 @@
"LOGO_HELP": "A imagem deve ser na escala de 80x80px.",
"CHANGE_LOGO": "Alterar logo",
"ACTION_USE_DEFAULT_LOGO": "Usar imagem padrão",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects allowed by your current plan",
"MAX_PRIVATE_PROJECTS_MEMBERS": "The maximum number of members for private projects has been exceeded",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects allowed by your current plan",
"MAX_PUBLIC_PROJECTS_MEMBERS": "The project exceeds your maximum number of members for public projects",
"MAX_PRIVATE_PROJECTS": "Você atingiu o número máximo de projetos privados permitidos para seu plano atual.",
"MAX_PRIVATE_PROJECTS_MEMBERS": "O número máximo de membros para projetos privados foi excedido.",
"MAX_PUBLIC_PROJECTS": "Infelizmente você atingiu o número máximo de projetos público permitidos para seu plano atual",
"MAX_PUBLIC_PROJECTS_MEMBERS": "Este projeto atingiu o seu limite atual de membros para projetos públicos",
"PROJECT_OWNER": "Dono do projeto",
"REQUEST_OWNERSHIP": "Solicitar propriedade",
"REQUEST_OWNERSHIP_CONFIRMATION_TITLE": "Gostaria de se tornar o novo dono do projeto?",
"REQUEST_OWNERSHIP_DESC": "Solicitar ao atual dono de projeto {{name}} a transferência da propriedade deste projeto para você.",
"REQUEST_OWNERSHIP_BUTTON": "Solicitação",
"REQUEST_OWNERSHIP_SUCCESS": "We'll notify the project owner",
"CHANGE_OWNER": "Change owner",
"CHANGE_OWNER_SUCCESS_TITLE": "Ok, your request has been sent!",
"REQUEST_OWNERSHIP_SUCCESS": "Vamos notificar o dono do projeto",
"CHANGE_OWNER": "Mudar dono",
"CHANGE_OWNER_SUCCESS_TITLE": "Ok, sua requisição foi enviada!",
"CHANGE_OWNER_SUCCESS_DESC": "We will notify you by email if the project ownership request is accepted or declined"
},
"REPORTS": {
@ -497,6 +572,7 @@
"REGENERATE_SUBTITLE": "Você está prestes a alterar a url de acesso a dados do CSV. A URL anterior será desabilitada. Você está certo disso?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "Relatórios de histórias de usuários",
"SECTION_TITLE_TASK": "relatórios de tarefas",
"SECTION_TITLE_ISSUE": "relatórios de problemas",
@ -509,6 +585,8 @@
"CUSTOM_FIELDS": {
"TITLE": "Campos Personalizados",
"SUBTITLE": "Especificar campos personalizados para histórias de usuários, tarefas e problemas",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Campos personalizados das histórias de usuários",
"US_ADD": "Adicionar campo personalizado nas histórias de usuários",
"TASK_DESCRIPTION": "Campos personalizados das Tarefas",
@ -534,7 +612,7 @@
"PROJECT_VALUES_PRIORITIES": {
"TITLE": "Prioridades",
"SUBTITLE": "Especifique as prioridades que seus problemas terão",
"ISSUE_TITLE": "Prioridades do problema",
"ISSUE_TITLE": "Severidade dos apontamentos",
"ACTION_ADD": "Adicionar nova prioridade"
},
"PROJECT_VALUES_SEVERITIES": {
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Status",
"SUBTITLE": "Especifique os status pelos quais suas histórias de usuários, tarefas e problemas passarão",
"US_TITLE": "Estados das Histórias de Usuários",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Estados da Tarefa",
"ISSUE_TITLE": "Estados do problema"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Tipos de problemas",
"ACTION_ADD": "Adicionar novo {{objName}}"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Tags",
"SUBTITLE": "View and edit the color of your tags",
"EMPTY": "Atualmente não há tags",
"EMPTY_SEARCH": "Parece que nada foi encontrado com os critérios de sua pesquisa.",
"ACTION_ADD": "Adicionar tag",
"NEW_TAG": "Nova tag",
"MIXING_HELP_TEXT": "Selecione as tags que você quer mesclar",
"MIXING_MERGE": "Mesclar Tags",
"SELECTED": "Selecionado"
},
"ROLES": {
"PAGE_TITLE": "Funções - {{projectName}}",
"WARNING_NO_ROLE": "Seja cuidadoso, nenhuma função em seu projeto será capaz de estimar o valor dos pontos para as histórias de usuários",
@ -565,7 +655,7 @@
"COUNT_MEMBERS": "{{ role.members_count }} membros com a mesma função",
"TITLE_DELETE_ROLE": "Apagar Função",
"REPLACEMENT_ROLE": "Todos os usuários com essa função serão movidos para",
"WARNING_DELETE_ROLE": "Be careful! All role estimations will be removed",
"WARNING_DELETE_ROLE": "Cuidado! Todas as estimativas de papéis serão removidas",
"ERROR_DELETE_ALL": "Você não pode apagar todos os valores",
"EXTERNAL_USER": "Usuário externo"
},
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Webhooks - {{projectName}}",
"SECTION_NAME": "Webhooks",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "o convite para {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Valores padrões para o seletor de pontos",
"LABEL_US": "Valor padrão para seletor de status da História de Usuário",
"LABEL_TASK_STATUS": "Valor padrão para seletor de status de tarefa",
"LABEL_PRIORITY": "Valor padão para seletor de prioridade",
"LABEL_SEVERITY": "Valor padrão para seletor de gravidade",
"LABEL_ISSUE_TYPE": "Valor padrão para seletor de tipo de problema ",
"LABEL_ISSUE_STATUS": "Valor padrão para seletor de status de problema"
"LABEL_ISSUE_STATUS": "Valor padrão para seletor de status de problema",
"LABEL_PRIORITY": "Valor padão para seletor de prioridade",
"LABEL_SEVERITY": "Valor padrão para seletor de gravidade"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Digite um nome para o novo status"
@ -681,7 +776,8 @@
"PRIORITIES": "Prioridades",
"SEVERITIES": "Gravidades",
"TYPES": "Tipos",
"CUSTOM_FIELDS": "Campos personalizados"
"CUSTOM_FIELDS": "Campos personalizados",
"TAGS": "Tags"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Perfil do Projeto"
@ -695,21 +791,21 @@
"TITLE": "Serviços"
},
"PROJECT_TRANSFER": {
"DO_YOU_ACCEPT_PROJECT_OWNERNSHIP": "Would you like to become the new project owner?",
"PRIVATE": "Private",
"ACCEPTED_PROJECT_OWNERNSHIP": "Congratulations! You're now the new project owner.",
"REJECTED_PROJECT_OWNERNSHIP": "OK. We'll contact the current project owner",
"DO_YOU_ACCEPT_PROJECT_OWNERNSHIP": "Você gostaria de se tornar o novo dono do projeto?",
"PRIVATE": "Privado",
"ACCEPTED_PROJECT_OWNERNSHIP": "Parabéns! Você é o proprietário do projeto agora.",
"REJECTED_PROJECT_OWNERNSHIP": "OK. Entraremos em contato com o atual dono do projeto.",
"ACCEPT": "Aceitar",
"REJECT": "Reject",
"REJECT": "Rejeitar",
"PROPOSE_OWNERSHIP": "<strong>{{owner}}</strong>, the current owner of the project <strong>{{project}}</strong> has asked that you become the new project owner.",
"ADD_COMMENT": "Would you like to add a comment for the project owner?",
"UNLIMITED_PROJECTS": "Unlimited",
"UNLIMITED_PROJECTS": "Ilimitado",
"OWNER_MESSAGE": {
"PRIVATE": "Please remember that you can own up to <strong>{{maxProjects}}</strong> private projects. You currently own <strong>{{currentProjects}}</strong> private projects",
"PUBLIC": "Please remember that you can own up to <strong>{{maxProjects}}</strong> public projects. You currently own <strong>{{currentProjects}}</strong> public projects"
},
"CANT_BE_OWNED": "At the moment you cannot become an owner of a project of this type. If you would like to become the owner of this project, please contact the administrator so they change your account settings to enable project ownership.",
"CHANGE_MY_PLAN": "Change my plan"
"CHANGE_MY_PLAN": "Mudar meu plano"
}
},
"USER": {
@ -751,12 +847,14 @@
"FILTER_TYPE_ALL_TITLE": "Mostrar tudo",
"FILTER_TYPE_PROJECTS": "Projetos",
"FILTER_TYPE_PROJECT_TITLES": "Mostrar somente projetos",
"FILTER_TYPE_EPICS": "Épicos",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Histórias",
"FILTER_TYPE_USER_STORIES_TITLES": "Mostrar apenas histórias de usuários",
"FILTER_TYPE_TASKS": "Tarefas",
"FILTER_TYPE_TASK_TITLES": "Mostrar apenas tarefas",
"FILTER_TYPE_ISSUES": "Problemas",
"FILTER_TYPE_ISSUES_TITLE": "mostrar apenas problemas",
"FILTER_TYPE_ISSUES_TITLE": "mostrar apenas apontamentos",
"EMPTY_TITLE": "Parece que não há nada para exibir aqui."
}
},
@ -772,8 +870,8 @@
"MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}",
"BLOCKED_PROJECT": {
"BLOCKED": "Projeto bloqueado",
"THIS_PROJECT_IS_BLOCKED": "This project is temporarily blocked",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "In order to unblock your projects, contact the administrator."
"THIS_PROJECT_IS_BLOCKED": "Este projeto está temporariamente bloqueado",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "Para desbloquear seus projetos, contate o administrador."
},
"STATS": {
"PROJECT": "projetos<br/> pontos",
@ -838,7 +936,7 @@
"ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) é muito pesado para nossos Oompa Loompas, tente algo menor que ({{maxFileSize}})",
"SYNC_SUCCESS": "Seu projeto foi importado com sucesso",
"PROJECT_RESTRICTIONS": {
"PROJECT_MEMBERS_DESC": "The project you are trying to import has {{members}} members, unfortunately, your current plan allows for a maximum of {{max_memberships}} members per project. If you would like to increase that limit please contact the administrator.",
"PROJECT_MEMBERS_DESC": "O projeto que você está tentando importar tem {{members}} membros e infelizmente seu plano atual tem um limite máximo de {{max_memberships}} membros por projeto. Se você deseja aumentar este limite entre em contato com o administrador.",
"PRIVATE_PROJECTS_SPACE": {
"TITLE": "Unfortunately, your current plan does not allow for additional private projects",
"DESC": "The project you are trying to import is private. Unfortunately, your current plan does not allow for additional private projects."
@ -855,7 +953,7 @@
},
"PRIVATE_PROJECTS_SPACE_MEMBERS": {
"TITLE": "Unfortunately your current plan doesn't allow additional private projects or an increase of more than {{max_memberships}} members per private project",
"DESC": "The project that you are trying to import is private and has {{members}} members."
"DESC": "O projeto que você está tentando importar é privado e tem {{members}} membros."
},
"PUBLIC_PROJECTS_SPACE_MEMBERS": {
"TITLE": "Unfortunately your current plan doesn't allow additional public projects or an increase of more than {{max_memberships}} members per public project",
@ -892,7 +990,7 @@
"NEWSLETTER_LABEL_TEXT": "Eu não quero receber mais os informativos",
"CANCEL": "Voltar para configurações",
"ACCEPT": "Remover conta",
"BLOCK_PROJECT": "Note that all the projects you own projects will be <strong>blocked</strong> after you delete your account. If you do want a project blocked, transfer ownership to another member of each project prior to deleting your account.",
"BLOCK_PROJECT": "Note que todos os projetos em que você é o dono serão <strong>bloqueados</strong> depois que você apagar sua conta. Se você quer um projeto bloqueado, transfira a posse para outro membro de cada projeto antes de apagar sua conta.",
"SUBTITLE": "É uma pena vê-lo partir. Estaremos aqui se você algum dia considerar-nos novamente! :("
},
"DELETE_PROJECT": {
@ -950,18 +1048,18 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Opcional) Adicione uma mensagem de texto ao convite. Diga algo animador para os novos membros ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Digite um Email",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
"TITLE": "Infelizmente, este projeto não pode ficar sem um dono",
"CURRENT_USER_OWNER": {
"DESC": "You are the current owner of this project. Before leaving, please transfer ownership to someone else.",
"BUTTON": "Change the project owner"
"DESC": "Você é o dono atual deste projeto. Antes de sair, por favor transfira o projeto para outra pessoa.",
"BUTTON": "Mude o dono do projeto"
},
"OTHER_USER_OWNER": {
"DESC": "Unfortunately, you can't delete a member who is also the current project owner. First, please assign a new project owner.",
"BUTTON": "Request project owner change"
"DESC": "Infelizmnete, você não pode apagar um membro que também é o dono de um projeto. Primeiro designe um novo proprietário para o projeto.",
"BUTTON": "Solicite a mudança do dono do projeto"
}
},
"CHANGE_OWNER": {
@ -970,28 +1068,57 @@
"BUTTON": "Pedir a este membro do projeto para se tornar o novo dono do projeto"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Épico",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Nova história de usuário",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Assunto",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - História de Usuário {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estado: {{userStoryStatus }}. Completos {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} de {{userStoryTotalTasks}} tarefas encerradas). Pontos: {{userStoryPoints}}. Descrição: {{userStoryDescription}}",
"SECTION_NAME": "Detalhes da História de Usuário",
"SECTION_NAME": "História de usuário",
"LINK_TASKBOARD": "Quadro de Tarefas",
"TITLE_LINK_TASKBOARD": "Ir para o quadro de tarefas",
"TOTAL_POINTS": "total de pontos",
"ADD": "+ Adicionar uma nova História de Usuário",
"ADD_BULK": "Adicionar Histórias de Usuários em lote",
"PROMOTED": "Esta história de usuário foi promovida do problema:",
"TITLE_LINK_GO_TO_ISSUE": "Ir para problema",
"PROMOTED": "Esta História de Usuário foi criada a partir do Problema:",
"TITLE_LINK_GO_TO_ISSUE": "Adicionar comentários aos apontamentos",
"EXTERNAL_REFERENCE": "Esta História de Usuário foi criada de",
"GO_TO_EXTERNAL_REFERENCE": "Ir para a origem",
"BLOCKED": "Esta história de usuário está bloqueada",
"PREVIOUS": "história de usuário anterior",
"NEXT": "proxima história de usuário",
"TITLE_DELETE_ACTION": "Apagar história de usuário",
"LIGHTBOX_TITLE_BLOKING_US": "História de usuário bloqueadora",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tarefas completas",
"ASSIGN": "Atribuir História de Usuário",
"NOT_ESTIMATED": "Não estimado",
"TOTAL_US_POINTS": "Total de pontos de histórias",
"TRIBE": {
"PUBLISH": "Publicar como Gig no Taiga Tribe",
"PUBLISH_INFO": "Mais informações",
"PUBLISH_TITLE": "Mais informações sobre como publicar na Tribo Taiga",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Editar link",
"CLOSE": "Fechar",
"SYNCHRONIZE_LINK": "sincronizar com a Tribo Taiga",
"PUBLISH_MORE_INFO_TITLE": "Você precisa de alguém para esta tarefa?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Requisitos da Equipe",
"CLIENT_REQUIREMENT": "Requisitos do Cliente",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Comentário apagado por {{user}} em {{date}}",
"DELETED_INFO": "Comentário apagado por {{user}}",
"TITLE": "Comentários",
"COMMENTS_COUNT": "{{comments}} comentários",
"ORDER": "Ordenação",
"OLDER_FIRST": "Antigos primeiro",
"RECENT_FIRST": "Recentes primeiro",
"COMMENT": "Comentário",
"EDIT_COMMENT": "Editar comentário",
"EDITED_COMMENT": "Editado:",
"SHOW_HISTORY": "Ver histórico",
"TYPE_NEW_COMMENT": "Escreva um novo comentário aqui",
"SHOW_DELETED": "Mostrar comentários apagados",
"HIDE_DELETED": "Esconder comentário apagado",
"DELETE": "Apagar comentário",
"RESTORE": "Restaurar comentário"
"RESTORE": "Restaurar comentário",
"HISTORY": {
"TITLE": "Atividade"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Exibir atividade",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Mostrar entradas anteriores (mais {{showMore}})",
"TITLE": "Atividade",
"ACTIVITIES_COUNT": "{{activities}} atividades",
"REMOVED": "removido",
"ADDED": "adicionado",
"US_POINTS": "pontos de história ({{name}})",
"NEW_ATTACHMENT": "novo anexo",
"DELETED_ATTACHMENT": "apagar anexo",
"UPDATED_ATTACHMENT": "anexo atualizado {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "atributo personalizado apagado",
"TAGS_ADDED": "tags adicionadas:",
"TAGS_REMOVED": "tags removidas:",
"US_POINTS": "{{role}} pontos",
"NEW_ATTACHMENT": "novo anexo:",
"DELETED_ATTACHMENT": "anexo apagado:",
"UPDATED_ATTACHMENT": "anexo atualizado ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "atributo personalizado criado",
"UPDATED_CUSTOM_ATTRIBUTE": "atributo personalizado atualizado",
"SIZE_CHANGE": "Feito {size, plural, one{one change} other{# changes}}",
"BECAME_DEPRECATED": "foi depreciado",
"BECAME_UNDEPRECATED": "foi depreciado",
"TEAM_REQUIREMENT": "Requisitos da Equipe",
"CLIENT_REQUIREMENT": "Requisitos do Cliente",
"BLOCKED": "Bloqueado",
"VALUES": {
"YES": "sim",
"NO": "não",
@ -1052,12 +1198,14 @@
"TAGS": "tags",
"ATTACHMENTS": "anexos",
"IS_DEPRECATED": "está obsoleto",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "ordem",
"BACKLOG_ORDER": "requisição do backlog",
"SPRINT_ORDER": "ordem de sprint ",
"KANBAN_ORDER": "pedido kanban",
"TASKBOARD_ORDER": "Ordem de quadro de tarefa",
"US_ORDER": "ordem da história de usuário"
"US_ORDER": "ordem da história de usuário",
"COLOR": "cor"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "tarefas<br />fechadas",
"IOCAINE_DOSES": "iocaine<br />doses",
"SHOW_STATISTICS_TITLE": "Mostrar estatísticas",
"TOGGLE_BAKLOG_GRAPH": "Mostrar/Esconder gráfico de burndown"
"TOGGLE_BAKLOG_GRAPH": "Mostrar/Esconder gráfico de burndown",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "pontos do<br />projeto",
@ -1122,9 +1271,7 @@
"TITLE": "Filtros",
"REMOVE": "Remover filtros",
"HIDE": "Esconder Filtros",
"SHOW": "Mostrar Filtros",
"FILTER_CATEGORY_STATUS": "Status",
"FILTER_CATEGORY_TAGS": "Tags"
"SHOW": "Mostrar Filtros"
},
"SPRINTS": {
"TITLE": "SPRINTS",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Tarefa {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Estado: {{taskStatus }}. Descrição: {{taskDescription}}",
"SECTION_NAME": "Detalhes da Tarefa",
"SECTION_NAME": "Tarefa",
"LINK_TASKBOARD": "Quadro de Tarefas",
"TITLE_LINK_TASKBOARD": "Ir para o quadro de tarefas",
"PLACEHOLDER_SUBJECT": "Digite um novo titulo para tarefa",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Essa tarefa foi criada a partir de",
"TITLE_LINK_GO_ORIGIN": "Ir para história de usuário",
"BLOCKED": "Esta tarefa está bloqueada",
"PREVIOUS": "tarefa anterior",
"NEXT": "nova tarefa",
"TITLE_DELETE_ACTION": "Apagar Tarefa",
"LIGHTBOX_TITLE_BLOKING_TASK": "Tarefa bloqueadora",
"FIELDS": {
@ -1225,19 +1370,16 @@
"SUCCESS": "Nossos Oompa Loompas atualizaram seu email"
},
"ISSUES": {
"PAGE_TITLE": "Problemas - {{projectName}}",
"PAGE_TITLE": "Apontamentos - {{projectName}}",
"PAGE_DESCRIPTION": "O painel de problemas do projeto {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Problemas",
"SECTION_NAME": "Detalhes do problema",
"LIST_SECTION_NAME": "Tipos de problemas",
"SECTION_NAME": "Problema",
"ACTION_NEW_ISSUE": "+ NOVO PROBLEMA",
"ACTION_PROMOTE_TO_US": "Promover para História de Usuário",
"PLACEHOLDER_FILTER_NAME": "Digite o nome do filtro e pressione Enter",
"PROMOTED": "Esse problema foi promovido para história de usuário",
"EXTERNAL_REFERENCE": "Esse problema foi criado a partir de",
"GO_TO_EXTERNAL_REFERENCE": "Ir para a origem",
"BLOCKED": "Esse problema está bloqueado",
"TITLE_PREVIOUS_ISSUE": "problema anterior",
"TITLE_NEXT_ISSUE": "próximo problema",
"BLOCKED": "Esse apontamento está bloqueado",
"ACTION_DELETE": "Problema apagado",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Problema que está bloqueando",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Promover esse problema para nova história de usuário",
"MESSAGE": "Você tem certeza que deseja criar uma nova História de Usuário a partir desse problema?"
},
"FILTERS": {
"TITLE": "Filtros",
"INPUT_SEARCH_PLACEHOLDER": "Assunto ou ref",
"TITLE_ACTION_SEARCH": "Procurar",
"ACTION_SAVE_CUSTOM_FILTER": "salve como filtro personalizado",
"BREADCRUMB": "Filtros",
"TITLE_BREADCRUMB": "Filtros",
"CATEGORIES": {
"TYPE": "Tipo",
"STATUS": "Status",
"SEVERITY": "Gravidade",
"PRIORITIES": "Prioridades",
"TAGS": "Tags",
"ASSIGNED_TO": "Atribuído a",
"CREATED_BY": "Criado por",
"CUSTOM_FILTERS": "Filtros personalizados"
},
"CONFIRM_DELETE": {
"TITLE": "Apagar filtro personalizado",
"MESSAGE": "O filtro personalizado '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Tipo",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Buscar - {{projectName}}",
"PAGE_DESCRIPTION": "Busque qualquer coisa, histórias de usuários, problemas, tarefas, ou páginas da wiki, no projeto {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Épicos",
"FILTER_USER_STORIES": "Histórias de Usuários",
"FILTER_ISSUES": "Problemas",
"FILTER_TASKS": "Tarefas",
@ -1332,7 +1453,7 @@
"APP_TITLE": "EQUIPE - {{projectName}}",
"PLACEHOLDER_INPUT_SEARCH": "Procurar pelo nome completo...",
"COLUMN_MR_WOLF": "Sr. Wolf",
"EXPLANATION_COLUMN_MR_WOLF": "Problemas fechados",
"EXPLANATION_COLUMN_MR_WOLF": "Adicionar apontamentos",
"COLUMN_IOCAINE": "Bebedor de Iocaine",
"EXPLANATION_COLUMN_IOCAINE": "Doses de Iocaine ingeridas",
"COLUMN_CERVANTES": "Pero Vaz de Caminha",
@ -1397,7 +1518,7 @@
"WIZARD": {
"SECTION_TITLE_CREATE_PROJECT": "Criar Projeto",
"CREATE_PROJECT_TEXT": "Novo em folha. Tão excitante!",
"CHOOSE_TEMPLATE": "Which template fits your project best?",
"CHOOSE_TEMPLATE": "Qual template se encaixa melhor no seu projeto?",
"CHOOSE_TEMPLATE_TITLE": "Mais informações sobre templates de projeto",
"CHOOSE_TEMPLATE_INFO": "Mais informações",
"PROJECT_DETAILS": "Detalhes do Projeto",
@ -1405,7 +1526,7 @@
"PRIVATE_PROJECT": "Projeto Privado",
"CREATE_PROJECT": "Criar projeto",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects",
"MAX_PUBLIC_PROJECTS": "Infelizmente, você atingiu o número máximo de projetos públicos",
"CHANGE_PLANS": "change plans"
},
"WIKI": {
@ -1417,13 +1538,24 @@
"DELETE_LIGHTBOX_TITLE": "Apagar página Wiki",
"DELETE_LINK_TITLE": "Remover link de Wiki",
"NAVIGATION": {
"SECTION_NAME": "Links",
"ACTION_ADD_LINK": "Adicionar link"
"HOME": "Página principal",
"SECTION_NAME": "BOOKMARKS",
"ACTION_ADD_LINK": "Add bookmark",
"ALL_PAGES": "Todas as páginas wiki"
},
"SUMMARY": {
"TIMES_EDITED": "vezes<br />editadas",
"LAST_EDIT": "última<br />edição",
"LAST_MODIFICATION": "ultima modificação"
},
"SECTION_PAGES_LIST": "Todas as páginas",
"PAGES_LIST_COLUMNS": {
"TITLE": "Título",
"EDITIONS": "Edições",
"CREATED": "Criado",
"MODIFIED": "Modificado",
"CREATOR": "Criador",
"LAST_MODIFIER": "Último modificador"
}
},
"HINTS": {
@ -1447,21 +1579,27 @@
"TASK_CREATED_WITH_US": "{{username}} criou nova tarefa {{obj_name}} em {{project_name}} que pertence a História de Usuário {{us_name}}",
"WIKI_CREATED": "{{username}} criou uma página wiki {{obj_name}} em {{project_name}}",
"MILESTONE_CREATED": "{{username}} criou uma nova sprint {{obj_name}} em {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} criou o projeto {{project_name}}",
"MILESTONE_UPDATED": "{{username}} atualizou a sprint {{obj_name}}",
"US_UPDATED": "{{username}} atualizou o atributo \"{{field_name}}\" da História de Usuário {{obj_name}}",
"US_UPDATED_WITH_NEW_VALUE": "{{username}} atualizou o trabalho \"{{field_name}}\" da US {{obj_name}} para {{new_value}}",
"US_UPDATED_POINTS": "{{username}} atualizou pontos de '{{role_name}}' da História de Usuário {{obj_name}} para {{new_value}}",
"US_UPDATED_WITH_NEW_VALUE": "{{username}} atualizou o atributo \"{{field_name}}\" da História de Usuário {{obj_name}} para {{new_value}}",
"US_UPDATED_POINTS": "{{username}} atualizou os pontos de '{{role_name}}' da História de Usuário {{obj_name}} para {{new_value}}",
"ISSUE_UPDATED": "{{username}} atualizou o atributo \"{{field_name}}\" do problema {{obj_name}}",
"ISSUE_UPDATED_WITH_NEW_VALUE": "{{username}} atualizou o atributo \"{{field_name}}\" do problema {{obj_name}} para {{new_value}}",
"TASK_UPDATED": "{{username}} atualizou o atributo \"{{field_name}}\" da tarefa {{obj_name}} para {{new_value}}",
"TASK_UPDATED_WITH_NEW_VALUE": "{{username}} Atualizou o atributo \"{{field_name}}\" da tarefa {{obj_name}} para {{new_value}}",
"TASK_UPDATED_WITH_NEW_VALUE": "{{username}} atualizou o atributo \"{{field_name}}\" da tarefa {{obj_name}} para {{new_value}}",
"TASK_UPDATED_WITH_US": "{{username}} atualizou o atributo \"{{field_name}}\" da tarefa {{obj_name}} que pertence à História de Usuário {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} atualizou o atributo \"{{field_name}}\" da tarefa {{obj_name}} que pertence à História de Usuário {{us_name}} para {{new_value}}",
"WIKI_UPDATED": "{{username}} atualizou a página wiki {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} comentou na História de Usuário {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} comentou no problema {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} comentou na tarefa {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "{{project_name}} tem um membro novo",
"US_ADDED_MILESTONE": "{{username}} adicionou a História de Usuário {{obj_name}} a {{sprint_name}}",
"US_MOVED": "{{username}} moveu a História de Usuário {{obj_name}}",
@ -1499,7 +1637,7 @@
},
"STEP3": {
"TITLE": "Observando",
"TEXT1": "And right here you will find the ones in your projects that you want to know about.",
"TEXT1": "E aqui você vai encontrar os do seu projeto que você escolheu seguir.",
"TEXT2": "Você já está trabalhando com Taiga ;)"
},
"STEP4": {
@ -1555,7 +1693,7 @@
"VIEW_MORE": "Visualizar mais",
"RECRUITING": "Este projeto esta procurando colaboradores",
"FEATURED": "Featured Projects",
"EMPTY": "There are no projects to show with this search criteria.<br> Try again!",
"EMPTY": "Não há projetos para exibir sob esse critério de pesquisa.<br> Tente novamente!",
"FILTERS": {
"ALL": "Tudo",
"KANBAN": "Kanban",

View File

@ -35,19 +35,26 @@
"ONE_ITEM_LINE": "Один объект на строку...",
"NEW_BULK": "Добавить пакетно",
"RELATED_TASKS": "Связанные задачи",
"PREVIOUS": "Предыдущий",
"NEXT": "Следующий",
"LOGOUT": "Выйти",
"EXTERNAL_USER": "внешний пользователь",
"GENERIC_ERROR": "Один из Умпа-Лумп говорит {{error}}.",
"IOCAINE_TEXT": "Чувствуете, что задание берет верх над вами? Дайте другим знать об этом, нажав на \"Иокаин\", когда редактируете задание. Возможно стать неуязвимым к этому (выдуманному) смертельном яду, потребляя небольшие количества время от времени, так же как возможно стать лучше в том, что вы делаете, временами беря на себя дополнительные препятствия!",
"CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project",
"TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client",
"OWNER": "Project Owner",
"OWNER": "Владелец проекта",
"CAPSLOCK_WARNING": "Be careful! You are using capital letters in an input field that is case sensitive.",
"CONFIRM_CLOSE_EDIT_MODE_TITLE": "Are you sure you want to close the edit mode?",
"CONFIRM_CLOSE_EDIT_MODE_MESSAGE": "Remember that if you close the edit mode without saving all the changes will be lost",
"RELATED_USERSTORIES": "Related user stories",
"CARD": {
"ASSIGN_TO": "Assign To",
"EDIT": "Edit card"
},
"FORM_ERRORS": {
"DEFAULT_MESSAGE": "Кажется, это значение некорректно.",
"TYPE_EMAIL": "Это значение должно быть корректным email-адресом.",
"TYPE_EMAIL": "Значение должно быть корректной электронной почтой.",
"TYPE_URL": "Это значение должно быть корректным URL-адресом.",
"TYPE_URLSTRICT": "Это значение должно быть корректным URL-адресом.",
"TYPE_NUMBER": "Это значение должно быть правильным числом",
@ -115,8 +122,9 @@
"USER_STORY": "Пользовательская история",
"TASK": "Задача",
"ISSUE": "Запрос",
"EPIC": "Epic",
"TAGS": {
"PLACEHOLDER": "Назначьте тэг",
"PLACEHOLDER": "Enter tag",
"DELETE": "Удалить тэг",
"ADD": "Добавить тэг"
},
@ -193,12 +201,29 @@
"CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?"
},
"FILTERS": {
"TITLE": "фильтры",
"TITLE": "Фильтры",
"INPUT_PLACEHOLDER": "Название ссылки",
"TITLE_ACTION_FILTER_BUTTON": "поиск",
"BREADCRUMB_TITLE": "назад к категориям",
"BREADCRUMB_FILTERS": "Фильтры",
"BREADCRUMB_STATUS": "cтатус"
"INPUT_SEARCH_PLACEHOLDER": "Название ссылки",
"TITLE_ACTION_SEARCH": "Поиск",
"ACTION_SAVE_CUSTOM_FILTER": "сохранить как специальный фильтр",
"PLACEHOLDER_FILTER_NAME": "Введите название фильтра и нажмите \"ввод\"",
"APPLIED_FILTERS_NUM": "filters applied",
"CATEGORIES": {
"TYPE": "Тип",
"STATUS": "Статус",
"SEVERITY": "Важность",
"PRIORITIES": "Приоритеты",
"TAGS": "Тэги",
"ASSIGNED_TO": "Назначено",
"CREATED_BY": "Создано",
"CUSTOM_FILTERS": "Собственные фильтры",
"EPIC": "Epic"
},
"CONFIRM_DELETE": {
"TITLE": "Удалить фильтр",
"MESSAGE": "специальный фильтр '{{customFilterName}}'"
}
},
"WYSIWYG": {
"H1_BUTTON": "Заголовок первого уровня",
@ -228,9 +253,18 @@
"PREVIEW_BUTTON": "Предварительный просмотр",
"EDIT_BUTTON": "Редактировать",
"ATTACH_FILE_HELP": "Attach files by dragging & dropping on the textarea above.",
"ATTACH_FILE_HELP_SAVE_FIRST": "Save first before if you want to attach files by dragging & dropping on the textarea above.",
"MARKDOWN_HELP": "Помощь по синтаксису Markdown"
},
"PERMISIONS_CATEGORIES": {
"EPICS": {
"NAME": "Epics",
"VIEW_EPICS": "View epics",
"ADD_EPICS": "Add epics",
"MODIFY_EPICS": "Modify epics",
"COMMENT_EPICS": "Comment epics",
"DELETE_EPICS": "Delete epics"
},
"SPRINTS": {
"NAME": "Спринты",
"VIEW_SPRINTS": "Посмотреть спринты",
@ -243,6 +277,7 @@
"VIEW_USER_STORIES": "Просматривать пользовательские истории",
"ADD_USER_STORIES": "Добавлять пользовательские истории",
"MODIFY_USER_STORIES": "Изменять пользовательские истории",
"COMMENT_USER_STORIES": "Comment user stories",
"DELETE_USER_STORIES": "Удалять пользовательские истории"
},
"TASKS": {
@ -250,6 +285,7 @@
"VIEW_TASKS": "Просмотреть задачи",
"ADD_TASKS": "Добавить задачи",
"MODIFY_TASKS": "Редактировать задачи",
"COMMENT_TASKS": "Comment tasks",
"DELETE_TASKS": "Удалить задачи"
},
"ISSUES": {
@ -257,6 +293,7 @@
"VIEW_ISSUES": "Посмотреть запросы",
"ADD_ISSUES": "Добавить запросы",
"MODIFY_ISSUES": "Изменить запросы",
"COMMENT_ISSUES": "Comment issues",
"DELETE_ISSUES": "Удалить запросы"
},
"WIKI": {
@ -287,7 +324,7 @@
},
"LOGIN_COMMON": {
"HEADER": "У меня уже есть логин в Taiga",
"PLACEHOLDER_AUTH_NAME": "Логин или email (с учетом регистра)",
"PLACEHOLDER_AUTH_NAME": "Имя пользователя или электронная почта (с учётом регистра)",
"LINK_FORGOT_PASSWORD": "Забыли?",
"TITLE_LINK_FORGOT_PASSWORD": "Вы забыли свой пароль?",
"ACTION_ENTER": "Ввод",
@ -295,7 +332,7 @@
"PLACEHOLDER_AUTH_PASSWORD": "Пароль (чувствителен к регистру)"
},
"LOGIN_FORM": {
"ERROR_AUTH_INCORRECT": "Oompa Loompas считает, что Ваш логин, email или пароль неправильный.",
"ERROR_AUTH_INCORRECT": "Oompa Loompas считает, что ваше имя пользователя, электронная почта или пароль неправильные.",
"SUCCESS": "Oompa Loompas счастлив, добро пожаловать в Тайгу!"
},
"REGISTER": {
@ -306,7 +343,7 @@
"TITLE": "Зарегистрируйте аккаунт Taiga (бесплатно)",
"PLACEHOLDER_NAME": "Выберите имя учётной записи (с учётом регистра)",
"PLACEHOLDER_FULL_NAME": "Введите Ваше полное имя",
"PLACEHOLDER_EMAIL": "Ваш email",
"PLACEHOLDER_EMAIL": "Ваша электронная почта",
"PLACEHOLDER_PASSWORD": "Задайте новый пароль (с учетом регистра)",
"ACTION_SIGN_UP": "Зарегистрироваться",
"TITLE_LINK_LOGIN": "Войти",
@ -318,12 +355,12 @@
},
"FORGOT_PASSWORD_FORM": {
"TITLE": "Упс, забыли пароль?",
"SUBTITLE": "Введите Ваш логин или email для получения нового пароля",
"PLACEHOLDER_FIELD": "Логин или e-mail",
"SUBTITLE": "Введите ваше имя пользователя или электронную почту, чтобы получить новый пароль",
"PLACEHOLDER_FIELD": "Имя пользователя или электронная почта",
"ACTION_RESET_PASSWORD": "Сбросить пароль",
"LINK_CANCEL": "Не, давай назад, думаю я вспомню.",
"SUCCESS_TITLE": "Check your inbox!",
"SUCCESS_TEXT": "We sent you an email with the instructions to set a new password",
"SUCCESS_TITLE": "Проверьте Вашу почту!",
"SUCCESS_TEXT": "Мы отправили вам письмо с инструкциями по восстановлению пароля",
"ERROR": "Умпа-Лумпы говорят, что вы еще не зарегистрированы."
},
"CHANGE_PASSWORD": {
@ -359,13 +396,48 @@
"HOME": {
"PAGE_TITLE": "Домашняя страница - Taiga",
"PAGE_DESCRIPTION": "Главная страница Taiga с вашими основными проектами, назначенными и отслеживаемыми ПИ, задачами и запросами",
"EMPTY_WORKING_ON": "<strong>It feels empty, doesn't it?</strong> Start working with Taiga and you'll see here the stories, tasks and issues you are working on.",
"EMPTY_WORKING_ON": "<strong>Тут кажется пусто, не правда ли?</strong> Начинайте использовать Taiga и вы увидите здесь истории, задачи и запросы над которыми вы сейчас работаете.",
"EMPTY_WATCHING": "<strong>Следите</strong> за пользовательскими историями, задачами, запросами в ваших проектах и будьте уведомлены об изменениях :)",
"EMPTY_PROJECT_LIST": "У Вас пока нет проектов",
"WORKING_ON_SECTION": "Работает над",
"WATCHING_SECTION": "Отслеживаемые",
"DASHBOARD": "Рабочий стол с проектами"
},
"EPICS": {
"TITLE": "EPICS",
"SECTION_NAME": "Epics",
"EPIC": "EPIC",
"PAGE_TITLE": "Epics - {{projectName}}",
"PAGE_DESCRIPTION": "The epics list of the project {{projectName}}: {{projectDescription}}",
"DASHBOARD": {
"ADD": "+ ADD EPIC",
"UNASSIGNED": "Не назначено"
},
"EMPTY": {
"TITLE": "It looks like there aren't any epics yet",
"EXPLANATION": "Epics are items at a higher level that encompass user stories.<br />Epics are at the top of the hierarchy and can be used to group user stories together.",
"HELP": "Learn more about epics"
},
"TABLE": {
"VOTES": "Голоса",
"NAME": "Имя",
"PROJECT": "Проект",
"SPRINT": "Спринт",
"ASSIGNED_TO": "Assigned",
"STATUS": "Статус",
"PROGRESS": "Progress",
"VIEW_OPTIONS": "View options"
},
"CREATE": {
"TITLE": "New Epic",
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this epic",
"TEAM_REQUIREMENT": "Team requirement",
"CLIENT_REQUIREMENT": "Client requirement",
"BLOCKED": "Заблокирован",
"BLOCKED_NOTE_PLACEHOLDER": "Why is this epic blocked?",
"CREATE_EPIC": "Create epic"
}
},
"PROJECTS": {
"PAGE_TITLE": "Мои проекты",
"PAGE_DESCRIPTION": "Список Ваших проектов, отсортируйте их или создайте новый.",
@ -402,7 +474,8 @@
"ADMIN": {
"COMMON": {
"TITLE_ACTION_EDIT_VALUE": "Изменить значение",
"TITLE_ACTION_DELETE_VALUE": "Удалить значение"
"TITLE_ACTION_DELETE_VALUE": "Удалить значение",
"TITLE_ACTION_DELETE_TAG": "Удалить тэг"
},
"HELP": "Вам нужна помощь? Проверьте нашу страницу техподдержки!",
"PROJECT_DEFAULT_VALUES": {
@ -425,7 +498,7 @@
"LOADING_TITLE": "Мы создали ваш файл резервной копии",
"DUMP_READY": "Файл резервной копии готов!",
"LOADING_MESSAGE": "Пожалуйста, не закрывайте эту страницу",
"ASYNC_MESSAGE": "Мы отправим вам email когда будет готово.",
"ASYNC_MESSAGE": "Мы отправим вам письмо когда будет готово.",
"SYNC_MESSAGE": "Если загрузка не начинается самостоятельно, нажмите <a href='{{url}}' download title='Download the dump file'>здесь</a>.",
"ERROR": "У Oompa Loompas возникли проблемы при создании резервной копии. Повторите еще раз.",
"ERROR_BUSY": "Извините, Oompa Loompas очень загружен сейчас. Повторите через несколько минут.",
@ -435,9 +508,11 @@
"TITLE": "Модули",
"ENABLE": "Включить",
"DISABLE": "Выключить",
"EPICS": "Epics",
"EPICS_DESCRIPTION": "Visualize and manage the most strategic part of your project",
"BACKLOG": "Список задач",
"BACKLOG_DESCRIPTION": "Управляйте пользовательскими историями, чтобы поддерживать организованное видение важных и приоритетных задач.",
"NUMBER_SPRINTS": "Expected number of sprints",
"NUMBER_SPRINTS": "Ожидаемое количество спринтов",
"NUMBER_SPRINTS_HELP": "0 for an undetermined number",
"NUMBER_US_POINTS": "Expected total of story points",
"NUMBER_US_POINTS_HELP": "0 for an undetermined number",
@ -448,9 +523,9 @@
"WIKI": "Вики",
"WIKI_DESCRIPTION": "Добавляйте, изменяйте или удаляйте контент совместно с остальными. Это самое правильное место для документации вашего проекта.",
"MEETUP": "Созвониться",
"MEETUP_DESCRIPTION": "Choose your videoconference system.",
"MEETUP_DESCRIPTION": "Выберите Вашу систему видеоконференций",
"SELECT_VIDEOCONFERENCE": "Выберите систему видеоконференций",
"SALT_CHAT_ROOM": "Add a prefix to the chatroom name",
"SALT_CHAT_ROOM": "Добавить префикс к имени чата",
"JITSI_CHAT_ROOM": "Jitsi",
"APPEARIN_CHAT_ROOM": "AppearIn",
"TALKY_CHAT_ROOM": "Talky",
@ -472,20 +547,20 @@
"PRIVATE_OR_PUBLIC": "В чём разница между публичными и приватными проектами?",
"DELETE": "Удалить проект",
"LOGO_HELP": "Изображение будет отмасштабировано до 80x80px.",
"CHANGE_LOGO": "Change logo",
"CHANGE_LOGO": "Изменить лого",
"ACTION_USE_DEFAULT_LOGO": "Использовать картинку по умолчанию",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects allowed by your current plan",
"MAX_PRIVATE_PROJECTS_MEMBERS": "The maximum number of members for private projects has been exceeded",
"MAX_PRIVATE_PROJECTS": "Вы достигли максимального числа приватных проектов которое разрешено вашим планом.",
"MAX_PRIVATE_PROJECTS_MEMBERS": "Максимальное количество участников в приватном проекте достигло лимита",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects allowed by your current plan",
"MAX_PUBLIC_PROJECTS_MEMBERS": "The project exceeds your maximum number of members for public projects",
"PROJECT_OWNER": "Project owner",
"REQUEST_OWNERSHIP": "Request ownership",
"REQUEST_OWNERSHIP_CONFIRMATION_TITLE": "Do you want to become the new project owner?",
"PROJECT_OWNER": "Владелец проекта",
"REQUEST_OWNERSHIP": "Запрос владельца",
"REQUEST_OWNERSHIP_CONFIRMATION_TITLE": "Хотите стать новым владельцем проекта?",
"REQUEST_OWNERSHIP_DESC": "Request that current project owner {{name}} transfer ownership of this project to you.",
"REQUEST_OWNERSHIP_BUTTON": "Запрос",
"REQUEST_OWNERSHIP_SUCCESS": "We'll notify the project owner",
"CHANGE_OWNER": "Change owner",
"CHANGE_OWNER_SUCCESS_TITLE": "Ok, your request has been sent!",
"REQUEST_OWNERSHIP_SUCCESS": "Мы уведомим владельца проекта",
"CHANGE_OWNER": "Сменить владельца",
"CHANGE_OWNER_SUCCESS_TITLE": "ОК, ваш запрос был отправлен!",
"CHANGE_OWNER_SUCCESS_DESC": "We will notify you by email if the project ownership request is accepted or declined"
},
"REPORTS": {
@ -497,18 +572,21 @@
"REGENERATE_SUBTITLE": "Вы собираетесь изменить ссылку доступа к данным CSV. Прежний вариант ссылки перестанет работать. Вы уверены?"
},
"CSV": {
"SECTION_TITLE_EPIC": "epics reports",
"SECTION_TITLE_US": "Отчёты по пользовательским историям",
"SECTION_TITLE_TASK": "отчёты о задачах",
"SECTION_TITLE_ISSUE": "отчёты о запросах",
"DOWNLOAD": "Скачать CSV",
"URL_FIELD_PLACEHOLDER": "Упс, забыли пароль?",
"TITLE_REGENERATE_URL": " Сделать CSV ссылку ещё раз",
"TITLE_REGENERATE_URL": "Сделать CSV ссылку ещё раз",
"ACTION_GENERATE_URL": "Сгенерировать ссылку",
"ACTION_REGENERATE": "Создать заново"
},
"CUSTOM_FIELDS": {
"TITLE": "Пользовательские поля",
"SUBTITLE": "Укажите специальные поля для ваших пользовательских историй, задач и запросов",
"EPIC_DESCRIPTION": "Epics custom fields",
"EPIC_ADD": "Add a custom field in epics",
"US_DESCRIPTION": "Специальные поля для пользовательских историй",
"US_ADD": "Добавить специальное поле для пользовательских историй",
"TASK_DESCRIPTION": "Специальные поля задач",
@ -546,7 +624,8 @@
"PROJECT_VALUES_STATUS": {
"TITLE": "Статус",
"SUBTITLE": "Укажите, какие статусы будут принимать ваши пользовательские истории, задачи и запросы",
"US_TITLE": "Статусы ПИ",
"EPIC_TITLE": "Epic Statuses",
"US_TITLE": "User Story Statuses",
"TASK_TITLE": "Статус задач",
"ISSUE_TITLE": "Статусы запроса"
},
@ -556,6 +635,17 @@
"ISSUE_TITLE": "Типы запросов",
"ACTION_ADD": "Добавить новый"
},
"PROJECT_VALUES_TAGS": {
"TITLE": "Тэги",
"SUBTITLE": "Просмотреть и изменить цвет ваших тэгов",
"EMPTY": "В данный момент тэги отсутствуют",
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria",
"ACTION_ADD": "Добавить тэг",
"NEW_TAG": "Новый тэг",
"MIXING_HELP_TEXT": "Выберите тэги которые вы хотели бы объединить",
"MIXING_MERGE": "Объединить Тэги",
"SELECTED": "Выбранные"
},
"ROLES": {
"PAGE_TITLE": "Роли - {{projectName}}",
"WARNING_NO_ROLE": "Осторожнее: ни с какими ролями на вашем проекте участники не смогут оценить очки для пользовательских историй.",
@ -588,6 +678,10 @@
"SECTION_NAME": "Github",
"PAGE_TITLE": "Github - {{projectName}}"
},
"GOGS": {
"SECTION_NAME": "Gogs",
"PAGE_TITLE": "Gogs - {{projectName}}"
},
"WEBHOOKS": {
"PAGE_TITLE": "Веб-хуки - {{projectName}}",
"SECTION_NAME": "Веб-хуки",
@ -643,13 +737,14 @@
"DEFAULT_DELETE_MESSAGE": "приглашение на {{email}}"
},
"DEFAULT_VALUES": {
"LABEL_EPIC_STATUS": "Default value for epic status selector",
"LABEL_US_STATUS": "Default value for user story status selector",
"LABEL_POINTS": "Значения по умолчанию для выбора очков",
"LABEL_US": "Значение по умолчанию для статуса ПИ",
"LABEL_TASK_STATUS": "Значение по умолчанию для статуса задачи",
"LABEL_PRIORITY": "Значение по умолчанию для выбора приоритета",
"LABEL_SEVERITY": "Значение важности по умолчанию",
"LABEL_ISSUE_TYPE": "Значение по умолчанию для типа запроса",
"LABEL_ISSUE_STATUS": "Значение по умолчанию для статуса запроса"
"LABEL_ISSUE_STATUS": "Значение по умолчанию для статуса запроса",
"LABEL_PRIORITY": "Значение по умолчанию для выбора приоритета",
"LABEL_SEVERITY": "Значение важности по умолчанию"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Укажите название для нового статуса"
@ -681,7 +776,8 @@
"PRIORITIES": "Приоритет",
"SEVERITIES": "Степени важности",
"TYPES": "Типы",
"CUSTOM_FIELDS": "Собственные поля"
"CUSTOM_FIELDS": "Собственные поля",
"TAGS": "Тэги"
},
"SUBMENU_PROJECT_PROFILE": {
"TITLE": "Профиль проекта"
@ -697,8 +793,8 @@
"PROJECT_TRANSFER": {
"DO_YOU_ACCEPT_PROJECT_OWNERNSHIP": "Would you like to become the new project owner?",
"PRIVATE": "Private",
"ACCEPTED_PROJECT_OWNERNSHIP": "Congratulations! You're now the new project owner.",
"REJECTED_PROJECT_OWNERNSHIP": "OK. We'll contact the current project owner",
"ACCEPTED_PROJECT_OWNERNSHIP": "Поздравляем! Вы новый владелец проекта.",
"REJECTED_PROJECT_OWNERNSHIP": "Хорошо. Мы свяжемся с текущим владельцем проекта",
"ACCEPT": "Принимаю",
"REJECT": "Reject",
"PROPOSE_OWNERSHIP": "<strong>{{owner}}</strong>, the current owner of the project <strong>{{project}}</strong> has asked that you become the new project owner.",
@ -751,6 +847,8 @@
"FILTER_TYPE_ALL_TITLE": "Показать все",
"FILTER_TYPE_PROJECTS": "Проекты",
"FILTER_TYPE_PROJECT_TITLES": "Показать только проекты",
"FILTER_TYPE_EPICS": "Epics",
"FILTER_TYPE_EPIC_TITLES": "Show only epics",
"FILTER_TYPE_USER_STORIES": "Истории",
"FILTER_TYPE_USER_STORIES_TITLES": "Показывать только пользовательские истории",
"FILTER_TYPE_TASKS": "Задачи",
@ -771,8 +869,8 @@
"WATCHERS_COUNTER_TITLE": "{total, plural, one{один наблюдатель} other{# наблюдателя (-ей)}}",
"MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}",
"BLOCKED_PROJECT": {
"BLOCKED": "Blocked project",
"THIS_PROJECT_IS_BLOCKED": "This project is temporarily blocked",
"BLOCKED": "Заблокированный проект",
"THIS_PROJECT_IS_BLOCKED": "Этот проект временно заблокирован",
"TO_UNBLOCK_CONTACT_THE_ADMIN_STAFF": "In order to unblock your projects, contact the administrator."
},
"STATS": {
@ -890,8 +988,8 @@
"SECTION_NAME": "Удалить аккаунт",
"CONFIRM": "Вы уверены, что хотите удалить ваш аккаунт?",
"NEWSLETTER_LABEL_TEXT": "Я больше не хочу получать вашу новостную рассылку",
"CANCEL": "Back to settings",
"ACCEPT": "Delete account",
"CANCEL": "Вернуться к настройкам",
"ACCEPT": "Удалить аккаунт",
"BLOCK_PROJECT": "Note that all the projects you own projects will be <strong>blocked</strong> after you delete your account. If you do want a project blocked, transfer ownership to another member of each project prior to deleting your account.",
"SUBTITLE": "Sorry to see you go. We'll be here if you should ever consider us again! :("
},
@ -949,31 +1047,51 @@
},
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Необязательно) Добавьте персональный текст в приглашение. Скажите что-нибудь приятное вашим новым участникам ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Укажите e-mail",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members.<br> If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "Unfortunately, this project can't have more than <strong>{{maxMembers}}</strong> members."
"PLACEHOLDER_TYPE_EMAIL": "Введите электронную почту",
"LIMIT_USERS_WARNING_MESSAGE_FOR_OWNER": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members. If you would like to increase the current limit, please contact the administrator.",
"LIMIT_USERS_WARNING_MESSAGE": "You are about to reach the maximum number of members allowed for this project, <strong>{{maxMembers}}</strong> members."
},
"LEAVE_PROJECT_WARNING": {
"TITLE": "Unfortunately, this project can't be left without an owner",
"CURRENT_USER_OWNER": {
"DESC": "You are the current owner of this project. Before leaving, please transfer ownership to someone else.",
"BUTTON": "Change the project owner"
"BUTTON": "Сменить владельца проекта"
},
"OTHER_USER_OWNER": {
"DESC": "Unfortunately, you can't delete a member who is also the current project owner. First, please assign a new project owner.",
"BUTTON": "Request project owner change"
"BUTTON": "Запрос смены владельца проекта"
}
},
"CHANGE_OWNER": {
"TITLE": "Who do you want to be the new project owner?",
"ADD_COMMENT": "Add comment",
"BUTTON": "Ask this project member to become the new project owner"
"TITLE": "Кого вы хотите назначить новым владельцем проекта?",
"ADD_COMMENT": "Добавить комментарий",
"BUTTON": "Предложить участнику проекта стать его новым владельцем"
}
},
"EPIC": {
"PAGE_TITLE": "{{epicSubject}} - Epic {{epicRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Status: {{epicStatus }}. Description: {{epicDescription}}",
"SECTION_NAME": "Epic",
"TITLE_LIGHTBOX_UNLINK_RELATED_USERSTORY": "Unlink related userstory",
"MSG_LIGHTBOX_UNLINK_RELATED_USERSTORY": "It will delete the link to the related userstory '{{subject}}'",
"ERROR_UNLINK_RELATED_USERSTORY": "We have not been able to unlink: {{errorMessage}}",
"CREATE_RELATED_USERSTORIES": "Create a relationship with",
"NEW_USERSTORY": "Новая пользовательская история",
"EXISTING_USERSTORY": "Existing user story",
"CHOOSE_PROJECT_FOR_CREATION": "What's the project?",
"SUBJECT": "Тема",
"SUBJECT_BULK_MODE": "Subject (bulk insert)",
"CHOOSE_PROJECT_FROM": "What's the project?",
"CHOOSE_USERSTORY": "What's the user story?",
"NO_USERSTORIES": "This project has no User Stories yet. Please select another project.",
"FILTER_USERSTORIES": "Filter user stories",
"LIGHTBOX_TITLE_BLOKING_EPIC": "Blocking epic",
"ACTION_DELETE": "Delete epic"
},
"US": {
"PAGE_TITLE": "{{userStorySubject}} - Пользовательская История {{userStoryRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Статус: {{userStoryStatus }}. Выполнено {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} из {{userStoryTotalTasks}} задач). Очки: {{userStoryPoints}}. Описание: {{userStoryDescription}}",
"SECTION_NAME": "Детали пользовательских историй",
"SECTION_NAME": "Пользовательская история",
"LINK_TASKBOARD": "Панель задач",
"TITLE_LINK_TASKBOARD": "Перейти к панели задач",
"TOTAL_POINTS": "общее число очков",
@ -983,15 +1101,24 @@
"TITLE_LINK_GO_TO_ISSUE": "Перейти к запросу",
"EXTERNAL_REFERENCE": "Эта ПИ была создана из:",
"GO_TO_EXTERNAL_REFERENCE": "Перейти в начало",
"BLOCKED": "Эта пользовательская история заблокирована ",
"PREVIOUS": "предыдущая пользовательская история",
"NEXT": "следующая пользовательская история",
"BLOCKED": "Эта пользовательская история заблокирована",
"TITLE_DELETE_ACTION": "Удалить пользовательскую историю",
"LIGHTBOX_TITLE_BLOKING_US": "Блокирующая ПИ",
"TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} задач выполнено",
"ASSIGN": "Поручить пользовательскую историю",
"NOT_ESTIMATED": "Не оценено",
"TOTAL_US_POINTS": "Всего очков за ПИ",
"TRIBE": {
"PUBLISH": "Publish as Gig in Taiga Tribe",
"PUBLISH_INFO": "Больше инфо",
"PUBLISH_TITLE": "More info on publishing in Taiga Tribe",
"PUBLISHED_AS_GIG": "Story published as Gig in Taiga Tribe",
"EDIT_LINK": "Редактировать ссылку",
"CLOSE": "Закрыть",
"SYNCHRONIZE_LINK": "synchronize with Taiga Tribe",
"PUBLISH_MORE_INFO_TITLE": "Do you need somebody for this task?",
"PUBLISH_MORE_INFO_TEXT": "<p>If you need help with a particular piece of work you can easily create gigs on<a href='taigatribe.com' title='Taiga Tribe'> Taiga Tribe </a> and receive help from all over the world. You will be able to control and manage the gig enjoying a great community eager to contribute.</p><p><a href='taigatribe.com' title='Taiga Tribe'> TaigaTribe </a> was born as a Taiga sibling. Both platforms can live separately but we believe that there is much power in using them combined so we are making sure the integration works like a charm.</p>"
},
"FIELDS": {
"TEAM_REQUIREMENT": "Требование от Команды",
"CLIENT_REQUIREMENT": "Требование клиента",
@ -999,28 +1126,47 @@
}
},
"COMMENTS": {
"DELETED_INFO": "Комментарий удален {{user}} {{date}}",
"DELETED_INFO": "Комментарий удалён {{user}}",
"TITLE": "Комментарии",
"COMMENTS_COUNT": "{{comments}} Comments",
"ORDER": "Порядок",
"OLDER_FIRST": "Сначала старые",
"RECENT_FIRST": "Сначала новые",
"COMMENT": "Комментарий",
"EDIT_COMMENT": "Редактировать комментарий",
"EDITED_COMMENT": "Изменено:",
"SHOW_HISTORY": "View historic",
"TYPE_NEW_COMMENT": "Добавить комментарий",
"SHOW_DELETED": "Показать удаленный комментарий",
"HIDE_DELETED": "Скрыть удаленный комментарий",
"DELETE": "Удалить комментарий",
"RESTORE": "Показать удаленный комментарий"
"RESTORE": "Показать удаленный комментарий",
"HISTORY": {
"TITLE": "Действия"
}
},
"ACTIVITY": {
"SHOW_ACTIVITY": "Показать действия",
"DATETIME": "DD MMM YYYY HH:mm",
"SHOW_MORE": "+ Показать предыдущие записи (ещё {{showMore}})",
"TITLE": "Действия",
"ACTIVITIES_COUNT": "{{activities}} Activities",
"REMOVED": "удален",
"ADDED": "добавлено",
"US_POINTS": "ПИ очки ({{name}})",
"NEW_ATTACHMENT": "новое вложение",
"DELETED_ATTACHMENT": "удаленное вложение",
"UPDATED_ATTACHMENT": "обновлено приложение {{filename}}",
"DELETED_CUSTOM_ATTRIBUTE": "удалить атрибут",
"TAGS_ADDED": "тэги добавлены:",
"TAGS_REMOVED": "tags removed:",
"US_POINTS": "{{role}} points",
"NEW_ATTACHMENT": "новое вложение:",
"DELETED_ATTACHMENT": "удалённое вложение:",
"UPDATED_ATTACHMENT": "updated attachment ({{filename}}):",
"CREATED_CUSTOM_ATTRIBUTE": "created custom attribute",
"UPDATED_CUSTOM_ATTRIBUTE": "updated custom attribute",
"SIZE_CHANGE": "Сделано {size, plural, one{изменение} other{# изменений}}",
"BECAME_DEPRECATED": "became deprecated",
"BECAME_UNDEPRECATED": "became undeprecated",
"TEAM_REQUIREMENT": "Требование от Команды",
"CLIENT_REQUIREMENT": "Требование клиента",
"BLOCKED": "Заблокирован",
"VALUES": {
"YES": "да",
"NO": "нет",
@ -1052,12 +1198,14 @@
"TAGS": "тэги",
"ATTACHMENTS": "Вложения",
"IS_DEPRECATED": "рекомендовано",
"IS_NOT_DEPRECATED": "is not deprecated",
"ORDER": "порядок",
"BACKLOG_ORDER": "порядок списка задач",
"SPRINT_ORDER": "порядок спринтов",
"KANBAN_ORDER": "порядок kanban",
"TASKBOARD_ORDER": "порядок панели задач",
"US_ORDER": "порядок ПИ"
"US_ORDER": "порядок ПИ",
"COLOR": "цвет"
}
},
"BACKLOG": {
@ -1109,7 +1257,8 @@
"CLOSED_TASKS": "завершённые <br /> задачи",
"IOCAINE_DOSES": "иокаина<br />дозы",
"SHOW_STATISTICS_TITLE": "Показать статистику",
"TOGGLE_BAKLOG_GRAPH": "Показать/Скрыть график решения задач"
"TOGGLE_BAKLOG_GRAPH": "Показать/Скрыть график решения задач",
"POINTS_PER_ROLE": "Points per role"
},
"SUMMARY": {
"PROJECT_POINTS": "проектные<br />очки",
@ -1122,9 +1271,7 @@
"TITLE": "Фильтры",
"REMOVE": "Сбросить фильтры",
"HIDE": "Спрятать фильтры",
"SHOW": "Показать фильтры",
"FILTER_CATEGORY_STATUS": "Статус",
"FILTER_CATEGORY_TAGS": "Тэги"
"SHOW": "Показать фильтры"
},
"SPRINTS": {
"TITLE": "СПРИНТЫ",
@ -1179,7 +1326,7 @@
"TASK": {
"PAGE_TITLE": "{{taskSubject}} - Задача {{taskRef}} - {{projectName}}",
"PAGE_DESCRIPTION": "Статус: {{taskStatus }}. Описание: {{taskDescription}}",
"SECTION_NAME": "Детали задачи",
"SECTION_NAME": "Задача",
"LINK_TASKBOARD": "Панель задач",
"TITLE_LINK_TASKBOARD": "Перейти к панели задач",
"PLACEHOLDER_SUBJECT": "Укажите новое название задачи",
@ -1189,8 +1336,6 @@
"ORIGIN_US": "Эта задача была создана из",
"TITLE_LINK_GO_ORIGIN": "Перейти к пользовательской истории",
"BLOCKED": "Эта задача заблокирована",
"PREVIOUS": "предыдущая задача",
"NEXT": "следующая задача",
"TITLE_DELETE_ACTION": "Удалить задачу",
"LIGHTBOX_TITLE_BLOKING_TASK": "Блокирующее задание",
"FIELDS": {
@ -1218,26 +1363,23 @@
"SUCCESS": "Oompa Loompas удалил ваш аккаунт"
},
"CHANGE_EMAIL_FORM": {
"TITLE": "Изменить e-mail",
"TITLE": "Сменить вашу электронную почту",
"SUBTITLE": "Ещё один клик и Ваш email будет обновлён!",
"PLACEHOLDER_INPUT_TOKEN": "изменить идентификатор email",
"ACTION_CHANGE_EMAIL": "изменить почту",
"SUCCESS": "Oompa Loompas обновил ваш e-mail"
"ACTION_CHANGE_EMAIL": "Сменить электронную почту",
"SUCCESS": "Наш Oompa Loompas обновил вашу электронную почту"
},
"ISSUES": {
"PAGE_TITLE": "Запросы - {{projectName}}",
"PAGE_DESCRIPTION": "Панель запросов проекта {{projectName}}: {{projectDescription}}",
"LIST_SECTION_NAME": "Запросы",
"SECTION_NAME": "Детали запроса",
"SECTION_NAME": "Запрос",
"ACTION_NEW_ISSUE": "+НОВЫЙ ЗАПРОС",
"ACTION_PROMOTE_TO_US": "Повысить до пользовательской истории",
"PLACEHOLDER_FILTER_NAME": "Введите название фильтра и нажмите \"ввод\"",
"PROMOTED": "Этот запрос был переделан в ПИ:",
"EXTERNAL_REFERENCE": "Этот запрос был создан из",
"GO_TO_EXTERNAL_REFERENCE": "Перейти в начало",
"BLOCKED": "Этот запрос заблокирована",
"TITLE_PREVIOUS_ISSUE": "предыдущий запрос",
"TITLE_NEXT_ISSUE": "следующий запрос",
"ACTION_DELETE": "Удалить запрос",
"LIGHTBOX_TITLE_BLOKING_ISSUE": "Блокирующий запрос",
"FIELDS": {
@ -1249,28 +1391,6 @@
"TITLE": "Превратить этот запрос в новую пользовательскую историю",
"MESSAGE": "Вы уверены, что хотите создать новую ПИ из этого запроса?"
},
"FILTERS": {
"TITLE": "Фильтры",
"INPUT_SEARCH_PLACEHOLDER": "Название ссылки",
"TITLE_ACTION_SEARCH": "Поиск",
"ACTION_SAVE_CUSTOM_FILTER": "сохранить как специальный фильтр",
"BREADCRUMB": "Фильтры",
"TITLE_BREADCRUMB": "Фильтры",
"CATEGORIES": {
"TYPE": "Тип",
"STATUS": "Статус",
"SEVERITY": "Важность",
"PRIORITIES": "Приоритет",
"TAGS": "Тэги",
"ASSIGNED_TO": "Назначено",
"CREATED_BY": "Создано",
"CUSTOM_FILTERS": "Собственные фильтры"
},
"CONFIRM_DELETE": {
"TITLE": "Удалить фильтр",
"MESSAGE": "специальный фильтр '{{customFilterName}}'"
}
},
"TABLE": {
"COLUMNS": {
"TYPE": "Тип",
@ -1316,6 +1436,7 @@
"SEARCH": {
"PAGE_TITLE": "Поиск - {{projectName}}",
"PAGE_DESCRIPTION": "Ищите что угодно, пользовательские истории, задачи, запросы и вики-страницы, в проекте {{projectName}}: {{projectDescription}}",
"FILTER_EPICS": "Epics",
"FILTER_USER_STORIES": "Пользовательские Истории",
"FILTER_ISSUES": "Запросы",
"FILTER_TASKS": "Задачи",
@ -1383,7 +1504,7 @@
"CHANGE_PHOTO": "Изменить фото",
"FIELD": {
"USERNAME": "Имя пользователя",
"EMAIL": "Email",
"EMAIL": "Электронная почта",
"FULL_NAME": "Полное имя",
"PLACEHOLDER_FULL_NAME": "Полное имя (например, Игорь Николаев)",
"BIO": "Биография (не более 210 символов)",
@ -1399,10 +1520,10 @@
"CREATE_PROJECT_TEXT": "Свежий и чистый! Так здóрово!",
"CHOOSE_TEMPLATE": "Which template fits your project best?",
"CHOOSE_TEMPLATE_TITLE": "More info about project templates",
"CHOOSE_TEMPLATE_INFO": "More info",
"CHOOSE_TEMPLATE_INFO": "Больше инфо",
"PROJECT_DETAILS": "Project Details",
"PUBLIC_PROJECT": "Public Project",
"PRIVATE_PROJECT": "Private Project",
"PRIVATE_PROJECT": "Частный проект",
"CREATE_PROJECT": "Создать проект",
"MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects",
"MAX_PUBLIC_PROJECTS": "Unfortunately, you've reached the maximum number of public projects",
@ -1415,15 +1536,26 @@
"PLACEHOLDER_PAGE": "Создать вики страницу",
"REMOVE": "Удалить эту вики страницу",
"DELETE_LIGHTBOX_TITLE": "Удалить вики страницу",
"DELETE_LINK_TITLE": "Delete Wiki link",
"DELETE_LINK_TITLE": "Удалить ссылку wiki",
"NAVIGATION": {
"SECTION_NAME": "Ссылки",
"ACTION_ADD_LINK": "Добавить ссылку"
"HOME": "Главная страница",
"SECTION_NAME": "ЗАКЛАДКИ",
"ACTION_ADD_LINK": "Добавить закладку",
"ALL_PAGES": "All wiki pages"
},
"SUMMARY": {
"TIMES_EDITED": "раз <br />отредактировано",
"LAST_EDIT": "последняя <br/> правка",
"LAST_MODIFICATION": "последнее изменение"
},
"SECTION_PAGES_LIST": "Все страницы",
"PAGES_LIST_COLUMNS": {
"TITLE": "Title",
"EDITIONS": "Editions",
"CREATED": "Создан",
"MODIFIED": "Modified",
"CREATOR": "Creator",
"LAST_MODIFIER": "Last modifier"
}
},
"HINTS": {
@ -1447,6 +1579,8 @@
"TASK_CREATED_WITH_US": "{{username}} создал новую задачу {{obj_name}} в {{project_name}}, которая принадлежит ПИ {{us_name}}",
"WIKI_CREATED": "{{username}} создал новую вики-страницу {{obj_name}} в {{project_name}}",
"MILESTONE_CREATED": "{{username}} создал новый спринт {{obj_name}} в {{project_name}}",
"EPIC_CREATED": "{{username}} has created a new epic {{obj_name}} in {{project_name}}",
"EPIC_RELATED_USERSTORY_CREATED": "{{username}} has related the userstory {{related_us_name}} to the epic {{epic_name}} in {{project_name}}",
"NEW_PROJECT": "{{username}} создал проект {{project_name}}",
"MILESTONE_UPDATED": "{{username}} обновил спринт {{obj_name}}",
"US_UPDATED": "{{username}} обновил атрибут \"{{field_name}}\" ПИ {{obj_name}}",
@ -1459,9 +1593,13 @@
"TASK_UPDATED_WITH_US": "{{username}} изменил атрибут \"{{field_name}}\" задачи {{obj_name}}, которая принадлежит ПИ {{us_name}}",
"TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} установил атрибут \"{{field_name}}\" задачи {{obj_name}}, которая принадлежит ПИ {{us_name}}, на {{new_value}}",
"WIKI_UPDATED": "{{username}} обновил вики-страницу {{obj_name}}",
"EPIC_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}}",
"EPIC_UPDATED_WITH_NEW_VALUE": "{{username}} has updated the attribute \"{{field_name}}\" of the epic {{obj_name}} to {{new_value}}",
"EPIC_UPDATED_WITH_NEW_COLOR": "{{username}} has updated the \"{{field_name}}\" of the epic {{obj_name}} to <span class=\"new-color\" style=\"background: {{new_value}}\"></span>",
"NEW_COMMENT_US": "{{username}} прокомментировал ПИ {{obj_name}}",
"NEW_COMMENT_ISSUE": "{{username}} прокомментировал запрос {{obj_name}}",
"NEW_COMMENT_TASK": "{{username}} прокомментировал задачу {{obj_name}}",
"NEW_COMMENT_EPIC": "{{username}} has commented in the epic {{obj_name}}",
"NEW_MEMBER": "У {{project_name}} появился новый участник",
"US_ADDED_MILESTONE": "{{username}} добавил ПИ {{obj_name}} для {{sprint_name}}",
"US_MOVED": "{{username}} переместил ПИ {{obj_name}}",

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