diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ffd3ec..d9a20a59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Activity timeline - Global user configuration - User contacts +- Improve SEO, fix meta tags and added social meta tags ### Misc - Improve performance: remove some unnecessary calls to the api. diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 5cdb7edd..c6f2693a 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -28,21 +28,26 @@ taiga.generateHash = (components=[]) -> components = _.map(components, (x) -> JSON.stringify(x)) return hex_sha1(components.join(":")) + taiga.generateUniqueSessionIdentifier = -> date = (new Date()).getTime() randomNumber = Math.floor(Math.random() * 0x9000000) return taiga.generateHash([date, randomNumber]) + taiga.sessionId = taiga.generateUniqueSessionIdentifier() -configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, $compileProvider, $translateProvider) -> + +configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, + $compileProvider, $translateProvider) -> $routeProvider.when("/", { templateUrl: "home/home.html", access: { requiresLogin: true }, - title: "PROJECT.WELCOME", + title: "HOME.PAGE_TITLE", + description: "HOME.PAGE_DESCRIPTION", loader: true } ) @@ -53,7 +58,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven access: { requiresLogin: true }, - title: "PROJECT.SECTION_PROJECTS", + title: "PROJECTS.PAGE_TITLE", + description: "PROJECTS.PAGE_DESCRIPTION", loader: true, controller: "ProjectsListing", controllerAs: "vm" @@ -305,17 +311,47 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven # Auth $routeProvider.when("/login", - {templateUrl: "auth/login.html"}) + { + templateUrl: "auth/login.html", + title: "LOGIN.PAGE_TITLE" + description: "LOGIN.PAGE_DESCRIPTION" + } + ) $routeProvider.when("/register", - {templateUrl: "auth/register.html"}) + { + templateUrl: "auth/register.html", + title: "REGISTER.PAGE_TITLE", + description: "REGISTER.PAGE_DESCRIPTION" + } + ) $routeProvider.when("/forgot-password", - {templateUrl: "auth/forgot-password.html"}) + { + templateUrl: "auth/forgot-password.html", + title: "FORGOT_PASSWORD.PAGE_TITLE", + description: "FORGOT_PASSWORD.PAGE_DESCRIPTION" + } + ) $routeProvider.when("/change-password", - {templateUrl: "auth/change-password-from-recovery.html"}) + { + templateUrl: "auth/change-password-from-recovery.html", + title: "CHANGE_PASSWORD.PAGE_TITLE", + description: "CHANGE_PASSWORD.PAGE_TITLE", + } + ) $routeProvider.when("/change-password/:token", - {templateUrl: "auth/change-password-from-recovery.html"}) + { + templateUrl: "auth/change-password-from-recovery.html", + title: "CHANGE_PASSWORD.PAGE_TITLE", + description: "CHANGE_PASSWORD.PAGE_TITLE", + } + ) $routeProvider.when("/invitation/:token", - {templateUrl: "auth/invitation.html"}) + { + templateUrl: "auth/invitation.html", + title: "INVITATION.PAGE_TITLE", + description: "INVITATION.PAGE_DESCRIPTION" + } + ) # Errors/Exceptions $routeProvider.when("/error", @@ -482,7 +518,7 @@ i18nInit = (lang, $translate) -> checksley.updateMessages('default', messages) -init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, $appTitle, projectService, loaderService) -> +init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $navUrls, appMetaService, projectService, loaderService) -> $log.debug("Initialize application") # Taiga Plugins @@ -517,8 +553,11 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na else projectService.cleanProject() - if next.title - $translate(next.title).then (text) => $appTitle.set(text) + if next.title or next.description + title = next.title or "" + description = next.description or "" + $translate([title, description]).then (translations) => + appMetaService.setAll(translations[title], translations[description]) if next.loader loaderService.startWithAutoClose() @@ -592,7 +631,7 @@ module.run([ "$translate", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "tgProjectService", "tgLoader", init diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee index 1d5953d4..9d46467c 100644 --- a/app/coffee/modules/admin/memberships.coffee +++ b/app/coffee/modules/admin/memberships.coffee @@ -43,11 +43,12 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai "$tgLocation", "$tgNavUrls", "$tgAnalytics", - "$appTitle" + "tgAppMetaService", + "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, - @location, @navUrls, @analytics, @appTitle) -> + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @analytics, + @appMetaService, @translate) -> bindMethods(@) @scope.project = {} @@ -56,7 +57,9 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai promise = @.loadInitialData() promise.then => - @appTitle.set("Membership - " + @scope.project.name) + title = @translate.instant("ADMIN.MEMBERSHIPS.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @@ -378,7 +381,9 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla $el.on "click", ".pending", (event) -> event.preventDefault() onSuccess = -> - text = $translate.instant("ADMIN.MEMBERSHIP.SUCCESS_SEND_INVITATION", {email: $scope.member.email}) + text = $translate.instant("ADMIN.MEMBERSHIP.SUCCESS_SEND_INVITATION", { + email: $scope.member.email + }) $confirm.notify("success", text) onError = -> text = $translate.instant("ADMIM.MEMBERSHIP.ERROR_SEND_INVITATION") @@ -415,4 +420,5 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla return {link: link} -module.directive("tgMembershipsRowActions", ["$log", "$tgRepo", "$tgResources", "$tgConfirm", "$compile", "$translate", MembershipsRowActionsDirective]) +module.directive("tgMembershipsRowActions", ["$log", "$tgRepo", "$tgResources", "$tgConfirm", "$compile", + "$translate", MembershipsRowActionsDirective]) diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index ada135de..b3c117ae 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -47,28 +47,31 @@ class ProjectProfileController extends mixOf(taiga.Controller, taiga.PageMixin) "$q", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle, @translate) -> + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, + @appMetaService, @translate) -> @scope.project = {} promise = @.loadInitialData() promise.then => sectionName = @translate.instant( @scope.sectionName) - appTitle = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", { + title = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", { sectionName: sectionName, projectName: @scope.project.name}) - @appTitle.set(appTitle) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @scope.$on "project:loaded", => sectionName = @translate.instant(@scope.sectionName) - appTitle = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", { + title = @translate.instant("ADMIN.PROJECT_PROFILE.PAGE_TITLE", { sectionName: sectionName, projectName: @scope.project.name}) - @appTitle.set(appTitle) + description = @scope.project.description + @appMetaService.setAll(title, description) loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => @@ -119,7 +122,9 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, proje promise.then -> $loading.finish(submitButton) $confirm.notify("success") - newUrl = $navurls.resolve("project-admin-project-profile-details", {project: $scope.project.slug}) + newUrl = $navurls.resolve("project-admin-project-profile-details", { + project: $scope.project.slug + }) $location.path(newUrl) $scope.$emit("project:loaded", $scope.project) @@ -137,7 +142,9 @@ ProjectProfileDirective = ($repo, $confirm, $loading, $navurls, $location, proje return {link:link} -module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation", "tgProjectService", ProjectProfileDirective]) +module.directive("tgProjectProfile", ["$tgRepo", "$tgConfirm", "$tgLoading", "$tgNavUrls", "$tgLocation", + "tgProjectService", ProjectProfileDirective]) + ############################################################################# ## Project Default Values Directive @@ -224,7 +231,8 @@ ProjectModulesDirective = ($repo, $confirm, $loading, projectService) -> return {link:link} -module.directive("tgProjectModules", ["$tgRepo", "$tgConfirm", "$tgLoading", "tgProjectService", ProjectModulesDirective]) +module.directive("tgProjectModules", ["$tgRepo", "$tgConfirm", "$tgLoading", "tgProjectService", + ProjectModulesDirective]) ############################################################################# diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index ee520752..2a58f101 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -46,11 +46,12 @@ class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageM "$q", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle, @translate) -> + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, + @appMetaService, @translate) -> @scope.project = {} promise = @.loadInitialData() @@ -58,12 +59,12 @@ class ProjectValuesSectionController extends mixOf(taiga.Controller, taiga.PageM promise.then () => sectionName = @translate.instant(@scope.sectionName) - title = @translate.instant("ADMIN.PROJECT_VALUES.APP_TITLE", { + title = @translate.instant("ADMIN.PROJECT_VALUES.PAGE_TITLE", { "sectionName": sectionName, "projectName": @scope.project.name }) - - @appTitle.set(title) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @@ -383,15 +384,24 @@ class ProjectCustomAttributesController extends mixOf(taiga.Controller, taiga.Pa "$q", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", + "$translate" ] - constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle) -> + constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appMetaService, + @translate) -> @scope.project = {} @rootscope.$on "project:loaded", => @.loadCustomAttributes() - @appTitle.set("Project Custom Attributes - " + @scope.sectionName + " - " + @scope.project.name) + + sectionName = @translate.instant(@scope.sectionName) + title = @translate.instant("ADMIN.CUSTOM_ATTRIBUTES.PAGE_TITLE", { + "sectionName": sectionName, + "projectName": @scope.project.name + }) + description = @scope.project.description + @appMetaService.setAll(title, description) ######################### # Custom Attribute @@ -640,4 +650,5 @@ ProjectCustomAttributesDirective = ($log, $confirm, animationFrame, $translate) return {link: link} -module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", "$translate", ProjectCustomAttributesDirective]) +module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", "$translate", + ProjectCustomAttributesDirective]) diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index f466c90d..b41cd2e5 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -44,12 +44,12 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil "$q", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @appTitle, - @translate) -> + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, + @appMetaService, @translate) -> bindMethods(@) @scope.sectionName = "ADMIN.MENU.PERMISSIONS" @@ -59,8 +59,9 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil promise = @.loadInitialData() promise.then () => - title = @translate.instant("ADMIN.ROLES.SECTION_NAME", {projectName: @scope.project.name}) - @appTitle.set(title) + title = @translate.instant("ADMIN.ROLES.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/admin/third-parties.coffee b/app/coffee/modules/admin/third-parties.coffee index b05318c3..737a8144 100644 --- a/app/coffee/modules/admin/third-parties.coffee +++ b/app/coffee/modules/admin/third-parties.coffee @@ -41,11 +41,11 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga. "$routeParams", "$tgLocation", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appTitle, @translate) -> + constructor: (@scope, @repo, @rs, @params, @location, @navUrls, @appMetaService, @translate) -> bindMethods(@) @scope.sectionName = "ADMIN.WEBHOOKS.SECTION_NAME" @@ -54,8 +54,9 @@ class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga. promise = @.loadInitialData() promise.then () => - text = @translate.instant("ADMIN.WEBHOOKS.APP_TITLE", {"projectName": @scope.project.name}) - @appTitle.set(text) + title = @translate.instant("ADMIN.WEBHOOKS.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @@ -292,11 +293,11 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi "$tgRepo", "$tgResources", "$routeParams", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) -> + constructor: (@scope, @repo, @rs, @params, @appMetaService, @translate) -> bindMethods(@) @scope.sectionName = @translate.instant("ADMIN.GITHUB.SECTION_NAME") @@ -305,8 +306,9 @@ class GithubController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi promise = @.loadInitialData() promise.then () => - title = @translate.instant("ADMIN.GITHUB.APP_TITLE", {projectName: @scope.project.name}) - @appTitle.set(title) + title = @translate.instant("ADMIN.GITHUB.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @@ -339,11 +341,11 @@ class GitlabController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi "$tgRepo", "$tgResources", "$routeParams", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) -> + constructor: (@scope, @repo, @rs, @params, @appMetaService, @translate) -> bindMethods(@) @scope.sectionName = @translate.instant("ADMIN.GITLAB.SECTION_NAME") @@ -351,8 +353,9 @@ class GitlabController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi promise = @.loadInitialData() promise.then () => - title = @translate.instant("ADMIN.GITLAB.APP_TITLE", {projectName: @scope.project.name}) - @appTitle.set(title) + title = @translate.instant("ADMIN.GITLAB.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) @@ -388,11 +391,11 @@ class BitbucketController extends mixOf(taiga.Controller, taiga.PageMixin, taiga "$tgRepo", "$tgResources", "$routeParams", - "$appTitle", + "tgAppMetaService", "$translate" ] - constructor: (@scope, @repo, @rs, @params, @appTitle, @translate) -> + constructor: (@scope, @repo, @rs, @params, @appMetaService, @translate) -> bindMethods(@) @scope.sectionName = @translate.instant("ADMIN.BITBUCKET.SECTION_NAME") @@ -400,8 +403,9 @@ class BitbucketController extends mixOf(taiga.Controller, taiga.PageMixin, taiga promise = @.loadInitialData() promise.then () => - title = @translate.instant("ADMIN.BITBUCKET.APP_TITLE", {projectName: @scope.project.name}) - @appTitle.set(title) + title = @translate.instant("ADMIN.BITBUCKET.PAGE_TITLE", {projectName: @scope.project.name}) + description = @scope.project.description + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index 9960c3e8..845f33a7 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -45,7 +45,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F "$routeParams", "$q", "$tgLocation", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgEvents", "$tgAnalytics", @@ -53,7 +53,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, - @location, @appTitle, @navUrls, @events, @analytics, @translate) -> + @location, @appMetaService, @navUrls, @events, @analytics, @translate) -> bindMethods(@) @scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME") @@ -66,7 +66,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F # On Success promise.then => - @appTitle.set("Backlog - " + @scope.project.name) + title = @translate.instant("BACKLOG.PAGE_TITLE", {projectName: @scope.project.name}) + description = @translate.instant("BACKLOG.PAGE_DESCRIPTION", { + projectName: @scope.project.name, + projectDescription: @scope.project.description + }) + @appMetaService.setAll(title, description) if @rs.userstories.getShowTags(@scope.projectId) @showTags = true diff --git a/app/coffee/modules/base/contrib.coffee b/app/coffee/modules/base/contrib.coffee index a5e3d695..8cb17162 100644 --- a/app/coffee/modules/base/contrib.coffee +++ b/app/coffee/modules/base/contrib.coffee @@ -22,9 +22,16 @@ taigaContribPlugins = @.taigaContribPlugins = @.taigaContribPlugins or [] class ContribController extends taiga.Controller - @.$inject = ["$rootScope", "$scope", "$routeParams", "$tgRepo", "$tgResources", "$tgConfirm", "$appTitle"] + @.$inject = [ + "$rootScope", + "$scope", + "$routeParams", + "$tgRepo", + "$tgResources", + "$tgConfirm" + ] - constructor: (@rootScope, @scope, @params, @repo, @rs, @confirm, @appTitle) -> + constructor: (@rootScope, @scope, @params, @repo, @rs, @confirm) -> @scope.adminPlugins = _.where(@rootScope.contribPlugins, {"type": "admin"}) @scope.currentPlugin = _.first(_.where(@scope.adminPlugins, {"slug": @params.plugin})) @scope.pluginTemplate = "contrib/#{@scope.currentPlugin.slug}" @@ -32,9 +39,6 @@ class ContribController extends taiga.Controller promise = @.loadInitialData() - promise.then () => - @appTitle.set(@scope.project.name) - promise.then null, => @confirm.notify("error") diff --git a/app/coffee/modules/common.coffee b/app/coffee/modules/common.coffee index 7c03560c..abe17a6c 100644 --- a/app/coffee/modules/common.coffee +++ b/app/coffee/modules/common.coffee @@ -138,17 +138,6 @@ ToggleCommentDirective = () -> module.directive("tgToggleComment", ToggleCommentDirective) -############################################################################# -## Set the page title -############################################################################# - -AppTitle = () -> - set = (text) -> - $("title").text(text) - - return {set: set} - -module.factory("$appTitle", AppTitle) ############################################################################# ## Get the appropiate section url for a project diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index c85b6a65..36bb542e 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -44,14 +44,14 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) "$q", "$tgLocation", "$log", - "$appTitle", + "tgAppMetaService", "$tgAnalytics", "$tgNavUrls", "$translate" ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, - @log, @appTitle, @analytics, @navUrls, @translate) -> + @log, @appMetaService, @analytics, @navUrls, @translate) -> @scope.issueRef = @params.issueref @scope.sectionName = @translate.instant("ISSUES.SECTION_NAME") @.initializeEventHandlers() @@ -60,12 +60,27 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) # On Success promise.then => - @appTitle.set(@scope.issue.subject + " - " + @scope.project.name) + @._setMeta() @.initializeOnDeleteGoToUrl() # On Error promise.then null, @.onInitialDataError.bind(@) + _setMeta: -> + title = @translate.instant("ISSUE.PAGE_TITLE", { + issueRef: "##{@scope.issue.ref}" + issueSubject: @scope.issue.subject + projectName: @scope.project.name + }) + description = @translate.instant("ISSUE.PAGE_DESCRIPTION", { + issueStatus: @scope.statusById[@scope.issue.status]?.name or "--" + issueType: @scope.typeById[@scope.issue.type]?.name or "--" + issueSeverity: @scope.severityById[@scope.issue.severity]?.name or "--" + issuePriority: @scope.priorityById[@scope.issue.priority]?.name or "--" + issueDescription: angular.element(@scope.issue.description_html or "").text() + }) + @appMetaService.setAll(title, description) + initializeEventHandlers: -> @scope.$on "attachment:create", => @rootscope.$broadcast("object:updated") diff --git a/app/coffee/modules/issues/list.coffee b/app/coffee/modules/issues/list.coffee index 9779dc1e..d1ef31f0 100644 --- a/app/coffee/modules/issues/list.coffee +++ b/app/coffee/modules/issues/list.coffee @@ -47,14 +47,15 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi "$routeParams", "$q", "$tgLocation", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgEvents", - "$tgAnalytics" + "$tgAnalytics", + "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @urls, @params, @q, @location, @appTitle, - @navUrls, @events, @analytics) -> + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @urls, @params, @q, @location, @appMetaService, + @navUrls, @events, @analytics, @translate) -> @scope.sectionName = "Issues" @scope.filters = {} @@ -69,7 +70,12 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi # On Success promise.then => - @appTitle.set("Issues - " + @scope.project.name) + title = @translate.instant("ISSUES.PAGE_TITLE", {projectName: @scope.project.name}) + description = @translate.instant("ISSUES.PAGE_DESCRIPTION", { + projectName: @scope.project.name, + projectDescription: @scope.project.description + }) + @appMetaService.setAll(title, description) # On Error promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index 8edc40a3..137521c6 100644 --- a/app/coffee/modules/kanban/main.coffee +++ b/app/coffee/modules/kanban/main.coffee @@ -58,7 +58,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi "$routeParams", "$q", "$tgLocation", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgEvents", "$tgAnalytics", @@ -66,7 +66,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, - @appTitle, @navUrls, @events, @analytics, @translate) -> + @appMetaService, @navUrls, @events, @analytics, @translate) -> bindMethods(@) @@ -78,7 +78,12 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi # On Success promise.then => - @appTitle.set("Kanban - " + @scope.project.name) + title = @translate.instant("KANBAN.PAGE_TITLE", {projectName: @scope.project.name}) + description = @translate.instant("KANBAN.PAGE_DESCRIPTION", { + projectName: @scope.project.name, + projectDescription: @scope.project.description + }) + @appMetaService.setAll(title, description) # On Error promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/search.coffee b/app/coffee/modules/search.coffee index 30955c32..91a974a5 100644 --- a/app/coffee/modules/search.coffee +++ b/app/coffee/modules/search.coffee @@ -43,17 +43,23 @@ class SearchController extends mixOf(taiga.Controller, taiga.PageMixin) "$routeParams", "$q", "$tgLocation", - "$appTitle", - "$tgNavUrls" + "tgAppMetaService", + "$tgNavUrls", + "$translate" ] - constructor: (@scope, @repo, @rs, @params, @q, @location, @appTitle, @navUrls) -> + constructor: (@scope, @repo, @rs, @params, @q, @location, @appMetaService, @navUrls, @translate) -> @scope.sectionName = "Search" promise = @.loadInitialData() promise.then () => - @appTitle.set("Search") + title = @translate.instant("SEARCH.PAGE_TITLE", {projectName: @scope.project.name}) + description = @translate.instant("SEARCH.PAGE_DESCRIPTION", { + projectName: @scope.project.name, + projectDescription: @scope.project.description + }) + @appMetaService.setAll(title, description) promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index c9aae4af..f4e82d92 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -44,7 +44,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) "$tgResources", "$routeParams", "$q", - "$appTitle", + "tgAppMetaService", "$tgLocation", "$tgNavUrls" "$tgEvents" @@ -52,7 +52,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) "$translate" ] - constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appTitle, @location, @navUrls, + constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @appMetaService, @location, @navUrls, @events, @analytics, @translate) -> bindMethods(@) @@ -62,12 +62,31 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.loadInitialData() # On Success - promise.then => - @appTitle.set("Taskboard - " + @scope.project.name) - + promise.then => @._setMeta() # On Error promise.then null, @.onInitialDataError.bind(@) + _setMeta: -> + prettyDate = @translate.instant("BACKLOG.SPRINTS.DATE") + + title = @translate.instant("TASKBOARD.PAGE_TITLE", { + projectName: @scope.project.name + sprintName: @scope.sprint.name + }) + description = @translate.instant("TASKBOARD.PAGE_DESCRIPTION", { + projectName: @scope.project.name + sprintName: @scope.sprint.name + startDate: moment(@scope.sprint.estimated_start).format(prettyDate) + endDate: moment(@scope.sprint.estimated_finish).format(prettyDate) + completedPercentage: @scope.stats.completedPercentage or "0" + completedPoints: @scope.stats.completedPointsSum or "--" + totalPoints: @scope.stats.totalPointsSum or "--" + openTasks: @scope.stats.openTasks or "--" + totalTasks: @scope.stats.total_tasks or "--" + }) + + @appMetaService.setAll(title, description) + initializeEventHandlers: -> # TODO: Reload entire taskboard after create/edit tasks seems # a big overhead. It should be optimized in near future. @@ -255,7 +274,7 @@ TaskboardDirective = ($rootscope) -> $el.on "click", ".toggle-analytics-visibility", (event) -> event.preventDefault() target = angular.element(event.currentTarget) - target.toggleClass('active'); + target.toggleClass('active') $rootscope.$broadcast("taskboard:graph:toggle-visibility") tableBodyDom = $el.find(".taskboard-table-body") diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 5e1c790c..228c4aff 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -42,14 +42,14 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) "$q", "$tgLocation", "$log", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgAnalytics", "$translate" ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, - @log, @appTitle, @navUrls, @analytics, @translate) -> + @log, @appMetaService, @navUrls, @analytics, @translate) -> @scope.taskRef = @params.taskref @scope.sectionName = @translate.instant("TASK.SECTION_NAME") @.initializeEventHandlers() @@ -57,11 +57,23 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.loadInitialData() promise.then () => - @appTitle.set(@scope.task.subject + " - " + @scope.project.name) + @._setMeta() @.initializeOnDeleteGoToUrl() promise.then null, @.onInitialDataError.bind(@) + _setMeta: -> + title = @translate.instant("TASK.PAGE_TITLE", { + taskRef: "##{@scope.task.ref}" + taskSubject: @scope.task.subject + projectName: @scope.project.name + }) + description = @translate.instant("TASK.PAGE_DESCRIPTION", { + taskStatus: @scope.statusById[@scope.task.status]?.name or "--" + taskDescription: angular.element(@scope.task.description_html or "").text() + }) + @appMetaService.setAll(title, description) + initializeEventHandlers: -> @scope.$on "attachment:create", => @analytics.trackEvent("attachment", "create", "create attachment on task", 1) diff --git a/app/coffee/modules/team/main.coffee b/app/coffee/modules/team/main.coffee index 5991b7c1..355a030e 100644 --- a/app/coffee/modules/team/main.coffee +++ b/app/coffee/modules/team/main.coffee @@ -39,21 +39,26 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin) "$q", "$location", "$tgNavUrls", - "$appTitle", + "tgAppMetaService", "$tgAuth", "$translate", "tgProjectService" ] - constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle, @auth, @translate, @projectService) -> + constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appMetaService, @auth, + @translate, @projectService) -> @scope.sectionName = "TEAM.SECTION_NAME" promise = @.loadInitialData() # On Success promise.then => - text = @translate.instant("TEAM.APP_TITLE", {"projectName": @scope.project.name}) - @appTitle.set(text) + title = @translate.instant("TEAM.PAGE_TITLE", {projectName: @scope.project.name}) + description = @translate.instant("TEAM.PAGE_DESCRIPTION", { + projectName: @scope.project.name, + projectDescription: @scope.project.description + }) + @appMetaService.setAll(title, description) # On Error promise.then null, @.onInitialDataError.bind(@) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 1d8acd13..006da05c 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -42,14 +42,14 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) "$q", "$tgLocation", "$log", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgAnalytics", "$translate" ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, - @log, @appTitle, @navUrls, @analytics, @translate) -> + @log, @appMetaService, @navUrls, @analytics, @translate) -> @scope.usRef = @params.usref @scope.sectionName = @translate.instant("US.SECTION_NAME") @.initializeEventHandlers() @@ -58,12 +58,33 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) # On Success promise.then => - @appTitle.set(@scope.us.subject + " - " + @scope.project.name) + @._setMeta() @.initializeOnDeleteGoToUrl() # On Error promise.then null, @.onInitialDataError.bind(@) + _setMeta: -> + totalTasks = @scope.tasks.length + closedTasks = _.filter(@scope.tasks, (t) => @scope.taskStatusById[t.status].is_closed).length + progressPercentage = if totalTasks > 0 then 100 * closedTasks / totalTasks else 0 + + title = @translate.instant("US.PAGE_TITLE", { + userStoryRef: "##{@scope.us.ref}" + userStorySubject: @scope.us.subject + projectName: @scope.project.name + }) + description = @translate.instant("US.PAGE_DESCRIPTION", { + userStoryStatus: @scope.statusById[@scope.us.status]?.name or "--" + userStoryPoints: @scope.us.total_points + userStoryDescription: angular.element(@scope.us.description_html or "").text() + userStoryClosedTasks: closedTasks + userStoryTotalTasks: totalTasks + userStoryProgressPercentage: progressPercentage + }) + + @appMetaService.setAll(title, description) + initializeEventHandlers: -> @scope.$on "related-tasks:update", => @.loadUs() diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 01f8fc9f..4386a1e0 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -46,14 +46,14 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) "$tgLocation", "$filter", "$log", - "$appTitle", + "tgAppMetaService", "$tgNavUrls", "$tgAnalytics", "$translate" ] constructor: (@scope, @rootscope, @repo, @model, @confirm, @rs, @params, @q, @location, - @filter, @log, @appTitle, @navUrls, @analytics, @translate) -> + @filter, @log, @appMetaService, @navUrls, @analytics, @translate) -> @scope.projectSlug = @params.pslug @scope.wikiSlug = @params.slug @scope.sectionName = "Wiki" @@ -61,12 +61,23 @@ class WikiDetailController extends mixOf(taiga.Controller, taiga.PageMixin) promise = @.loadInitialData() # On Success - promise.then () => - @appTitle.set("Wiki - " + @scope.project.name) + promise.then () => @._setMeta() # On Error promise.then null, @.onInitialDataError.bind(@) + _setMeta: -> + title = @translate.instant("WIKI.PAGE_TITLE", { + wikiPageName: @scope.wiki.slug + projectName: unslugify(@scope.wiki.slug) + }) + description = @translate.instant("WIKI.PAGE_DESCRIPTION", { + wikiPageContent: angular.element(@scope.wiki.html or "").text() + totalEditions: @scope.wiki.editions or 0 + lastModifiedDate: moment(@scope.wiki.modified_date).format(@translate.instant("WIKI.DATETIME")) + }) + @appMetaService.setAll(title, description) + loadProject: -> return @rs.projects.getBySlug(@params.pslug).then (project) => if not project.is_wiki_activated diff --git a/app/coffee/utils.coffee b/app/coffee/utils.coffee index 58874d4f..621f09aa 100644 --- a/app/coffee/utils.coffee +++ b/app/coffee/utils.coffee @@ -126,6 +126,19 @@ startswith = (str1, str2) -> return _.str.startsWith(str1, str2) +truncate = (str, maxLength, suffix="...") -> + return str if (typeof str != "string") and not (str instanceof String) + + out = str.slice(0) + + if out.length > maxLength + out = out.substring(0, maxLength + 1) + out = out.substring(0, Math.min(out.length, out.lastIndexOf(" "))) + out = out + suffix + + return out + + sizeFormat = (input, precision=1) -> if isNaN(parseFloat(input)) or not isFinite(input) return "-" @@ -187,6 +200,7 @@ taiga.cancelTimeout = cancelTimeout taiga.scopeDefer = scopeDefer taiga.toString = toString taiga.joinStr = joinStr +taiga.truncate = truncate taiga.debounce = debounce taiga.debounceLeading = debounceLeading taiga.startswith = startswith diff --git a/app/index.jade b/app/index.jade index c8068c33..105ca868 100644 --- a/app/index.jade +++ b/app/index.jade @@ -3,10 +3,12 @@ html(lang="en") head meta(charset="utf-8") meta(http-equiv="content-type", content="text/html; charset=utf-8") + meta(name="fragment", content="!") + + // Main meta title Taiga meta(name="description", content="Taiga is a project management platform for startups and agile developers & designers who want a simple, beautiful tool that makes work truly enjoyable.") meta(name="keywords", content="agile, scrum, taiga, management, project, developer, designer, user experience") - meta(name="fragment", content="!") //-meta(name="viewport", content="width=device-width, user-scalable=no") link(rel="stylesheet", href="/styles/main.css") link(rel="icon", type="image/png", href="/images/favicon.png") diff --git a/app/locales/locale-en.json b/app/locales/locale-en.json index e389d273..38459a8e 100644 --- a/app/locales/locale-en.json +++ b/app/locales/locale-en.json @@ -236,20 +236,105 @@ "ADD_WIKI_LINKS": "Add wiki links", "DELETE_WIKI_LINKS": "Delete wiki links" } + }, + "META": { + "PAGE_TITLE": "Taiga", + "PAGE_DESCRIPTION": "Taiga is a project management platform for startups and agile developers & designers who want a simple, beautiful tool that makes work truly enjoyable." } }, + "LOGIN": { + "PAGE_TITLE": "Login - Taiga", + "PAGE_DESCRIPTION": "Logging in to Taiga, a project management platform for startups and agile developers & designers who want a simple, beautif ul tool that makes work truly enjoyable." + }, "AUTH": { "INVITED_YOU": "has invited you to join the project", "NOT_REGISTERED_YET": "Not registered yet?", "REGISTER": "Register", "CREATE_ACCOUNT": "create your free account here" }, + "LOGIN_COMMON": { + "HEADER": "I already have a Taiga login", + "PLACEHOLDER_AUTH_NAME": "Username or email (case sensitive)", + "LINK_FORGOT_PASSWORD": "Forgot it?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forgot your password?", + "ACTION_ENTER": "Enter", + "ACTION_SIGN_IN": "Sign in", + "PLACEHOLDER_AUTH_PASSWORD": "Password (case sensitive)" + }, + "LOGIN_FORM": { + "ERROR_AUTH_INCORRECT": "According to our Oompa Loompas, your username/email or password are incorrect.", + "ERROR_GENERIC": "According to our Oompa Loompas there was an error.", + "SUCCESS": "Our Oompa Loompas are happy, welcome to Taiga." + }, + "REGISTER": { + "PAGE_TITLE": "Register - Taiga", + "PAGE_DESCRIPTION": "Create your account in Taiga, a project management platform for startups and agile developers & designers who want a simple, beautif ul tool that makes work truly enjoyable." + }, + "REGISTER_FORM": { + "TITLE": "Register a new Taiga account (free)", + "PLACEHOLDER_NAME": "Pick a username (case sensitive)", + "PLACEHOLDER_FULL_NAME": "Pick your full name", + "PLACEHOLDER_EMAIL": "Your email", + "PLACEHOLDER_PASSWORD": "Set a password (case sensitive)", + "ACTION_SIGN_UP": "Sign up", + "TITLE_LINK_LOGIN": "Log in", + "LINK_LOGIN": "Are you already registered? Log in" + }, + "FORGOT_PASSWORD": { + "PAGE_TITLE": "Forgot password - Taiga", + "PAGE_DESCRIPTION": "Enter your username or email to get a new password and you can access to Taiga again." + }, + "FORGOT_PASSWORD_FORM": { + "TITLE": "Oops, did you forget your password?", + "SUBTITLE": "Enter your username or email to get a new one", + "PLACEHOLDER_FIELD": "Username or email", + "ACTION_RESET_PASSWORD": "Reset Password", + "LINK_CANCEL": "Nah, take me back. I think I remember it.", + "SUCCESS": "Check your inbox!
We have sent you an email with the instructions to set a new password", + "ERROR": "According to our Oompa Loompas, your are not registered yet." + }, + "CHANGE_PASSWORD": { + "PAGE_TITLE": "Change you password - Taiga", + "PAGE_DESCRIPTION": "Set a new passoword for your Taiga account and hey!, you may want to eat some more iron-rich food, it's good for your brain :P", + "SECTION_NAME": "Change password", + "FIELD_CURRENT_PASSWORD": "Current password", + "PLACEHOLDER_CURRENT_PASSWORD": "Your current password (or empty if you have no password yet)", + "FIELD_NEW_PASSWORD": "New password", + "PLACEHOLDER_NEW_PASSWORD": "Type a new password", + "FIELD_RETYPE_PASSWORD": "Retype new password", + "PLACEHOLDER_RETYPE_PASSWORD": "Retype the new password", + "ERROR_PASSWORD_MATCH": "The passwords doesn't match" + }, + "CHANGE_PASSWORD_RECOVERY_FORM": { + "TITLE": "Create a new Taiga pass", + "SUBTITLE": "And hey, you may want to eat some more iron-rich food, it's good for your brain :P", + "PLACEHOLDER_RECOVER_PASSWORD_TOKEN": "Recover password token", + "LINK_NEED_TOKEN": "Need one?", + "TITLE_LINK_NEED_TOKEN": "Did you need a token to recover your password because you forgot it?", + "PLACEHOLDER_NEW_PASSWORD": "New password", + "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Re-type new password", + "ACTION_RESET_PASSWORD": "Reset Password", + "SUCCESS": "Our Oompa Loompas saved your new password.
Try to sign in with it." + }, + "INVITATION": { + "PAGE_TITLE": "Invitation acceptance - Taiga", + "PAGE_DESCRIPTION": "Accept the invitation to join a project in Taiga, a project management platform for startups and agile developers & designers who want a simple, beautif ul tool that makes work truly enjoyable." + }, + "INVITATION_LOGIN_FORM": { + "NOT_FOUND": "Ooops, we have a problem
Our Oompa Loompas can't find your invitation.", + "SUCCESS": "You've successfully joined this project, Welcome to {{project_name}}", + "ERROR": "According to our Oompa Loompas, your are not registered yet or typed an invalid password." + }, "HOME": { + "PAGE_TITLE": "Home - Taiga", + "PAGE_DESCRIPTION": "The Taiga home page with your main projects and all your assigned and watched user stories, tasks and issues", "EMPTY_WATCHING": "Follow the projects, User Stories, Tasks, Issues... that you want to know about :)", "WORKING_ON_SECTION": "Working on", "WATCHING_SECTION": "Watching" }, "PROJECTS": { + "PAGE_TITLE": "My projects - Taiga", + "PAGE_DESCRIPTION": "A list with all your projects, you can reorder or create a new one.", "MY_PROJECTS": "My projects" }, "ATTACHMENT": { @@ -289,6 +374,7 @@ }, "MEMBERSHIPS": { "TITLE": "Manage members", + "PAGE_TITLE": "Memberships - {{projectName}}", "ADD_BUTTON": "+ New member", "ADD_BUTTON_TITLE": "Add new member" }, @@ -324,7 +410,7 @@ "SALT_CHAT_ROOM": "If you want you can append a salt code to the name of the chat room" }, "PROJECT_PROFILE": { - "PAGE_TITLE": "Project profile - {{sectionName}} - {{projectName}}", + "PAGE_TITLE": "{{sectionName}} - Project profile - {{projectName}}", "PROJECT_DETAILS": "Project details", "PROJECT_NAME": "Project name", "PROJECT_SLUG": "Project slug", @@ -365,23 +451,26 @@ "ISSUE_ADD": "Add a custom field in issues" }, "PROJECT_VALUES": { - "APP_TITLE": "Project values - {{sectionName}} - {{projectName}}", + "PAGE_TITLE": "{{sectionName}} - Project values - {{projectName}}", "REPLACEMENT": "All items with this value will be changed to", "ERROR_DELETE_ALL": "You can't delete all values." }, "PROJECT_VALUES_POINTS": { - "TITLE": "Us points", + "TITLE": "Points", "SUBTITLE": "Specify the points your user stories could be estimated to", + "US_TITLE": "US points", "ACTION_ADD": "Add new point" }, "PROJECT_VALUES_PRIORITIES": { - "TITLE": "Issue priorities", + "TITLE": "Priorities", "SUBTITLE": "Specify the priorities your issues will have", + "ISSUE_TITLE": "Issue priorities", "ACTION_ADD": "Add new priority" }, "PROJECT_VALUES_SEVERITIES": { - "TITLE": "Issue severities", + "TITLE": "Severities", "SUBTITLE": "Specify the severities your issues will have", + "ISSUE_TITLE": "Issue severities", "ACTION_ADD": "Add new severity" }, "PROJECT_VALUES_STATUS": { @@ -398,7 +487,7 @@ "ACTION_ADD": "Add new {{objName}}" }, "ROLES": { - "SECTION_NAME": "Roles - {{projectName}}", + "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", "HELP_ROLE_ENABLED": "When enabled, members assigned to this role will be able to estimate the point value for user stories", "COUNT_MEMBERS": "{{ role.members_count }} members with this role", @@ -415,20 +504,20 @@ }, "BITBUCKET": { "SECTION_NAME": "Bitbucket", - "APP_TITLE": "Bitbucket - {{projectName}}", + "PAGE_TITLE": "Bitbucket - {{projectName}}", "INFO_VERIFYING_IP": "Bitbucket requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation." }, "GITLAB": { "SECTION_NAME": "Gitlab", - "APP_TITLE": "Gitlab - {{projectName}}", + "PAGE_TITLE": "Gitlab - {{projectName}}", "INFO_VERIFYING_IP": "Gitlab requests are not signed so the best way of verifying the origin is by IP. If the field is empty there will be no IP validation." }, "GITHUB": { "SECTION_NAME": "Github", - "APP_TITLE": "Github - {{projectName}}" + "PAGE_TITLE": "Github - {{projectName}}" }, "WEBHOOKS": { - "APP_TITLE": "Webhooks - {{projectName}}", + "PAGE_TITLE": "Webhooks - {{projectName}}", "SECTION_NAME": "Webhooks", "SUBTITLE": "Webhooks notify external services about events in Taiga, like comments, user stories....", "ADD_NEW": "Add a New Webhook", @@ -454,6 +543,7 @@ "WEBHOOK_NAME": "Webhook '{{name}}'" }, "CUSTOM_ATTRIBUTES": { + "PAGE_TITLE": "{{sectionName}} - Custom Attributes - {{projectName}}", "ADD": "Add custom field", "EDIT": "Edit Custom Field", "DELETE": "Delete Custom Field", @@ -533,6 +623,7 @@ }, "USER": { "PROFILE": { + "PAGE_TITLE": "{{userFullName}} (@{{userUsername}})", "EDIT": "Edit profile", "FOLLOW": "Follow", "PROJECTS": "Projects", @@ -555,6 +646,7 @@ } }, "PROJECT": { + "PAGE_TITLE": "{{projectName}}", "WELCOME": "Welcome", "SECTION_PROJECTS": "Projects", "HELP": "Reorder your projects to set in the top the most used ones.
The top 10 projects will appear in the top navigation bar project list", @@ -685,6 +777,9 @@ } }, "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", "LINK_TASKBOARD": "Taskboard", "TITLE_LINK_TASKBOARD": "Go to the taskboard", @@ -772,6 +867,8 @@ } }, "BACKLOG": { + "PAGE_TITLE": "Backlog - {{projectName}}", + "PAGE_DESCRIPTION": "The backlog panel, with user stories and sprints of the project {{projectName}}: {{projectDescription}}", "SECTION_NAME": "Backlog", "MOVE_US_TO_CURRENT_SPRINT": "Move to Current Sprint", "SHOW_FILTERS": "Show filters", @@ -852,6 +949,8 @@ "VERSION_ERROR": "Someone inside Taiga has changed this before and our Oompa Loompas cannot apply your changes. Please reload and apply your changes again (they will be lost)." }, "TASKBOARD": { + "PAGE_TITLE": "{{sprintName}} - Sprint taskboard - {{projectName}}", + "PAGE_DESCRIPTION": "Sprint {{sprintName}} (from {{startDate}} to {{endDate}}) of {{projectName}}. Completed {{completedPercentage}}% ({{completedPoints}} of {{totalPoints}} points). {{openTasks}} opened tasks of {{totalTasks}}." , "SECTION_NAME": "Taskboard", "TITLE_ACTION_ADD": "Add a new Task", "TITLE_ACTION_ADD_BULK": "Add some new Tasks in bulk", @@ -875,6 +974,8 @@ } }, "TASK": { + "PAGE_TITLE": "{{taskSubject}} - Task {{taskRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Status: {{taskStatus }}. Description: {{taskDescription}}", "SECTION_NAME": "Task details", "LINK_TASKBOARD": "Taskboard", "TITLE_LINK_TASKBOARD": "Go to the taskboard", @@ -920,56 +1021,9 @@ "ACTION_CHANGE_EMAIL": "Change email", "SUCCESS": "Our Oompa Loompas updated your email" }, - "CHANGE_PASSWORD_RECOVERY_FORM": { - "TITLE": "Create a new Taiga pass", - "SUBTITLE": "And hey, you may want to eat some more iron-rich food, it's good for your brain :P", - "PLACEHOLDER_RECOVER_PASSWORD_TOKEN": "Recover password token", - "LINK_NEED_TOKEN": "Need one?", - "TITLE_LINK_NEED_TOKEN": "Did you need a token to recover your password because you forgot it?", - "PLACEHOLDER_NEW_PASSWORD": "New password", - "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Re-type new password", - "ACTION_RESET_PASSWORD": "Reset Password", - "SUCCESS": "Our Oompa Loompas saved your new password.
Try to sign in with it." - }, - "FORGOT_PASSWORD_FORM": { - "TITLE": "Oops, did you forget your password?", - "SUBTITLE": "Enter your username or email to get a new one", - "PLACEHOLDER_FIELD": "Username or email", - "ACTION_RESET_PASSWORD": "Reset Password", - "LINK_CANCEL": "Nah, take me back. I think I remember it.", - "SUCCESS": "Check your inbox!
We have sent you an email with the instructions to set a new password", - "ERROR": "According to our Oompa Loompas, your are not registered yet." - }, - "LOGIN_COMMON": { - "HEADER": "I already have a Taiga login", - "PLACEHOLDER_AUTH_NAME": "Username or email (case sensitive)", - "LINK_FORGOT_PASSWORD": "Forgot it?", - "TITLE_LINK_FORGOT_PASSWORD": "Did you forgot your password?", - "ACTION_ENTER": "Enter", - "ACTION_SIGN_IN": "Sign in", - "PLACEHOLDER_AUTH_PASSWORD": "Password (case sensitive)" - }, - "LOGIN_FORM": { - "ERROR_AUTH_INCORRECT": "According to our Oompa Loompas, your username/email or password are incorrect.", - "ERROR_GENERIC": "According to our Oompa Loompas there was an error.", - "SUCCESS": "Our Oompa Loompas are happy, welcome to Taiga." - }, - "INVITATION_LOGIN_FORM": { - "NOT_FOUND": "Ooops, we have a problem
Our Oompa Loompas can't find your invitation.", - "SUCCESS": "You've successfully joined this project, Welcome to {{project_name}}", - "ERROR": "According to our Oompa Loompas, your are not registered yet or typed an invalid password." - }, - "REGISTER_FORM": { - "TITLE": "Register a new Taiga account (free)", - "PLACEHOLDER_NAME": "Pick a username (case sensitive)", - "PLACEHOLDER_FULL_NAME": "Pick your full name", - "PLACEHOLDER_EMAIL": "Your email", - "PLACEHOLDER_PASSWORD": "Set a password (case sensitive)", - "ACTION_SIGN_UP": "Sign up", - "TITLE_LINK_LOGIN": "Log in", - "LINK_LOGIN": "Are you already registered? Log in" - }, "ISSUES": { + "PAGE_TITLE": "Issues - {{projectName}}", + "PAGE_DESCRIPTION": "The issues list panel of the project {{projectName}}: {{projectDescription}}", "LIST_SECTION_NAME": "Issues", "SECTION_NAME": "Issue details", "ACTION_NEW_ISSUE": "+ NEW ISSUE", @@ -1033,7 +1087,13 @@ } } }, + "ISSUE": { + "PAGE_TITLE": "{{issueSubject}} - Issue {{issueRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Status: {{issueStatus }}. Type: {{issueType}}, Priority: {{issuePriority}}. Severity: {{issueSeverity}}. Description: {{issueDescription}}" + }, "KANBAN": { + "PAGE_TITLE": "Kanban - {{projectName}}", + "PAGE_DESCRIPTION": "The kanban panel, with user stories of the project {{projectName}}: {{projectDescription}}", "SECTION_NAME": "Kanban", "TITLE_ACTION_FOLD": "Fold column", "TITLE_ACTION_UNFOLD": "Unfold column", @@ -1048,6 +1108,8 @@ "UNDO_ARCHIVED": "Drag & drop again to undo" }, "SEARCH": { + "PAGE_TITLE": "Search - {{projectName}}", + "PAGE_DESCRIPTION": "Search anything, user stories, issues, tasks or wiki pages, in the project {{projectName}}: {{projectDescription}}", "FILTER_USER_STORIES": "User Stories", "FILTER_ISSUES": "Issues", "FILTER_TASKS": "Tasks", @@ -1058,6 +1120,8 @@ "EMPTY_DESCRIPTION": "Maybe try one of the tabs above or search again" }, "TEAM": { + "PAGE_TITLE": "Team - {{projectName}}", + "PAGE_DESCRIPTION": "The team panel to show all the members of the project {{projectName}}: {{projectDescription}}", "SECTION_NAME": "Team", "APP_TITLE": "TEAM - {{projectName}}", "PLACEHOLDER_INPUT_SEARCH": "Search by full name...", @@ -1078,16 +1142,6 @@ "CONFIRM_LEAVE_PROJECT": "Are you sure you want to leave the project?", "ACTION_LEAVE_PROJECT": "Leave this project" }, - "CHANGE_PASSWORD": { - "SECTION_NAME": "Change password", - "FIELD_CURRENT_PASSWORD": "Current password", - "PLACEHOLDER_CURRENT_PASSWORD": "Your current password (or empty if you have no password yet)", - "FIELD_NEW_PASSWORD": "New password", - "PLACEHOLDER_NEW_PASSWORD": "Type a new password", - "FIELD_RETYPE_PASSWORD": "Retype new password", - "PLACEHOLDER_RETYPE_PASSWORD": "Retype the new password", - "ERROR_PASSWORD_MATCH": "The passwords doesn't match" - }, "USER_SETTINGS": { "AVATAR_MAX_SIZE": "[Max. size: {{maxFileSize}}]", "MENU": { @@ -1141,6 +1195,8 @@ "PROGRESS_NAME_DESCRIPTION": "Name and description" }, "WIKI": { + "PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}", + "PAGE_DESCRIPTION": "Last edition on {{lastModifiedDate}} ({{totalEditions}} editions in total) Content: {{ wikiPageContent }}", "DATETIME": "DD MMM YYYY HH:mm", "PLACEHOLDER_PAGE": "Write your wiki page", "REMOVE": "Remove this wiki page", diff --git a/app/modules/profile/profile.controller.coffee b/app/modules/profile/profile.controller.coffee index cca54f1c..e5ac1770 100644 --- a/app/modules/profile/profile.controller.coffee +++ b/app/modules/profile/profile.controller.coffee @@ -1,13 +1,14 @@ class ProfileController @.$inject = [ - "$appTitle", + "tgAppMetaService", "tgCurrentUserService", "$routeParams", "tgUserService", - "tgXhrErrorService" + "tgXhrErrorService", + "$translate" ] - constructor: (@appTitle, @currentUserService, @routeParams, @userService, @xhrError) -> + constructor: (@appMetaService, @currentUserService, @routeParams, @userService, @xhrError, @translate) -> @.isCurrentUser = false if @routeParams.slug @@ -16,13 +17,21 @@ class ProfileController .then (user) => @.user = user @.isCurrentUser = false - @appTitle.set(@.user.get('full_name')) + @._setMeta(@.user) .catch (xhr) => @xhrError.response(xhr) else @.user = @currentUserService.getUser() @.isCurrentUser = true - @appTitle.set(@.user.get('full_name_display')) + @._setMeta(@.user) + + _setMeta: (user) -> + title = @translate.instant("USER.PROFILE.PAGE_TITLE", { + userFullName: user.get("full_name_display"), + userUsername: user.get("username") + }) + description = user.get("bio") + @appMetaService.setAll(title, description) angular.module("taigaProfile").controller("Profile", ProfileController) diff --git a/app/modules/profile/profile.controller.spec.coffee b/app/modules/profile/profile.controller.spec.coffee index e624113a..d471f100 100644 --- a/app/modules/profile/profile.controller.spec.coffee +++ b/app/modules/profile/profile.controller.spec.coffee @@ -10,18 +10,21 @@ describe "ProfileController", -> {id: 3} ]) - _mockAppTitle = () -> - stub = sinon.stub() - - mocks.appTitle = { - set: sinon.spy() + _mockTranslate = () -> + mocks.translate = { + instant: sinon.stub() } - provide.value "$appTitle", mocks.appTitle + provide.value "$translate", mocks.translate + + _mockAppMetaService = () -> + mocks.appMetaService = { + setAll: sinon.spy() + } + + provide.value "tgAppMetaService", mocks.appMetaService _mockCurrentUser = () -> - stub = sinon.stub() - mocks.currentUser = { getUser: sinon.stub() } @@ -29,8 +32,6 @@ describe "ProfileController", -> provide.value "tgCurrentUserService", mocks.currentUser _mockUserService = () -> - stub = sinon.stub() - mocks.userService = { getUserByUserName: sinon.stub() } @@ -38,15 +39,11 @@ describe "ProfileController", -> provide.value "tgUserService", mocks.userService _mockRouteParams = () -> - stub = sinon.stub() - mocks.routeParams = {} provide.value "$routeParams", mocks.routeParams _mockXhrErrorService = () -> - stub = sinon.stub() - mocks.xhrErrorService = { response: sinon.spy() } @@ -56,12 +53,12 @@ describe "ProfileController", -> _mocks = () -> module ($provide) -> provide = $provide - _mockAppTitle() + _mockTranslate() + _mockAppMetaService() _mockCurrentUser() _mockRouteParams() _mockUserService() _mockXhrErrorService() - return null _inject = (callback) -> @@ -81,9 +78,18 @@ describe "ProfileController", -> mocks.routeParams.slug = "user-slug" user = Immutable.fromJS({ - full_name: "full-name" + username: "username" + full_name_display: "full-name-display" + bio: "bio" }) + mocks.translate.instant + .withArgs('USER.PROFILE.PAGE_TITLE', { + userFullName: user.get("full_name_display"), + userUsername: user.get("username") + }) + .returns('user-profile-page-title') + mocks.userService.getUserByUserName.withArgs(mocks.routeParams.slug).promise().resolve(user) ctrl = $controller("Profile") @@ -91,8 +97,7 @@ describe "ProfileController", -> setTimeout ( -> expect(ctrl.user).to.be.equal(user) expect(ctrl.isCurrentUser).to.be.false - expect(mocks.appTitle.set.calledWithExactly("full-name")).to.be.true - + expect(mocks.appMetaService.setAll.calledWithExactly("user-profile-page-title", "bio")).to.be.true done() ) @@ -111,7 +116,6 @@ describe "ProfileController", -> setTimeout ( -> expect(mocks.xhrErrorService.response.withArgs(xhr)).to.be.calledOnce - done() ) @@ -119,13 +123,22 @@ describe "ProfileController", -> $scope = $rootScope.$new() user = Immutable.fromJS({ + username: "username" full_name_display: "full-name-display" + bio: "bio" }) + mocks.translate.instant + .withArgs('USER.PROFILE.PAGE_TITLE', { + userFullName: user.get("full_name_display"), + userUsername: user.get("username") + }) + .returns('user-profile-page-title') + mocks.currentUser.getUser.returns(user) ctrl = $controller("Profile") expect(ctrl.user).to.be.equal(user) expect(ctrl.isCurrentUser).to.be.true - expect(mocks.appTitle.set.calledWithExactly("full-name-display")).to.be.true + expect(mocks.appMetaService.setAll.withArgs("user-profile-page-title", "bio")).to.be.calledOnce diff --git a/app/modules/projects/project/project.controller.coffee b/app/modules/projects/project/project.controller.coffee index 2da83dc1..55816d52 100644 --- a/app/modules/projects/project/project.controller.coffee +++ b/app/modules/projects/project/project.controller.coffee @@ -2,22 +2,28 @@ class ProjectController @.$inject = [ "tgProjectsService", "$routeParams", - "$appTitle", + "tgAppMetaService", "$tgAuth", - "tgXhrErrorService" + "tgXhrErrorService", + "$translate" ] - constructor: (@projectsService, @routeParams, @appTitle, @auth, @xhrError) -> + constructor: (@projectsService, @routeParams, @appMetaService, @auth, @xhrError, @translate) -> projectSlug = @routeParams.pslug @.user = @auth.userData @projectsService .getProjectBySlug(projectSlug) .then (project) => - @appTitle.set(project.get("name")) - @.project = project + @._setMeta(@.project) + .catch (xhr) => @xhrError.response(xhr) + _setMeta: (project)-> + title = @translate.instant("PROJECT.PAGE_TITLE", {projectName: project.get("name")}) + description = project.get("description") + @appMetaService.setAll(title, description) + angular.module("taigaProjects").controller("Project", ProjectController) diff --git a/app/modules/projects/project/project.controller.spec.coffee b/app/modules/projects/project/project.controller.spec.coffee index b28d7f87..03951199 100644 --- a/app/modules/projects/project/project.controller.spec.coffee +++ b/app/modules/projects/project/project.controller.spec.coffee @@ -12,12 +12,12 @@ describe "ProjectController", -> provide.value "tgProjectsService", mocks.projectService - _mockAppTitle = () -> - mocks.appTitle = { - set: sinon.stub() + _mockAppMetaService = () -> + mocks.appMetaService = { + setAll: sinon.stub() } - provide.value "$appTitle", mocks.appTitle + provide.value "tgAppMetaService", mocks.appMetaService _mockAuth = () -> mocks.auth = { @@ -38,15 +38,22 @@ describe "ProjectController", -> provide.value "tgXhrErrorService", mocks.xhrErrorService + _mockTranslate = () -> + mocks.translate = { + instant: sinon.stub() + } + + provide.value "$translate", mocks.translate + _mocks = () -> module ($provide) -> provide = $provide _mockProjectsService() _mockRouteParams() - _mockAppTitle() + _mockAppMetaService() _mockAuth() _mockXhrErrorService() - + _mockTranslate() return null _inject = (callback) -> @@ -73,16 +80,24 @@ describe "ProjectController", -> expect(ctrl.user).to.be.equal(mocks.auth.userData) it "set page title", (done) -> + $scope = $rootScope.$new() project = Immutable.fromJS({ name: "projectName" + description: "projectDescription" }) + mocks.translate.instant + .withArgs('PROJECT.PAGE_TITLE', { + projectName: project.get("name") + }) + .returns('projectTitle') + mocks.projectService.getProjectBySlug.withArgs("project-slug").promise().resolve(project) ctrl = $controller("Project") setTimeout ( -> - expect(mocks.appTitle.set.withArgs("projectName")).to.be.calledOnce + expect(mocks.appMetaService.setAll.calledWithExactly("projectTitle", "projectDescription")).to.be.true done() ) diff --git a/app/modules/services/app-meta.service.coffee b/app/modules/services/app-meta.service.coffee new file mode 100644 index 00000000..1b729006 --- /dev/null +++ b/app/modules/services/app-meta.service.coffee @@ -0,0 +1,63 @@ +taiga = @.taiga + +truncate = taiga.truncate + + +class AppMetaService extends taiga.Service = -> + _set: (key, value) -> + return if not key + + if key == "title" + meta = $("title") + + if meta.length == 0 + meta = $("") + $("head").append(meta) + + meta.text(value or "") + if key.indexOf("og:") == 0 + meta = $("meta[property='#{key}']") + + if meta.length == 0 + meta = $("") + $("head").append(meta) + + meta.attr("content", value or "") + else + meta = $("meta[name='#{key}']") + + if meta.length == 0 + meta = $("") + $("head").append(meta) + + meta.attr("content", value or "") + + setTitle: (title) -> + @._set('title', title) + + setDescription: (description) -> + @._set("description", truncate(description, 250)) + + setTwitterMetas: (title, description) -> + @._set("twitter:card", "summary") + @._set("twitter:site", "@taigaio") + @._set("twitter:title", title) + @._set("twitter:description", truncate(description, 250)) + @._set("twitter:image", "#{window.location.origin}/images/favicon.png") + + setOpenGraphMetas: (title, description) -> + @._set("og:type", "object") + @._set("og:site_name", "Taiga - Love your projects") + @._set("og:title", title) + @._set("og:description", truncate(description, 250)) + @._set("og:image", "#{window.location.origin}/images/favicon.png") + @._set("og:url", window.location.href) + + setAll: (title, description) -> + @.setTitle(title) + @.setDescription(description) + @.setTwitterMetas(title, description) + @.setOpenGraphMetas(title, description) + + +angular.module("taigaCommon").service("tgAppMetaService", AppMetaService) diff --git a/app/modules/services/app-meta.service.spec.coffee b/app/modules/services/app-meta.service.spec.coffee new file mode 100644 index 00000000..d2a4ad0d --- /dev/null +++ b/app/modules/services/app-meta.service.spec.coffee @@ -0,0 +1,55 @@ +describe "AppMetaService", -> + appMetaService = null + data = { + title: "--title--", + description: "--description--" + } + + _inject = () -> + inject (_tgAppMetaService_) -> + appMetaService = _tgAppMetaService_ + + beforeEach -> + module "taigaCommon" + _inject() + + it "set meta title", () -> + appMetaService.setTitle(data.title) + expect($("title")).to.have.text(data.title) + + it "set meta description", () -> + appMetaService.setDescription(data.description) + expect($("meta[name='description']")).to.have.attr("content", data.description) + + it "set meta for twitter", () -> + appMetaService.setTwitterMetas(data.title, data.description) + expect($("meta[name='twitter:card']")).to.have.attr("content", "summary") + expect($("meta[name='twitter:site']")).to.have.attr("content", "@taigaio") + expect($("meta[name='twitter:title']")).to.have.attr("content", data.title) + expect($("meta[name='twitter:description']")).to.have.attr("content", data.description) + expect($("meta[name='twitter:image']")).to.have.attr("content", "#{window.location.origin}/images/favicon.png") + + it "set meta for open graph", () -> + appMetaService.setOpenGraphMetas(data.title, data.description) + expect($("meta[property='og:type']")).to.have.attr("content", "object") + expect($("meta[property='og:site_name']")).to.have.attr("content", "Taiga - Love your projects") + expect($("meta[property='og:title']")).to.have.attr("content", data.title) + expect($("meta[property='og:description']")).to.have.attr("content", data.description) + expect($("meta[property='og:image']")).to.have.attr("content", "#{window.location.origin}/images/favicon.png") + expect($("meta[property='og:url']")).to.have.attr("content", window.location.href) + + it "set all meta", () -> + appMetaService.setAll(data.title, data.description) + expect($("title")).to.have.text(data.title) + expect($("meta[name='description']")).to.have.attr("content", data.description) + expect($("meta[name='twitter:card']")).to.have.attr("content", "summary") + expect($("meta[name='twitter:site']")).to.have.attr("content", "@taigaio") + expect($("meta[name='twitter:title']")).to.have.attr("content", data.title) + expect($("meta[name='twitter:description']")).to.have.attr("content", data.description) + expect($("meta[name='twitter:image']")).to.have.attr("content", "#{window.location.origin}/images/favicon.png") + expect($("meta[property='og:type']")).to.have.attr("content", "object") + expect($("meta[property='og:site_name']")).to.have.attr("content", "Taiga - Love your projects") + expect($("meta[property='og:title']")).to.have.attr("content", data.title) + expect($("meta[property='og:description']")).to.have.attr("content", data.description) + expect($("meta[property='og:image']")).to.have.attr("content", "#{window.location.origin}/images/favicon.png") + expect($("meta[property='og:url']")).to.have.attr("content", window.location.href) diff --git a/app/partials/admin/admin-project-values-points.jade b/app/partials/admin/admin-project-values-points.jade index 0f977feb..52c30e15 100644 --- a/app/partials/admin/admin-project-values-points.jade +++ b/app/partials/admin/admin-project-values-points.jade @@ -1,6 +1,7 @@ doctype html -div.wrapper(ng-controller="ProjectValuesSectionController") +div.wrapper(ng-controller="ProjectValuesSectionController", + ng-init="sectionName='ADMIN.PROJECT_VALUES_POINTS.TITLE'") tg-project-menu @@ -15,7 +16,7 @@ div.wrapper(ng-controller="ProjectValuesSectionController") p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_POINTS.SUBTITLE") div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='userstories'; type='points'; sectionName='ADMIN.PROJECT_VALUES_POINTS.TITLE'", + ng-init="section='admin'; resource='userstories'; type='points'; sectionName='ADMIN.PROJECT_VALUES_POINTS.US_TITLE'", objName="points", type="points") include ../includes/modules/admin/project-points diff --git a/app/partials/admin/admin-project-values-priorities.jade b/app/partials/admin/admin-project-values-priorities.jade index cc27c4d3..21766a19 100644 --- a/app/partials/admin/admin-project-values-priorities.jade +++ b/app/partials/admin/admin-project-values-priorities.jade @@ -1,6 +1,8 @@ doctype html -div.wrapper(ng-controller="ProjectValuesSectionController") +div.wrapper(ng-controller="ProjectValuesSectionController", + ng-init="sectionName='ADMIN.PROJECT_VALUES_PRIORITIES.TITLE'") + tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-values") @@ -14,7 +16,7 @@ div.wrapper(ng-controller="ProjectValuesSectionController") p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_PRIORITIES.SUBTITLE") div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='ADMIN.PROJECT_VALUES_PRIORITIES.TITLE';", + ng-init="section='admin'; resource='issues'; type='priorities'; sectionName='ADMIN.PROJECT_VALUES_PRIORITIES.ISSUE_TITLE';", objName="priorities", type="priorities") include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-severities.jade b/app/partials/admin/admin-project-values-severities.jade index f950ed18..e3501b48 100644 --- a/app/partials/admin/admin-project-values-severities.jade +++ b/app/partials/admin/admin-project-values-severities.jade @@ -1,6 +1,8 @@ doctype html -div.wrapper(ng-controller="ProjectValuesSectionController") +div.wrapper(ng-controller="ProjectValuesSectionController", + ng-init="sectionName='ADMIN.PROJECT_VALUES_SEVERITIES.TITLE'") + tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-values") @@ -14,7 +16,7 @@ div.wrapper(ng-controller="ProjectValuesSectionController") p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_SEVERITIES.SUBTITLE") div.admin-attributes-section(tg-project-values, ng-controller="ProjectValuesController as ctrl", - ng-init="section='admin'; resource='issues'; type='severities'; sectionName='ADMIN.PROJECT_VALUES_SEVERITIES.TITLE';", + ng-init="section='admin'; resource='issues'; type='severities'; sectionName='ADMIN.PROJECT_VALUES_SEVERITIES.ISSUE_TITLE';", objName="severities", type="severities") include ../includes/modules/admin/project-types diff --git a/app/partials/admin/admin-project-values-status.jade b/app/partials/admin/admin-project-values-status.jade index 818c05c8..8eab5f9b 100644 --- a/app/partials/admin/admin-project-values-status.jade +++ b/app/partials/admin/admin-project-values-status.jade @@ -2,6 +2,7 @@ doctype html div.wrapper(ng-controller="ProjectValuesSectionController", ng-init="section='admin'; sectionName='ADMIN.PROJECT_VALUES_STATUS.TITLE'") + tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-values") diff --git a/app/partials/admin/admin-project-values-types.jade b/app/partials/admin/admin-project-values-types.jade index f4c9c3db..0cc1b084 100644 --- a/app/partials/admin/admin-project-values-types.jade +++ b/app/partials/admin/admin-project-values-types.jade @@ -2,6 +2,7 @@ doctype html div.wrapper(ng-controller="ProjectValuesSectionController" ng-init="sectionName='ADMIN.PROJECT_VALUES_TYPES.TITLE'") + tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-values") diff --git a/app/partials/wiki/editable-wiki-content.jade b/app/partials/wiki/editable-wiki-content.jade index f1009bc3..0aa56352 100644 --- a/app/partials/wiki/editable-wiki-content.jade +++ b/app/partials/wiki/editable-wiki-content.jade @@ -1,11 +1,16 @@ .view-wiki-content section.wysiwyg(tg-bind-html='wiki.html') span.edit.icon.icon-edit(title="{{'COMMON.EDIT' | translate}}", ng-if="wiki") + .edit-wiki-content(style='display: none;') - textarea(ng-attr-placeholder="{{'WIKI.PLACEHOLDER_PAGE' | translate}}", ng-model='wiki.content', tg-markitup='tg-markitup') - a.help-markdown(href='https://taiga.io/support/taiga-markdown-syntax/', target='_blank', title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") - span.icon.icon-help - span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") - span.action-container - a.save.icon.icon-floppy(href='', title="{{'COMMON.SAVE' | translate}}") - a.cancel.icon.icon-delete(href='', title="{{'COMMON.CANCEL' | translate}}") + textarea(ng-attr-placeholder="{{'WIKI.PLACEHOLDER_PAGE' | translate}}", + ng-model='wiki.content', tg-markitup='tg-markitup') + + a.help-markdown(href='https://taiga.io/support/taiga-markdown-syntax/', target='_blank', + title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") + span.icon.icon-help + span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") + + span.action-container + a.save.icon.icon-floppy(href='', title="{{'COMMON.SAVE' | translate}}") + a.cancel.icon.icon-delete(href='', title="{{'COMMON.CANCEL' | translate}}")