Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Commit

Permalink
feat(sanitize): sanitize override on instant call
Browse files Browse the repository at this point in the history
Sometimes non html messages need to be translated. For example in
notification popups, etc.
The only solution as for now, was to the disable sanitize strategy globally.
This modification add a sanitizeStrategy parameter to the
$translate.instant function to allow to override this only for one call.

Relates #1297
  • Loading branch information
spiroid committed Sep 22, 2016
1 parent 132e49a commit 01fecd0
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 34 deletions.
6 changes: 3 additions & 3 deletions src/service/default-interpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
*
* @returns {string} interpolated string.
*/
$translateInterpolator.interpolate = function (value, interpolationParams, context) {
$translateInterpolator.interpolate = function (value, interpolationParams, context, sanitizeStrategy) {
interpolationParams = interpolationParams || {};
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', undefined, context);
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy, context);

var interpolatedText;
if (angular.isNumber(value)) {
Expand All @@ -84,7 +84,7 @@ function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
} else if (angular.isString(value)) {
// strings must be interpolated (that's the job here)
interpolatedText = $interpolate(value)(interpolationParams);
interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', undefined, context);
interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy, context);
} else {
// neither a number or a string, cant interpolate => empty string
interpolatedText = '';
Expand Down
6 changes: 3 additions & 3 deletions src/service/messageformat-interpolation.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ function $translateMessageFormatInterpolation($translateSanitization, $cacheFact
*
* @returns {string} interpolated string.
*/
$translateInterpolator.interpolate = function (string, interpolationParams/*, context*/) {
$translateInterpolator.interpolate = function (string, interpolationParams, context, sanitizeStrategy) {
interpolationParams = interpolationParams || {};
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params');
interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy);

var compiledFunction = $cache.get('mf:' + string);

Expand All @@ -122,7 +122,7 @@ function $translateMessageFormatInterpolation($translateSanitization, $cacheFact
}

var interpolatedText = compiledFunction(interpolationParams);
return $translateSanitization.sanitize(interpolatedText, 'text');
return $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy);
};

return $translateInterpolator;
Expand Down
54 changes: 30 additions & 24 deletions src/service/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -1395,17 +1395,19 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
* @param translationId
* @param interpolateParams
* @param Interpolator
* @param sanitizeStrategy sanitize strategy override
*
* @returns {string} translation
*/
var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator) {
var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
var result, translationTable = $translationTable[langKey];

if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId)) {
Interpolator.setLocale(langKey);
result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter');
result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey);
result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter', sanitizeStrategy);
result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey, sanitizeStrategy);
if (result.substr(0, 2) === '@:') {
return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator);
return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator, sanitizeStrategy);
}
Interpolator.setLocale($uses);
}
Expand All @@ -1423,14 +1425,16 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
* @param translationId
* @param interpolateParams
* @param defaultTranslationText
* @param sanitizeStrategy sanitize strategy override
*
* @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
* absent
*/
var translateByHandler = function (translationId, interpolateParams, defaultTranslationText) {
var translateByHandler = function (translationId, interpolateParams, defaultTranslationText, sanitizeStrategy) {
// If we have a handler factory - we might also call it here to determine if it provides
// a default text for a translationid that can't be found anywhere in our tables
if ($missingTranslationHandlerFactory) {
var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText);
var resultString = $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText, sanitizeStrategy);
if (resultString !== undefined) {
return resultString;
} else {
Expand Down Expand Up @@ -1499,16 +1503,17 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
* @param translationId
* @param interpolateParams
* @param Interpolator
* @param sanitizeStrategy
* @returns {string} translation
*/
var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator) {
var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
var result;

if (fallbackLanguageIndex < $fallbackLanguage.length) {
var langKey = $fallbackLanguage[fallbackLanguageIndex];
result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator);
result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy);
if (!result) {
result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, sanitizeStrategy);
}
}
return result;
Expand All @@ -1535,9 +1540,9 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
* @param Interpolator
* @returns {String} translation
*/
var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator) {
var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator, sanitizeStrategy) {
// Start with the fallbackLanguage with index 0
return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator);
return resolveForFallbackLanguageInstant((startFallbackIteration>0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, sanitizeStrategy);
};

var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText, uses) {
Expand Down Expand Up @@ -1599,7 +1604,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
return deferred.promise;
};

var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses) {
var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses, sanitizeStrategy) {

var result, table = uses ? $translationTable[uses] : $translationTable,
Interpolator = defaultInterpolator;
Expand All @@ -1615,24 +1620,24 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide

// If using link, rerun $translate with linked translationId and return it
if (translation.substr(0, 2) === '@:') {
result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses);
result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses, sanitizeStrategy);
} else {
result = Interpolator.interpolate(translation, interpolateParams, 'filter');
result = applyPostProcessing(translationId, translation, result, interpolateParams, uses);
result = Interpolator.interpolate(translation, interpolateParams, 'filter', sanitizeStrategy);
result = applyPostProcessing(translationId, translation, result, interpolateParams, uses, sanitizeStrategy);
}
} else {
var missingTranslationHandlerTranslation;
// for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
if ($missingTranslationHandlerFactory && !pendingLoader) {
missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams);
missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
}

// since we couldn't translate the inital requested translation id,
// we try it now with one or more fallback languages, if fallback language(s) is
// configured.
if (uses && $fallbackLanguage && $fallbackLanguage.length) {
fallbackIndex = 0;
result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator);
result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator, sanitizeStrategy);
} else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
// looks like the requested translation id doesn't exists.
// Now, if there is a registered handler for missing translations and no
Expand All @@ -1653,7 +1658,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
langPromises[key] = undefined;
};

