diff --git a/scripts/langindex.json b/scripts/langindex.json index 18166ff20..3d0223dbb 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2090,10 +2090,12 @@ "core.settings.debugdisplaydescription": "local_moodlemobileapp", "core.settings.deletesitefiles": "local_moodlemobileapp", "core.settings.deletesitefilestitle": "local_moodlemobileapp", + "core.settings.developeroptions": "local_moodlemobileapp", "core.settings.deviceinfo": "local_moodlemobileapp", "core.settings.deviceos": "local_moodlemobileapp", "core.settings.disableall": "message", "core.settings.disabled": "lesson", + "core.settings.disabledfeatures": "tool_mobile", "core.settings.displayformat": "local_moodlemobileapp", "core.settings.enabledownloadsection": "local_moodlemobileapp", "core.settings.enablefirebaseanalytics": "local_moodlemobileapp", @@ -2109,6 +2111,7 @@ "core.settings.fontsize": "local_moodlemobileapp", "core.settings.fontsizecharacter": "block_accessibility/char", "core.settings.forcedsetting": "local_moodlemobileapp", + "core.settings.forcesafeareamargins": "local_moodlemobileapp", "core.settings.general": "moodle", "core.settings.helpusimprove": "local_moodlemobileapp", "core.settings.ioscookies": "local_moodlemobileapp", @@ -2124,15 +2127,18 @@ "core.settings.navigatoruseragent": "local_moodlemobileapp", "core.settings.networkstatus": "local_moodlemobileapp", "core.settings.opensourcelicenses": "local_moodlemobileapp", + "core.settings.pluginstyles": "local_moodlemobileapp", "core.settings.preferences": "moodle", "core.settings.privacypolicy": "local_moodlemobileapp", "core.settings.publisher": "local_moodlemobileapp", "core.settings.pushid": "local_moodlemobileapp", + "core.settings.remotestyles": "local_moodlemobileapp", "core.settings.reportinbackground": "local_moodlemobileapp", "core.settings.screen": "local_moodlemobileapp", "core.settings.settings": "moodle", "core.settings.showdownloadoptions": "local_moodlemobileapp", "core.settings.siteinfo": "local_moodlemobileapp", + "core.settings.siteplugins": "local_moodlemobileapp", "core.settings.sites": "moodle", "core.settings.spaceusage": "local_moodlemobileapp", "core.settings.spaceusagehelp": "local_moodlemobileapp", @@ -2140,8 +2146,10 @@ "core.settings.synchronizenow": "local_moodlemobileapp", "core.settings.synchronizenowhelp": "local_moodlemobileapp", "core.settings.syncsettings": "local_moodlemobileapp", + "core.settings.textdirection": "local_moodlemobileapp", "core.settings.total": "moodle", "core.settings.wificonnection": "local_moodlemobileapp", + "core.settings.youradev": "local_moodlemobileapp", "core.sharedfiles.chooseaccountstorefile": "local_moodlemobileapp", "core.sharedfiles.chooseactionrepeatedfile": "local_moodlemobileapp", "core.sharedfiles.errorreceivefilenosites": "local_moodlemobileapp", diff --git a/src/core/features/settings/lang.json b/src/core/features/settings/lang.json index 81a87ded2..55af8e563 100644 --- a/src/core/features/settings/lang.json +++ b/src/core/features/settings/lang.json @@ -22,10 +22,12 @@ "debugdisplaydescription": "If enabled, error modals will display more data about the error if possible.", "deletesitefiles": "Are you sure that you want to delete the downloaded files and cached data from the site '{{sitename}}'? You won't be able to use the app in offline mode.", "deletesitefilestitle": "Delete site files", + "developeroptions": "Developer options", "deviceinfo": "Device info", "deviceos": "Device OS", "disableall": "Disable notifications", "disabled": "Disabled", + "disabledfeatures": "Disabled features", "displayformat": "Display format", "enabledownloadsection": "Enable download sections", "enablefirebaseanalytics": "Enable Firebase analytics", @@ -41,6 +43,7 @@ "fontsize": "Text size", "fontsizecharacter": "A", "forcedsetting": "This setting has been forced by your site configuration.", + "forcesafeareamargins": "Force safe area margins", "general": "General", "helpusimprove": "Help us improve this app", "ioscookies": "Cross-Website Tracking", @@ -56,15 +59,18 @@ "navigatoruseragent": "Navigator userAgent", "networkstatus": "Internet connection status", "opensourcelicenses": "Open Source Licences", + "pluginstyles": "Enable site plugin styles", "preferences": "Preferences", "privacypolicy": "Privacy policy", "publisher": "Publisher", "pushid": "Push notifications ID", + "remotestyles": "Enable remote styles", "reportinbackground": "Report errors automatically", "screen": "Screen information", "settings": "Settings", "showdownloadoptions": "Show download options", "siteinfo": "Site info", + "siteplugins": "Site plugins", "sites": "Sites", "spaceusage": "Space usage", "spaceusagehelp": "Deleting the stored information of the site will remove all the site offline data. This information allows you to use the app when offline. ", @@ -72,6 +78,8 @@ "synchronizenow": "Synchronise now", "synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.", "syncsettings": "Synchronisation settings", + "textdirection": "Text direction", "total": "Total", - "wificonnection": "Wi-Fi connection" + "wificonnection": "Wi-Fi connection", + "youradev": "You are now a developer" } diff --git a/src/core/features/settings/pages/about/about.html b/src/core/features/settings/pages/about/about.html index 807d240ce..73169981e 100644 --- a/src/core/features/settings/pages/about/about.html +++ b/src/core/features/settings/pages/about/about.html @@ -11,12 +11,9 @@ - -

