summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2021-03-27 10:37:22 +0100
committerJohn Molakvoæ <skjnldsv@protonmail.com>2022-10-19 10:02:51 +0200
commitbd303388e3d4dae90c2266d183395db8321c11de (patch)
treefddc23eff000713596cd024914667320c8c1312e /core
parentd5edcf8c9570618d9008b355b7f432575ff9d357 (diff)
downloadnextcloud-server-bd303388e3d4dae90c2266d183395db8321c11de.tar.gz
nextcloud-server-bd303388e3d4dae90c2266d183395db8321c11de.zip
Cleanup ie and old edge properties
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'core')
-rw-r--r--core/Controller/UnsupportedBrowserController.php51
-rw-r--r--core/css/apps.scss10
-rw-r--r--core/css/public.scss6
-rw-r--r--core/routes.php5
-rw-r--r--core/src/Polyfill/closest.js41
-rw-r--r--core/src/Polyfill/index.js3
-rw-r--r--core/src/components/MainMenu.js1
-rw-r--r--core/src/init.js12
-rw-r--r--core/src/main.js17
-rw-r--r--core/src/services/BrowserStorageService.js (renamed from core/src/Polyfill/console.js)24
-rw-r--r--core/src/services/BrowsersListService.js30
-rw-r--r--core/src/services/LoggerService.js (renamed from core/src/Polyfill/windows-phone.js)21
-rw-r--r--core/src/unsupported-browser.js39
-rw-r--r--core/src/utils/RedirectUnsupportedBrowsers.js54
-rw-r--r--core/src/views/UnsupportedBrowser.vue191
-rw-r--r--core/templates/unsupportedbrowser.php1
16 files changed, 406 insertions, 100 deletions
diff --git a/core/Controller/UnsupportedBrowserController.php b/core/Controller/UnsupportedBrowserController.php
new file mode 100644
index 00000000000..8cdc190deea
--- /dev/null
+++ b/core/Controller/UnsupportedBrowserController.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Core\Controller;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IRequest;
+use OCP\Util;
+
+class UnsupportedBrowserController extends Controller {
+ public function __construct(IRequest $request) {
+ parent::__construct('core', $request);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ *
+ * @return Response
+ */
+ public function index(): Response {
+ Util::addScript('core', 'unsupported-browser');
+ Util::addStyle('core', 'icons');
+ return new TemplateResponse('core', 'unsupportedbrowser', [], TemplateResponse::RENDER_AS_ERROR);
+ }
+}
diff --git a/core/css/apps.scss b/core/css/apps.scss
index dea8cbf9217..f0d96d55e9e 100644
--- a/core/css/apps.scss
+++ b/core/css/apps.scss
@@ -970,16 +970,6 @@ $popoveritem-height: 44px;
$popovericon-size: 16px;
$outter-margin: math.div($popoveritem-height - $popovericon-size, 2);
-.ie,
-.edge {
- .bubble, .bubble:after,
- .popovermenu, .popovermenu:after,
- #app-navigation .app-navigation-entry-menu,
- #app-navigation .app-navigation-entry-menu:after {
- border: 1px solid var(--color-border);
- }
-}
-
.contact .popovermenu ul,
.popover__menu {
> li > a > img {
diff --git a/core/css/public.scss b/core/css/public.scss
index 0e75a938cfa..ddeb444970d 100644
--- a/core/css/public.scss
+++ b/core/css/public.scss
@@ -47,12 +47,6 @@ $footer-height: 65px;
padding-top: 0;
}
- /* force layout to make sure the content element's height matches its contents' height */
- .ie #content {
- display: inline-block;
- }
-
-
p.info {
margin: 20px auto;
text-shadow: 0 0 2px rgba(0, 0, 0, .4);
diff --git a/core/routes.php b/core/routes.php
index 820db44bf90..02e27c9cfaf 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -58,11 +58,13 @@ $application->registerRoutes($this, [
['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'],
['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'],
['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'],
+
// Original login flow used by all clients
['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'],
['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'],
['name' => 'ClientFlowLogin#grantPage', 'url' => '/login/flow/grant', 'verb' => 'GET'],
['name' => 'ClientFlowLogin#apptokenRedirect', 'url' => '/login/flow/apptoken', 'verb' => 'POST'],
+
// NG login flow used by desktop client in case of Kerberos/fancy 2fa (smart cards for example)
['name' => 'ClientFlowLoginV2#poll', 'url' => '/login/v2/poll', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#showAuthPickerPage', 'url' => '/login/v2/flow', 'verb' => 'GET'],
@@ -97,6 +99,9 @@ $application->registerRoutes($this, [
// Well known requests https://tools.ietf.org/html/rfc5785
['name' => 'WellKnown#handle', 'url' => '.well-known/{service}'],
+
+ // Unsupported browser
+ ['name' => 'UnsupportedBrowser#index', 'url' => 'unsupported'],
],
'ocs' => [
['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
diff --git a/core/src/Polyfill/closest.js b/core/src/Polyfill/closest.js
deleted file mode 100644
index 68751fad38c..00000000000
--- a/core/src/Polyfill/closest.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * @copyright Copyright (c) 2016 John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- *
- * @license AGPL-3.0-or-later
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
-
-if (!Element.prototype.matches) {
- Element.prototype.matches
- = Element.prototype.msMatchesSelector
- || Element.prototype.webkitMatchesSelector
-}
-
-if (!Element.prototype.closest) {
- Element.prototype.closest = function(s) {
- let el = this
-
- do {
- if (el.matches(s)) return el
- el = el.parentElement || el.parentNode
- } while (el !== null && el.nodeType === 1)
- return null
- }
-}
diff --git a/core/src/Polyfill/index.js b/core/src/Polyfill/index.js
index 5a190318327..610619217d2 100644
--- a/core/src/Polyfill/index.js
+++ b/core/src/Polyfill/index.js
@@ -21,7 +21,4 @@
*
*/
-import './console'
-import './closest'
-import './windows-phone'
import 'focus-visible'
diff --git a/core/src/components/MainMenu.js b/core/src/components/MainMenu.js
index 267a3d9a361..be27e752237 100644
--- a/core/src/components/MainMenu.js
+++ b/core/src/components/MainMenu.js
@@ -28,7 +28,6 @@ import Vue from 'vue'
import AppMenu from './AppMenu.vue'
export const setUp = () => {
-
Vue.mixin({
methods: {
t,
diff --git a/core/src/init.js b/core/src/init.js
index ae8db0abf49..867ba94483f 100644
--- a/core/src/init.js
+++ b/core/src/init.js
@@ -29,12 +29,12 @@ import _ from 'underscore'
import $ from 'jquery'
import moment from 'moment'
-import { initSessionHeartBeat } from './session-heartbeat'
-import OC from './OC/index'
-import { setUp as setUpContactsMenu } from './components/ContactsMenu'
-import { setUp as setUpMainMenu } from './components/MainMenu'
-import { setUp as setUpUserMenu } from './components/UserMenu'
-import PasswordConfirmation from './OC/password-confirmation'
+import { initSessionHeartBeat } from './session-heartbeat.js'
+import OC from './OC/index.js'
+import { setUp as setUpContactsMenu } from './components/ContactsMenu.js'
+import { setUp as setUpMainMenu } from './components/MainMenu.js'
+import { setUp as setUpUserMenu } from './components/UserMenu.js'
+import PasswordConfirmation from './OC/password-confirmation.js'
// keep in sync with core/css/variables.scss
const breakpointMobileWidth = 1024
diff --git a/core/src/main.js b/core/src/main.js
index f9d89327a6a..959110e86a4 100644
--- a/core/src/main.js
+++ b/core/src/main.js
@@ -26,16 +26,21 @@
import $ from 'jquery'
import 'core-js/stable'
import 'regenerator-runtime/runtime'
-import './Polyfill/index'
+import './Polyfill/index.js'
// If you remove the line below, tests won't pass
// eslint-disable-next-line no-unused-vars
-import OC from './OC/index'
+import OC from './OC/index.js'
-import './globals'
-import './jquery/index'
-import { initCore } from './init'
-import { registerAppsSlideToggle } from './OC/apps'
+import './globals.js'
+import './jquery/index.js'
+import { initCore } from './init.js'
+import { registerAppsSlideToggle } from './OC/apps.js'
+import { testSupportedBrowser } from './utils/RedirectUnsupportedBrowsers.js'
+
+if (window.TESTING === undefined) {
+ testSupportedBrowser()
+}
window.addEventListener('DOMContentLoaded', function() {
initCore()
diff --git a/core/src/Polyfill/console.js b/core/src/services/BrowserStorageService.js
index be3aca1a9b3..d383e1caaaf 100644
--- a/core/src/Polyfill/console.js
+++ b/core/src/services/BrowserStorageService.js
@@ -1,10 +1,9 @@
/**
- * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
*
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
- * @license AGPL-3.0-or-later
+ * @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -17,18 +16,13 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-/* eslint-disable no-console */
-if (typeof console === 'undefined' || typeof console.log === 'undefined') {
- if (!window.console) {
- window.console = {}
- }
- const noOp = () => {}
- const methods = ['log', 'debug', 'warn', 'info', 'error', 'assert', 'time', 'timeEnd']
- for (let i = 0; i < methods.length; i++) {
- console[methods[i]] = noOp
- }
-}
+import { getBuilder } from '@nextcloud/browser-storage'
+
+export default getBuilder('nextcloud')
+ .clearOnLogout()
+ .persist()
+ .build()
diff --git a/core/src/services/BrowsersListService.js b/core/src/services/BrowsersListService.js
new file mode 100644
index 00000000000..c5d546665dc
--- /dev/null
+++ b/core/src/services/BrowsersListService.js
@@ -0,0 +1,30 @@
+/**
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import { getUserAgentRegExp } from 'browserslist-useragent-regexp'
+// eslint-disable-next-line node/no-extraneous-import
+import browserslist from 'browserslist'
+import browserslistConfig from '@nextcloud/browserslist-config'
+
+// Generate a regex that matches user agents to detect incompatible browsers
+export const supportedBrowsersRegExp = getUserAgentRegExp({ allowHigherVersions: true, browsers: browserslistConfig })
+export const supportedBrowsers = browserslist(browserslistConfig)
diff --git a/core/src/Polyfill/windows-phone.js b/core/src/services/LoggerService.js
index 27b45d701c6..f0b8fc9e61d 100644
--- a/core/src/Polyfill/windows-phone.js
+++ b/core/src/services/LoggerService.js
@@ -1,10 +1,9 @@
/**
- * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
*
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
- * @license AGPL-3.0-or-later
+ * @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -17,15 +16,13 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-// fix device width on windows phone
-if ('-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/)) {
- const msViewportStyle = document.createElement('style')
- msViewportStyle.appendChild(
- document.createTextNode('@-ms-viewport{width:auto!important}')
- )
- document.getElementsByTagName('head')[0].appendChild(msViewportStyle)
-}
+import { getLoggerBuilder } from '@nextcloud/logger'
+
+export default getLoggerBuilder()
+ .setApp('core')
+ .detectUser()
+ .build()
diff --git a/core/src/unsupported-browser.js b/core/src/unsupported-browser.js
new file mode 100644
index 00000000000..cac5f145a7b
--- /dev/null
+++ b/core/src/unsupported-browser.js
@@ -0,0 +1,39 @@
+/**
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { generateUrl } from '@nextcloud/router'
+import Vue from 'vue'
+
+import { browserStorageKey } from './utils/RedirectUnsupportedBrowsers.js'
+import browserStorage from './services/BrowserStorageService.js'
+import UnsupportedBrowser from './views/UnsupportedBrowser.vue'
+
+// If the ignore token is set, redirect
+if (browserStorage.getItem(browserStorageKey) === 'true') {
+ window.location = generateUrl('/')
+}
+
+export default new Vue({
+ el: '#unsupported-browser',
+ // eslint-disable-next-line vue/match-component-file-name
+ name: 'UnsupportedBrowserRoot',
+ render: h => h(UnsupportedBrowser),
+})
diff --git a/core/src/utils/RedirectUnsupportedBrowsers.js b/core/src/utils/RedirectUnsupportedBrowsers.js
new file mode 100644
index 00000000000..74074cec558
--- /dev/null
+++ b/core/src/utils/RedirectUnsupportedBrowsers.js
@@ -0,0 +1,54 @@
+/**
+ * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @author John Molakvoæ <skjnldsv@protonmail.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import { generateUrl } from '@nextcloud/router'
+
+import { supportedBrowsersRegExp } from '../services/BrowsersListService.js'
+import browserStorage from '../services/BrowserStorageService.js'
+import logger from '../services/LoggerService.js'
+
+const redirectPath = '/unsupported'
+export const browserStorageKey = 'unsupported-browser-ignore'
+
+const isBrowserOverridden = browserStorage.getItem(browserStorageKey) === 'true'
+
+/**
+ * Test the current browser user agent against our official browserslist config
+ * and redirect if unsupported
+ */
+export const testSupportedBrowser = function() {
+ if (supportedBrowsersRegExp.test(navigator.userAgent)) {
+ logger.debug('this browser is officially supported ! 🚀')
+ return
+ }
+
+ // If incompatible BUT ignored, let's keep going
+ if (isBrowserOverridden) {
+ logger.debug('this browser is NOT supported but has been manually overridden ! ⚠️')
+ return
+ }
+
+ // If incompatible, NOT overridden AND NOT already on the warning page,
+ // redirect to the unsupported warning page
+ if (window.location.pathname.indexOf(redirectPath) === -1) {
+ window.location = generateUrl(redirectPath)
+ }
+}
diff --git a/core/src/views/UnsupportedBrowser.vue b/core/src/views/UnsupportedBrowser.vue
new file mode 100644
index 00000000000..ef2a33ca213
--- /dev/null
+++ b/core/src/views/UnsupportedBrowser.vue
@@ -0,0 +1,191 @@
+ <!--
+ - @copyright Copyright (c) 2021 John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @author John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+<template>
+ <div class="content-unsupported-browser guest-box">
+ <NcEmptyContent>
+ {{ t('core', 'This browser is not supported') }}
+ <template #icon>
+ <Web />
+ </template>
+ <template #action>
+ <div>
+ <h2>
+ {{ t('core', 'Please upgrade to a more recent browser') }}
+ </h2>
+ <NcButton class="content-unsupported-browser__continue" type="primary" @click="forceBrowsing">
+ {{ t('core', 'Continue with this outdated browser') }}
+ </NcButton>
+ </div>
+
+ <ul class="content-unsupported-browser__list">
+ <h3>{{ t('core', 'Supported versions') }}</h3>
+ <li v-for="browser in formattedBrowsersList" :key="browser">
+ {{ browser }}
+ </li>
+ </ul>
+ </template>
+ </NcEmptyContent>
+ </div>
+</template>
+
+<script>
+import { generateUrl } from '@nextcloud/router'
+import { translate as t, translatePlural as n } from '@nextcloud/l10n'
+import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent'
+import Web from 'vue-material-design-icons/Web'
+
+import { browserStorageKey } from '../utils/RedirectUnsupportedBrowsers.js'
+import { supportedBrowsers } from '../services/BrowsersListService.js'
+import browserStorage from '../services/BrowserStorageService.js'
+import logger from '../services/LoggerService.js'
+
+logger.debug('Supported browsers', { supportedBrowsers })
+
+export default {
+ name: 'UnsupportedBrowser',
+ components: {
+ Web,
+ NcButton,
+ NcEmptyContent,
+ },
+
+ data() {
+ return {
+ agents: {},
+ }
+ },
+
+ computed: {
+ isMobile() {
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
+ },
+
+ /**
+ * Filter out or include mobile/desktop browsers depending
+ * on the current user platform/device
+ */
+ filteredSupportedBrowsers() {
+ return supportedBrowsers.filter(browser => {
+ if (!browser) {
+ return false
+ }
+
+ if (this.isMobile) {
+ return this.isMobileBrowser(browser)
+ }
+ return !this.isMobileBrowser(browser)
+ })
+ },
+
+ formattedBrowsersList() {
+ const list = {}
+
+ // supportedBrowsers is generated by webpack at compilation time
+ this.filteredSupportedBrowsers.forEach(browser => {
+ const [id, version] = browser.split(' ')
+ if (!list[id] || list[id] < parseFloat(version, 10)) {
+ list[id] = parseFloat(version, 10)
+ }
+ })
+
+ return Object.keys(list).map(id => {
+ if (!this.agents[id]?.browser) {
+ return null
+ }
+
+ const version = list[id]
+ const name = this.agents[id]?.browser
+ return this.t('core', '{name} version {version} and above', {
+ name, version,
+ })
+ }).filter(entry => entry !== null)
+ },
+ },
+
+ async beforeMount() {
+ // Dynamic load big list of user agents
+ // eslint-disable-next-line node/no-extraneous-import
+ const { agents } = await import('caniuse-lite')
+ this.agents = agents
+ },
+
+ methods: {
+ t,
+ n,
+
+ // Set the flag allowing this browser and redirect to home
+ forceBrowsing() {
+ browserStorage.setItem(browserStorageKey, true)
+ window.location = generateUrl('/')
+ },
+
+ /**
+ * Detect if the browserslist browser is a mobile one
+ * https://github.com/browserslist/browserslist#query-composition
+ *
+ * @param {string} browser a valid browserlist browser. e.g `and_chr 90`
+ */
+ isMobileBrowser(browser) {
+ browser = browser.toLowerCase()
+ return browser.includes('and_')
+ || browser.includes('android')
+ || browser.includes('ios_')
+ || browser.includes('mobile')
+ || browser.includes('_mob')
+ || browser.includes('samsung')
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.content-unsupported-browser {
+ display: flex;
+ justify-content: center;
+ width: 400px;
+ max-width: 90vw;
+ margin: auto;
+ padding: 30px;
+
+ .empty-content {
+ margin: 0;
+ &::v-deep .empty-content__icon {
+ opacity: 1;
+ }
+ }
+
+ &__continue {
+ display: block;
+ margin: 20px auto;
+ }
+
+ &__list {
+ margin-top: 60px;
+ margin-bottom: 30px;
+ li {
+ text-align: left;
+ }
+ }
+}
+
+</style>
diff --git a/core/templates/unsupportedbrowser.php b/core/templates/unsupportedbrowser.php
new file mode 100644
index 00000000000..54547e204f3
--- /dev/null
+++ b/core/templates/unsupportedbrowser.php
@@ -0,0 +1 @@
+<div id="unsupported-browser"></div>