var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses) {
var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy) {
var fn = postProcessFn;

if (fn) {
Expand All @@ -1663,7 +1668,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
fn = $injector.get(fn);
}
if (fn) {
return fn(translationId, translation, resolvedTranslation, interpolateParams, uses);
return fn(translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy);
}
}

Expand Down Expand Up @@ -2111,10 +2116,11 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
* @param {object} interpolateParams Params
* @param {string} interpolationId The id of the interpolation to use
* @param {string} forceLanguage A language to be used instead of the current language
* @param {string} sanitizeStrategy force sanitize strategy for this call instead of using the configured one
*
* @return {string|object} translation
*/
$translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage) {
$translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage, sanitizeStrategy) {

// we don't want to re-negotiate $uses
var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses
Expand All @@ -2135,7 +2141,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
if (angular.isArray(translationId)) {
var results = {};
for (var i = 0, c = translationId.length; i < c; i++) {
results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage);
results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage, sanitizeStrategy);
}
return results;
}
Expand Down Expand Up @@ -2164,7 +2170,7 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
var possibleLangKey = possibleLangKeys[j];
if ($translationTable[possibleLangKey]) {
if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses);
result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses, sanitizeStrategy);
}
}
if (typeof result !== 'undefined') {
Expand All @@ -2177,9 +2183,9 @@ function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvide
result = applyNotFoundIndicators(translationId);
} else {
// Return translation of default interpolator if not found anything.
result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter');
result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter', sanitizeStrategy);
if ($missingTranslationHandlerFactory && !pendingLoader) {
result = translateByHandler(translationId, interpolateParams);
result = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions test/unit/service/default-interpolation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ describe('pascalprecht.translate', function () {
expect($translateSanitization.sanitize).toHaveBeenCalledWith(params, 'params', undefined, undefined);
}));

it('should not sanitize the interpolation params when a null strategy value is passed',
inject(function ($translateSanitization) {
var text = 'Foo bar {{value}}';
var paramValue = '<span>Test</span>';
var params = { value: paramValue };
var unsanitizedText = 'Foo bar <span>Test</span>';

spyOn($translateSanitization, 'sanitize').and.callThrough();
$translateDefaultInterpolation.useSanitizeValueStrategy('escapeParameters');

expect($translateDefaultInterpolation.interpolate(text, params, 'service', null)).toBe(unsanitizedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(params, 'params', null, 'service');
}));

it('should not sanitize the interpolation params (defaults)', inject(function ($translateSanitization) {
var text = 'Foo <span>bar</span> {{value}}';
var params = { value: 'value' };
Expand Down Expand Up @@ -136,6 +150,19 @@ describe('pascalprecht.translate', function () {
expect($translateDefaultInterpolation.interpolate(text, params)).toBe(sanitizedText);
//expect()
}));

it('should not sanitize the interpolated text when a null strategy value is passed',
inject(function ($translateSanitization) {
var text = 'Foo <span>bar</span> {{value}}';
var params = { value: 'value' };
var interPolatedText = 'Foo <span>bar</span> value';

spyOn($translateSanitization, 'sanitize').and.callThrough();
$translateDefaultInterpolation.useSanitizeValueStrategy('escape');

expect($translateDefaultInterpolation.interpolate(text, params, 'service', null)).toBe(interPolatedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(interPolatedText, 'text', null, 'service');
}));
});

describe('$translateDefaultInterpolation#useSanitizeValueStrategy', function () {
Expand Down
31 changes: 29 additions & 2 deletions test/unit/service/messageformat-interpolation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,21 @@ describe('pascalprecht.translate', function () {
$translateMessageFormatInterpolation.useSanitizeValueStrategy('escapeParameters');

expect($translateMessageFormatInterpolation.interpolate(text, params)).toBe(sanitizedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(params, 'params');
expect($translateSanitization.sanitize).toHaveBeenCalledWith(params, 'params', undefined);
}));

it('should not sanitize the interpolation params when a null strategy value is passed',
inject(function ($translateSanitization) {
var text = 'Foo bar {value}';
var paramValue = '<span>Test</span>';
var params = { value: paramValue };
var unsanitizedText = 'Foo bar <span>Test</span>';

spyOn($translateSanitization, 'sanitize').and.callThrough();
$translateMessageFormatInterpolation.useSanitizeValueStrategy('escapeParameters');

expect($translateMessageFormatInterpolation.interpolate(text, params, 'service', null)).toBe(unsanitizedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(params, 'params', null);
}));

it('should sanitize the interpolated text', inject(function ($translateSanitization) {
Expand All @@ -152,7 +166,20 @@ describe('pascalprecht.translate', function () {
$translateMessageFormatInterpolation.useSanitizeValueStrategy('escape');

expect($translateMessageFormatInterpolation.interpolate(text, params)).toBe(sanitizedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(interPolatedText, 'text');
expect($translateSanitization.sanitize).toHaveBeenCalledWith(interPolatedText, 'text', undefined);
}));

it('should not sanitize the interpolated text when a null strategy value is passed',
inject(function ($translateSanitization) {
var text = 'Foo <span>bar</span> {value}';
var params = { value: 'value' };
var interPolatedText = 'Foo <span>bar</span> value';

spyOn($translateSanitization, 'sanitize').and.callThrough();
$translateMessageFormatInterpolation.useSanitizeValueStrategy('escape');

expect($translateMessageFormatInterpolation.interpolate(text, params, 'service', null)).toBe(interPolatedText);
expect($translateSanitization.sanitize).toHaveBeenCalledWith(interPolatedText, 'text', null);
}));
});
});

0 comments on commit 01fecd0

Please sign in to comment.