diff --git a/_data/messages.yml b/_data/messages.yml
new file mode 100644
index 0000000000..642a7ac557
--- /dev/null
+++ b/_data/messages.yml
@@ -0,0 +1 @@
+scam-banner: "**⚠️ Beware of Scams**: since Feb 2024, scammers are using [fake Scala websites to sell courses](https://www.scala-lang.org/blog/2024/03/01/fake-scala-courses.html), please check you are using an official source."
diff --git a/_includes/alert-banner.html b/_includes/alert-banner.html
new file mode 100644
index 0000000000..405cbd8fb0
--- /dev/null
+++ b/_includes/alert-banner.html
@@ -0,0 +1,8 @@
+{% comment %}use the variable 'message' to include markdown text to display in the alert.{% endcomment %}
+
+
diff --git a/_layouts/root-content-layout.html b/_layouts/root-content-layout.html
index 1828666a99..f8d99b1427 100644
--- a/_layouts/root-content-layout.html
+++ b/_layouts/root-content-layout.html
@@ -3,6 +3,8 @@
+{% include alert-banner.html message=site.data.messages.scam-banner message_id='scam-courses-feb-2024' %}
+
{% include navbar-inner.html %}
diff --git a/_layouts/root-index-layout.html b/_layouts/root-index-layout.html
index 97304053e1..baf0558d44 100644
--- a/_layouts/root-index-layout.html
+++ b/_layouts/root-index-layout.html
@@ -2,28 +2,30 @@
+{% include alert-banner.html message=site.data.messages.scam-banner message_id='scam-courses-feb-2024' %}
+
{% include navbar-inner.html %}
-
-
-
- {% comment %}Specific content from child layouts{% endcomment %} {{content}}
+
+
+
+ {% comment %}Specific content from child layouts{% endcomment %} {{content}}
diff --git a/_sass/layout/header.scss b/_sass/layout/header.scss
index ab392dfd77..ff1b4fdb70 100755
--- a/_sass/layout/header.scss
+++ b/_sass/layout/header.scss
@@ -18,6 +18,23 @@
padding: 10px 40px;
}
+ &.alert-warning {
+ background: $warning-bg;
+ color: $warning-text;
+
+ a {
+ color: $warning-link;
+ font-weight: bold;
+ text-decoration: underline;
+
+ &:active,
+ &:focus,
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ }
+
span {
position: absolute;
right: 20px;
diff --git a/_sass/utils/_variables.scss b/_sass/utils/_variables.scss
index 630716433a..d018dbae37 100755
--- a/_sass/utils/_variables.scss
+++ b/_sass/utils/_variables.scss
@@ -18,6 +18,10 @@ $gray-light: #E5EAEA;
$gray-lighter: #F0F3F3;
$apple-blue: #6dccf5;
+$warning-bg: #FFA500;
+$warning-link: #185eb3;
+$warning-text: #000;
+
//-------------------------------------------------
$headings-font-color: $gray-dark;
$base-font-color: #4A5659;
diff --git a/resources/js/functions.js b/resources/js/functions.js
index ccbf9753ac..686a38f47b 100644
--- a/resources/js/functions.js
+++ b/resources/js/functions.js
@@ -72,11 +72,6 @@ $(document).ready(function() {
hljs.highlightAll();
});
-// Show Blog
-$(".hide").click(function() {
- $(".new-on-the-blog").hide();
-});
-
// Documentation menu dropdown toggle
$(document).ready(function() { // DOM ready
// If a link has a dropdown, add sub menu toggle.
@@ -435,9 +430,8 @@ $(document).ready(function() {
* that when the page is refreshed, the same tab will be selected.
* On page load, selects the tab corresponding to stored value.
*/
- function setupTabs(tabs, namespace, defaultValue) {
- const PreferenceStorage = Storage('org.scala-lang.docs.preferences');
- const preferredValue = PreferenceStorage.getPreference(namespace, defaultValue);
+ function setupTabs(tabs, namespace, defaultValue, storage) {
+ const preferredValue = storage.getPreference(namespace, defaultValue);
activateTab(tabs, preferredValue)
@@ -448,7 +442,7 @@ $(document).ready(function() {
const parent = $(this).parent();
const newValue = $(this).data('target');
- PreferenceStorage.setPreference(namespace, newValue, oldValue => {
+ storage.setPreference(namespace, newValue, _ => {
// when we set a new scalaVersion, find scalaVersionTabs except current one
// and activate those tabs.
activateTab(tabs.not(parent), newValue);
@@ -459,17 +453,48 @@ $(document).ready(function() {
});
}
- if (storageAvailable('localStorage')) {
+ function setupAlertCancel(alert, storage) {
+ const messageId = alert.data('message_id');
+ let onHide = () => {};
+ if (messageId) {
+ const key = `alert.${messageId}`;
+ const isHidden = storage.getPreference(key, 'show') === 'hidden';
+ if (isHidden) {
+ alert.hide();
+ }
+ onHide = () => storage.setPreference(key, 'hidden', _ => {});
+ }
+
+
+ alert.find('.hide').click(function() {
+ alert.hide(), onHide();
+ });
+ }
+
+ function setupAllTabs(storage) {
var scalaVersionTabs = $(".tabsection.tabs-scala-version");
if (scalaVersionTabs.length) {
- setupTabs(scalaVersionTabs, "scalaVersion", "scala-3");
+ setupTabs(scalaVersionTabs, "scalaVersion", "scala-3", storage);
}
var buildToolTabs = $(".tabsection.tabs-build-tool");
if (buildToolTabs.length) {
- setupTabs(buildToolTabs, "buildTool", "scala-cli");
+ setupTabs(buildToolTabs, "buildTool", "scala-cli", storage);
+ }
+ }
+
+ function setupAllAlertCancels(storage) {
+ var alertBanners = $(".new-on-the-blog.alert-warning");
+ if (alertBanners.length) {
+ setupAlertCancel(alertBanners, storage);
}
}
+ if (storageAvailable('localStorage')) {
+ const PreferenceStorage = Storage('org.scala-lang.docs.preferences');
+ setupAllTabs(PreferenceStorage);
+ setupAllAlertCancels(PreferenceStorage);
+ }
+
});
// OS detection