Skip to content
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.

Commit

Permalink
fix(legal): fix rendering of legal pages when loaded directly via url
Browse files Browse the repository at this point in the history
Fixes #1372.
  • Loading branch information
zaach committed Jul 11, 2014
1 parent c7d9570 commit 148475c
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 62 deletions.
4 changes: 2 additions & 2 deletions grunttasks/build.js
Expand Up @@ -9,6 +9,8 @@ module.exports = function (grunt) {
// Clean everything
'clean',
'selectconfig:dist',
'l10n-generate-tos-pp:dist',
'l10n-create-json',
// l10n-generate-pages needs to be run before useminPrepare to seed
// the list of resources to minimize. Generated pages are placed into
// `server/templates/pages/dist` where they will be post-processed
Expand All @@ -17,8 +19,6 @@ module.exports = function (grunt) {
// use error pages from en_US as the static error pages
'copy:error_pages',
'useminPrepare',
'l10n-create-json',
'l10n-generate-tos-pp:dist',
'requirejs',
'css',
'concurrent:dist',
Expand Down
41 changes: 37 additions & 4 deletions grunttasks/l10n-generate-pages.js
Expand Up @@ -11,9 +11,15 @@ module.exports = function (grunt) {

var path = require('path');
var Handlebars = require('handlebars');
var Promise = require('bluebird');
var legalTemplates = require('../server/lib/legal-templates');

var defaultLang;
var templateSrc;
var templateDest;
var templates = {
'db-LB': {}
};

// Make the 'gettext' function available in the templates.
Handlebars.registerHelper('t', function (string) {
Expand All @@ -28,17 +34,40 @@ module.exports = function (grunt) {
grunt.registerTask('l10n-generate-pages',
'Generate localized versions of the static pages', function () {

var done = this.async();

var i18n = require('../server/lib/i18n')(grunt.config.get('server.i18n'));

// server config is set in the selectconfig task
var supportedLanguages = grunt.config.get('server.i18n.supportedLanguages');
defaultLang = grunt.config.get('server.i18n.defaultLang');

templateSrc = grunt.config.get('yeoman.page_template_src');
templateDest = grunt.config.get('yeoman.page_template_dist');

supportedLanguages.forEach(function (lang) {
generatePagesForLanguage(i18n, lang);
});
var getTemplate = legalTemplates(i18n, templateDest);

// Create a cache of the templates so we can reference them synchronously later
Promise.settle(supportedLanguages.map(function (lang) {

return Promise.all([
getTemplate('terms', lang, defaultLang),
getTemplate('privacy', lang, defaultLang)
])
.then(function (temps) {
templates[lang] = {
terms: temps[0],
privacy: temps[1]
};
});

})).then(function () {
supportedLanguages.forEach(function (lang) {
generatePagesForLanguage(i18n, lang);
});
done();
}).then(null, done);

});


Expand All @@ -61,13 +90,17 @@ module.exports = function (grunt) {

grunt.file.copy(srcPath, destPath, {
process: function (contents, path) {
var terms = templates[context.lang].terms || templates[defaultLang].terms;
var privacy = templates[context.lang].privacy || templates[defaultLang].privacy;
var template = Handlebars.compile(contents);
var out = template({
l10n: context,
locale: context.locale,
lang: context.lang,
lang_dir: context.lang_dir,
fontSupportDisabled: context.fontSupportDisabled
fontSupportDisabled: context.fontSupportDisabled,
terms: terms,
privacy: privacy
});
return out;
}
Expand Down
69 changes: 69 additions & 0 deletions server/lib/legal-templates.js
@@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict';

var fs = require('fs');
var path = require('path');
var Promise = require('bluebird');
var logger = require('intel').getLogger('legal-templates');

module.exports = function (i18n, root) {

var TOS_ROOT_PATH = path.join(root, 'terms');
var PP_ROOT_PATH = path.join(root, 'privacy');

function getRoot(type) {
return type === 'terms' ? TOS_ROOT_PATH : PP_ROOT_PATH;
}

var templateCache = {};
function getTemplate(type, lang, defaultLang) {
var DEFAULT_LOCALE = i18n.localeFrom(defaultLang);

// Filenames are normalized to locale, not language.
var locale = i18n.localeFrom(lang);
var templatePath = path.join(getRoot(type), locale + '.html');
var resolver = Promise.defer();

// cache the promises to avoid multiple concurrent checks for
// the same template due to async calls to the file system.
if (templateCache[templatePath]) {
resolver.resolve(templateCache[templatePath]);
return resolver.promise;
}

fs.exists(templatePath, function (exists) {
if (! exists) {
var bestLang = i18n.bestLanguage(i18n.parseAcceptLanguage(lang));

if (locale === DEFAULT_LOCALE) {
var err = new Error(type + ' missing `' + DEFAULT_LOCALE + '` template: ' + templatePath);
return resolver.reject(err);
} else if (lang !== bestLang) {
logger.warn('`%s` does not exist, trying next best `%s`', lang, bestLang);
return resolver.resolve(getTemplate(type, bestLang, defaultLang));
}


templateCache[templatePath] = null;
return resolver.resolve(null);
}

fs.readFile(templatePath, 'utf8', function(err, data) {
if (err) {
return resolver.reject(err);
}

templateCache[templatePath] = data;
resolver.resolve(data);
});
});

return resolver.promise;
}

return getTemplate;

};
62 changes: 8 additions & 54 deletions server/lib/routes/get-terms-privacy.js
Expand Up @@ -25,14 +25,13 @@ var Promise = require('bluebird');
var config = require('../configuration');

var PAGE_TEMPLATE_DIRECTORY = path.join(config.get('page_template_root'), 'dist');
var TOS_ROOT_PATH = path.join(PAGE_TEMPLATE_DIRECTORY, 'terms');
var PP_ROOT_PATH = path.join(PAGE_TEMPLATE_DIRECTORY, 'privacy');

var templates = require('../legal-templates');

module.exports = function verRoute (i18n) {

var DEFAULT_LANG = config.get('i18n.defaultLang');
var DEFAULT_LOCALE = i18n.localeFrom(DEFAULT_LANG);

var getTemplate = templates(i18n, PAGE_TEMPLATE_DIRECTORY);

var route = {};
route.method = 'get';
Expand All @@ -44,54 +43,6 @@ module.exports = function verRoute (i18n) {
// * /<locale>/legal/privacy
route.path = /^\/(?:([a-zA-Z-\_]*)\/)?legal\/(terms|privacy)(?:\/)?$/;

function getRoot(type) {
return type === 'terms' ? TOS_ROOT_PATH : PP_ROOT_PATH;
}

var templateCache = {};
function getTemplate(type, lang) {
// Filenames are normalized to locale, not language.
var locale = i18n.localeFrom(lang);
var templatePath = path.join(getRoot(type), locale + '.html');
var resolver = Promise.defer();

// cache the promises to avoid multiple concurrent checks for
// the same template due to async calls to the file system.
if (templateCache[templatePath]) {
resolver.resolve(templateCache[templatePath]);
return resolver.promise;
}

fs.exists(templatePath, function (exists) {
if (! exists) {
var bestLang = i18n.bestLanguage(i18n.parseAcceptLanguage(lang));

if (locale === DEFAULT_LOCALE) {
var err = new Error(type + ' missing `' + DEFAULT_LOCALE + '` template: ' + templatePath);
return resolver.reject(err);
} else if (lang !== bestLang) {
logger.warn('`%s` does not exist, trying next best `%s`', lang, bestLang);
return resolver.resolve(getTemplate(type, bestLang));
}


templateCache[templatePath] = null;
return resolver.resolve(null);
}

fs.readFile(templatePath, 'utf8', function(err, data) {
if (err) {
return resolver.reject(err);
}

templateCache[templatePath] = data;
resolver.resolve(data);
});
});

return resolver.promise;
}

route.process = function (req, res) {
var lang = req.params[0];
var page = req.params[1];
Expand All @@ -101,7 +52,7 @@ module.exports = function verRoute (i18n) {
return res.redirect(getRedirectURL(req.lang || DEFAULT_LANG, page));
}

getTemplate(page, lang)
getTemplate(page, lang, DEFAULT_LANG)
.then(function (template) {
if (! template) {
logger.warn('%s->`%s` does not exist, redirecting to `%s`',
Expand All @@ -114,9 +65,12 @@ module.exports = function verRoute (i18n) {
res.send(template);
},
'text/html': function () {
var context = {};
context[page] = template;

// the HTML page removes the header to allow embedding.
res.removeHeader('X-FRAME-OPTIONS');
res.render(page, { body: template });
res.render(page, context);
}
});
}, function(err) {
Expand Down
2 changes: 1 addition & 1 deletion server/templates/pages/src/privacy.html
Expand Up @@ -34,7 +34,7 @@ <h1 id="fxa-pp-header">{{#t}}Privacy Notice{{/t}}</h1>

<section>
<article id="legal-copy">
{{{ body }}}
{{{ privacy }}}
</article>
</section>
</div>
Expand Down
2 changes: 1 addition & 1 deletion server/templates/pages/src/terms.html
Expand Up @@ -34,7 +34,7 @@ <h1 id="fxa-tos-header">{{#t}}Terms of Service{{/t}}</h1>

<section>
<article id="legal-copy">
{{{ body }}}
{{{ terms }}}
</article>
</section>
</div>
Expand Down
30 changes: 30 additions & 0 deletions tests/functional/legal.js
Expand Up @@ -49,6 +49,36 @@ define([
// success is going back to the legal screen.
.waitForElementById('fxa-legal-header')
.end();
},

'start at terms page': function () {

return this.get('remote')
.get(require.toUrl(url + '/terms'))
.waitForVisibleByCssSelector('#legal-copy')

.elementById('legal-copy')
.text()
.then(function (resultText) {
// the legal text shouldn't be empty
assert.ok(resultText.trim().length);
})
.end();
},

'start at privacy page': function () {

return this.get('remote')
.get(require.toUrl(url + '/privacy'))
.waitForVisibleByCssSelector('#legal-copy')

.elementById('legal-copy')
.text()
.then(function (resultText) {
// the legal text shouldn't be empty
assert.ok(resultText.trim().length);
})
.end();
}
});
});

0 comments on commit 148475c

Please sign in to comment.