diff --git a/bedrock/base/templates/base-protocol.html b/bedrock/base/templates/base-protocol.html
index 33f71c2b24..e144cde145 100644
--- a/bedrock/base/templates/base-protocol.html
+++ b/bedrock/base/templates/base-protocol.html
@@ -152,7 +152,7 @@
{% endblock %}
diff --git a/bedrock/firefox/redirects.py b/bedrock/firefox/redirects.py
index 607d6f2baa..ce6fc0b1b4 100644
--- a/bedrock/firefox/redirects.py
+++ b/bedrock/firefox/redirects.py
@@ -632,4 +632,6 @@ redirectpatterns = (
redirect(r"^firefox/privacy/safe-passwords/?$", "firefox.features.password-manager"),
redirect(r"^firefox/privacy/book/?$", "https://support.mozilla.org/kb/how-stay-safe-web"),
redirect(r"^firefox/nothingpersonal/?$", "firefox.nothing-personal.index"),
+ # issue 15841
+ redirect(r"^firefox/tech/?$", "firefox.landing.tech"),
)
diff --git a/bedrock/firefox/templates/firefox/landing/tech.html b/bedrock/firefox/templates/firefox/landing/tech.html
new file mode 100644
index 0000000000..56059a9166
--- /dev/null
+++ b/bedrock/firefox/templates/firefox/landing/tech.html
@@ -0,0 +1,57 @@
+{#
+ 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 https://mozilla.org/MPL/2.0/.
+#}
+
+{% extends "firefox/new/desktop/download.html" %}
+
+{% block canonical_urls %}{% endblock %}
+
+{% set win_custom_download_id = 'partner-firefox-release-smi-smi-001-stub' %}
+{% set mac_custom_download_id = 'partner-firefox-release-smi-smi-001-latest' %}
+
+{% set android_campaign_url = play_store_url('firefox', 'custom-001') %}
+{% set ios_campaign_url = app_store_url('firefox', 'custom-001') %}
+
+{% block string_data %}
+ data-win-custom-id="{{ win_custom_download_id }}"
+ data-mac-custom-id="{{ mac_custom_download_id }}"
+{% endblock %}
+
+{% block primary_cta %}
+ {{ download_firefox(dom_id='download-primary', force_direct=True, download_location='primary cta') }}
+{% endblock %}
+
+{% block features_cta %}
+ {{ download_firefox(dom_id='download-features', force_direct=True, download_location='features cta') }}
+{% endblock %}
+
+{% block discover_cta %}
+ {{ download_firefox(dom_id='download-discover', force_direct=True, download_location='discover cta') }}
+{% endblock %}
+
+{% block mobile_primary_cta %}
+
+ {{ google_play_button(href=android_campaign_url) }}
+
+
+ {{ apple_app_store_button(href=ios_campaign_url) }}
+
+{% endblock %}
+
+{% block mobile_secondary_cta %}
+
+{% endblock %}
+
+{% block js %}
+ {{ super() }}
+ {{ js_bundle('firefox_partner_build_download') }}
+{% endblock %}
diff --git a/bedrock/firefox/templates/firefox/new/desktop/download.html b/bedrock/firefox/templates/firefox/new/desktop/download.html
index 654b7d9a82..759f75458e 100644
--- a/bedrock/firefox/templates/firefox/new/desktop/download.html
+++ b/bedrock/firefox/templates/firefox/new/desktop/download.html
@@ -89,7 +89,9 @@
{{ ftl('firefox-desktop-download-get-the-browser') }}
{{ ftl('firefox-desktop-download-no-shady') }}
- {{ download_firefox_thanks(locale_in_transition=True, download_location='primary cta') }}
+ {% block primary_cta %}
+ {{ download_firefox_thanks(locale_in_transition=True, download_location='primary cta') }}
+ {% endblock %}
@@ -279,14 +281,16 @@
{{ ftl('firefox-desktop-download-all-your-devices') }}
{{ ftl('firefox-desktop-download-take-your-privacy') }}
@@ -384,8 +388,9 @@
- {{ download_firefox_thanks(dom_id='download-features', locale_in_transition=True, download_location='features cta') }}
-
+ {% block features_cta %}
+ {{ download_firefox_thanks(dom_id='download-features', locale_in_transition=True, download_location='features cta') }}
+ {% endblock %}
@@ -545,7 +550,9 @@
{{ ftl('firefox-desktop-download-from-watching-a') }}
- {{ download_firefox_thanks(dom_id='download-discover', locale_in_transition=True, download_location='discover cta') }}
+ {% block discover_cta %}
+ {{ download_firefox_thanks(dom_id='download-discover', locale_in_transition=True, download_location='discover cta') }}
+ {% endblock %}
@@ -563,12 +570,14 @@
{{ ftl('firefox-desktop-download-download-the-mobile') }}
-
- {{ google_play_button() }}
-
-
- {{ apple_app_store_button(href=ios_url) }}
-
+ {% block mobile_primary_cta %}
+
+ {{ google_play_button() }}
+
+
+ {{ apple_app_store_button(href=ios_url) }}
+
+ {% endblock %}
diff --git a/bedrock/firefox/urls.py b/bedrock/firefox/urls.py
index 59b4350acc..75c7f3515b 100644
--- a/bedrock/firefox/urls.py
+++ b/bedrock/firefox/urls.py
@@ -270,6 +270,8 @@ urlpatterns = (
"firefox/firefox-20th/index.html",
active_locales=["de", "fr", "en-US", "en-CA", "en-GB"],
),
+ # Issue 15841 - UK influencer campaign
+ page("firefox/landing/tech/", "firefox/landing/tech.html", ftl_files="firefox/new/desktop", active_locales="en-GB"),
)
# Contentful
diff --git a/bedrock/settings/base.py b/bedrock/settings/base.py
index 510c1399b6..42557766ac 100644
--- a/bedrock/settings/base.py
+++ b/bedrock/settings/base.py
@@ -504,6 +504,7 @@ NOINDEX_URLS = [
r"^firefox/this-browser-comes-highly-recommended/",
r"^firefox/nightly/notes/feed/$",
r"^firefox.*/all/$",
+ r"^firefox/landing/",
r"^.+/(firstrun|whatsnew)/$",
r"^m/",
r"^newsletter/(confirm|existing|hacks\.mozilla\.org|recovery|updated|fxa-error)/",
diff --git a/media/js/base/consent/allow-list.es6.js b/media/js/base/consent/allow-list.es6.js
index 8c6d52020c..db257993ae 100644
--- a/media/js/base/consent/allow-list.es6.js
+++ b/media/js/base/consent/allow-list.es6.js
@@ -7,6 +7,7 @@
const MozAllowList = [
'/firefox/built-for-you/',
'/firefox/challenge-the-default/',
+ '/firefox/landing/*',
'/newsletter/firefox/',
'/products/vpn/*'
];
diff --git a/media/js/base/datalayer-productdownload.es6.js b/media/js/base/datalayer-productdownload.es6.js
index 2f0ec2fca8..940b98c3c1 100644
--- a/media/js/base/datalayer-productdownload.es6.js
+++ b/media/js/base/datalayer-productdownload.es6.js
@@ -112,7 +112,9 @@ TrackProductDownload.getEventFromUrl = (downloadURL) => {
const productParam = params.product;
const productSplit = productParam.split('-');
// product is first word of product param
- const product = productSplit[0];
+ let product = productSplit[0];
+ // partner builds are labelled as ?product=partner-firefox so class these as regular 'firefox' download events.
+ product = product === 'partner' ? 'firefox' : product;
let platform = params.os;
// change platform to macos if it's osx
platform = platform === 'osx' ? 'macos' : platform;
diff --git a/media/js/firefox/landing/partner-build-download-init.es6.js b/media/js/firefox/landing/partner-build-download-init.es6.js
new file mode 100644
index 0000000000..7dc46226a2
--- /dev/null
+++ b/media/js/firefox/landing/partner-build-download-init.es6.js
@@ -0,0 +1,9 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+import PartnerBuildDownload from './partner-build-download.es6';
+
+PartnerBuildDownload.init();
diff --git a/media/js/firefox/landing/partner-build-download.es6.js b/media/js/firefox/landing/partner-build-download.es6.js
new file mode 100644
index 0000000000..4d6a28c8a3
--- /dev/null
+++ b/media/js/firefox/landing/partner-build-download.es6.js
@@ -0,0 +1,56 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+const PartnerBuildDownload = {};
+
+let winDownloadID;
+let macDownloadID;
+
+PartnerBuildDownload.createCustomDownloadURL = (link, id) => {
+ const url = new URL(link.href);
+
+ // update product to custom build ID.
+ url.searchParams.set('product', id);
+
+ link.href = url;
+};
+
+PartnerBuildDownload.replaceWithCustomDownloadLinks = () => {
+ const downloadLinksWin = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="win"]'
+ )
+ );
+ const downloadLinksMac = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="osx"]'
+ )
+ );
+
+ downloadLinksWin.forEach((link) =>
+ PartnerBuildDownload.createCustomDownloadURL(link, winDownloadID)
+ );
+ downloadLinksMac.forEach((link) =>
+ PartnerBuildDownload.createCustomDownloadURL(link, macDownloadID)
+ );
+};
+
+PartnerBuildDownload.init = () => {
+ const strings = document.getElementById('strings');
+ winDownloadID = strings.getAttribute('data-win-custom-id');
+ macDownloadID = strings.getAttribute('data-mac-custom-id');
+
+ if (
+ typeof window.URL === 'function' &&
+ typeof Array.from === 'function' &&
+ winDownloadID &&
+ macDownloadID
+ ) {
+ PartnerBuildDownload.replaceWithCustomDownloadLinks();
+ }
+};
+
+export default PartnerBuildDownload;
diff --git a/media/static-bundles.json b/media/static-bundles.json
index e51f53d0ee..d450ab4339 100644
--- a/media/static-bundles.json
+++ b/media/static-bundles.json
@@ -1788,6 +1788,12 @@
"js/base/banners/fundraiser-experiment.es6.js"
],
"name": "fundraising-banner-experiment"
+ },
+ {
+ "files": [
+ "js/firefox/landing/partner-build-download-init.es6.js"
+ ],
+ "name": "firefox_partner_build_download"
}
]
}
diff --git a/tests/redirects/map_globalconf.py b/tests/redirects/map_globalconf.py
index 87e7faa15e..48dd80cc24 100644
--- a/tests/redirects/map_globalconf.py
+++ b/tests/redirects/map_globalconf.py
@@ -1328,5 +1328,7 @@ URLS = flatten(
),
# Issue 15386
url_test("/products/vpn/resource-center/no-Logging-vpn-from-mozilla/", "/products/vpn/resource-center/no-logging-vpn-from-mozilla/"),
+ # Issue 15841
+ url_test("/firefox/tech/", "/firefox/landing/tech/"),
)
)
diff --git a/tests/unit/spec/base/datalayer-productdownload.js b/tests/unit/spec/base/datalayer-productdownload.js
index 1eb6b61bcc..8f2e7ebd65 100644
--- a/tests/unit/spec/base/datalayer-productdownload.js
+++ b/tests/unit/spec/base/datalayer-productdownload.js
@@ -111,6 +111,12 @@ describe('TrackProductDownload.getEventFromUrl', function () {
);
expect(testEvent['product']).toBe('firefox');
});
+ it('should identify product for Firefox Desktop partner builds', function () {
+ const testEvent = TrackProductDownload.getEventFromUrl(
+ 'https://download.mozilla.org/?product=partner-firefox-release-smi-smi-001-latest&os=osx&lang=en-GB'
+ );
+ expect(testEvent['product']).toBe('firefox');
+ });
it('should identify product for Firefox in the App Store', function () {
const testEvent = TrackProductDownload.getEventFromUrl(
'https://itunes.apple.com/app/firefox-private-safe-browser/id989804926'
diff --git a/tests/unit/spec/firefox/landing/partner-build-download.js b/tests/unit/spec/firefox/landing/partner-build-download.js
new file mode 100644
index 0000000000..22912f6d25
--- /dev/null
+++ b/tests/unit/spec/firefox/landing/partner-build-download.js
@@ -0,0 +1,265 @@
+/*
+ * 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 https://mozilla.org/MPL/2.0/.
+ */
+
+/* For reference read the Jasmine and Sinon docs
+ * Jasmine docs: https://jasmine.github.io/
+ * Sinon docs: http://sinonjs.org/docs/
+ */
+
+import PartnerBuildDownload from '../../../../../media/js/firefox/landing/partner-build-download.es6';
+
+describe('partner-build-download.es6.js', function () {
+ describe('init()', function () {
+ beforeEach(function () {
+ const strings = ``;
+
+ const button1 = ``;
+
+ const button2 = ``;
+
+ document.body.insertAdjacentHTML('beforeend', strings);
+ document.body.insertAdjacentHTML('beforeend', button1);
+ document.body.insertAdjacentHTML('beforeend', button2);
+ });
+
+ afterEach(function () {
+ const strings = document.getElementById('strings');
+ strings.parentNode.removeChild(strings);
+
+ const button1 = document.getElementById('download-primary');
+ button1.parentNode.removeChild(button1);
+
+ const button2 = document.getElementById('download-secondary');
+ button2.parentNode.removeChild(button2);
+ });
+
+ it('should update Windows and macOS download URLs with a custom build link', function () {
+ PartnerBuildDownload.init();
+
+ const downloadLinksWin = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="win"]'
+ )
+ );
+ const downloadLinksMac = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="osx"]'
+ )
+ );
+
+ downloadLinksWin.forEach((link) =>
+ expect(link.href).toContain(
+ '?product=partner-firefox-release-smi-smi-001-stub'
+ )
+ );
+ downloadLinksMac.forEach((link) =>
+ expect(link.href).toContain(
+ '?product=partner-firefox-release-smi-smi-001-latest'
+ )
+ );
+ });
+
+ it('should not update Windows URLs that are not visible on landing page', function () {
+ PartnerBuildDownload.init();
+
+ const downloadLinksWin64 = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="win64"]'
+ )
+ );
+ const downloadLinksWinMsi = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="win64-msi"]'
+ )
+ );
+ const downloadLinksWinAarch64 = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="win64-aarch64"]'
+ )
+ );
+
+ downloadLinksWin64.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001-stub'
+ )
+ );
+ downloadLinksWinMsi.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ downloadLinksWinAarch64.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ });
+
+ it('should not update Linux download URLs with a custom build link', function () {
+ PartnerBuildDownload.init();
+
+ const downloadLinksLinux = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="linux"]'
+ )
+ );
+ const downloadLinksLinux64 = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="linux64"]'
+ )
+ );
+
+ downloadLinksLinux.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ downloadLinksLinux64.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ });
+
+ it('should not update iOS download URLs with a custom build link', function () {
+ PartnerBuildDownload.init();
+
+ const downloadLinksIOS = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="ios"]'
+ )
+ );
+
+ downloadLinksIOS.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ });
+
+ it('should not update Android download URLs with a custom build link', function () {
+ PartnerBuildDownload.init();
+
+ const downloadLinksAndroid = Array.from(
+ document.querySelectorAll(
+ '.download-button .download-list .download-link[data-download-version="android"]'
+ )
+ );
+
+ downloadLinksAndroid.forEach((link) =>
+ expect(link.href).not.toContain(
+ '?product=partner-firefox-release-smi-smi-001'
+ )
+ );
+ });
+ });
+});