diff --git a/bedrock/base/templates/product-all-unified-macros.html b/bedrock/base/templates/product-all-unified-macros.html deleted file mode 100644 index 570d4d349b1..00000000000 --- a/bedrock/base/templates/product-all-unified-macros.html +++ /dev/null @@ -1,148 +0,0 @@ -{# - 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/. -#} - -{% macro select_product_list(products, disabled=False) %} - -{% endmacro %} - -{% macro select_version_list(id, version, disabled=False) %} - {% set version_select_id = 'select_' ~ id ~ '_version' %} - - -{% endmacro %} - -{% macro product_options(id, platforms, builds, version, disabled=False, hide_version=False, hide_language=False, hide_platform=False) %} -
-

- {{ select_version_list(id, version, disabled) }} -

-

- {% set platform_select_id = 'select_' ~ id ~ '_platform' %} - {{ select_platform_list(platform_select_id, platforms, disabled) }} -

-

- {% set platform_lang_id = 'select_' ~ id ~ '_language' %} - {{ select_language_list(platform_lang_id, builds, disabled) }} -

-
-{% endmacro %} - -{% macro select_platform_list(id, platforms, disabled=False) %} - - - {{ ftl('firefox-all-get-help') }} - - -{% endmacro %} - -{% macro select_language_list(id, builds, disabled=False) %} - - -{% endmacro %} - -{% macro build_locale_list(id, product_label, platforms, builds, channel) %} - {% if builds %} -
    - {% if id.startswith('desktop') %} - {% for build in builds %} - {{ build_locale_list_item(product_label, platforms, build) }} - {% endfor %} - {% else %} - {% for build in builds %} - {{ build_mobile_list_item(id, platforms, build) }} - {% endfor %} - {% endif %} -
- {% endif %} -{% endmacro %} - -{% macro build_locale_list_item(product_label, platforms, build) %} -
  • -

    - {%- if build.name_native and build.name_native != build.name_en -%} - {{ build.name_en }} {{ build.name_native }} - {%- else -%} - {{ build.name_en }} - {%- endif -%} -

    - - -
  • -{% endmacro %} - -{% macro build_mobile_list_item(id, platforms, build) %} -
  • -

    - {{ build.name_en }} -

    - - {% if id.startswith('ios') %} - - {% else %} - - {% endif %} -
  • -{% endmacro %} - - -{# - build: Locale dictionary from product details. - platform: Value in build.platforms to use. One of 'win', 'win64', 'osx', 'linux' and 'linux64'. - tooltip: Text to display as tooltip for download link. -#} -{% macro build_link(build, platform, platform_label, tooltip) %} - {% if build.platforms[platform] %} - {{ platform_label }} - - {% endif %} -{% endmacro %} diff --git a/bedrock/firefox/redirects.py b/bedrock/firefox/redirects.py index 6244d7f7d46..045c6811703 100644 --- a/bedrock/firefox/redirects.py +++ b/bedrock/firefox/redirects.py @@ -33,6 +33,7 @@ def mobile_app(request, *args, **kwargs): "firefox-welcome-17-fr", "firefox-browsers-mobile-get-app", "firefox-browsers-mobile-focus", + "firefox-all", ] for p in product_options: @@ -49,14 +50,14 @@ def mobile_app(request, *args, **kwargs): redirectpatterns = ( # overrides # issue 8096 - redirect(r"^firefox/beta/all/?$", "firefox.all", anchor="product-desktop-beta"), - redirect(r"^firefox/developer/all/?$", "firefox.all", anchor="product-desktop-developer"), - redirect(r"^firefox/aurora/all/?$", "firefox.all", anchor="product-desktop-developer"), - redirect(r"^firefox/nightly/all/?$", "firefox.all", anchor="product-desktop-nightly"), - redirect(r"^firefox/organizations/all/?$", "firefox.all", anchor="product-desktop-esr"), - redirect(r"^firefox/android/all/?$", "firefox.all", anchor="product-android-release"), - redirect(r"^firefox/android/beta/all/?$", "firefox.all", anchor="product-android-beta"), - redirect(r"^firefox/android/nightly/all/?$", "firefox.all", anchor="product-android-nightly"), + redirect(r"^firefox/beta/all/?$", "firefox.all.platforms", to_args=["desktop-beta"]), + redirect(r"^firefox/developer/all/?$", "firefox.all.platforms", to_args=["desktop-developer"]), + redirect(r"^firefox/aurora/all/?$", "firefox.all.platforms", to_args=["desktop-developer"]), + redirect(r"^firefox/nightly/all/?$", "firefox.all.platforms", to_args=["desktop-nightly"]), + redirect(r"^firefox/organizations/all/?$", "firefox.all.platforms", to_args=["desktop-esr"]), + redirect(r"^firefox/android/all/?$", "firefox.all.platforms", to_args=["android-release"]), + redirect(r"^firefox/android/beta/all/?$", "firefox.all.platforms", to_args=["android-beta"]), + redirect(r"^firefox/android/nightly/all/?$", "firefox.all.platforms", to_args=["android-nightly"]), # bug 831810 & 1142583 & 1239960, 1329931 redirect(r"^mwc/?$", "https://support.mozilla.org/products/firefox-os", re_flags="i"), # bug 748503 @@ -107,10 +108,10 @@ def mobile_app(request, *args, **kwargs): # bug 727561 redirect(r"^firefox/search(?:\.html)?$", "firefox.new"), # bug 860865, 1101220, issue 8096 - redirect(r"^firefox/all-(?:beta|rc)(?:/|\.html)?$", "firefox.all", anchor="product-desktop-beta"), - redirect(r"^firefox/all-aurora(?:/|\.html)?$", "firefox.all", anchor="product-desktop-developer"), + redirect(r"^firefox/all-(?:beta|rc)(?:/|\.html)?$", "firefox.all.platforms", to_args=["desktop-beta"]), + redirect(r"^firefox/all-aurora(?:/|\.html)?$", "firefox.all.platforms", to_args=["desktop-developer"]), redirect(r"^firefox/aurora/(?Pall|notes|system-requirements)/?$", "/firefox/developer/{page}/"), - redirect(r"^firefox/organizations/all\.html$", "firefox.all", anchor="product-desktop-esr"), + redirect(r"^firefox/organizations/all\.html$", "firefox.all.platforms", to_args=["desktop-esr"]), # bug 729329 redirect(r"^mobile/sync", "firefox.features.sync"), # bug 882845 diff --git a/bedrock/firefox/templates/firefox/all-unified.html b/bedrock/firefox/templates/firefox/all-unified.html deleted file mode 100644 index ab968cbfc95..00000000000 --- a/bedrock/firefox/templates/firefox/all-unified.html +++ /dev/null @@ -1,386 +0,0 @@ -{# - 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/. -#} - -{% from "macros.html" import google_play_button, apple_app_store_button with context %} -{% from "product-all-unified-macros.html" import build_locale_list, bw, build_link, select_product_list, product_options with context %} - -{% extends "firefox/base/base-protocol.html" %} - -{%- block page_title -%} - {{ ftl('firefox-all-download-the-firefox') }} -{%- endblock -%} - -{%- block page_desc -%} - {{ ftl('firefox-all-everyone-deserves-access') }} -{%- endblock -%} - -{% block page_css %} - {{ css_bundle('firefox_all_unified') }} -{% endblock %} - -{% block old_ie_styles %} - {{ super() }} - {{ css_bundle('firefox_all_unified_old_ie') }} -{% endblock %} - -{% block site_header %} - {% with hide_nav_cta=True %} - {% include 'includes/protocol/navigation/navigation.html' %} - {% endwith %} -{% endblock %} - -{% set android_url = android_release_full_builds[0].platforms.android.download_url %} -{% set android_beta_url = android_beta_full_builds[0].platforms.android.download_url %} -{% set android_nightly_url = android_nightly_full_builds[0].platforms.android.download_url %} -{% set ios_url = ios_release_full_builds[0].platforms.ios.download_url %} - -{% block sub_navigation %} - {% include 'firefox/includes/sub-nav-firefox.html' %} -{% endblock %} - -{% block content %} -
    -
    -
    - -
    -
    -

    - - - {{ ftl('firefox-all-get-help') }} - - {{ select_product_list(products, disabled=True) }} -

    - - {{ product_options('desktop_release', desktop_release_platforms, desktop_release_full_builds, desktop_release_latest_version, disabled=True, hide_version=True, hide_language=False) }} - {{ product_options('desktop_beta', desktop_beta_platforms, desktop_beta_full_builds, desktop_beta_latest_version, disabled=True, hide_version=True, hide_language=False) }} - {{ product_options('desktop_developer', desktop_developer_platforms, desktop_developer_full_builds, desktop_developer_latest_version, disabled=True, hide_version=True, hide_language=False) }} - {{ product_options('desktop_nightly', desktop_nightly_platforms, desktop_nightly_full_builds, desktop_nightly_latest_version, disabled=True, hide_version=True, hide_language=False) }} - - {# Show the ESR version select dropdown if there are multiple ESR's available. #} - {% set hide_esr_version_select = not desktop_esr_next_version %} - {{ product_options('desktop_esr', desktop_esr_platforms, desktop_esr_full_builds, desktop_esr_latest_version, disabled=True, hide_version=hide_esr_version_select, hide_language=False) }} - {% if desktop_esr_next_version %} - {{ product_options('desktop_esr_next', desktop_esr_platforms_next, desktop_esr_full_builds_next, desktop_esr_next_version, disabled=True, hide_version=hide_esr_version_select, hide_language=False) }} - {% endif %} - - {{ product_options('android_release', android_release_platforms, android_release_full_builds, android_release_latest_version, disabled=True, hide_version=True, hide_language=True, hide_platform=True) }} - {{ product_options('android_beta', android_beta_platforms, android_beta_full_builds, android_beta_latest_version, disabled=True, hide_version=True, hide_language=True, hide_platform=True) }} - {{ product_options('android_nightly', android_nightly_platforms, android_nightly_full_builds, android_nightly_latest_version, disabled=True, hide_version=True, hide_language=True, hide_platform=True) }} - - {{ product_options('ios_release', ios_release_platforms, ios_release_full_builds, ios_release_latest_version, disabled=True, hide_version=True, hide_language=True, hide_platform=True ) }} -
    - - -
    -
    - -
    - - -
    -
    -

    {{ ftl('firefox-all-product-firefox') }}

    - - {{ build_locale_list('desktop_release', desktop_release_channel_label, desktop_release_platforms, desktop_release_full_builds, 'release') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-beta') }}

    - - {{ build_locale_list('desktop_beta', desktop_beta_channel_label, desktop_beta_platforms, desktop_beta_full_builds, 'beta') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-developer') }}

    - - {{ build_locale_list('desktop_developer', desktop_developer_channel_label, desktop_developer_platforms, desktop_developer_full_builds, 'alpha') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-nightly') }}

    - - {{ build_locale_list('desktop_nightly', desktop_nightly_channel_label, desktop_nightly_platforms, desktop_nightly_full_builds, 'nightly') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-esr') }}

    - - - {% if desktop_esr_next_version %} -

    {{ desktop_esr_next_version }}

    - {{ build_locale_list('desktop_esr_next', desktop_esr_channel_label_next, desktop_esr_platforms_next, desktop_esr_full_builds_next, 'organizations') }} - -

    {{ desktop_esr_latest_version }}

    - {% endif %} - - {{ build_locale_list('desktop_esr', desktop_esr_channel_label, desktop_esr_platforms, desktop_esr_full_builds, 'organizations') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-android') }}

    - - {{ build_locale_list('android_release', android_release_channel_label, android_release_platforms, android_release_full_builds, 'release') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-android-beta') }}

    - {{ build_locale_list('android_beta', android_beta_channel_label, android_beta_platforms, android_beta_full_builds, 'beta') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-android-nightly') }}

    - {{ build_locale_list('android_nightly', android_nightly_channel_label, android_nightly_platforms, android_nightly_full_builds, 'nightly') }} -
    - -
    -

    {{ ftl('firefox-all-product-firefox-ios') }}

    - - {{ build_locale_list('ios_release', ios_release_channel_label, ios_release_platforms, ios_release_full_builds, 'release') }} -
    -
    -
    -
    -
    - -
    - -
    - -
    - -
    -{% endblock %} - -{% block js %} - {{ js_bundle('firefox_all_unified') }} -{% endblock %} diff --git a/bedrock/firefox/templates/firefox/all/base.html b/bedrock/firefox/templates/firefox/all/base.html new file mode 100644 index 00000000000..ee0eb610d5c --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/base.html @@ -0,0 +1,139 @@ +{# + 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/base/base-protocol.html" %} + +{%- block page_title -%} + {{ ftl('firefox-all-download-the-firefox-v2', fallback="firefox-all-download-the-firefox") }} +{%- endblock -%} + +{%- block page_desc -%} + {{ ftl('firefox-all-everyone-deserves-access-v2', fallback="'firefox-all-everyone-deserves-access") }} +{%- endblock -%} + +{% block page_css %} + {{ css_bundle('firefox_all') }} +{% endblock %} + +{% block canonical_urls %} + {% if product %} + {# do not index child pages, we prefer people visit more user friendly pages from search results #} + + {% else %} + {{ super() }} + {% endif %} +{% endblock %} + +{% block site_header %} + {% with hide_nav_cta=True %} + {% include 'includes/protocol/navigation/navigation.html' %} + {% endwith %} +{% endblock %} + +{% set campaign = "firefox-all" %} + +{% if product and platform %} + {% set current_step = ftl('firefox-all-choose-language') %} +{% elif product %} + {% set current_step = ftl('firefox-all-choose-platform') %} +{% else %} + {% set current_step = ftl('firefox-all-choose-browser') %} +{% endif %} + +{% block content %} +
    +
    +
    +
    +

    {{ ftl('firefox-all-choose-which-firefox') }}

    +

    {{ self.page_desc() }}

    + {% if product %} + {% if product.slug == "desktop-release" %} + + {% elif product.slug == "desktop-beta" %} + + {% elif product.slug == "desktop-developer" %} + + {% elif product.slug == "desktop-nightly" %} + + {% elif product.slug == "desktop-esr" %} + + {% elif product.slug == "android-beta" %} + + {% elif product.slug in ["mobile-release", "android-release", "ios-release"] %} +
    + {% if product.slug in ["android-release", "mobile-release"] %} + Android: + + {% endif %} + {% if product.slug in ["ios-release", "mobile-release"] %} + iOS: + + {% endif %} +
    + {% endif %} + {% endif %} + +
    +
    + {% include 'firefox/all/product.html' %} + {% include 'firefox/all/platform.html' %} + {% include 'firefox/all/lang.html' %} + {% include 'firefox/all/download.html' %} +
    +
    +
    +
    +{% endblock %} + +{% block js %} + {{ js_bundle('firefox_all') }} +{% endblock %} diff --git a/bedrock/firefox/templates/firefox/all/download.html b/bedrock/firefox/templates/firefox/all/download.html new file mode 100644 index 00000000000..a49844346da --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/download.html @@ -0,0 +1,90 @@ +{# + 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/. +#} + +{% from "macros.html" import google_play_button, apple_app_store_button, ms_store_button with context %} + +{% set icon_download ='' %} + +{# Show download #} + +

    + {{ ftl('firefox-all-download') }} + {% if product and platform and locale %} + {{ ftl('firefox-all-down-arrow') }} + {% else %} + {{ current_step }} + {% endif %} +

    + +{% if product and platform and locale %} +
    + {% if platform.startswith("linux") %} + {# Linux preamble #} +


    + {{ ftl('download-button-using-debian', attrs='href="https://support.mozilla.org/kb/install-firefox-linux#w_install-firefox-deb-package-for-debian-based-distributions" rel="external noopener" data-cta-type="link" data-cta-text="You can set up our APT repository instead"'|safe) }} +

    + {% endif %} + {% if product.slug.startswith("desktop") %} + {% if platform.startswith("win-store") %} + {# Windows Store #} +

    + {% set ms_store_product = 'firefox_beta' if product.slug.endswith('beta') else 'firefox' %} + {% set ms_store_href = ms_store_url(product=ms_store_product, campaign='firefox-all') %} + {{ ms_store_button(href=ms_store_href, id='msStoreLink') }} +

    + {% elif product.slug == "desktop-esr" %} + {# ESR #} + {% set is_esr_next_version = desktop_esr_next_version %} + {# If there are multiple ESR's available, show next as well. #} + {% if is_esr_next_version %} +

    + + {{ ftl('firefox-all-download-esr-version', esr_version=desktop_esr_next_version) }} {{ icon_download|safe }} + +

    + + {% if ftl_has_messages('firefox-all-esr-we-are-in-transition', 'firefox-all-esr-when-there-is', 'firefox-all-esr-if-you-choose') %} +

    {{ ftl('firefox-all-esr-we-are-in-transition', esr_next_major=desktop_esr_next_version.split('.')[0], esr_current_major=desktop_esr_latest_version.split('.')[0] ) }}

    +

    {{ ftl('firefox-all-esr-when-there-is') }}

    +

    {{ ftl('firefox-all-esr-if-you-choose') }}

    + {% endif %} + +

    + + {{ ftl('firefox-all-download-esr-version', esr_version=desktop_esr_latest_version) }} {{ icon_download|safe }} + +

    + + {% if ftl_has_messages('firefox-all-esr-read-more') %} +

    {{ ftl('firefox-all-esr-read-more', attrs='href="https://support.mozilla.org/kb/firefox-esr-release-cycle" rel="external noopener" data-link-text="Firefox ESR release cycle"') }}

    + {% endif %} + + {% else %} + {# ESR latest #} +

    + + {{ ftl('download-button-download-now') }} {{ icon_download|safe }} + +

    + {% endif %} + {% else %} + {# All other desktop releases #} +

    + + {{ ftl('download-button-download-now') }} {{ icon_download|safe }} + +

    + {% endif %} + {% elif product.slug.startswith('android') or product.slug.startswith('ios') or product.slug.startswith('mobile') %} + {# all mobile releases #} + {% include 'firefox/all/mobile.html' %} + {% else %} +

    + {{ ftl('firefox-all-sorry-we-couldnt-find') }} +

    + {% endif %} +
    +{% endif %} diff --git a/bedrock/firefox/templates/firefox/all/lang.html b/bedrock/firefox/templates/firefox/all/lang.html new file mode 100644 index 00000000000..f6009b632e4 --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/lang.html @@ -0,0 +1,31 @@ +{# + 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/. +#} + +{# Choose language #} +

    + {{ ftl('firefox-all-language') }} + {% if product and platform and locale %} + {{ locale_name }} + {% if product.slug.startswith('desktop') and platform != "win-store" %} + {{ ftl('firefox-all-change-language') }} + {% endif %} + {% elif product and platform %} + {{ ftl('firefox-all-down-arrow') }} + {% else %} + {{ current_step }} + {% endif %} +

    + +{% if product and platform and not locale %} +
    +

    {{ ftl('firefox-all-select-your-preferred-language') }}{{ ftl('firefox-all-down-arrow') }}

    + +
    +{% endif %} diff --git a/bedrock/firefox/templates/firefox/all/mobile.html b/bedrock/firefox/templates/firefox/all/mobile.html new file mode 100644 index 00000000000..996548b4034 --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/mobile.html @@ -0,0 +1,53 @@ +{# + 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/. +#} + +{% set android_url = play_store_url('firefox', 'firefox-all') %} +{% set android_beta_url = play_store_url('firefox_beta', 'firefox-all') %} +{% set android_nightly_url = play_store_url('firefox_nightly', 'firefox-all') %} +{% set ios_url = app_store_url('firefox', 'firefox-all') %} + +{% if product.slug == 'android-beta' %} + {% set qr_url = android_beta_url %} + {% set android_url = android_beta_url %} +{% elif product.slug == 'android-nightly' %} + {% set qr_url = android_nightly_url %} + {% set android_url = android_nightly_url %} +{% else %} + {% set qr_url ="https://www.mozilla.org/firefox/browsers/mobile/app/?product=firefox&campaign=firefox-all" %} +{% endif %} + +
    + + {% if product.slug.startswith("ios-beta") %} +

    + {{ ftl('firefox-all-testflight') }} +

    + {% else %} +
    + {{ qrcode(qr_url, 12) }} + +
    +
      + {% if product.slug.startswith(('mobile', 'android')) %} +
    • + {{ google_play_button(href=android_url, id='playStoreLink') }} +
    • + {% endif %} + {% if product.slug.startswith(('mobile', 'ios')) %} +
    • + {{ apple_app_store_button(href=ios_url, id='appStoreLink') }} +
    • + {% endif %} +
    +
    +
    + {% endif %} + + {% if product.slug.startswith('mobile') %} +

    {{ ftl('firefox-all-product-send-link') }}

    + {% endif %} + +
    diff --git a/bedrock/firefox/templates/firefox/all/platform.html b/bedrock/firefox/templates/firefox/all/platform.html new file mode 100644 index 00000000000..6e1d13106e4 --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/platform.html @@ -0,0 +1,75 @@ +{# + 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/. + #} + + +{# Choose platform #} + +

    + {{ ftl('firefox-all-platform') }} + {% if product and platform %} + {{ platform_name }} + {% if product.slug.startswith('desktop') %} + {{ ftl('firefox-all-change-platform') }} + {% endif %} + {% elif product %} + {{ ftl('firefox-all-down-arrow') }} + {% else %} + {{ current_step }} + {% endif %} + + {% if product and not platform %} + + {{ ftl('firefox-all-get-help') }} + + {% endif %} +

    + +{% if product and not platform %} +
    + +
    +

    {{ ftl('firefox-all-learn-about-installers')}}

    +
      +
    • +

      {{ ftl('firefox-all-64-bit-installers') }}

      +

      + {{ ftl('firefox-all-choose-a-64-bit-installer') }} +

      +
    • +
    • +

      {{ ftl('firefox-all-32-bit-installers') }}

      +

      + {{ ftl('firefox-all-choose-a-32-bit-installer', url='https://support.mozilla.org/kb/choosing-firefox-cpu-architecture-windows-os') }} +

      +
    • +
    • +

      {{ ftl('firefox-all-msi-installers') }}

      +

      + {{ ftl('firefox-all-windows-installers-for') }} +

      +
    • +
    • +

      {{ ftl('firefox-all-arm64-installers') }}

      +

      + {{ ftl('firefox-all-arm64-installers-optimized-v2') }} +

      +
    • + {% if ftl_has_messages('firefox-all-choose-a-microsoft-store-installer') %} +
    • +

      {{ ftl('firefox-all-microsoft-store-installers') }}

      +

      + {{ ftl('firefox-all-choose-a-microsoft-store-installer') }} +

      +
    • + {% endif %} +
    +
    +
    +{% endif %} diff --git a/bedrock/firefox/templates/firefox/all/product.html b/bedrock/firefox/templates/firefox/all/product.html new file mode 100644 index 00000000000..6bd6a93b8f6 --- /dev/null +++ b/bedrock/firefox/templates/firefox/all/product.html @@ -0,0 +1,74 @@ +{# + 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/. +#} + +{# Choose product #} + +

    + {{ ftl('firefox-all-browser') }} + {% if product %} + {{ product and product.name }} {{ ftl('firefox-all-change-browser') }} + {% else %} + {{ ftl('firefox-all-down-arrow') }} + {% endif %} + + {% if not product %} + + {{ ftl('firefox-all-get-help') }} + + {% endif %} +

    + +{% if not product %} +
    +

    {{ ftl('firefox-all-desktop') }}

    + + +

    {{ ftl('firefox-all-mobile') }}

    + + + +
    +

    {{ ftl('firefox-all-learn-about-firefox') }}

    + +
    +
    +{% endif %} diff --git a/bedrock/firefox/templates/firefox/includes/download-unsupported.html b/bedrock/firefox/templates/firefox/includes/download-unsupported.html index 429c8d6511a..8416a8529cb 100644 --- a/bedrock/firefox/templates/firefox/includes/download-unsupported.html +++ b/bedrock/firefox/templates/firefox/includes/download-unsupported.html @@ -30,7 +30,7 @@

    - + {{ ftl('download-a-different-build') }} diff --git a/bedrock/firefox/templatetags/helpers.py b/bedrock/firefox/templatetags/helpers.py index 7e400b602c6..e32406bbf12 100644 --- a/bedrock/firefox/templatetags/helpers.py +++ b/bedrock/firefox/templatetags/helpers.py @@ -311,7 +311,6 @@ def firefox_url(platform, page, channel=None): """ kwargs = {} - anchor = None # Tweak the channel name for the naming URL pattern in urls.py if channel == "release": @@ -328,22 +327,22 @@ def firefox_url(platform, page, channel=None): if page == "all": if platform == "desktop": if channel == "beta": - anchor = "product-desktop-beta" + product = "desktop-beta" elif channel == "developer": - anchor = "product-desktop-developer" + product = "desktop-developer" elif channel == "nightly": - anchor = "product-desktop-nightly" + product = "desktop-nightly" elif channel == "organizations": - anchor = "product-desktop-esr" + product = "desktop-esr" else: - anchor = "product-desktop-release" + product = "desktop-release" elif platform == "android": if channel == "beta": - anchor = "product-android-beta" + product = "android-beta" elif channel == "nightly": - anchor = "product-android-nightly" + product = "android-nightly" else: - anchor = "product-android-release" + product = "android-release" else: if channel: kwargs["channel"] = channel @@ -354,8 +353,11 @@ def firefox_url(platform, page, channel=None): if platform in ["android", "ios"] and page == "sysreq": return settings.FIREFOX_MOBILE_SYSREQ_URL - anchor = "#" + anchor if anchor else "" - return reverse(f"firefox.{page}", kwargs=kwargs) + anchor + if page == "all" and product: + kwargs["product_slug"] = product + return reverse("firefox.all.platforms", kwargs=kwargs) + + return reverse(f"firefox.{page}", kwargs=kwargs) @library.global_function diff --git a/bedrock/firefox/tests/test_base.py b/bedrock/firefox/tests/test_base.py index 97b12727385..899312bed16 100644 --- a/bedrock/firefox/tests/test_base.py +++ b/bedrock/firefox/tests/test_base.py @@ -4,18 +4,15 @@ import os from unittest.mock import Mock, call, patch -from django.core.cache import caches from django.http import HttpResponse from django.test.client import RequestFactory from django.test.utils import override_settings from django_jinja.backend import Jinja2 from markupsafe import Markup -from pyquery import PyQuery as pq from bedrock.base.urlresolvers import reverse from bedrock.firefox import views as fx_views -from bedrock.firefox.firefox_details import FirefoxDesktop from bedrock.mozorg.tests import TestCase TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "test_data") @@ -197,74 +194,6 @@ def test_one_button_when_channel_specified(self): ) -class TestFirefoxAll(TestCase): - pd_cache = caches["product-details"] - - def setUp(self): - self.pd_cache.clear() - self.firefox_desktop = FirefoxDesktop(json_dir=PROD_DETAILS_DIR) - self.patcher = patch.object(fx_views, "firefox_desktop", self.firefox_desktop) - self.patcher.start() - - def tearDown(self): - self.patcher.stop() - - def test_all_builds_results(self): - """ - The unified page should display builds for all products - """ - resp = self.client.get(reverse("firefox.all")) - doc = pq(resp.content) - assert len(doc(".c-all-downloads-build")) == 9 - - desktop_release_builds = len(self.firefox_desktop.get_filtered_full_builds("release")) - assert len(doc('.c-locale-list[data-product="desktop_release"] > li')) == desktop_release_builds - assert len(doc('.c-locale-list[data-product="desktop_release"] > li[data-language="en-US"] > ul > li > a')) == 8 - - desktop_beta_builds = len(self.firefox_desktop.get_filtered_full_builds("beta")) - assert len(doc('.c-locale-list[data-product="desktop_beta"] > li')) == desktop_beta_builds - assert len(doc('.c-locale-list[data-product="desktop_beta"] > li[data-language="en-US"] > ul > li > a')) == 8 - - desktop_developer_builds = len(self.firefox_desktop.get_filtered_full_builds("alpha")) - assert len(doc('.c-locale-list[data-product="desktop_developer"] > li')) == desktop_developer_builds - assert len(doc('.c-locale-list[data-product="desktop_developer"] > li[data-language="en-US"] > ul > li > a')) == 8 - - desktop_nightly_builds = len(self.firefox_desktop.get_filtered_full_builds("nightly")) - assert len(doc('.c-locale-list[data-product="desktop_nightly"] > li')) == desktop_nightly_builds - assert len(doc('.c-locale-list[data-product="desktop_nightly"] > li[data-language="en-US"] > ul > li > a')) == 9 - - desktop_esr_builds = len(self.firefox_desktop.get_filtered_full_builds("esr")) - assert len(doc('.c-locale-list[data-product="desktop_esr"] > li')) == desktop_esr_builds - assert len(doc('.c-locale-list[data-product="desktop_esr"] > li[data-language="en-US"] > ul > li > a')) == 8 - - android_release_builds = 1 - assert len(doc('.c-locale-list[data-product="android_release"] > li')) == android_release_builds - assert len(doc('.c-locale-list[data-product="android_release"] > li[data-language="multi"] > ul > li > a')) == 2 - - android_beta_builds = 1 - assert len(doc('.c-locale-list[data-product="android_beta"] > li')) == android_beta_builds - assert len(doc('.c-locale-list[data-product="android_beta"] > li[data-language="multi"] > ul > li > a')) == 1 - - android_nightly_builds = 1 - assert len(doc('.c-locale-list[data-product="android_nightly"] > li')) == android_nightly_builds - assert len(doc('.c-locale-list[data-product="android_nightly"] > li[data-language="multi"] > ul > li > a')) == 1 - - ios_release_builds = 1 - assert len(doc('.c-locale-list[data-product="ios_release"] > li')) == ios_release_builds - assert len(doc('.c-locale-list[data-product="ios_release"] > li[data-language="multi"] > ul > li > a')) == 2 - - def test_no_locale_details(self): - """ - When a localized build has been added to the Firefox details while the - locale details are not updated yet, the filtered build list should not - include the localized build. - """ - builds = self.firefox_desktop.get_filtered_full_builds("release") - assert "uz" in self.firefox_desktop.firefox_primary_builds - assert "uz" not in self.firefox_desktop.languages - assert len([build for build in builds if build["locale"] == "uz"]) == 0 - - @patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse()) class TestWhatsNew(TestCase): def setUp(self): diff --git a/bedrock/firefox/tests/test_firefox_all.py b/bedrock/firefox/tests/test_firefox_all.py new file mode 100644 index 00000000000..b8b55493fa0 --- /dev/null +++ b/bedrock/firefox/tests/test_firefox_all.py @@ -0,0 +1,413 @@ +# 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 os +from unittest.mock import patch + +from django.conf import settings +from django.core.cache import caches + +import pytest +from pyquery import PyQuery as pq + +from bedrock.base.urlresolvers import reverse +from bedrock.firefox.firefox_details import firefox_desktop + +TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "test_data") +PROD_DETAILS_DIR = os.path.join(TEST_DATA_DIR, "product_details_json") + + +# All tests require the database +pytestmark = pytest.mark.django_db + +pd_cache = caches["product-details"] + +OS_LANG_PAIRS = [ + # windows + ("win64", "en-US"), + ("win64-msi", "en-US"), + ("win64-aarch64", "en-US"), + ("win", "en-US"), + ("win-msi", "en-US"), + ("win64", "de"), + ("win64-msi", "fr"), + ("win64-aarch64", "hi-IN"), + ("win", "ja"), + ("win-msi", "es-ES"), + # macos + ("osx", "en-US"), + ("osx", "de"), + ("osx", "fr"), + ("osx", "hi-IN"), + # linux + ("linux64", "en-US"), + ("linux", "en-US"), + ("linux64", "de"), + ("linux", "fr"), + ("linux64", "hi-IN"), + ("linux", "ja"), +] + + +def test_all_step_1(client): + resp = client.get(reverse("firefox.all")) + doc = pq(resp.content) + + # Step 1 is active, steps 2,3,4 are disabled. + assert len(doc(".t-step-disabled")) == 3 + # 5 desktop products, 4 mobile products. + assert len(doc(".c-product-list > li")) == 9 + assert len(doc(".qa-desktop-list > li")) == 5 + assert len(doc(".qa-mobile-list > li")) == 4 + + +@pytest.mark.parametrize( + "product_slug, name, count", + ( + ("desktop-release", "Firefox", 9), + ("desktop-esr", "Firefox Extended Support Release", 8), + ("desktop-beta", "Firefox Beta", 9), + ("desktop-developer", "Firefox Developer Edition", 8), + ("desktop-nightly", "Firefox Nightly", 9), + ), +) +def test_all_step_2(client, product_slug, name, count): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": product_slug})) + doc = pq(resp.content) + + # Step 1 is done, step 2 is active, steps 3,4 are disabled. + assert doc(".c-steps > h2").eq(0).find(".c-step-choice").text() == name + assert len(doc(".t-step-disabled")) == 2 + # platforms for desktop-release, including Windows Store + assert len(doc(".c-platform-list > li")) == count + + +def test_all_step_3(client): + resp = client.get(reverse("firefox.all.locales", kwargs={"product_slug": "desktop-release", "platform": "win64"})) + doc = pq(resp.content) + + # Step 1,2 is done, step 3 is active, step 4 are disabled. + assert doc(".c-steps > h2").eq(0).find(".c-step-choice").text() == "Firefox" + assert doc(".c-steps > h2").eq(1).find(".c-step-choice").text() == "Windows 64-bit" + assert len(doc(".t-step-disabled")) == 1 + # first locale matches request.locale + assert doc(".c-lang-list > li").eq(0).text() == "English (US) - English (US)" + # number of locales equals the number of builds + assert len(doc(".c-lang-list > li")) == len(firefox_desktop.get_filtered_full_builds("release")) + + +def test_all_step_4(client): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-release", "platform": "win64", "locale": "en-US"})) + doc = pq(resp.content) + + # Step 1,2,3 is done, step 4 is active, no more steps + assert doc(".c-steps > h2").eq(0).find(".c-step-choice").text() == "Firefox" + assert doc(".c-steps > h2").eq(1).find(".c-step-choice").text() == "Windows 64-bit" + assert doc(".c-steps > h2").eq(2).find(".c-step-choice").text() == "English (US) - English (US)" + assert len(doc(".t-step-disabled")) == 0 + # The download button should be present and correct. + assert len(doc(".c-download-button")) == 1 + assert ( + doc(".c-download-button").attr("href") + == list(filter(lambda b: b["locale"] == "en-US", firefox_desktop.get_filtered_full_builds("release")))[0]["platforms"]["win64"][ + "download_url" + ] + ) + + +@pytest.mark.parametrize("os, lang", OS_LANG_PAIRS) +def test_firefox_release(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-release", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + if "msi" in os: + product = "firefox-msi-latest-ssl" + os = os.replace("-msi", "") + else: + product = "firefox-latest-ssl" + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +def test_firefox_microsoft_store_release(client): + resp = client.get(reverse("firefox.all.locales", kwargs={"product_slug": "desktop-release", "platform": "win-store"})) + doc = pq(resp.content) + + assert len(doc("#msStoreLink")) == 1 + assert settings.MICROSOFT_WINDOWS_STORE_FIREFOX_WEB_LINK in doc("#msStoreLink").attr("href") + + +@pytest.mark.parametrize("os, lang", OS_LANG_PAIRS) +def test_firefox_beta(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-beta", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + if "msi" in os: + product = "firefox-beta-msi-latest-ssl" + os = os.replace("-msi", "") + else: + product = "firefox-beta-latest-ssl" + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +def test_firefox_microsoft_store_beta(client): + resp = client.get(reverse("firefox.all.locales", kwargs={"product_slug": "desktop-beta", "platform": "win-store"})) + doc = pq(resp.content) + + assert len(doc("#msStoreLink")) == 1 + assert settings.MICROSOFT_WINDOWS_STORE_FIREFOX_BETA_WEB_LINK in doc("#msStoreLink").attr("href") + + +@pytest.mark.parametrize("os, lang", OS_LANG_PAIRS) +def test_firefox_developer(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-developer", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + if "msi" in os: + product = "firefox-devedition-msi-latest-ssl" + os = os.replace("-msi", "") + else: + product = "firefox-devedition-latest-ssl" + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +@pytest.mark.parametrize("os, lang", OS_LANG_PAIRS) +def test_firefox_nightly(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-nightly", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + if "msi" in os: + product = "firefox-nightly-msi-latest-l10n-ssl" + os = os.replace("-msi", "") + else: + product = "firefox-nightly-latest-l10n-ssl" + if lang == "en-US": + # en-us downloads don't get the l10n releases + product = product.replace("-l10n", "") + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +@pytest.mark.parametrize("os, lang", [("linux64-aarch64", "es-ES"), ("linux64-aarch64", "pt-BR")]) +def test_firefox_linux_nightly_aarch(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-nightly", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + product = "firefox-nightly-latest-l10n-ssl" + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +@pytest.mark.parametrize("os, lang", OS_LANG_PAIRS) +def test_firefox_esr(client, os, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-esr", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 1 + download_url = link.attr("href") + if "msi" in os: + product = "firefox-esr-msi-latest-ssl" + os = os.replace("-msi", "") + else: + product = "firefox-esr-latest-ssl" + assert all(substr in download_url for substr in [f"product={product}", f"os={os}", f"lang={lang}"]) + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +@pytest.mark.parametrize("os, lang", [("win64", "en-US"), ("win64", "de"), ("osx", "en-US"), ("linux", "en-US")]) +def test_firefox_esr_next(client, os, lang): + # Note: Only testing a few os/lang pairs to avoid mocking too much. We're mostly checking that 2 buttons show up. + + # Set an esr_next version. + orig_latest_version = firefox_desktop.latest_version + orig_get_filtered_full_builds = firefox_desktop.get_filtered_full_builds + + def mock_latest_version(channel="release"): + if channel == "esr_next": + return "128.0" + else: + return orig_latest_version(channel) + + def mock_get_filtered_full_builds(channel, query=None): + if channel == "esr_next": + return [ + { + "locale": "en-US", + "platforms": { + "win64": { + "download_url": "https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&os=win64&lang=en-US", + }, + "linux": { + "download_url": "https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&os=linux&lang=en-US", + }, + "osx": { + "download_url": "https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&os=osx&lang=en-US", + }, + }, + }, + { + "locale": "de", + "platforms": { + "win64": { + "download_url": "https://download.mozilla.org/?product=firefox-esr-next-latest-ssl&os=win64&lang=de", + }, + }, + }, + ] + else: + return orig_get_filtered_full_builds(channel, query) + + with patch("bedrock.firefox.views.firefox_desktop.latest_version", side_effect=mock_latest_version): + with patch("bedrock.firefox.views.firefox_desktop.get_filtered_full_builds", side_effect=mock_get_filtered_full_builds): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-esr", "platform": os, "locale": lang})) + doc = pq(resp.content) + + link = doc(".c-download-button") + assert len(link) == 2 # We show both the current ESR and the next ESR download buttons. + + download_esr_next_url = link.eq(0).attr("href") + download_esr_url = link.eq(1).attr("href") + + assert all(substr in download_esr_next_url for substr in ["product=firefox-esr-next-latest-ssl", f"os={os}", f"lang={lang}"]) + assert all(substr in download_esr_url for substr in ["product=firefox-esr-latest-ssl", f"os={os}", f"lang={lang}"]) + + if os.startswith("linux"): + linux_link = doc(".c-linux-debian a") + assert len(linux_link) == 1 + assert "https://support.mozilla.org/kb/install-firefox-linux" in linux_link.attr("href") + + +def test_firefox_mobile_release(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "mobile-release"})) + doc = pq(resp.content) + + assert len(doc("#playStoreLink")) == 1 + assert len(doc("#appStoreLink")) == 1 + + +def test_firefox_android_release(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "android-release"})) + doc = pq(resp.content) + + assert len(doc("#playStoreLink")) == 1 + assert len(doc("#appStoreLink")) == 0 + + +def test_firefox_android_beta(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "android-beta"})) + doc = pq(resp.content) + + assert len(doc("#playStoreLink")) == 1 + assert len(doc("#appStoreLink")) == 0 + + +def test_firefox_android_nightly(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "android-nightly"})) + doc = pq(resp.content) + + assert len(doc("#playStoreLink")) == 1 + assert len(doc("#appStoreLink")) == 0 + + +def test_firefox_ios_beta(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "ios-beta"})) + doc = pq(resp.content) + + assert len(doc("#playStoreLink")) == 0 + assert len(doc("#appStoreLink")) == 0 + assert doc(".c-step-download a").attr("href") == reverse("firefox.ios.testflight") + + +@pytest.mark.parametrize( + "slug, count", + [ + ("", 0), + ("desktop-release/", 1), + ("desktop-release/win64/", 2), + ("desktop-release/win64/en-US/", 3), + ("desktop-release/win-store/", 2), + ("mobile-release/", 1), + ("android-release/", 1), + ], +) +def test_close_icons(client, slug, count): + url = reverse("firefox.all") + slug + resp = client.get(url) + doc = pq(resp.content) + assert len(doc("[src='/media/protocol/img/icons/close.svg']")) == count + + +def test_product_404(client): + resp = client.get(reverse("firefox.all.platforms", kwargs={"product_slug": "xxx"})) + assert resp.status_code == 404 + + +def test_platform_404(client): + resp = client.get(reverse("firefox.all.locales", kwargs={"product_slug": "desktop-release", "platform": "xxx"})) + assert resp.status_code == 404 + + +def test_locale_404(client): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": "desktop-release", "platform": "win64", "locale": "xxx"})) + assert resp.status_code == 404 + + +@pytest.mark.parametrize("product_slug", [("desktop-release"), ("desktop-esr"), ("desktop-beta"), ("desktop-developer")]) +@pytest.mark.parametrize("lang", [("ckb"), ("ltg"), ("hye"), ("wo"), ("lo"), ("scn"), ("brx"), ("meh"), ("bo")]) +def test_nightly_locales_only_on_nightly(client, product_slug, lang): + resp = client.get(reverse("firefox.all.download", kwargs={"product_slug": product_slug, "platform": "win64", "locale": lang})) + assert resp.status_code == 404 + + +@pytest.mark.parametrize( + "product_slug", + [ + ("desktop-developer"), + ("desktop-nightly"), + ("desktop-esr"), + ("android-release"), + ("android-beta"), + ("android-nightly"), + ("ios-release"), + ("ios-beta"), + ("mobile-release"), + ], +) +def test_win_store_only_on_release_and_beta(client, product_slug): + resp = client.get(reverse("firefox.all.locales", kwargs={"product_slug": product_slug, "platform": "win-store"})) + assert resp.status_code == 404 diff --git a/bedrock/firefox/tests/test_helpers.py b/bedrock/firefox/tests/test_helpers.py index 16d129fd4cd..b3884706ee2 100644 --- a/bedrock/firefox/tests/test_helpers.py +++ b/bedrock/firefox/tests/test_helpers.py @@ -501,18 +501,18 @@ def _render(self, platform, page, channel=None): def test_firefox_all(self): """Should return a reversed path for the Firefox all downloads page""" - assert self._render("desktop", "all").endswith("/firefox/all/#product-desktop-release") - assert self._render("desktop", "all", "release").endswith("/firefox/all/#product-desktop-release") - assert self._render("desktop", "all", "beta").endswith("/firefox/all/#product-desktop-beta") - assert self._render("desktop", "all", "alpha").endswith("/firefox/all/#product-desktop-developer") - assert self._render("desktop", "all", "developer").endswith("/firefox/all/#product-desktop-developer") - assert self._render("desktop", "all", "nightly").endswith("/firefox/all/#product-desktop-nightly") - assert self._render("desktop", "all", "esr").endswith("/firefox/all/#product-desktop-esr") - assert self._render("desktop", "all", "organizations").endswith("/firefox/all/#product-desktop-esr") - assert self._render("android", "all").endswith("/firefox/all/#product-android-release") - assert self._render("android", "all", "release").endswith("/firefox/all/#product-android-release") - assert self._render("android", "all", "beta").endswith("/firefox/all/#product-android-beta") - assert self._render("android", "all", "nightly").endswith("/firefox/all/#product-android-nightly") + assert self._render("desktop", "all").endswith("/firefox/all/desktop-release/") + assert self._render("desktop", "all", "release").endswith("/firefox/all/desktop-release/") + assert self._render("desktop", "all", "beta").endswith("/firefox/all/desktop-beta/") + assert self._render("desktop", "all", "alpha").endswith("/firefox/all/desktop-developer/") + assert self._render("desktop", "all", "developer").endswith("/firefox/all/desktop-developer/") + assert self._render("desktop", "all", "nightly").endswith("/firefox/all/desktop-nightly/") + assert self._render("desktop", "all", "esr").endswith("/firefox/all/desktop-esr/") + assert self._render("desktop", "all", "organizations").endswith("/firefox/all/desktop-esr/") + assert self._render("android", "all").endswith("/firefox/all/android-release/") + assert self._render("android", "all", "release").endswith("/firefox/all/android-release/") + assert self._render("android", "all", "beta").endswith("/firefox/all/android-beta/") + assert self._render("android", "all", "nightly").endswith("/firefox/all/android-nightly/") def test_firefox_sysreq(self): """Should return a reversed path for the Firefox sysreq page""" diff --git a/bedrock/firefox/urls.py b/bedrock/firefox/urls.py index d9defbc613b..251240361ac 100644 --- a/bedrock/firefox/urls.py +++ b/bedrock/firefox/urls.py @@ -36,6 +36,9 @@ ), ), path("firefox/all/", views.firefox_all, name="firefox.all"), + path("firefox/all//", views.firefox_all, name="firefox.all.platforms"), + path("firefox/all///", views.firefox_all, name="firefox.all.locales"), + path("firefox/all////", views.firefox_all, name="firefox.all.download"), page("firefox/channel/desktop/", "firefox/channel/desktop.html", ftl_files=["firefox/channel"]), page("firefox/channel/android/", "firefox/channel/android.html", ftl_files=["firefox/channel"]), page("firefox/channel/ios/", "firefox/channel/ios.html", ftl_files=["firefox/channel"]), diff --git a/bedrock/firefox/views.py b/bedrock/firefox/views.py index 6ce5c5655b2..d33bbec503b 100644 --- a/bedrock/firefox/views.py +++ b/bedrock/firefox/views.py @@ -8,12 +8,13 @@ from urllib.parse import urlparse from django.conf import settings -from django.http import HttpResponsePermanentRedirect, JsonResponse +from django.http import Http404, HttpResponsePermanentRedirect, JsonResponse from django.utils.cache import patch_response_headers from django.utils.encoding import force_str from django.views.decorators.http import require_safe import querystringsafe_base64 +from product_details import product_details from product_details.version_compare import Version from bedrock.base.geo import get_country_from_request @@ -169,150 +170,216 @@ def sign_attribution_codes(codes): @require_safe -def firefox_all(request): +def firefox_all(request, product_slug=None, platform=None, locale=None): ftl_files = "firefox/all" - product_android = firefox_android - product_desktop = firefox_desktop - product_ios = firefox_ios - - # Human-readable product labels - products = OrderedDict( - [ - ( - "desktop_release", - ftl("firefox-all-product-firefox", ftl_files=ftl_files), - ), - ( - "desktop_beta", - ftl("firefox-all-product-firefox-beta", ftl_files=ftl_files), - ), - ( - "desktop_developer", - ftl("firefox-all-product-firefox-developer", ftl_files=ftl_files), - ), - ( - "desktop_nightly", - ftl("firefox-all-product-firefox-nightly", ftl_files=ftl_files), - ), - ( - "desktop_esr", - ftl("firefox-all-product-firefox-esr", ftl_files=ftl_files), - ), - ( - "android_release", - ftl("firefox-all-product-firefox-android", ftl_files=ftl_files), - ), - ( - "android_beta", - ftl("firefox-all-product-firefox-android-beta", ftl_files=ftl_files), - ), - ( - "android_nightly", - ftl("firefox-all-product-firefox-android-nightly", ftl_files=ftl_files), - ), - ( - "ios_release", - ftl("firefox-all-product-firefox-ios", ftl_files=ftl_files), - ), - ] - ) - channel_release = "release" - channel_beta = "beta" - channel_dev = "devedition" - channel_nightly = "nightly" - channel_esr = "esr" - channel_esr_next = "esr_next" - - latest_release_version_desktop = product_desktop.latest_version(channel_release) - latest_beta_version_desktop = product_desktop.latest_version(channel_beta) - latest_developer_version_desktop = product_desktop.latest_version(channel_dev) - latest_nightly_version_desktop = product_desktop.latest_version(channel_nightly) - latest_esr_version_desktop = product_desktop.latest_version(channel_esr) - latest_esr_next_version_desktop = product_desktop.latest_version(channel_esr_next) - - latest_release_version_android = product_android.latest_version(channel_release) - latest_beta_version_android = product_android.latest_version(channel_beta) - latest_nightly_version_android = product_android.latest_version(channel_nightly) - latest_release_version_ios = product_ios.latest_version(channel_release) + # A product object for android OR ios. + class MobileRelease: + slug = "mobile-release" + + mobile_release = MobileRelease() + + product_map = { + "desktop-release": { + "slug": "desktop-release", + "product": firefox_desktop, + "channel": "release", + "name": ftl("firefox-all-product-firefox", ftl_files=ftl_files), + }, + "desktop-beta": { + "slug": "desktop-beta", + "product": firefox_desktop, + "channel": "beta", + "name": ftl("firefox-all-product-firefox-beta", ftl_files=ftl_files), + }, + "desktop-developer": { + "slug": "desktop-developer", + "product": firefox_desktop, + "channel": "devedition", + "name": ftl("firefox-all-product-firefox-developer", ftl_files=ftl_files), + }, + "desktop-nightly": { + "slug": "desktop-nightly", + "product": firefox_desktop, + "channel": "nightly", + "name": ftl("firefox-all-product-firefox-nightly", ftl_files=ftl_files), + }, + "desktop-esr": { + "slug": "desktop-esr", + "product": firefox_desktop, + "channel": "esr", + "name": ftl("firefox-all-product-firefox-esr", ftl_files=ftl_files), + }, + "android-release": { + "slug": "android-release", + "product": firefox_android, + "channel": "release", + "name": ftl("firefox-all-product-firefox-android", ftl_files=ftl_files), + }, + "android-beta": { + "slug": "android-beta", + "product": firefox_android, + "channel": "beta", + "name": ftl("firefox-all-product-firefox-android-beta", ftl_files=ftl_files), + }, + "android-nightly": { + "slug": "android-nightly", + "product": firefox_android, + "channel": "nightly", + "name": ftl("firefox-all-product-firefox-android-nightly", ftl_files=ftl_files), + }, + "ios-release": { + "slug": "ios-release", + "product": firefox_ios, + "channel": "release", + "name": ftl("firefox-all-product-firefox-ios", ftl_files=ftl_files), + }, + "ios-beta": { + "slug": "ios-beta", + "product": firefox_ios, + "channel": "beta", + "name": ftl("firefox-all-product-firefox-ios", ftl_files=ftl_files), + }, + # mobile-release is a special case for both android and ios. + "mobile-release": { + "slug": "mobile-release", + "product": mobile_release, + "channel": "release", + "name": ftl("firefox-all-product-firefox", ftl_files=ftl_files), + }, + } + + platform_map = { + "win64": "Windows 64-bit", + "win64-msi": "Windows 64-bit MSI", + "win64-aarch64": "Windows ARM64/AArch64", + "win": "Windows 32-bit", + "win-msi": "Windows 32-bit MSI", + "win-store": "Microsoft Store", + "osx": "macOS", + "linux64": "Linux 64-bit", + "linux": "Linux 32-bit", + "linux64-aarch64": "Linux ARM64/AArch64", + } + # 404 checks. + if product_slug and product_slug not in product_map.keys(): + raise Http404() + if platform and platform not in platform_map.keys(): + raise Http404() + if locale and locale not in product_details.languages.keys(): + raise Http404() + # 404 if win-store and not desktop-release. + if platform == "win-store" and product_slug not in ["desktop-release", "desktop-beta"]: + raise Http404() + + product = product_map.get(product_slug) + platform_name = None + locale_name = None + download_url = None + template_name = "firefox/all/base.html" lang_multi = ftl("firefox-all-lang-multi", ftl_files=ftl_files) + if product: + if product_slug.startswith(("mobile", "android", "ios")): + locale = "en-US" + locale_name = lang_multi + download_url = True # Set to True to avoid trying to generate this later below. + if product_slug.startswith("mobile"): + platform = "mobile" + platform_name = ftl("firefox-all-plat-mobile", ftl_files=ftl_files) + elif product_slug.startswith("android"): + platform = "android" + platform_name = "Android" + elif product_slug.startswith("ios"): + platform = "ios" + platform_name = "iOS" + elif product_slug in ("desktop-release", "desktop-beta") and platform == "win-store": + platform_name = "Microsoft Store" + locale = "en-US" + locale_name = lang_multi + download_url = { + "desktop-release": settings.MICROSOFT_WINDOWS_STORE_FIREFOX_WEB_LINK, + "desktop-beta": settings.MICROSOFT_WINDOWS_STORE_FIREFOX_BETA_WEB_LINK, + }.get(product_slug) + else: + platform_name = platform and platform_map[platform] + locale_name = None + if locale: + try: + build = list(filter(lambda b: b["locale"] == locale, product["product"].get_filtered_full_builds(product["channel"])))[0] + except IndexError: + raise Http404() + locale_name = f"{build['name_en']} - {build['name_native']}" + context = { - "products": products.items(), - "desktop_release_platforms": product_desktop.platforms(channel_release), - "desktop_release_full_builds": product_desktop.get_filtered_full_builds(channel_release, latest_release_version_desktop), - "desktop_release_channel_label": product_desktop.channel_labels.get(channel_release, "Firefox"), - "desktop_release_latest_version": latest_release_version_desktop, - "desktop_beta_platforms": product_desktop.platforms(channel_beta), - "desktop_beta_full_builds": product_desktop.get_filtered_full_builds(channel_beta, latest_beta_version_desktop), - "desktop_beta_channel_label": product_desktop.channel_labels.get(channel_beta, "Firefox"), - "desktop_beta_latest_version": latest_beta_version_desktop, - "desktop_developer_platforms": product_desktop.platforms(channel_dev), - "desktop_developer_full_builds": product_desktop.get_filtered_full_builds(channel_dev, latest_developer_version_desktop), - "desktop_developer_channel_label": product_desktop.channel_labels.get(channel_dev, "Firefox"), - "desktop_developer_latest_version": latest_developer_version_desktop, - "desktop_nightly_platforms": product_desktop.platforms(channel_nightly), - "desktop_nightly_full_builds": product_desktop.get_filtered_full_builds(channel_nightly, latest_nightly_version_desktop), - "desktop_nightly_channel_label": product_desktop.channel_labels.get(channel_nightly, "Firefox"), - "desktop_nightly_latest_version": latest_nightly_version_desktop, - "desktop_esr_platforms": product_desktop.platforms(channel_esr), - "desktop_esr_full_builds": product_desktop.get_filtered_full_builds(channel_esr, latest_esr_version_desktop), - "desktop_esr_channel_label": product_desktop.channel_labels.get(channel_esr, "Firefox"), - "desktop_esr_latest_version": latest_esr_version_desktop, - "android_release_platforms": [("android", "Android")], - "android_release_full_builds": [ - { - "locale": "multi", - "name_en": lang_multi, - "name_native": lang_multi, - "platforms": {"android": {"download_url": settings.GOOGLE_PLAY_FIREFOX_LINK_UTMS}}, - } - ], - "android_release_channel_label": product_android.channel_labels.get(channel_release, "Firefox"), - "android_release_latest_version": latest_release_version_android, - "android_beta_platforms": [("android", "Android")], - "android_beta_full_builds": [ - { - "locale": "multi", - "name_en": lang_multi, - "name_native": lang_multi, - "platforms": {"android": {"download_url": settings.GOOGLE_PLAY_FIREFOX_BETA_LINK}}, - } - ], - "android_beta_channel_label": product_android.channel_labels.get(channel_beta, "Firefox"), - "android_beta_latest_version": latest_beta_version_android, - "android_nightly_platforms": [("android", "Android")], - "android_nightly_full_builds": [ - { - "locale": "multi", - "name_en": lang_multi, - "name_native": lang_multi, - "platforms": {"android": {"download_url": settings.GOOGLE_PLAY_FIREFOX_NIGHTLY_LINK}}, - } - ], - "android_nightly_channel_label": product_android.channel_labels.get(channel_nightly, "Firefox"), - "android_nightly_latest_version": latest_nightly_version_android, - "ios_release_platforms": [("ios", "iOS")], - "ios_release_full_builds": [ - { - "locale": "multi", - "name_en": lang_multi, - "name_native": lang_multi, - "platforms": {"ios": {"download_url": settings.APPLE_APPSTORE_FIREFOX_LINK.replace("/{country}/", "/")}}, - } - ], - "ios_release_channel_label": product_ios.channel_labels.get(channel_release, "Firefox"), - "ios_release_latest_version": latest_release_version_ios, + "product": product, + "platform": platform, + "platform_name": platform_name, + "locale": locale, + "locale_name": locale_name, } - if latest_esr_next_version_desktop: - context["desktop_esr_platforms_next"] = product_desktop.platforms(channel_esr_next, True) - context["desktop_esr_full_builds_next"] = product_desktop.get_filtered_full_builds(channel_esr_next, latest_esr_next_version_desktop) - context["desktop_esr_channel_label_next"] = (product_desktop.channel_labels.get(channel_esr_next, "Firefox"),) - context["desktop_esr_next_version"] = latest_esr_next_version_desktop + # `firefox_desktop.esr_minor_versions` could have 0, 1, or 2 elements. This adds defaults so we always have 2 to unpack. + esr_latest_version, esr_next_version = (firefox_desktop.esr_minor_versions + [None, None])[:2] + if esr_next_version: + context.update( + desktop_esr_latest_version=esr_latest_version, + desktop_esr_next_version=esr_next_version, + ) + + # Show download link + if locale: + if not download_url: + download_url = list(filter(lambda b: b["locale"] == locale, product["product"].get_filtered_full_builds(product["channel"])))[0][ + "platforms" + ][platform]["download_url"] + context.update( + download_url=download_url, + ) + if product_slug == "desktop-esr" and esr_next_version: + try: + download_esr_next_url = list(filter(lambda b: b["locale"] == locale, firefox_desktop.get_filtered_full_builds("esr_next")))[0][ + "platforms" + ][platform]["download_url"] + context.update( + download_esr_next_url=download_esr_next_url, + ) + except IndexError: + # If the ESR next version is not available for the locale, remove the context variables. + context.pop("desktop_esr_latest_version", None) + context.pop("desktop_esr_next_version", None) + + # Show platforms with download links + elif platform: + locales = [] + for b in product["product"].get_filtered_full_builds(product["channel"]): + locale_name = f"{b['name_en']} - {b['name_native']}" + if b["locale"] == request.locale: + # If locale matches request's locale, put it at the top. + locales.insert(0, (b["locale"], locale_name)) + else: + locales.append((b["locale"], locale_name)) + + context.update( + locales=locales, + ) + + # Show locales. + elif product_slug: + platforms = product["product"].platforms(product["channel"]) + if product_slug in ["desktop-release", "desktop-beta"]: + idx = platforms.index(("osx", "macOS")) + platforms.insert(idx, ("win-store", "Microsoft Store")) + context.update(platforms=platforms) + + # Show products. + else: + context.update( + products=[{"slug": k, "name": v["name"]} for k, v in product_map.items()], + ) - return l10n_utils.render(request, "firefox/all-unified.html", context, ftl_files=ftl_files) + return l10n_utils.render(request, template_name, context, ftl_files=ftl_files) def detect_channel(version): diff --git a/l10n/en/firefox/all.ftl b/l10n/en/firefox/all.ftl index a2074ce35f6..c24e9dc708a 100644 --- a/l10n/en/firefox/all.ftl +++ b/l10n/en/firefox/all.ftl @@ -4,31 +4,65 @@ ### URL: https://www-dev.allizom.org/firefox/all/ -firefox-all-check-the-system-requirements = Check the system requirements -firefox-all-release-notes = Release notes -firefox-all-source-code = Source code -firefox-all-need-help = Need help? -firefox-all-which-browser-would = Which browser would you like to download? +# HTML page title. Replace "English (US)" with your local language. +firefox-all-download-the-firefox-v2 = Download { -brand-name-firefox } in English (US) and more than 90 other languages +# Obsolete string (expires 2024-10-27) +firefox-all-download-the-firefox = Download the { -brand-name-firefox-browser } in English (US) and more than 90 other languages + +# HTML page description, also used as the introductory text. +firefox-all-everyone-deserves-access-v2 = Everyone deserves access to the internet — your language should never be a barrier. That’s why — with the help of dedicated volunteers around the world — we make { -brand-name-firefox } available in more than 90 languages. +# Obsolete string (expires 2024-10-27) +firefox-all-everyone-deserves-access = Everyone deserves access to the internet — your language should never be a barrier. That’s why — with the help of dedicated volunteers around the world — we make the { -brand-name-firefox-browser } available in more than 90 languages. + +# Variables: +# $product_label (string) e.g. Firefox, Firefox Nightly +firefox-all-qrcode = Scan QR code to get { $product_name } +firefox-all-choose-browser = Choose a browser to continue +firefox-all-choose-platform = Choose a platform to continue +firefox-all-choose-language = Choose a language to continue +firefox-all-change-browser = Choose a different product +firefox-all-change-platform = Choose a different platform +firefox-all-change-language = Choose a different language + +# Used as an accessible label for an image that points down +firefox-all-down-arrow = Choose from the list below # Used as an accessible label for a help button. The text is replaced with a "?" icon. firefox-all-get-help = Get help - -firefox-all-you-are-about-to-download = You are about to download: -firefox-all-browser = Browser: -firefox-all-platform = Platform: -firefox-all-language = Language: +firefox-all-browser = 1. Browser: +firefox-all-platform = 2. Platform: +firefox-all-language = 3. Language: +firefox-all-download = 4. Download: +firefox-all-desktop = Desktop +firefox-all-mobile = Mobile +firefox-all-recommended = (Recommended) firefox-all-lang-multi = Multiple languages +firefox-all-plat-mobile = Android and iOS firefox-all-sorry-we-couldnt-find = Sorry, we couldn’t find the download you’re looking for. Please try again, or select a download from the list below. firefox-all-the-pre-alpha-version = The pre-alpha version for power users who like to hunt crashes and test new features as they’re coded. firefox-all-64-bit-installers = 64-bit installers firefox-all-choose-a-64-bit-installer = Choose a 64-bit installer for computers with 64-bit processors, which allow them to allocate more RAM to individual programs — particularly important for games and other demanding applications. firefox-all-32-bit-installers = 32-bit installers +firefox-all-microsoft-store-installers = Microsoft Store +firefox-all-choose-a-microsoft-store-installer = This option will open a { -brand-name-firefox } product page in the Microsoft Store web portal. If you are running Windows 10 or Windows 11, you will have the option to open the Microsoft Store from this page and install { -brand-name-firefox }. If you install from the Microsoft Store, updates to { -brand-name-firefox } will also be handled by the Microsoft Store. There are minor differences in { -brand-name-firefox } behaviors and capabilities when installed from the Microsoft Store, but for most users these differences are not noticeable. -# HTML page title. Replace "English (US)" with your local language. -firefox-all-download-the-firefox = Download the { -brand-name-firefox-browser } in English (US) and more than 90 other languages +# Variables: +# $esr_version (string) e.g. 115.13.0esr +firefox-all-download-esr-version = Download { $esr_version } +# Variables: +# $esr_next_major (string) Next major version number e.g. 128 +# $esr_current_major (string) Current major version number e.g. 115 +firefox-all-esr-we-are-in-transition = We are in the transition period between major ESR versions {$esr_next_major} and { $esr_current_major }. +firefox-all-esr-when-there-is = When there is a major version update, there is always an overlap of a few { -brand-name-firefox-esr } point releases to allow people who are using the old version to prepare to move to the new one. +firefox-all-esr-if-you-choose = If you choose to download the older ESR version, you will be automatically updated to the new one when the transition period ends. +# Variables: +# $attrs (string) link to https://support.mozilla.org/kb/firefox-esr-release-cycle +firefox-all-esr-read-more = Read more about the { -brand-name-firefox-esr } release cycle. -# HTML page description, also used as the introductory text. -firefox-all-everyone-deserves-access = Everyone deserves access to the internet — your language should never be a barrier. That’s why — with the help of dedicated volunteers around the world — we make the { -brand-name-firefox-browser } available in more than 90 languages. +firefox-all-check-the-system-requirements = Check the system requirements +firefox-all-release-notes = Release notes +firefox-all-source-code = Source code +firefox-all-need-help = Need help? firefox-all-choose-which-firefox = Choose which { -brand-name-firefox-browser } to download in your language firefox-all-firefox-privacy-notice = { -brand-name-firefox } Privacy Notice @@ -41,6 +75,7 @@ firefox-all-windows-installers-for = Windows installers for corporate IT that si firefox-all-arm64-installers = ARM64/AArch64 installers firefox-all-arm64-installers-optimized-v2 = ARM64/AArch64 installers optimized for Windows and Linux PCs. +firefox-all-testflight = Sign up to test { -brand-name-firefox } for iOS with TestFlight firefox-all-product-send-link = Send a download link to your phone # Variables: @@ -67,3 +102,4 @@ firefox-all-product-firefox-android = { -brand-name-firefox } { -brand-name-andr firefox-all-product-firefox-android-beta = { -brand-name-firefox } { -brand-name-android } { -brand-name-beta } firefox-all-product-firefox-android-nightly = { -brand-name-firefox } { -brand-name-android } { -brand-name-nightly } firefox-all-product-firefox-ios = { -brand-name-firefox } { -brand-name-ios } +firefox-all-product-firefox-ios-testflight = { -brand-name-firefox } iOS TestFlight diff --git a/l10n/en/firefox/enterprise.ftl b/l10n/en/firefox/enterprise.ftl index c32065fc0ba..362ecec1c94 100644 --- a/l10n/en/firefox/enterprise.ftl +++ b/l10n/en/firefox/enterprise.ftl @@ -50,5 +50,5 @@ firefox-enterprise-pkg-installer = PKG installer firefox-enterprise-windows-32-bit = { -brand-name-windows } 32-bit # Variables: -# $firefox_all (url) - link to https://www.mozilla.org/firefox/all/#product-desktop-esr +# $firefox_all (url) - link to https://www.mozilla.org/firefox/all/desktop-esr/ firefox-enterprise-download-firefox-esr-or-rapid = Download { -brand-name-firefox-esr } or Rapid Release for
    another language or platform. diff --git a/media/css/firefox/all.scss b/media/css/firefox/all.scss new file mode 100644 index 00000000000..3719e21e0e5 --- /dev/null +++ b/media/css/firefox/all.scss @@ -0,0 +1,286 @@ +// 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/. + +@use 'sass:color'; + +$font-path: '/media/protocol/fonts'; +$image-path: '/media/protocol/img'; + +@import '~@mozilla-protocol/core/protocol/css/includes/lib'; +@import '~@mozilla-protocol/core/protocol/css/includes/forms'; +@import '~@mozilla-protocol/core/protocol/css/components/modal'; +@import '~@mozilla-protocol/core/protocol/css/components/sidebar-menu'; + + +.c-product-info { + @include clearfix; + position: relative; +} + +.c-intro { + margin-bottom: $layout-md; + + .c-intro-heading { + @include background-size(64px, 64px); + @include bidi(((background-position, top left, top right),)); + @include text-title-md; + background-image: url('/media/protocol/img/logos/firefox/browser/logo.svg'); + background-repeat: no-repeat; + margin-bottom: $spacing-xl; + padding-top: 64px + $spacing-lg; + + [data-current='desktop-beta'] &, + [data-current='android-beta'] & { + background-image: url('/media/protocol/img/logos/firefox/browser/beta/logo.svg'); + } + + [data-current='desktop-developer'] & { + background-image: url('/media/protocol/img/logos/firefox/browser/developer/logo.svg'); + } + + [data-current='desktop-nightly'] &, + [data-current='android-nightly'] & { + background-image: url('/media/protocol/img/logos/firefox/browser/nightly/logo.svg'); + } + } + + @media #{$mq-md} { + @include bidi(((float, left, right),)); + width: calc(50% - #{$spacing-lg}); + } + + @media #{$mq-lg} { + width: calc(50% - #{$spacing-2xl}); + } +} + +.c-steps { + @media #{$mq-md} { + @include bidi(((float, right, left),)); + margin-bottom: $layout-xl; + width: calc(50% - #{$spacing-lg}); + } + + @media #{$mq-lg} { + width: calc(50% - #{$spacing-2xl}); + } +} + +.c-step-name { + @include text-body-lg; + @include bidi(((padding-right, 30px, padding-left, 0),)); + font-weight: bold; + margin-top: $spacing-xl; + position: relative; + + &:first-child { + margin-top: 0; + } + + &.t-step-disabled { + opacity: 0.4; + + + [dir='rtl'] & .c-step-icon { + transform: rotate(180deg); + } + } +} + +.c-step-icon { + position: absolute; + top: 0; + @include bidi(((right, 0, left, auto),)); +} + +.c-step-choice { + font-weight: normal; +} + +.c-step-contents { + @include bidi(((padding-left, $spacing-lg, padding-right, 0),)); +} + +.c-step-prompt { + @include text-title-sm; + margin-top: 0; + display: none; +} + +.c-step-download { + margin-top: $layout-md; +} + +.c-product-list { + @include bidi(((padding-left, 36px, padding-right, 0),)); // to match the desktop & mobile icons + + li, + .release { + @include bidi(( + (background-position, top left, top right), + (padding-left, 1.9em, padding-right, 0), + )); + background-size: 1.5em auto; + background-repeat: no-repeat; + margin-bottom: 0.6rem; + } + + .release, + .desktop-release, + .desktop-esr { + background-image: url('#{$image-path}/logos/firefox/browser/logo.svg'); + } + + .desktop-beta, + .android-beta { + background-image: url('#{$image-path}/logos/firefox/browser/beta/logo.svg'); + } + + .desktop-developer { + background-image: url('#{$image-path}/logos/firefox/browser/developer/logo.svg'); + } + + .desktop-nightly, + .android-nightly { + background-image: url('#{$image-path}/logos/firefox/browser/nightly/logo.svg'); + } + + .ios-testflight { + background-image: url('/media/img/logos/testflight/testflight.svg'); + } +} + +.c-platform-list, +.c-lang-list { + margin-bottom: $spacing-xl; +} + + +// mobile downloads + +.c-mobile { + width: fit-content; +} + +.qr-code-wrapper { + background: $color-white; + border-radius: $border-radius-lg; + border: 2px solid $color-marketing-gray-20; + box-shadow: $box-shadow-sm; + margin: $layout-sm auto; + max-width: 220px; + padding: $spacing-lg; + + svg { + height: auto; + max-width: 100%; + } + + figcaption { + color: $color-black; + font-weight: bold; + line-height: 1.125; + padding: 0 $spacing-lg; + text-align: center; + + em { + display: block; + } + } +} + +.c-store-badges { + margin-bottom: 0; + text-align: center; + + li { + display: inline-block; + margin: 0 $spacing-sm; + } +} + +.c-get-app { + @include bidi(( + (padding-right, 20px, padding-left, 0), + (background-position, center right, center left), + )); + background-image: url('/media/img/firefox/all/icon-get.svg'); + background-repeat: no-repeat; + background-size: auto 1em; + display: inline-block; + margin-top: $spacing-sm; +} + +// help buttons & modals + +.c-button-help { + @include bidi(( + (background-position, top left, top right), + )); + background-image: url('/media/img/firefox/all/icon-question.svg'); + background-size: 16px 16px; + @include image-replaced; + background-repeat: no-repeat; + display: inline-block; + height: 21px; + padding-bottom: $spacing-sm; + vertical-align: middle; + width: 21px; +} + +.c-help { + ul { + background: $color-white; + color: $color-black; + padding: $spacing-lg; + } + + a:link, + a:visited { + color: $color-black; + + &:hover, + &:active, + &:focus { + color: $color-black; + } + } + + .c-help-title { + @include text-title-xs; + + span { + font-weight: normal; + } + } + + .c-help-desc { + @include text-body-sm; + } +} + +// Custom narrow modal style. +// These should be standardized into a narrow theme. +// https://github.com/mozilla/protocol/issues/358 + +.mzp-c-modal.help-modal { + h3 { + color: $color-white; + } + + .mzp-c-modal-window > .mzp-c-modal-inner { + background: transparent; + max-width: 600px; + padding: $spacing-xl 0 0; + + header { + // Hide header visually and add top padding to compensate + @include visually-hidden; + } + } + + .mzp-c-modal-close { + @include bidi(((right, 0, left, auto),)); + } +} diff --git a/media/css/firefox/all/all-unified-old-ie.scss b/media/css/firefox/all/all-unified-old-ie.scss deleted file mode 100644 index 2ce0164e4b3..00000000000 --- a/media/css/firefox/all/all-unified-old-ie.scss +++ /dev/null @@ -1,16 +0,0 @@ -// 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/. - -$font-path: '/media/protocol/fonts'; -$image-path: '/media/protocol/img'; - -@import '~@mozilla-protocol/core/protocol/css/includes/lib'; - -.c-selection-form { - display: none; -} - -.c-product-links { - display: none; -} diff --git a/media/css/firefox/all/all-unified.scss b/media/css/firefox/all/all-unified.scss deleted file mode 100644 index 69cdde26251..00000000000 --- a/media/css/firefox/all/all-unified.scss +++ /dev/null @@ -1,418 +0,0 @@ -// 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/. - -@use 'sass:color'; - -$font-path: '/media/protocol/fonts'; -$image-path: '/media/protocol/img'; - -@import '~@mozilla-protocol/core/protocol/css/includes/lib'; -@import '~@mozilla-protocol/core/protocol/css/includes/forms'; -@import '~@mozilla-protocol/core/protocol/css/templates/main-with-sidebar'; -@import '~@mozilla-protocol/core/protocol/css/components/modal'; -@import '~@mozilla-protocol/core/protocol/css/components/sidebar-menu'; - -.c-product-select-form { - @include clearfix; - position: relative; - - .c-download-button { - display: none; - } - - &[data-current^='desktop'] { - #download-button-primary { - display: inline-block; - } - } - - &[data-current='desktop_release'] { - [data-product='desktop_release'] { - display: block; - } - } - - &[data-current='desktop_beta'] { - [data-product='desktop_beta'] { - display: block; - } - } - - &[data-current='desktop_developer'] { - [data-product='desktop_developer'] { - display: block; - } - } - - &[data-current='desktop_nightly'] { - [data-product='desktop_nightly'] { - display: block; - } - } - - &[data-current='desktop_esr'] { - [data-product='desktop_esr'] { - display: block; - } - } - - &[data-current='desktop_esr_next'] { - [data-product='desktop_esr_next'] { - display: block; - } - - // ESR Next has the same product links as regular ESR - .c-product-links[data-product='desktop_esr'] { - display: block; - } - } - - &[data-current='android_release'] { - [data-product='android_release'] { - display: block; - } - } - - &[data-current='android_beta'] { - [data-product='android_beta'] { - display: block; - } - } - - &[data-current='android_nightly'] { - [data-product='android_nightly'] { - display: block; - } - } - - &[data-current='ios_release'] { - [data-product='ios_release'] { - display: block; - } - } -} - -// display additional instructions when linux is selected - -.c-download[data-platform="linux"], -.c-download[data-platform="linux64"] { - .c-linux-debian { - display: block; - } -} - -.c-selection-formset { - margin-bottom: 0; -} - -.c-intro { - margin-bottom: $layout-md; - - .c-intro-heading { - @include background-size(64px, 64px); - @include bidi(((background-position, top left, top right),)); - @include text-title-md; - background-image: url('/media/protocol/img/logos/firefox/browser/logo.svg'); - background-repeat: no-repeat; - margin-bottom: $spacing-xl; - padding-top: 64px + $spacing-lg; - - [data-current='desktop_beta'] &, - [data-current='android_beta'] & { - background-image: url('/media/protocol/img/logos/firefox/browser/beta/logo.svg'); - } - - [data-current='desktop_developer'] & { - background-image: url('/media/protocol/img/logos/firefox/browser/developer/logo.svg'); - } - - [data-current='desktop_nightly'] &, - [data-current='android_nightly'] & { - background-image: url('/media/protocol/img/logos/firefox/browser/nightly/logo.svg'); - } - } - - .c-product-links { - display: none; - } - - @media #{$mq-md} { - @include bidi(((float, left, right),)); - width: calc(50% - #{$spacing-lg}); - } - - @media #{$mq-lg} { - width: calc(50% - #{$spacing-2xl}); - } -} - -.c-button-help { - @include at2x('/media/img/firefox/all/icon-question.png', 21px, 21px); - @include image-replaced; - background-position: top left; - background-repeat: no-repeat; - display: inline-block; - height: 21px; - padding-bottom: $spacing-sm; - vertical-align: middle; - width: 21px; -} - -.c-get-app { - @include font-size(14px); - background: center right url('/media/img/firefox/all/icon-get.svg') no-repeat; - background-size: auto 1em; - display: inline-block; - padding-right: 20px; - margin-top: $spacing-sm; -} - -.c-selection-form { - display: none; - - @media #{$mq-md} { - @include bidi(((float, right, left),)); - margin-bottom: $layout-xl; - padding-top: 64px + $spacing-lg; - width: calc(50% - #{$spacing-lg}); - } - - @media #{$mq-lg} { - width: calc(50% - #{$spacing-2xl}); - } - - .c-selection { - margin-bottom: $spacing-xl; - } - - .c-selection-label { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - - .c-selection-input { - margin-top: $spacing-sm; - width: 100%; - } - - .c-selection-options { - display: none; - } -} - -.c-download-info { - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - margin-bottom: $layout-sm; - width: 100%; -} - -.c-download-info-caption { - @include field-label; - display: table-caption; -} - -.c-download-info-label { - font-weight: normal; - - // make first column as narrow as possible https://stackoverflow.com/a/11267268/462195 - width: 1%; - white-space: nowrap; -} - -.c-download-info-content { - font-weight: bold; -} - -.c-download { - @include bidi (((text-align, right, left),)); - - .c-download-error { - color: $color-red-60; - display: none; - } - - &.has-error { - .c-download-error { - display: block; - } - - .c-download-info { - display: none; - } - - #download-button-primary { - display: none; - } - } -} - -.c-linux-debian { - @include bidi(((text-align, left, right), )); - display: none; -} - -.c-download-button { - a { - display: inline-block; - } - - a[data-link-type='download']:focus, - a[data-link-type='download']:active { - box-shadow: 0 0 0 2px $field-border-color-hover, 0 0 0 4px color.mix($color-blue-40, $color-white); - border-radius: 6px; - background-color: $field-border-color-hover; - } - - img { - display: block; // get rid of line height - border-radius: 6px; - } -} - -.c-all-downloads { - @include clearfix; - - .c-all-downloads-build { - position: relative; - } - - .mzp-l-sidebar { - margin-top: $spacing-lg; - - @media #{$mq-md} { - margin-top: $spacing-md; - position: -webkit-sticky; - position: sticky; - top: $spacing-lg; - } - } - - .mzp-c-sidemenu-main { - display: block; - } -} - -.c-product-heading { - @include text-title-md; - background: $color-white; - padding: $spacing-md 0; - position: -webkit-sticky; - position: sticky; - top: 0; - - span { - font-weight: normal; - } -} - -.c-product-subheading { - @include text-title-sm; -} - -.c-locale-list { - margin-bottom: $layout-lg; - - .c-locale-list-item { - border-top: 2px solid $color-marketing-gray-20; - padding-top: $spacing-md; - } - - .c-locale-label { - @include text-title-sm; - - span { - @include text-title-xs; - display: block; - font-weight: normal; - } - } - - .c-download-list li { - @include bidi(((margin-right, $spacing-sm, margin-left, 0),)); - display: inline-block; - - &:last-child { - @include bidi(((margin-right, 0, margin-left, 0),)); - } - } - - @media #{$mq-md} { - margin-bottom: $layout-xl; - } -} - -// Custom narrow modal style. -// These should be standardized into a narrow theme. -// https://github.com/mozilla/protocol/issues/358 - -.mzp-c-modal.help-modal { - .mzp-c-modal-window > .mzp-c-modal-inner { - background: transparent; - max-width: 600px; - padding: $spacing-xl 0 0; - - header { - // Hide header visually and add top padding to compensate - @include visually-hidden; - } - } - - .mzp-c-modal-close { - @include bidi(((right, 0, left, auto),)); - } -} - -.c-help { - ul { - background: $color-white; - color: $color-black; - padding: $spacing-lg; - } - - a:link, - a:visited { - color: $color-black; - - &:hover, - &:active, - &:focus { - color: $color-black; - } - } - - .c-help-title { - @include text-title-xs; - - span { - font-weight: normal; - } - } - - .c-help-desc { - @include text-body-sm; - } -} - -// Modern browsers get the form, legacy browsers the download list. -.is-modern-browser { - .c-all-downloads { - display: none; - - // still allow the list to be shown as a fallback. - &.is-fallback { - display: block; - } - } - - .c-selection-form { - display: block; - } - - .c-product-select-form { - @media #{$mq-lg} { - min-height: 700px; - } - } -} diff --git a/media/img/firefox/all/icon-question-high-res.png b/media/img/firefox/all/icon-question-high-res.png deleted file mode 100644 index 0feec436203..00000000000 Binary files a/media/img/firefox/all/icon-question-high-res.png and /dev/null differ diff --git a/media/img/firefox/all/icon-question.png b/media/img/firefox/all/icon-question.png deleted file mode 100644 index cf655947251..00000000000 Binary files a/media/img/firefox/all/icon-question.png and /dev/null differ diff --git a/media/img/firefox/all/icon-question.svg b/media/img/firefox/all/icon-question.svg new file mode 100644 index 00000000000..f1def11381b --- /dev/null +++ b/media/img/firefox/all/icon-question.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/media/img/logos/testflight/testflight.svg b/media/img/logos/testflight/testflight.svg new file mode 100644 index 00000000000..01c30d79ffe --- /dev/null +++ b/media/img/logos/testflight/testflight.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/media/js/base/stub-attribution/stub-attribution.js b/media/js/base/stub-attribution/stub-attribution.js index ebe0d05f888..7c961bbe031 100644 --- a/media/js/base/stub-attribution/stub-attribution.js +++ b/media/js/base/stub-attribution/stub-attribution.js @@ -145,9 +145,7 @@ if (typeof window.Mozilla === 'undefined') { } // target download buttons and other-platforms modal links. - var downloadLinks = document.querySelectorAll( - '.download-list .download-link, .c-button-download-thanks .download-link, .download-platform-list .download-link, .firefox-platform-button .download-link' - ); + var downloadLinks = document.querySelectorAll('.download-link'); for (var i = 0; i < downloadLinks.length; i++) { var link = downloadLinks[i]; diff --git a/media/js/firefox/all/all-downloads-unified.js b/media/js/firefox/all/all-downloads-unified.js deleted file mode 100644 index af8c3442ab3..00000000000 --- a/media/js/firefox/all/all-downloads-unified.js +++ /dev/null @@ -1,645 +0,0 @@ -/* - * 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/. - */ - -(function () { - 'use strict'; - - var form = document.getElementById('product-select-form'); - var productSelect = document.getElementById('select-product'); - var versionSelect = document.querySelectorAll( - '.c-selection-version select' - ); - var languageSelect = document.querySelectorAll( - '.c-selection-language select' - ); - var platformSelect = document.querySelectorAll( - '.c-selection-platform select' - ); - var downloadInfo = document.querySelector('.c-download'); - var downloadInfoProduct = document.getElementById('download-info-product'); - var downloadInfoPlatform = document.getElementById( - 'download-info-platform' - ); - var downloadInfoLanguage = document.getElementById( - 'download-info-language' - ); - var downloadInfoButton = document.getElementById('download-button-primary'); - - var FirefoxDownloader = {}; - - /** - * Get the currently selected - - '`; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', select); - }); - - afterEach(function () { - document.getElementById('select-product').remove(); - }); - - it('should return the selection option', function () { - const el = document.getElementById('select-product'); - const result = Mozilla.FirefoxDownloader.getSelectOption(el); - expect(result.id).toEqual('desktop_nightly'); - expect(result.label).toEqual('Firefox Nightly'); - }); - }); - - describe('setAllSelectOptions', function () { - const select = ` - `; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', select); - }); - - afterEach(function () { - document.getElementById('select_desktop_release_platform').remove(); - document.getElementById('select_desktop_beta_platform').remove(); - }); - - it('should set all the options correctly', function () { - const el = document.querySelectorAll('.c-selection-input'); - Mozilla.FirefoxDownloader.setAllSelectOptions('linux', el); - const result = Mozilla.FirefoxDownloader.getSelectOption(el[0]); - expect(result.id).toEqual('linux'); - expect(result.label).toEqual('Linux 32-bit'); - const result2 = Mozilla.FirefoxDownloader.getSelectOption(el[1]); - expect(result2.id).toEqual('linux'); - expect(result2.label).toEqual('Linux 32-bit'); - }); - }); - - describe('getPlatform', function () { - it('should return known platforms', function () { - expect(Mozilla.FirefoxDownloader.getPlatform('windows')).toEqual( - 'win64' - ); - expect(Mozilla.FirefoxDownloader.getPlatform('linux')).toEqual( - 'linux64' - ); - expect(Mozilla.FirefoxDownloader.getPlatform('osx')).toEqual('osx'); - }); - - it('should return false for unknown platforms', function () { - expect(Mozilla.FirefoxDownloader.getPlatform('other')).toBeFalsy(); - }); - }); - - describe('getPageLanguage', function () { - it('should return en-US for region neutral en values', function () { - expect(Mozilla.FirefoxDownloader.getPageLanguage('en')).toEqual( - 'en-US' - ); - }); - }); - - describe('getDownloadLink', function () { - const downloadList = `
      -
    1. - -
    2. -
    `; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', downloadList); - }); - - afterEach(function () { - document.querySelector('.c-locale-list').remove(); - }); - - it('should return a download link as expected', function () { - const result = Mozilla.FirefoxDownloader.getDownloadLink( - 'desktop_release', - 'win64', - 'ach' - ); - expect(result).toEqual( - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=ach' - ); - }); - - it('should return an error if a download link is not found', function () { - const product = 'desktop_release'; - const platform = 'win64'; - const language = 'de'; - spyOn(Mozilla.FirefoxDownloader, 'onError'); - const error = new Error( - `A download link was not found for: ${product}, platform: ${platform}, language: ${language}` - ); - Mozilla.FirefoxDownloader.getDownloadLink( - product, - platform, - language - ); - expect(Mozilla.FirefoxDownloader.onError).toHaveBeenCalledWith( - error - ); - }); - }); - - describe('setDownloadLink', function () { - const downloadLink = - 'Download Now'; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', downloadLink); - }); - - afterEach(function () { - document.getElementById('download-button-primary').remove(); - }); - - it('should set desktop download links as expected', function () { - const el = document.getElementById('download-button-primary'); - const url = - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win64&lang=ach'; - - const product = { - id: 'desktop_beta', - label: 'Firefox Beta' - }; - - const platform = { - id: 'win64', - label: 'Windows 64-bit' - }; - - const language = { - id: 'ach', - label: 'Acholi' - }; - - Mozilla.FirefoxDownloader.setDownloadLink( - url, - product, - platform, - language, - el - ); - expect(el.href).toEqual(url); - expect(el.getAttribute('data-display-name')).toEqual(product.label); - expect(el.getAttribute('data-download-version')).toEqual( - platform.id - ); - expect(el.getAttribute('data-download-language')).toEqual( - language.id - ); - expect(el.getAttribute('data-download-os')).toEqual('Desktop'); - }); - }); - - describe('setAttributionURL', function () { - beforeEach(function () { - spyOn(Mozilla.StubAttribution, 'getCookie').and.returnValue({ - attribution_code: 'some-attribution-code', - attribution_sig: 'some-attribution-signature' - }); - }); - - it('should return a well formatted attribution link if data exists', function () { - const winUrl = - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US'; - const macOSNightlyUrl = - 'https://download.mozilla.org/?product=firefox-nightly-latest&os=osx&lang=en-US'; - const macOSBetaUrl = - 'https://download.mozilla.org/?product=firefox-beta-latest&os=osx&lang=en-US'; - const macOSDevUrl = - 'https://download.mozilla.org/?product=firefox-devedition-latest&os=osx&lang=en-US'; - const macOSUrl = - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=osx&lang=en-US'; - - spyOn(Mozilla.StubAttribution, 'hasCookie').and.returnValue(true); - expect(Mozilla.FirefoxDownloader.setAttributionURL(winUrl)).toEqual( - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US&attribution_code=some-attribution-code&attribution_sig=some-attribution-signature' - ); - expect( - Mozilla.FirefoxDownloader.setAttributionURL(macOSNightlyUrl) - ).toEqual( - 'https://download.mozilla.org/?product=firefox-nightly-latest&os=osx&lang=en-US&attribution_code=some-attribution-code&attribution_sig=some-attribution-signature' - ); - expect( - Mozilla.FirefoxDownloader.setAttributionURL(macOSBetaUrl) - ).toEqual( - 'https://download.mozilla.org/?product=firefox-beta-latest&os=osx&lang=en-US&attribution_code=some-attribution-code&attribution_sig=some-attribution-signature' - ); - expect( - Mozilla.FirefoxDownloader.setAttributionURL(macOSDevUrl) - ).toEqual( - 'https://download.mozilla.org/?product=firefox-devedition-latest&os=osx&lang=en-US&attribution_code=some-attribution-code&attribution_sig=some-attribution-signature' - ); - expect( - Mozilla.FirefoxDownloader.setAttributionURL(macOSUrl) - ).toEqual( - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=osx&lang=en-US&attribution_code=some-attribution-code&attribution_sig=some-attribution-signature' - ); - }); - - it('should return the original link if data does not exist', function () { - const url = - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=win&lang=en-US'; - spyOn(Mozilla.StubAttribution, 'hasCookie').and.returnValue(false); - expect(Mozilla.FirefoxDownloader.setAttributionURL(url)).toEqual( - url - ); - }); - }); - - describe('isValidURL', function () { - it('should return true for bouncer prod links', function () { - const url = - 'https://download.mozilla.org/?product=firefox-latest-ssl&os=osx&lang=en-US'; - expect(Mozilla.FirefoxDownloader.isValidURL(url)).toBeTruthy(); - }); - - it('should return true for bouncer stage links', function () { - const url = - 'https://bouncer-bouncer.stage.mozaws.net/?product=firefox-latest-ssl&os=osx&lang=en-US'; - expect(Mozilla.FirefoxDownloader.isValidURL(url)).toBeTruthy(); - }); - - it('should return true for bouncer dev links', function () { - const url = - 'https://dev.bouncer.nonprod.webservices.mozgcp.net/?product=firefox-latest-ssl&os=osx&lang=en-US'; - expect(Mozilla.FirefoxDownloader.isValidURL(url)).toBeTruthy(); - }); - - it('should return false for everything else', function () { - const url = - 'https://some.other.domain/?product=firefox-latest-ssl&os=osx&lang=en-US'; - expect(Mozilla.FirefoxDownloader.isValidURL(url)).toBeFalsy(); - expect(Mozilla.FirefoxDownloader.isValidURL(null)).toBeFalsy(); - expect(Mozilla.FirefoxDownloader.isValidURL({})).toBeFalsy(); - }); - }); - - describe('setDownloadButtonDesktop', function () { - const product = { - id: 'desktop_beta', - label: 'Firefox Beta' - }; - - const platform = { - id: 'win64', - label: 'Windows 64-bit' - }; - - const language = { - id: 'ach', - label: 'Acholi' - }; - - const downloadInfo = `
    `; - - const options = `
    -

    - - -

    -

    - - -

    -

    - - -

    -
    -
      -
    1. - -
    2. -
    `; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', options); - document.body.insertAdjacentHTML('beforeend', downloadInfo); - }); - - afterEach(function () { - document.querySelector('.c-selection-options').remove(); - document.querySelector('.c-locale-list').remove(); - document.querySelector('.c-download').remove(); - }); - - it('should set the download link as expected', function () { - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue(product); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadLink'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadInfo'); - spyOn(Mozilla.FirefoxDownloader, 'offError'); - Mozilla.FirefoxDownloader.setDownloadButton(); - expect( - Mozilla.FirefoxDownloader.setDownloadLink - ).toHaveBeenCalledWith( - 'https://download.mozilla.org/?product=firefox-beta-latest-ssl&os=win64&lang=ach', - product, - platform, - language - ); - expect( - Mozilla.FirefoxDownloader.setDownloadInfo - ).toHaveBeenCalledWith( - product.label, - platform.label, - language.label - ); - expect(Mozilla.FirefoxDownloader.offError).toHaveBeenCalled(); - }); - - it('should throw an error if a download link is not valid', function () { - const badURL = - 'https://download.mozilla.org.somebadactor.com/download.exe'; - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue(product); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadInfo'); - spyOn(Mozilla.FirefoxDownloader, 'getDownloadLink').and.returnValue( - badURL - ); - spyOn(Mozilla.FirefoxDownloader, 'isValidURL').and.returnValue( - false - ); - spyOn(Mozilla.FirefoxDownloader, 'onError'); - Mozilla.FirefoxDownloader.setDownloadButton(); - const error = new Error( - `An unrecognised download link was found: ${badURL}` - ); - expect(Mozilla.FirefoxDownloader.onError).toHaveBeenCalledWith( - error - ); - }); - }); - - describe('setDownloadButtonAndroid', function () { - const product = { - id: 'android_release', - label: 'Firefox Android' - }; - - const platform = { - id: 'android', - label: 'Android' - }; - - const language = { - id: 'all', - label: 'Multiple languages' - }; - - const downloadInfo = `
    `; - const options = `
    - -

    - - - Get help - - -

    -

    - - -

    -
    -
      -
    1. -

      Multiple languages

      - -
    2. -
    `; - - beforeEach(function () { - document.body.insertAdjacentHTML('beforeend', options); - document.body.insertAdjacentHTML('beforeend', downloadInfo); - }); - - afterEach(function () { - document.querySelector('.c-selection-options').remove(); - document.querySelector('.c-locale-list').remove(); - document.querySelector('.c-download').remove(); - }); - - it('should set the download link as expected', function () { - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue(product); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadLink'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadInfo'); - spyOn(Mozilla.FirefoxDownloader, 'offError'); - Mozilla.FirefoxDownloader.setDownloadButton(); - expect( - Mozilla.FirefoxDownloader.setDownloadLink - ).not.toHaveBeenCalled(); - expect( - Mozilla.FirefoxDownloader.setDownloadInfo - ).toHaveBeenCalledWith( - product.label, - platform.label, - language.label - ); - expect(Mozilla.FirefoxDownloader.offError).toHaveBeenCalled(); - }); - }); - - describe('getProductSelection', function () { - it('should return the selected product', function () { - const product = { - id: 'desktop_esr', - label: 'Firefox Extended Support Release' - }; - - spyOn(Mozilla.FirefoxDownloader, 'getSelectOption').and.returnValue( - product - ); - spyOn(Mozilla.FirefoxDownloader, 'getFormSelection'); - expect(Mozilla.FirefoxDownloader.getProductSelection()).toEqual( - product - ); - }); - - it('should return the correct ESR product', function () { - const next = 'desktop_esr_next'; - const product = { - id: 'desktop_esr', - label: 'Firefox Extended Support Release' - }; - - spyOn(Mozilla.FirefoxDownloader, 'getSelectOption').and.returnValue( - product - ); - spyOn( - Mozilla.FirefoxDownloader, - 'getFormSelection' - ).and.returnValue(next); - const result = Mozilla.FirefoxDownloader.getProductSelection(); - expect(result.id).toEqual(next); - expect(result.label).toEqual(product.label); - }); - }); - - describe('onVersionChange', function () { - it('should update the form fields and generate a download URL', function () { - const e = { - target: { - value: 'desktop_release' - } - }; - - spyOn(Mozilla.FirefoxDownloader, 'setFormSelection'); - spyOn(Mozilla.FirefoxDownloader, 'setAllSelectOptions'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadButton'); - - Mozilla.FirefoxDownloader.onVersionChange(e); - expect( - Mozilla.FirefoxDownloader.setFormSelection - ).not.toHaveBeenCalled(); - expect( - Mozilla.FirefoxDownloader.setAllSelectOptions - ).toHaveBeenCalledWith(e.target.value, jasmine.any(Object)); - expect( - Mozilla.FirefoxDownloader.setDownloadButton - ).toHaveBeenCalled(); - }); - - it('should update the product selection for ESR', function () { - const e = { - target: { - value: 'desktop_esr_next' - } - }; - - spyOn(Mozilla.FirefoxDownloader, 'setFormSelection'); - spyOn(Mozilla.FirefoxDownloader, 'setAllSelectOptions'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadButton'); - - Mozilla.FirefoxDownloader.onVersionChange(e); - expect( - Mozilla.FirefoxDownloader.setFormSelection - ).toHaveBeenCalledWith(e.target.value); - }); - }); - - describe('getHash', function () { - it('should return a product id if a valid hash identifier exists', function () { - expect( - Mozilla.FirefoxDownloader.getHash('#product-desktop-release') - ).toEqual('desktop_release'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-desktop-beta') - ).toEqual('desktop_beta'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-desktop-developer') - ).toEqual('desktop_developer'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-desktop-nightly') - ).toEqual('desktop_nightly'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-desktop-esr') - ).toEqual('desktop_esr'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-android-release') - ).toEqual('android_release'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-android-beta') - ).toEqual('android_beta'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-android-nightly') - ).toEqual('android_nightly'); - expect( - Mozilla.FirefoxDownloader.getHash('#product-ios-release') - ).toEqual('ios_release'); - }); - - it('should return null if the hash identifier does not map to a valid product id', function () { - expect( - Mozilla.FirefoxDownloader.getHash('#product-firefox-fortress') - ).toEqual(null); - }); - }); - - describe('setHash', function () { - it('should set a hash identifier when passed a product id', function () { - expect( - Mozilla.FirefoxDownloader.setHash('desktop_nightly_dude') - ).toEqual('#product-desktop-nightly-dude'); - }); - }); - - describe('onHashChange', function () { - it('should update the product selection if a valid hash identifier exists', function () { - const id = 'firefox_beta'; - spyOn(Mozilla.FirefoxDownloader, 'getHash').and.returnValue(id); - spyOn(Mozilla.FirefoxDownloader, 'setProductSelection'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadButton'); - - Mozilla.FirefoxDownloader.onHashChange(); - expect( - Mozilla.FirefoxDownloader.setProductSelection - ).toHaveBeenCalledWith(id); - expect( - Mozilla.FirefoxDownloader.setDownloadButton - ).toHaveBeenCalled(); - }); - - it('should not update the product selection if a hash identifier is invalid', function () { - spyOn(Mozilla.FirefoxDownloader, 'getHash').and.returnValue(null); - spyOn(Mozilla.FirefoxDownloader, 'setProductSelection'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadButton'); - - Mozilla.FirefoxDownloader.onHashChange(); - expect( - Mozilla.FirefoxDownloader.setProductSelection - ).not.toHaveBeenCalled(); - expect( - Mozilla.FirefoxDownloader.setDownloadButton - ).not.toHaveBeenCalled(); - }); - }); - - describe('init', function () { - const platform = 'windows'; - const language = 'de'; - const product = { - id: 'desktop_beta', - label: 'Firefox Beta' - }; - - beforeEach(function () { - spyOn(Mozilla.FirefoxDownloader, 'setHash'); - spyOn(Mozilla.FirefoxDownloader, 'setFormSelection'); - spyOn(Mozilla.FirefoxDownloader, 'setAllSelectOptions'); - spyOn(Mozilla.FirefoxDownloader, 'setDownloadButton'); - spyOn(Mozilla.FirefoxDownloader, 'enableForm'); - }); - - it('should initialize the form as expected', function () { - spyOn(Mozilla.FirefoxDownloader, 'getHash').and.returnValue(false); - spyOn(Mozilla.FirefoxDownloader, 'getPageLanguage').and.returnValue( - language - ); - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue(product); - spyOn(Mozilla.FirefoxDownloader, 'getPlatform').and.returnValue( - platform - ); - - Mozilla.FirefoxDownloader.init(); - expect( - Mozilla.FirefoxDownloader.setAllSelectOptions - ).toHaveBeenCalledTimes(2); - expect( - Mozilla.FirefoxDownloader.setDownloadButton - ).toHaveBeenCalled(); - expect(Mozilla.FirefoxDownloader.enableForm).toHaveBeenCalled(); - expect(Mozilla.FirefoxDownloader.setHash).toHaveBeenCalled(); - }); - - it('should update the product selection of a hash identifier exists', function () { - spyOn(Mozilla.FirefoxDownloader, 'getHash').and.returnValue( - 'desktop_beta' - ); - spyOn(Mozilla.FirefoxDownloader, 'getPageLanguage').and.returnValue( - language - ); - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue(product); - spyOn(Mozilla.FirefoxDownloader, 'getPlatform').and.returnValue( - platform - ); - spyOn(Mozilla.FirefoxDownloader, 'setProductSelection'); - - Mozilla.FirefoxDownloader.init(); - expect( - Mozilla.FirefoxDownloader.setProductSelection - ).toHaveBeenCalledWith('desktop_beta'); - }); - - it('should error if product or language cannot be determined', function () { - spyOn(Mozilla.FirefoxDownloader, 'getHash').and.returnValue(false); - spyOn(Mozilla.FirefoxDownloader, 'getPageLanguage').and.returnValue( - false - ); - spyOn( - Mozilla.FirefoxDownloader, - 'getProductSelection' - ).and.returnValue({ - id: undefined, - label: undefined - }); - spyOn(Mozilla.FirefoxDownloader, 'getPlatform'); - spyOn(Mozilla.FirefoxDownloader, 'onError'); - - Mozilla.FirefoxDownloader.init(); - expect( - Mozilla.FirefoxDownloader.setAllSelectOptions - ).not.toHaveBeenCalled(); - expect( - Mozilla.FirefoxDownloader.setDownloadButton - ).not.toHaveBeenCalled(); - expect(Mozilla.FirefoxDownloader.enableForm).not.toHaveBeenCalled(); - expect(Mozilla.FirefoxDownloader.setHash).not.toHaveBeenCalled(); - expect(Mozilla.FirefoxDownloader.onError).toHaveBeenCalled(); - }); - }); -}); diff --git a/webpack.test.config.js b/webpack.test.config.js index a3a2b921de6..9d2d0299d77 100644 --- a/webpack.test.config.js +++ b/webpack.test.config.js @@ -25,7 +25,6 @@ module.exports = { 'media/js/base/mozilla-fxa.js', 'media/js/base/mozilla-smoothscroll.js', 'media/js/base/stub-attribution/stub-attribution.js', - 'media/js/firefox/all/all-downloads-unified.js', 'media/js/firefox/new/common/thanks.js', 'media/js/pocket/analytics.es6.js', 'node_modules/sinon/pkg/sinon.js'