Merge pull request #3774 from overleaf/jpa-meta

[frontend] import meta tag processing from das7pads fork

GitOrigin-RevId: ca74ff9fbbcb51091a626a45468ff3d24d6136ca
This commit is contained in:
Jakob Ackermann 2021-03-22 10:51:07 +01:00 committed by Copybot
parent 2e305f36bc
commit 535c97e8cf
8 changed files with 135 additions and 47 deletions

View file

@ -220,6 +220,10 @@ lint_misc:
--ignore-pattern 'modules/*/test/**/*.js' \
--max-warnings=0
lint: lint_pug
lint_pug:
bin/lint_pug_templates
lint_in_docker:
$(RUN_LINT_FORMAT) make lint -j --output-sync

View file

@ -69,45 +69,42 @@ html(
script(type='text/javascript').
window.ga = function() { console.log("would send to GA", arguments) };
script(type="text/javascript").
window.csrfToken = "#{csrfToken}";
block meta
meta(name="ol-csrfToken" content=csrfToken)
//- Configure dynamically loaded assets (via webpack) to be downloaded from CDN
//- See: https://webpack.js.org/guides/public-path/#on-the-fly
window.baseAssetPath = "#{buildBaseAssetPath()}"
meta(name="ol-baseAssetPath" content=buildBaseAssetPath())
meta(name="ol-usersEmail" content=getUserEmail())
meta(name="ol-sharelatex" data-type="json" content={
siteUrl: settings.siteUrl,
wsUrl,
})
meta(name="ol-ab" data-type="json" content={})
meta(name="ol-user_id" content=getLoggedInUserId())
//- Internationalisation settings
meta(name="ol-i18n" data-type="json" content={
currentLangCode: currentLngCode
})
//- Expose some settings globally to the frontend
meta(name="ol-ExposedSettings" data-type="json" content=ExposedSettings)
if (typeof(settings.algolia) != "undefined")
meta(name="ol-sharelatex.algolia" data-type="json" content={
app_id: settings.algolia.app_id,
api_key: settings.algolia.read_only_api_key,
indexes: settings.algolia.indexes
})
if (typeof(settings.templates) != "undefined")
meta(name="ol-sharelatex.templates" data-type="json" content={
user_id : settings.templates.user_id,
cdnDomain : settings.templates.cdnDomain,
indexName : settings.templates.indexName
})
block head-scripts
meta(id="ol-usersEmail" content=getUserEmail())
script.
window.sharelatex = {
siteUrl: '#{settings.siteUrl}',
wsUrl: '#{wsUrl}',
};
window.ab = {};
window.user_id = '#{getLoggedInUserId()}';
//- Internationalisation settings
window.i18n = {
currentLangCode: '#{currentLngCode}'
}
//- Expose some settings globally to the frontend
window.ExposedSettings = JSON.parse('!{StringHelper.stringifyJsonForScript(ExposedSettings)}');
- if (typeof(settings.algolia) != "undefined")
script.
window.sharelatex.algolia = {
app_id:'#{settings.algolia.app_id}',
api_key:'#{settings.algolia.read_only_api_key}',
indexes:!{StringHelper.stringifyJsonForScript(settings.algolia.indexes)}
}
- if (typeof(settings.templates) != "undefined")
script.
window.sharelatex.templates = {
user_id : '!{settings.templates.user_id}',
cdnDomain : '!{settings.templates.cdnDomain}',
indexName : '!{settings.templates.indexName}'
}
body
if(settings.recaptcha && settings.recaptcha.siteKeyV3)

View file

@ -3,18 +3,23 @@ extends ../layout
block vars
- var suppressNavContentLinks = true
block content
script#data(type="application/json").
!{StringHelper.stringifyJsonForScript({ projects, tags, notifications, notificationsInstitution, userAffiliations, userEmails, allInReconfirmNotificationPeriods, reconfirmedViaSAML })}
script(type="text/javascript").
window.data = JSON.parse(document.querySelector("#data").text);
window.algolia = {
block append meta
meta(name="ol-projects" data-type="json" content=projects)
meta(name="ol-tags" data-type="json" content=tags)
meta(name="ol-notifications" data-type="json" content=notifications)
meta(name="ol-notificationsInstitution" data-type="json" content=notificationsInstitution)
meta(name="ol-userAffiliations" data-type="json" content=userAffiliations)
meta(name="ol-userEmails" data-type="json" content=userEmails)
meta(name="ol-allInReconfirmNotificationPeriods" data-type="json" content=allInReconfirmNotificationPeriods)
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
meta(name="ol-algolia" data-type="json" content={
institutions: {
app_id: '#{algolia_app_id}',
api_key: '#{algolia_api_key}'
app_id: algolia_app_id,
api_key: algolia_api_key
}
};
})
block content
main.content.content-alt.project-list-page(
ng-controller="ProjectPageController"

View file

@ -0,0 +1,31 @@
#!/bin/sh
set -e
TEMPLATES_EXTENDING_META_BLOCK=$(\
grep \
--files-with-matches \
--recursive app/views modules/*/app/views \
--regex 'block append meta' \
--regex 'block prepend meta' \
--regex 'append meta' \
--regex 'prepend meta' \
)
for file in ${TEMPLATES_EXTENDING_META_BLOCK}; do
if ! grep "$file" --quiet --extended-regexp -e 'extends .+layout'; then
cat <<MSG >&2
ERROR: $file is a partial template and extends 'block meta'.
Using block append/prepend in a partial will duplicate the block contents into
the <body> due to a bug in pug.
Putting meta tags in the <body> can lead to Angular XSS.
You will need to refactor the partial and move the block into the top level
page template that extends the global layout.pug.
MSG
exit 1
fi
done

View file

@ -22,6 +22,7 @@ import './modules/recursionHelper'
import './modules/errorCatcher'
import './modules/localStorage'
import './modules/sessionStorage'
import getMeta from './utils/meta'
const App = angular
.module('SharelatexApp', [
@ -80,8 +81,7 @@ const App = angular
})
App.run(($rootScope, $templateCache) => {
const usersEmailElement = document.getElementById('ol-usersEmail')
$rootScope.usersEmail = usersEmailElement && usersEmailElement.content
$rootScope.usersEmail = getMeta('ol-usersEmail')
// UI Select templates are hard-coded and use Glyphicon icons (which we don't import).
// The line below simply overrides the hard-coded template with our own, which is

View file

@ -14,6 +14,9 @@ import 'libs/select/select'
// Polyfill fetch for IE11
import 'isomorphic-unfetch'
// Rewrite meta elements
import './utils/meta'
// Configure dynamically loaded assets (via webpack) to be downloaded from CDN
// See: https://webpack.js.org/guides/public-path/#on-the-fly
// eslint-disable-next-line no-undef, camelcase

View file

@ -0,0 +1,48 @@
import _ from 'lodash'
// cache for parsed values
const cache = new Map()
export default function getMeta(name, fallback) {
if (cache.has(name)) return cache.get(name)
const element = document.head.querySelector(`meta[name="${name}"]`)
if (!element) {
return fallback
}
const plainTextValue = element.content
let value
switch (element.dataset.type) {
case 'boolean':
// in pug: content=false -> no content field
// in pug: content=true -> empty content field
value = element.hasAttribute('content')
break
case 'json':
if (!plainTextValue) {
// JSON.parse('') throws
value = undefined
} else {
value = JSON.parse(plainTextValue)
}
break
default:
value = plainTextValue
}
cache.set(name, value)
return value
}
function convertMetaToWindowAttributes() {
window.data = window.data || {}
Array.from(document.querySelectorAll('meta[name^="ol-"]'))
.map(element => element.name)
// process short labels before long ones:
// e.g. assign 'sharelatex' before 'sharelatex.templates'
.sort()
.forEach(nameWithNamespace => {
const label = nameWithNamespace.slice('ol-'.length)
_.set(window, label, getMeta(nameWithNamespace))
_.set(window.data, label, getMeta(nameWithNamespace))
})
}
convertMetaToWindowAttributes()

View file

@ -1,6 +1,6 @@
const OError = require('@overleaf/o-error')
const { assertHasStatusCode } = require('./requestHelper')
const CSRF_REGEX = /window.csrfToken = "(.+?)"/
const CSRF_REGEX = /<meta name="ol-csrfToken" content="(.+?)">/
function _parseCsrf(body) {
const match = CSRF_REGEX.exec(body)