Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EB-134: Add a Content Security Policy to the website #81

Merged
merged 10 commits into from
Jan 13, 2025
6 changes: 5 additions & 1 deletion config/clupea_harengus/config.json
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
{}
{
"configuration": {
"disableAnalytics": true
}
}
5 changes: 4 additions & 1 deletion config/linum_tenue/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,5 +713,8 @@
}
]
}
]
],
"configuration": {
"disableAnalytics": true
}
}
5 changes: 4 additions & 1 deletion config/linum_trigynum/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -707,5 +707,8 @@
}
]
}
]
],
"configuration": {
"disableAnalytics": true
}
}
3 changes: 3 additions & 0 deletions config/littorina_saxatilis/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@
"trackLabels": "offset"
}
]
},
"configuration": {
"disableAnalytics": true
}
}
5 changes: 4 additions & 1 deletion config/meganyctiphanes_norvegica/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
]
}
]
},
"configuration": {
"disableAnalytics": true
}
}
}

5 changes: 4 additions & 1 deletion config/parnassius_mnemosyne/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@
]
}
]
},
"configuration": {
"disableAnalytics": true
}
}
}

5 changes: 4 additions & 1 deletion config/skeletonema_marinoi/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
]
}
]
},
"configuration": {
"disableAnalytics": true
}
}
}

29 changes: 29 additions & 0 deletions hugo/assets/js/check_for_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

/* Check if a config file exists before loading JBrowse2
Otherwise, display an alert to the user that no config file exists
*/
document.addEventListener('DOMContentLoaded', async function () {
const urlParams = new URLSearchParams(window.location.search);
const configFile = urlParams.get('config');
const noConfigBlock = document.getElementById('no-config');
const jbrowseBlock = document.getElementById('root');

if (!configFile) {
noConfigBlock.style.display = 'block';
jbrowseBlock.style.display = 'none';
return;
}

try {
const response = await fetch(configFile);
if (response.ok) {
return;
} else {
noConfigBlock.style.display = 'block';
jbrowseBlock.style.display = 'none';
}
} catch (error) {
noConfigBlock.style.display = 'block';
jbrowseBlock.style.display = 'none';
}
});
34 changes: 34 additions & 0 deletions hugo/assets/js/contact_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
document.addEventListener('DOMContentLoaded', function () {
const form = document.querySelector('.needs-validation');
const formAlert = document.getElementById('formAlert');
const reCAPTCHAAlert = document.getElementById('reCAPTCHAAlert');

form.addEventListener('submit', function (event) {
const isFormValid = form.checkValidity();
const recaptchaValue = grecaptcha.getResponse();

if (!isFormValid || recaptchaValue === "") {
event.preventDefault();
event.stopPropagation();

if (!isFormValid) {
form.classList.add('was-validated');
formAlert.style.display = 'block';
} else {
formAlert.style.display = 'none';
}

if (recaptchaValue === "") {
reCAPTCHAAlert.style.display = 'block';
} else {
reCAPTCHAAlert.style.display = 'none';
}
grecaptcha.reset();

} else {
formAlert.style.display = 'none';
reCAPTCHAAlert.style.display = 'none';
// Form submission occurs now
}
});
});
85 changes: 85 additions & 0 deletions hugo/assets/js/gbif_map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Creates a population map using the GBIF API and leaflet for a given species.
*/