{{ appName }} {{ versionName }}

-
- + - {{ 'core.settings.deviceinfo' | translate }} +

{{ appName }} {{ versionName }}

diff --git a/src/core/features/settings/pages/about/about.ts b/src/core/features/settings/pages/about/about.ts index d341352a9..9fe71636a 100644 --- a/src/core/features/settings/pages/about/about.ts +++ b/src/core/features/settings/pages/about/about.ts @@ -49,8 +49,6 @@ export class CoreSettingsAboutPage { * @param page The component deeplink name you want to push onto the navigation stack. */ openPage(page: string): void { - // const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; - // navCtrl.push(page); CoreNavigator.navigate(page); } diff --git a/src/core/features/settings/pages/dev/dev.html b/src/core/features/settings/pages/dev/dev.html new file mode 100644 index 000000000..1e970cc73 --- /dev/null +++ b/src/core/features/settings/pages/dev/dev.html @@ -0,0 +1,61 @@ + + + + + + +

{{ 'core.settings.developeroptions' | translate }}

+ + + + + + +
+
+ + + + +

{{ 'core.settings.textdirection' | translate }}

+

{{ direction }}

+
+ +
+ + +

{{ 'core.settings.forcesafeareamargins' | translate }}

+
+ +
+ + + +

{{ 'core.settings.remotestyles' | translate }} {{remoteStylesCount}}

+
+ +
+ + +

{{ 'core.settings.pluginstyles' | translate }} {{pluginStylesCount}}

+
+ +
+ +

{{ 'core.settings.disabledfeatures' | translate }}

+ + +

{{ feature }}

+
+
+ +

{{ 'core.settings.siteplugins' | translate }}

+ + +

{{ plugin.addon }} ({{plugin.component}})

+

{{plugin.version}}

+
+
+
+
+
diff --git a/src/core/features/settings/pages/dev/dev.ts b/src/core/features/settings/pages/dev/dev.ts new file mode 100644 index 000000000..9b14f3e42 --- /dev/null +++ b/src/core/features/settings/pages/dev/dev.ts @@ -0,0 +1,149 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, OnInit } from '@angular/core'; +import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; +import { CoreSites } from '@services/sites'; +import { CoreUtils } from '@services/utils/utils'; +import { Platform } from '@singletons'; + +/** + * Page that displays the developer options. + */ +@Component({ + selector: 'page-core-app-settings-dev', + templateUrl: 'dev.html', +}) +export class CoreSettingsDevPage implements OnInit { + + rtl = false; + forceSafeAreaMargins = false; + direction = 'ltr'; + + remoteStyles = true; + remoteStylesCount = 0; + pluginStyles = true; + pluginStylesCount = 0; + sitePlugins: CoreSitePluginsBasicInfo[] = []; + + disabledFeatures: string[] = []; + + siteId: string | undefined; + + async ngOnInit(): Promise { + this.rtl = Platform.isRTL; + this.RTLChanged(); + + this.forceSafeAreaMargins = document.documentElement.classList.contains('force-safe-area-margins'); + this.safeAreaChanged(); + + this.siteId = CoreSites.getCurrentSite()?.getId(); + + if (!this.siteId) { + return; + } + + this.remoteStyles = false; + this.remoteStylesCount = 0; + + this.pluginStyles = false; + this.pluginStylesCount = 0; + + document.head.querySelectorAll('style').forEach((style) => { + if (this.siteId && style.id.endsWith(this.siteId)) { + if (style.innerHTML.length > 0) { + this.remoteStylesCount++; + } + this.remoteStyles = this.remoteStyles || style.getAttribute('media') != 'disabled'; + } + + if (style.id.startsWith('siteplugin-')) { + if (style.innerHTML.length > 0) { + this.pluginStylesCount++; + } + this.pluginStyles = this.pluginStyles || style.getAttribute('media') != 'disabled'; + } + }); + + this.sitePlugins = CoreSitePlugins.getCurrentSitePluginList().map((plugin) => ({ + addon: plugin.addon, + component: plugin.component, + version: plugin.version, + })); + + const disabledFeatures = (await CoreSites.getCurrentSite()?.getPublicConfig())?.tool_mobile_disabledfeatures; + + this.disabledFeatures = disabledFeatures?.split(',') || []; + } + + /** + * Called when the rtl is enabled or disabled. + */ + RTLChanged(): void { + this.direction = this.rtl ? 'rtl' : 'ltr'; + document.dir = this.direction; + } + + /** + * Called when safe area margins is enabled or disabled. + */ + safeAreaChanged(): void { + document.documentElement.classList.toggle('force-safe-area-margins', this.forceSafeAreaMargins); + } + + /** + * Called when remote styles is enabled or disabled. + */ + remoteStylesChanged(): void { + document.head.querySelectorAll('style').forEach((style) => { + if (this.siteId && style.id.endsWith(this.siteId)) { + if (this.remoteStyles) { + style.removeAttribute('media'); + } else { + style.setAttribute('media', 'disabled'); + } + } + }); + } + + /** + * Called when remote styles is enabled or disabled. + */ + pluginStylesChanged(): void { + document.head.querySelectorAll('style').forEach((style) => { + if (style.id.startsWith('siteplugin-')) { + if (this.pluginStyles) { + style.removeAttribute('media'); + } else { + style.setAttribute('media', 'disabled'); + } + } + }); + } + + /** + * Copies site info. + */ + copyInfo(): void { + CoreUtils.copyToClipboard(JSON.stringify({ disabledFeatures: this.disabledFeatures, sitePlugins: this.sitePlugins })); + } + +} + +// Basic site plugin info. +type CoreSitePluginsBasicInfo = { + component: string; + addon: string; + version: string; +}; diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.html b/src/core/features/settings/pages/deviceinfo/deviceinfo.html index 162c08fce..8379b26df 100644 --- a/src/core/features/settings/pages/deviceinfo/deviceinfo.html +++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.html @@ -16,13 +16,19 @@ + + + + {{ 'core.settings.developeroptions' | translate }} + +

{{ 'core.settings.appversion' | translate}}

{{ deviceInfo.versionName }} ({{ deviceInfo.versionCode }})

- +

{{ 'core.settings.compilationinfo' | translate }}

{{ deviceInfo.compilationTime | coreFormatDate: "LLL Z": false }}

diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.module.ts b/src/core/features/settings/pages/deviceinfo/deviceinfo.module.ts index d2cecabe2..2bc510b76 100644 --- a/src/core/features/settings/pages/deviceinfo/deviceinfo.module.ts +++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.module.ts @@ -17,12 +17,17 @@ import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreSettingsDeviceInfoPage } from './deviceinfo'; +import { CoreSettingsDevPage } from '../dev/dev'; const routes: Routes = [ { path: '', component: CoreSettingsDeviceInfoPage, }, + { + path: 'dev', + component: CoreSettingsDevPage, + }, ]; @NgModule({ @@ -32,6 +37,7 @@ const routes: Routes = [ ], declarations: [ CoreSettingsDeviceInfoPage, + CoreSettingsDevPage, ], exports: [RouterModule], }) diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts index 980adb015..2911ec1cd 100644 --- a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts +++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts @@ -23,6 +23,9 @@ import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { Subscription } from 'rxjs'; import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; +import { CoreConfig } from '@services/config'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreNavigator } from '@services/navigator'; /** * Device Info to be shown and copied to clipboard. @@ -69,6 +72,10 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { deviceOsTranslated?: string; currentLangName?: string; fsClickable = false; + showDevOptions = false; + protected devOptionsClickCounter = 0; + protected devOptionsForced = false; + protected devOptionsClickTimeout?: number; protected onlineObserver?: Subscription; @@ -171,7 +178,7 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { this.onlineObserver = Network.onChange().subscribe(() => { // Execute the callback in the Angular zone, so change detection doesn't stop working. NgZone.run(() => { - this.deviceInfo!.networkStatus = appProvider.isOnline() ? 'online' : 'offline'; + this.deviceInfo.networkStatus = appProvider.isOnline() ? 'online' : 'offline'; }); }); @@ -193,6 +200,10 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { this.deviceInfo.fileSystemRoot = basepath; this.fsClickable = fileProvider.usesHTMLAPI(); } + + const showDevOptionsOnConfig = await CoreConfig.get('showDevOptions', 0); + this.devOptionsForced = CoreConstants.BUILD.isDevelopment || CoreConstants.BUILD.isTesting; + this.showDevOptions = this.devOptionsForced || showDevOptionsOnConfig == 1; } /** @@ -221,4 +232,44 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy { this.onlineObserver && this.onlineObserver.unsubscribe(); } + /** + * 5 clicks will enable dev options. + */ + async enableDevOptions(): Promise { + if (this.devOptionsForced) { + return; + } + + clearTimeout(this.devOptionsClickTimeout); + this.devOptionsClickCounter++; + + if (this.devOptionsClickCounter == 5) { + if (!this.showDevOptions) { + this.showDevOptions = true; + await CoreConfig.set('showDevOptions', 1); + + CoreDomUtils.showToast('core.settings.youradev', true); + } else { + this.showDevOptions = false; + await CoreConfig.delete('showDevOptions'); + } + + this.devOptionsClickCounter = 0; + + return; + } + + this.devOptionsClickTimeout = window.setTimeout(() => { + this.devOptionsClickTimeout = undefined; + this.devOptionsClickCounter = 0; + }, 500); + } + + /** + * Navigate to dev options. + */ + gotoDevOptions(): void { + CoreNavigator.navigate('dev'); + } + } diff --git a/src/core/features/siteplugins/services/siteplugins.ts b/src/core/features/siteplugins/services/siteplugins.ts index 382c06f40..6d7fdb702 100644 --- a/src/core/features/siteplugins/services/siteplugins.ts +++ b/src/core/features/siteplugins/services/siteplugins.ts @@ -314,6 +314,15 @@ export class CoreSitePluginsProvider { return this.sitePlugins[name]; } + /** + * Get the current site plugin list. + * + * @return Plugin list ws info. + */ + getCurrentSitePluginList(): CoreSitePluginsWSPlugin[] { + return CoreUtils.objectToArray(this.sitePlugins).map((plugin) => plugin.plugin); + } + /** * Invalidate all WS call to a certain method. * diff --git a/src/core/features/styles/services/styles.ts b/src/core/features/styles/services/styles.ts index b0854fe00..7bb761f42 100644 --- a/src/core/features/styles/services/styles.ts +++ b/src/core/features/styles/services/styles.ts @@ -330,9 +330,9 @@ export class CoreStylesService { ( element).disabled = !!disable; // eslint-disable-line @typescript-eslint/no-explicit-any if (disable) { - element.setAttribute('disabled', 'true'); + element.setAttribute('media', 'disabled'); } else { - element.removeAttribute('disabled'); + element.removeAttribute('media'); } }