diff --git a/moodle.config.json b/moodle.config.json index b61f913ea..e4cddbd91 100644 --- a/moodle.config.json +++ b/moodle.config.json @@ -86,6 +86,7 @@ }, "customurlscheme": "moodlemobile", "sites": [], + "stagingsites": [], "multisitesdisplay": "", "sitefindersettings": {}, "onlyallowlistedsites": false, diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index b7044eae9..aed2c04ca 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, ViewChild, ElementRef, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; import { CoreApp } from '@services/app'; @@ -46,6 +46,7 @@ import { CoreUserSupportConfig } from '@features/user/classes/support/support-co import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest-support-config'; import { CoreLoginError } from '@classes/errors/loginerror'; import { CorePlatform } from '@services/platform'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; /** * Site (url) chooser when adding a new site. @@ -55,11 +56,11 @@ import { CorePlatform } from '@services/platform'; templateUrl: 'site.html', styleUrls: ['site.scss', '../../login.scss'], }) -export class CoreLoginSitePage implements OnInit { +export class CoreLoginSitePage implements OnInit, OnDestroy { @ViewChild('siteFormEl') formElement?: ElementRef; - siteForm: FormGroup; + siteForm!: FormGroup; fixedSites?: CoreLoginSiteInfoExtended[]; filteredSites?: CoreLoginSiteInfoExtended[]; siteSelector: CoreLoginSiteSelectorListMethod = 'sitefinder'; @@ -68,14 +69,13 @@ export class CoreLoginSitePage implements OnInit { sites: CoreLoginSiteInfoExtended[] = []; hasSites = false; loadingSites = false; - searchFunction: (search: string) => void; - showScanQR: boolean; + searchFunction!: (search: string) => void; + showScanQR!: boolean; enteredSiteUrl?: CoreLoginSiteInfoExtended; - siteFinderSettings: CoreLoginSiteFinderSettings; + siteFinderSettings!: CoreLoginSiteFinderSettings; + stagingSitesChangeListener: CoreEventObserver; - constructor( - protected formBuilder: FormBuilder, - ) { + constructor(protected formBuilder: FormBuilder) { let url = ''; this.siteSelector = CoreConstants.CONFIG.multisitesdisplay; @@ -122,13 +122,36 @@ export class CoreLoginSitePage implements OnInit { this.loadingSites = false; }, 1000); + + this.stagingSitesChangeListener = CoreEvents.on( + CoreEvents.STAGING_SITES_CHANGE, + async ({ enabled }: { enabled: boolean }) => { + if (enabled) { + await this.loadStagingSites(); + + return; + } + + if (CoreLoginHelper.hasSeveralFixedSites()) { + this.siteForm = this.formBuilder.group({ + siteUrl: [this.initSiteSelector(), this.moodleUrlValidator()], + }); + + return; + } + + this.filteredSites = undefined; + this.fixedSites = undefined; + }, + ); } /** * Initialize the component. */ - ngOnInit(): void { + async ngOnInit(): Promise { this.showKeyboard = !!CoreNavigator.getRouteBooleanParam('showKeyboard'); + await this.loadStagingSites(); } /** @@ -150,6 +173,35 @@ export class CoreLoginSitePage implements OnInit { return this.fixedSites[0].url; } + /** + * Load staging sites list if they are enabled. + */ + protected async loadStagingSites(): Promise { + const stagingSites = await CoreLoginHelper.getStagingSites(); + + if (!stagingSites.length) { + return; + } + + const sites = this.extendCoreLoginSiteInfo( stagingSites); + this.siteSelector = 'list'; + + if (!this.fixedSites) { + this.fixedSites = []; + } + + for (const site of sites) { + if (this.fixedSites.some(item => item.url === site.url)) { + continue; + } + + this.fixedSites.push(site); + } + + this.siteFinderSettings.displayimage = false; + this.filteredSites = this.fixedSites; + } + /** * Initialize and show onboarding if needed. * @@ -612,6 +664,13 @@ export class CoreLoginSitePage implements OnInit { CoreNavigator.navigate('/settings'); } + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.stagingSitesChangeListener.off(); + } + } /** diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 7fc945b2f..75755be12 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -40,6 +40,7 @@ import { CorePath } from '@singletons/path'; import { CorePromisedValue } from '@classes/promised-value'; import { SafeHtml } from '@angular/platform-browser'; import { CoreLoginError } from '@classes/errors/loginerror'; +import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; const PASSWORD_RESETS_CONFIG_KEY = 'password-resets'; @@ -385,6 +386,21 @@ export class CoreLoginHelperProvider { return CoreLoginHelper.isUniqueFixedSite() ? CoreConstants.CONFIG.sites[0].url : CoreConstants.CONFIG.sites; } + /** + * Get staging sites. + * + * @returns Staging sites. + */ + async getStagingSites(): Promise { + const hasEnabledStagingSites = await CoreSettingsHelper.hasEnabledStagingSites(); + + if (!hasEnabledStagingSites) { + return []; + } + + return CoreConstants.CONFIG.stagingsites; + } + /** * Get the valid identity providers from a site config. * diff --git a/src/core/features/settings/pages/dev/dev.html b/src/core/features/settings/pages/dev/dev.html index d1a0deffd..a2459840a 100644 --- a/src/core/features/settings/pages/dev/dev.html +++ b/src/core/features/settings/pages/dev/dev.html @@ -30,6 +30,12 @@ + + +

Enable staging sites

+
+ +
diff --git a/src/core/features/settings/pages/dev/dev.ts b/src/core/features/settings/pages/dev/dev.ts index c7de23c24..74912c54f 100644 --- a/src/core/features/settings/pages/dev/dev.ts +++ b/src/core/features/settings/pages/dev/dev.ts @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { CoreConstants } from '@/core/constants'; import { Component, OnInit } from '@angular/core'; import { CoreLoginHelperProvider } from '@features/login/services/login-helper'; +import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreUserTours } from '@features/usertours/services/user-tours'; import { CoreConfig } from '@services/config'; import { CorePlatform } from '@services/platform'; -import { CoreSites } from '@services/sites'; +import { CoreLoginSiteInfo, CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -41,6 +43,8 @@ export class CoreSettingsDevPage implements OnInit { pluginStylesCount = 0; sitePlugins: CoreSitePluginsBasicInfo[] = []; userToursEnabled = true; + stagingSites: CoreLoginSiteInfo[] = []; + enableStagingSites = false; disabledFeatures: string[] = []; @@ -55,6 +59,12 @@ export class CoreSettingsDevPage implements OnInit { this.siteId = CoreSites.getCurrentSite()?.getId(); + this.stagingSites = CoreConstants.CONFIG.stagingsites; + + if (this.stagingSites.length) { + this.enableStagingSites = await CoreSettingsHelper.hasEnabledStagingSites(); + } + if (!this.siteId) { return; } @@ -157,6 +167,10 @@ export class CoreSettingsDevPage implements OnInit { CoreDomUtils.showToast('User tours have been reseted'); } + async setEnabledStagingSites(): Promise { + await CoreSettingsHelper.setEnabledStagingSites(this.enableStagingSites); + } + } // Basic site plugin info. diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts index 3fea5fd3a..bd88b449b 100644 --- a/src/core/features/settings/services/settings-helper.ts +++ b/src/core/features/settings/services/settings-helper.ts @@ -477,6 +477,17 @@ export class CoreSettingsHelperProvider { return this.darkModeObservable; } + async hasEnabledStagingSites(): Promise { + const staging = await CoreConfig.get('staging_sites', 'false'); + + return CoreUtils.isTrueOrOne(staging); + } + + async setEnabledStagingSites(enabled: boolean): Promise { + await CoreConfig.set('staging_sites', `${enabled}`); + CoreEvents.trigger(CoreEvents.STAGING_SITES_CHANGE, { enabled }); + } + } export const CoreSettingsHelper = makeSingleton(CoreSettingsHelperProvider); diff --git a/src/types/config.d.ts b/src/types/config.d.ts index 331ab4712..a54c42d47 100644 --- a/src/types/config.d.ts +++ b/src/types/config.d.ts @@ -43,6 +43,7 @@ export interface EnvironmentConfig { defaultZoomLevel?: CoreZoomLevel; // Set the default zoom level of the app. customurlscheme: string; sites: CoreLoginSiteInfo[]; + stagingsites: CoreLoginSiteInfo[]; multisitesdisplay: CoreLoginSiteSelectorListMethod; sitefindersettings: Partial; onlyallowlistedsites: boolean;