document.addEventListener("DOMContentLoaded", function () {
const mapElement = document.getElementById('map');
const latitude = mapElement.getAttribute('data-latitude');
const longitude = mapElement.getAttribute('data-longitude');
const initialZoom = mapElement.getAttribute('data-initial-zoom');
const gbifTaxonId = mapElement.getAttribute('data-gbif-taxon-id');

const map = L.map('map', {
minZoom: 1,
maxZoom: 15,
}).setView([latitude, longitude], initialZoom);

map.attributionControl.setPrefix('<a href="https://leafletjs.com" title="A JavaScript library for interactive maps">Leaflet</a>')

// Add the map tile layer from GBIF
// format is: https://tile.gbif.org/{srs}/{tileset}/{z}/{x}/{y}{format}{params}
const baseLayer = L.tileLayer('https://tile.gbif.org/3857/omt/{z}/{x}/{y}@1x.png?style=gbif-geyser-en', {
attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
maxZoom: 15,
}).addTo(map);

// add the GBIF occurrence overlay
const gbifUrl = `https://api.gbif.org/v2/map/occurrence/density/{z}/{x}/{y}@1x.png?srs=EPSG%3A3857&taxonKey=${gbifTaxonId}&style=blue.marker&year=2000,2099`;
const gbifAttrib = '<a href="https://www.gbif.org">GBIF</a>';

const gbifLayer = L.tileLayer(gbifUrl, {
minZoom: 1,
maxZoom: 15,
zoomOffset: -1,
tileSize: 512,
attribution: gbifAttrib
}).addTo(map);

// event listeners for api errors
let mapError = false;
baseLayer.on('tileerror', function (error) {
mapError = true;
});
gbifLayer.on('tileerror', function (error) {
mapError = true;
});

let hasRun = false;
gbifLayer.on('load', function () {
if (!hasRun) {
hasRun = true;

if (mapError) {
// Update map with a generic error message.
if (!document.querySelector('.map-error-message')) {
const errorMessage = L.control({ position: 'bottomright' });

errorMessage.onAdd = function (map) {
const div = L.DomUtil.create('div', 'map-error-message');
div.innerHTML = '<h1>The distribution map is temporarily unavailable</h1>';
return div;
};
errorMessage.addTo(map);
}
} else {
// Add a help button, when clicked, toast pop-up happens.
const helpControl = L.control({ position: 'topright' });
const helpButtonHTML = '<button class="btn btn-light" id="helpButton" style="font-size: 1.3rem"><img src="/img/icons/question.svg" alt="Help button" class="scilife-icons-xl"></button>';

helpControl.onAdd = function (map) {
let div = L.DomUtil.create('div', 'help-control');
div.innerHTML = helpButtonHTML;
return div;
};

helpControl.addTo(map);

document.getElementById('helpButton').addEventListener('click', function () {
document.querySelectorAll('.toast').forEach(function (toast) {
toast.classList.add('show');
});
});
}
}
});
});
24 changes: 24 additions & 0 deletions hugo/assets/js/init_datatables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Find tables on a page marked as wanting to be datatables and adds event listeners to them.
Tables with data-table="true" attribute are made into datatables.
*/

document.addEventListener('DOMContentLoaded', function() {
var tables = document.querySelectorAll('table[data-table="true"]');
tables.forEach(function(table) {

// Table customisation options
var info = table.getAttribute('data-info') === 'true' ? true : false;
var ordering = table.getAttribute('data-ordering') === 'true' ? true : false;
var paging = table.getAttribute('data-paging') === 'true' ? true : false;
var searching = table.getAttribute('data-searching') === 'true' ? true : false;

var tableName = '#' + table.id;
new DataTable(tableName, {
info: info,
ordering: ordering,
paging: paging,
searching: searching
});
});
});
12 changes: 12 additions & 0 deletions hugo/assets/js/matomo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* Matomo tracking setup located in the footer of each page */
var _paq = window._paq = window._paq || [];
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "//matomo.dc.scilifelab.se/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '11']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
})();
8 changes: 8 additions & 0 deletions hugo/assets/js/recaptcha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Google reCAPTCHA v2 callback
*/
var onloadCallback = function () {
kwentine marked this conversation as resolved.
Show resolved Hide resolved
grecaptcha.render(id = "recaptcha-widget", {
'sitekey': '6LfLRQMqAAAAACSectjtigWkNvif1G2J8EFlj1nV'
});
};
27 changes: 27 additions & 0 deletions hugo/assets/js/toggle_tables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Add toggle to swap between the long and short tables on each species download page.
*/

