diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 51d14005..c94ff19a 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -510,6 +510,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven prefix: "/locales/locale-", suffix: ".json" }) + .useSanitizeValueStrategy('escapeParameters') .addInterpolation('$translateMessageFormatInterpolation') .preferredLanguage(preferedLangCode) @@ -669,6 +670,7 @@ modules = [ "templates", # Vendor modules + "ngSanitize", "ngRoute", "ngAnimate", "ngAria", diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index b71abdb4..6f41595a 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -135,27 +135,18 @@ CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)-> # 'owner'(ng-model) # - scope.usersById object is required. - template = $template.get("common/components/created-by.html", true) - link = ($scope, $el, $attrs) -> - render = (model) -> - owner = model.owner_extra_info or { - full_name_display: $translate.instant("COMMON.EXTERNAL_USER") - photo: "/images/user-noimage.png" - } - - html = template({ - owner: owner - url: if owner?.is_active then $navUrls.resolve("user-profile", {username: owner.username}) else "" - date: moment(model.created_date).format($translate.instant("COMMON.DATETIME")) - }) - - html = $compile(html)($scope) - - $el.html(html) - bindOnce $scope, $attrs.ngModel, (model) -> - render(model) if model? + if model? + $scope.owner = model.owner_extra_info or { + full_name_display: $translate.instant("COMMON.EXTERNAL_USER") + photo: "/images/user-noimage.png" + } + + $scope.url = if $scope.owner?.is_active then $navUrls.resolve("user-profile", {username: $scope.owner.username}) else "" + + + $scope.date = moment(model.created_date).format($translate.instant("COMMON.DATETIME")) $scope.$on "$destroy", -> $el.off() @@ -163,13 +154,14 @@ CreatedByDisplayDirective = ($template, $compile, $translate, $navUrls)-> return { link: link restrict: "EA" - require: "ngModel" + require: "ngModel", + scope: true, + templateUrl: "common/components/created-by.html" } module.directive("tgCreatedByDisplay", ["$tgTemplate", "$compile", "$translate", "$tgNavUrls", CreatedByDisplayDirective]) - ############################################################################# ## Watchers directive ############################################################################# diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee index f362ddf7..772d05e3 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.coffee @@ -21,7 +21,8 @@ unslugify = @.taiga.unslugify class UserTimelineItemTitle @.$inject = [ - "$translate" + "$translate", + "$sce" ] _fieldTranslationKey: { @@ -105,7 +106,7 @@ class UserTimelineItemTitle return _.escape(timeline.getIn(['data', 'value_diff', 'value']).keySeq().first()) } - constructor: (@translate) -> + constructor: (@translate, @sce) -> _translateTitleParams: (param, timeline, event) -> @@ -152,7 +153,18 @@ class UserTimelineItemTitle return params getTitle: (timeline, event, type) -> - return @translate.instant(type.key, @._getParams(timeline, event, type)) + params = @._getParams(timeline, event, type) + + paramsKeys = {} + Object.keys(params).forEach (key) -> paramsKeys[key] = '{{' +key + '}}' + + translation = @translate.instant(type.key, paramsKeys) + + Object.keys(params).forEach (key) -> + find = '{{' +key + '}}' + translation = translation.replace(new RegExp(find, 'g'), params[key]) + + return translation angular.module("taigaUserTimeline") .service("tgUserTimelineItemTitle", UserTimelineItemTitle) diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee index ef60ffce..5855c0fc 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-title.service.spec.coffee @@ -72,12 +72,8 @@ describe "tgUserTimelineItemTitle", -> .withArgs('COMMON.SEE_USER_PROFILE', {username: timeline.getIn(['data', 'user', 'username'])}) .returns('user-param') - usernamelink = sinon.match ((value) -> - return value.username == 'oo' - ), "usernamelink" - mockTranslate.instant - .withArgs('TITLE_USER_NAME', usernamelink) + .withArgs('TITLE_USER_NAME', {username: '{{username}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -106,12 +102,8 @@ describe "tgUserTimelineItemTitle", -> .withArgs('COMMON.SEE_USER_PROFILE', {username: timeline.getIn(['data', 'user', 'username'])}) .returns('user-param') - usernamelink = sinon.match ((value) -> - return value.username == 'oo' - ), "usernamelink" - mockTranslate.instant - .withArgs('TITLE_USER_NAME', usernamelink) + .withArgs('TITLE_USER_NAME', {username: '{{username}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -138,12 +130,8 @@ describe "tgUserTimelineItemTitle", -> .withArgs('COMMON.FIELDS.STATUS') .returns('field-params') - fieldparam = sinon.match ((value) -> - return value.field_name == 'field-params' - ), "fieldparam" - mockTranslate.instant - .withArgs('TITLE_FIELD', fieldparam) + .withArgs('TITLE_FIELD', {field_name: '{{field_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -168,7 +156,7 @@ describe "tgUserTimelineItemTitle", -> } mockTranslate.instant - .withArgs('NEW_VALUE', {new_value: 'new'}) + .withArgs('NEW_VALUE', {new_value: '{{new_value}}'}) .returns('new_value_ok') title = mySvc.getTitle(timeline, event, type) @@ -191,12 +179,8 @@ describe "tgUserTimelineItemTitle", -> translate_params: ['project_name'] } - projectparam = sinon.match ((value) -> - return value.project_name == 'project_name' - ), "projectparam" - mockTranslate.instant - .withArgs('TITLE_PROJECT', projectparam) + .withArgs('TITLE_PROJECT', {project_name: '{{project_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -224,7 +208,7 @@ describe "tgUserTimelineItemTitle", -> ), "milestoneparam" mockTranslate.instant - .withArgs('TITLE_MILESTONE', milestoneparam) + .withArgs('TITLE_MILESTONE', {sprint_name: '{{sprint_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -250,12 +234,8 @@ describe "tgUserTimelineItemTitle", -> translate_params: ['obj_name'] } - objparam = sinon.match ((value) -> - return value.obj_name == '#123 subject' - ), "objparam" - mockTranslate.instant - .withArgs('TITLE_OBJ', objparam) + .withArgs('TITLE_OBJ', obj_name: '{{obj_name}}') .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -280,12 +260,8 @@ describe "tgUserTimelineItemTitle", -> translate_params: ['obj_name'] } - objparam = sinon.match ((value) -> - return value.obj_name == 'Slug wiki' - ), "objparam" - mockTranslate.instant - .withArgs('TITLE_OBJ', objparam) + .withArgs('TITLE_OBJ', {obj_name: '{{obj_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -315,7 +291,7 @@ describe "tgUserTimelineItemTitle", -> ), "objparam" mockTranslate.instant - .withArgs('TITLE_OBJ', objparam) + .withArgs('TITLE_OBJ', {obj_name: '{{obj_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) @@ -349,7 +325,7 @@ describe "tgUserTimelineItemTitle", -> ), "objparam" mockTranslate.instant - .withArgs('TITLE_OBJ', objparam) + .withArgs('TITLE_OBJ', {us_name: '{{us_name}}'}) .returns('title_ok') title = mySvc.getTitle(timeline, event, type) diff --git a/app/partials/common/components/created-by.jade b/app/partials/common/components/created-by.jade index 03ed8980..3689afef 100644 --- a/app/partials/common/components/created-by.jade +++ b/app/partials/common/components/created-by.jade @@ -1,9 +1,9 @@ .user-avatar - a(href!="<%- url %>", title!="<%- owner.full_name_display %>") - img(src!="<%- owner.photo %>", alt!="<%- owner.full_name_display %>") + a(href="{{url}}", title="{{owner.full_name_display}}") + img(src="{{owner.photo}}", alt="{{owner.full_name_display}}") .created-by - a(href!="<%- url %>", title!="<%- owner.full_name_display %>") - span.created-title(translate="COMMON.CREATED_BY", translate-values!="{ 'fullDisplayName': '<%- owner.full_name_display %>'}") + a(href="{{url}}", title="{{owner.full_name_display}}") + span.created-title(translate="COMMON.CREATED_BY", translate-values="{ 'fullDisplayName': owner.full_name_display}") span.created-date - | <%- date %> + | {{date}}