document.addEventListener('DOMContentLoaded', function () {
const checkbox = document.getElementById('flexSwitchCheckChecked');
const longTable = document.getElementById('table-container-long');
const shortTable = document.getElementById('table-container-short');

if (!checkbox || !longTable || !shortTable) {
console.error('Required elements not found');
return;
}

checkbox.addEventListener('change', function () {
if (this.checked) {
longTable.style.display = 'none';
shortTable.style.display = 'block';
} else {
longTable.style.display = 'block';
shortTable.style.display = 'none';
}
});

// Initial state setup
checkbox.dispatchEvent(new Event('change'));
});
5 changes: 4 additions & 1 deletion hugo/layouts/_default/baseof.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@

<!-- Any page specific scripts included here. -->
{{ block "script_includes" . }} {{ end }}
</head>

<!-- Matomo tracking - Matomo recommends having it immediately before the closing </head> tag -->
{{ $matomoJS := resources.Get "js/matomo.js" | fingerprint | minify }}
<script src="{{ $matomoJS.RelPermalink }}"></script>
</head>


<body class="d-flex flex-column min-vh-100">
Expand Down
25 changes: 15 additions & 10 deletions hugo/layouts/contact/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@

{{ define "script_includes" }}

<!-- Load the Google reCAPTCHA API asynchronously and defer its execution until the page has finished parsing -->
<!--
Google recapatcha V2 implementation:
These scripts work with the contact_form.html and contact_form.js files to prevent form submission if not verified.
Docs: https://developers.google.com/recaptcha/docs/display

Note that the "onloadCallback" function needs to be loaded before reCAPTCHA API to avoid race conditions.
The reCAPTCHA API calls the "onloadCallback" function so it needs to be ready when the API calls it.
-->
{{ $recaptchaJS := resources.Get "js/recaptcha.js" | fingerprint | minify }}
<script src="{{ $recaptchaJS.RelPermalink }}"></script>

<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>

<!-- For submission of contact form -->
{{ $contactFormJS := resources.Get "js/contact_form.js" | fingerprint | minify }}
<script src="{{ $contactFormJS.RelPermalink }}"></script>

{{ end }}


Expand All @@ -24,13 +38,4 @@
</div>
</div>


<script type="text/javascript">
var onloadCallback = function () {
grecaptcha.render(id = "recaptcha-widget", {
'sitekey': '6LfLRQMqAAAAACSectjtigWkNvif1G2J8EFlj1nV'
});
};
</script>

{{ end }}
38 changes: 11 additions & 27 deletions hugo/layouts/genome-browser/list.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
{{ define "script_includes" }}

<!-- Check if the config file exists - if not display error message -->
{{ $configCheckJS := resources.Get "js/check_for_config.js" | fingerprint | minify }}
<script src="{{ $configCheckJS.RelPermalink }}"></script>

<script defer src="/browser/static/js/main.js"></script>

{{ end }}


{{ define "main" }}

<div class="container-fluid mt-3">
Expand Down Expand Up @@ -29,32 +40,5 @@ <h2>
</script>
{{ end }}

<!-- Check if the config file exists before loading JBrowse2 -->
<script>
document.addEventListener('DOMContentLoaded', async function() {
const urlParams = new URLSearchParams(window.location.search);
const configFile = urlParams.get('config');


if (!configFile) {
document.getElementById('no-config').style.display = 'block';
return;
}

try {
const response = await fetch(configFile);
if (response.ok) {
const script = document.createElement('script');
script.defer = true;
script.src = '/browser/static/js/main.js';
document.head.appendChild(script);
} else {
document.getElementById('no-config').style.display = 'block';
}
} catch (error) {
document.getElementById('no-config').style.display = 'block';
}
});
</script>

{{ end }}
Loading
Loading