diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts index a32c5f97e..8dccdcb02 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.ts @@ -86,7 +86,7 @@ export class AddonModForumPostOptionsMenuComponent implements OnInit { */ protected setOpenInBrowserUrl(): void { const site = CoreSites.getRequiredCurrentSite(); - if (!CoreSites.shouldDisplayInformativeLinks(site)) { + if (!site.shouldDisplayInformativeLinks()) { return; } diff --git a/src/addons/mod/forum/pages/discussion/discussion.ts b/src/addons/mod/forum/pages/discussion/discussion.ts index 3d40cf2da..0779c78fa 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.ts +++ b/src/addons/mod/forum/pages/discussion/discussion.ts @@ -167,7 +167,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes const currentSite = CoreSites.getCurrentSite(); this.isOnline = CoreNetwork.isOnline(); - this.externalUrl = currentSite && CoreSites.shouldDisplayInformativeLinks(currentSite) ? + this.externalUrl = currentSite && currentSite.shouldDisplayInformativeLinks() ? currentSite.createSiteUrl('/mod/forum/discuss.php', { d: this.discussionId.toString() }) : undefined; this.onlineObserver = CoreNetwork.onChange().subscribe(() => { diff --git a/src/addons/remotethemes/services/remotethemes-handler.ts b/src/addons/remotethemes/services/remotethemes-handler.ts index b356ff49a..c4c96e5b5 100644 --- a/src/addons/remotethemes/services/remotethemes-handler.ts +++ b/src/addons/remotethemes/services/remotethemes-handler.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreConstants } from '@/core/constants'; -import { CoreSitePublicConfigResponse } from '@classes/sites/site'; +import { CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site'; import { CoreFile } from '@services/file'; import { CoreFilepool } from '@services/filepool'; import { CoreSites } from '@services/sites'; diff --git a/src/core/classes/sites/site.ts b/src/core/classes/sites/site.ts index 1c8fb5a69..9e461dd25 100644 --- a/src/core/classes/sites/site.ts +++ b/src/core/classes/sites/site.ts @@ -24,7 +24,6 @@ import { CoreWS, CoreWSPreSets, CoreWSFileUploadOptions, - CoreWSAjaxPreSets, CoreWSExternalWarning, CoreWSUploadFileResult, CoreWSPreSetsSplitRequest, @@ -62,19 +61,9 @@ import { finalize, map, mergeMap } from 'rxjs/operators'; import { firstValueFrom } from '../../utils/rxjs'; import { CoreSiteError } from '@classes/errors/siteerror'; import { CoreUserAuthenticatedSupportConfig } from '@features/user/classes/support/authenticated-support-config'; -import { CoreLoginHelper } from '@features/login/services/login-helper'; -import { CorePath } from '@singletons/path'; import { CoreErrorLogs } from '@singletons/error-logs'; import { CoreFilepool } from '@services/filepool'; - -/** - * QR Code type enumeration. - */ -export enum CoreSiteQRCodeType { - QR_CODE_DISABLED = 0, // QR code disabled value - QR_CODE_URL = 1, // QR code type URL value - QR_CODE_LOGIN = 2, // QR code type login value -} +import { CoreSiteInfo, CoreSiteInfoResponse, CoreSitePublicConfigResponse, CoreUnauthenticatedSite } from './unauthenticated-site'; // WS that we allow to call even if the site is logged out. const ALLOWED_LOGGEDOUT_WS = [ @@ -89,7 +78,7 @@ const ALLOWED_LOGGEDOUT_WS = [ * * @todo Refactor this class to improve "temporary" sites support (not fully authenticated). */ -export class CoreSite { +export class CoreSite extends CoreUnauthenticatedSite { static readonly REQUEST_QUEUE_FORCE_WS = false; // Use "tool_mobile_call_external_functions" even for calling a single function. @@ -161,8 +150,9 @@ export class CoreSite { public config?: CoreSiteConfig, public loggedOut?: boolean, ) { + super(siteUrl); + this.logger = CoreLogger.getInstance('CoreSite'); - this.siteUrl = CoreUrlUtils.removeUrlParams(this.siteUrl); // Make sure the URL doesn't have params. this.cacheTable = asyncInstance(() => CoreSites.getSiteTable(WS_CACHE_TABLE, { siteId: this.getId(), @@ -212,15 +202,6 @@ export class CoreSite { return this.id; } - /** - * Get site URL. - * - * @returns Site URL. - */ - getURL(): string { - return this.siteUrl; - } - /** * Get site token. * @@ -236,9 +217,7 @@ export class CoreSite { } /** - * Get site info. - * - * @returns Site info. + * @inheritdoc */ getInfo(): CoreSiteInfo | undefined { return this.infos; @@ -290,32 +269,6 @@ export class CoreSite { return this.infos?.siteid || 1; } - /** - * Get site name. - * - * @returns Site name. - */ - async getSiteName(): Promise { - if (this.isDemoModeSite()) { - return CoreConstants.CONFIG.appname; - } - - if (this.infos?.sitename) { - return this.infos?.sitename; - } - - // Fallback. - const isSingleFixedSite = await CoreLoginHelper.isSingleFixedSite(); - - if (isSingleFixedSite) { - const sites = await CoreLoginHelper.getAvailableSites(); - - return sites[0].name; - } - - return ''; - } - /** * Set site ID. * @@ -1623,47 +1576,11 @@ export class CoreSite { } /** - * Returns a url to link an specific page on the site. - * - * @param path Path of the url to go to. - * @param params Object with the params to add. - * @param anchor Anchor text if needed. - * @returns URL with params. - */ - createSiteUrl(path: string, params?: Record, anchor?: string): string { - return CoreUrlUtils.addParamsToUrl(CorePath.concatenatePaths(this.siteUrl, path), params, anchor); - } - - /** - * Check if a URL belongs to this site. - * - * @param url URL to check. - * @returns Whether the URL belongs to this site. - */ - containsUrl(url?: string): boolean { - if (!url) { - return false; - } - - const siteUrl = CoreTextUtils.addEndingSlash(CoreUrlUtils.removeProtocolAndWWW(this.siteUrl)); - url = CoreTextUtils.addEndingSlash(CoreUrlUtils.removeProtocolAndWWW(url)); - - return url.indexOf(siteUrl) == 0; - } - - /** - * Get the public config of this site. - * - * @param options Options. - * @returns Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax. + * @inheritdoc */ async getPublicConfig(options: { readingStrategy?: CoreSitesReadingStrategy } = {}): Promise { if (!this.db) { - if (options.readingStrategy === CoreSitesReadingStrategy.ONLY_CACHE) { - throw new CoreError('Cache not available to read public config'); - } - - return this.requestPublicConfig(); + return super.getPublicConfig(options); } const method = 'tool_mobile_get_public_config'; @@ -1754,47 +1671,12 @@ export class CoreSite { } /** - * Perform a request to the server to get the public config of this site. + * Check if GET method is supported for AJAX calls. * - * @returns Promise resolved with public config. + * @returns Whether it's supported. */ - protected async requestPublicConfig(): Promise { - const preSets: CoreWSAjaxPreSets = { - siteUrl: this.siteUrl, - }; - - let config: CoreSitePublicConfigResponse; - - try { - config = await CoreWS.callAjax('tool_mobile_get_public_config', {}, preSets); - } catch (error) { - if (!error || error.errorcode !== 'codingerror' || (this.getInfo() && !this.isVersionGreaterEqualThan('3.8'))) { - throw error; - } - - // This error probably means that there is a redirect in the site. Try to use a GET request. - preSets.noLogin = true; - preSets.useGet = true; - - try { - config = await CoreWS.callAjax('tool_mobile_get_public_config', {}, preSets); - } catch (error2) { - if (this.getInfo() && this.isVersionGreaterEqualThan('3.8')) { - // GET is supported, return the second error. - throw error2; - } else { - // GET not supported or we don't know if it's supported. Return first error. - throw error; - } - } - } - - // Use the wwwroot returned by the server. - if (config.httpswwwroot) { - this.siteUrl = CoreUrlUtils.removeUrlParams(config.httpswwwroot); // Make sure the URL doesn't have params. - } - - return config; + protected isAjaxGetSupported(): boolean { + return !!this.getInfo() && this.isVersionGreaterEqualThan('3.8'); } /** @@ -2325,35 +2207,6 @@ export class CoreSite { return this.tokenPluginFileWorksPromise; } - /** - * Check if a URL to a file belongs to the site and uses the pluginfileurl or tokenpluginfileurl endpoints. - * - * @param url File URL to check. - * @returns Whether it's a site file URL. - */ - isSitePluginFileUrl(url: string): boolean { - const isPluginFileUrl = CoreUrlUtils.isPluginFileUrl(url) || CoreUrlUtils.isTokenPluginFileUrl(url); - if (!isPluginFileUrl) { - return false; - } - - return this.containsUrl(url); - } - - /** - * Check if a URL to a file belongs to the site and is a theme image file. - * - * @param url File URL to check. - * @returns Whether it's a site theme image URL. - */ - isSiteThemeImageUrl(url: string): boolean { - if (!CoreUrlUtils.isThemeImageUrl(url)) { - return false; - } - - return this.containsUrl(url); - } - /** * Deletes last viewed records based on some conditions. * @@ -2431,17 +2284,6 @@ export class CoreSite { }); } - /** - * Check if the site is a demo mode site. - * - * @returns Whether the site is a demo mode site. - */ - isDemoModeSite(): boolean { - const demoSiteData = CoreLoginHelper.getDemoModeSiteInfo(); - - return this.containsUrl(demoSiteData?.url); - } - } /** @@ -2647,65 +2489,6 @@ type RequestQueueItem = { deferred: CorePromisedValue; }; -/** - * Result of WS core_webservice_get_site_info. - */ -export type CoreSiteInfoResponse = { - sitename: string; // Site name. - username: string; // Username. - firstname: string; // First name. - lastname: string; // Last name. - fullname: string; // User full name. - lang: string; // Current language. - userid: number; // User id. - siteurl: string; // Site url. - userpictureurl: string; // The user profile picture. - functions: { - name: string; // Function name. - version: string; // The version number of the component to which the function belongs. - }[]; - downloadfiles?: number; // 1 if users are allowed to download files, 0 if not. - uploadfiles?: number; // 1 if users are allowed to upload files, 0 if not. - release?: string; // Moodle release number. - version?: string; // Moodle version number. - mobilecssurl?: string; // Mobile custom CSS theme. - advancedfeatures?: { // Advanced features availability. - name: string; // Feature name. - value: number; // Feature value. Usually 1 means enabled. - }[]; - usercanmanageownfiles?: boolean; // True if the user can manage his own files. - userquota?: number; // User quota (bytes). 0 means user can ignore the quota. - usermaxuploadfilesize?: number; // User max upload file size (bytes). -1 means the user can ignore the upload file size. - userhomepage?: CoreSiteInfoUserHomepage; // The default home page for the user. - userprivateaccesskey?: string; // Private user access key for fetching files. - siteid?: number; // Site course ID. - sitecalendartype?: string; // Calendar type set in the site. - usercalendartype?: string; // Calendar typed used by the user. - userissiteadmin?: boolean; // Whether the user is a site admin or not. - theme?: string; // Current theme for the user. -}; - -/** - * Site info, including some calculated data. - */ -export type CoreSiteInfo = CoreSiteInfoResponse & { - functionsByName?: { - [name: string]: { - name: string; // Function name. - version: string; // The version number of the component to which the function belongs. - }; - }; -}; - -/** - * Enum constants that define default user home page. - */ -export enum CoreSiteInfoUserHomepage { - HOMEPAGE_SITE = 0, // Site home. - HOMEPAGE_MY = 1, // Dashboard. - HOMEPAGE_MYCOURSES = 3, // My courses. -} - /** * Result of WS tool_mobile_get_config. */ @@ -2717,15 +2500,6 @@ export type CoreSiteConfigResponse = { warnings?: CoreWSExternalWarning[]; }; -/** - * Possible values for 'supportavailability' config. - */ -export const enum CoreSiteConfigSupportAvailability { - Disabled = 0, - Authenticated = 1, - Anyone = 2, -} - /** * Site config indexed by name. */ @@ -2735,64 +2509,6 @@ export type CoreSiteConfig = Record & { searchbannerenable?: string; // Whether search banner is enabled. }; -/** - * Result of WS tool_mobile_get_public_config. - */ -export type CoreSitePublicConfigResponse = { - wwwroot: string; // Site URL. - httpswwwroot: string; // Site https URL (if httpslogin is enabled). - sitename: string; // Site name. - guestlogin: number; // Whether guest login is enabled. - rememberusername: number; // Values: 0 for No, 1 for Yes, 2 for optional. - authloginviaemail: number; // Whether log in via email is enabled. - registerauth: string; // Authentication method for user registration. - forgottenpasswordurl: string; // Forgotten password URL. - authinstructions: string; // Authentication instructions. - authnoneenabled: number; // Whether auth none is enabled. - enablewebservices: number; // Whether Web Services are enabled. - enablemobilewebservice: number; // Whether the Mobile service is enabled. - maintenanceenabled: number; // Whether site maintenance is enabled. - maintenancemessage: string; // Maintenance message. - logourl?: string; // The site logo URL. - compactlogourl?: string; // The site compact logo URL. - typeoflogin: TypeOfLogin; // The type of login. 1 for app, 2 for browser, 3 for embedded. - launchurl?: string; // SSO login launch URL. - mobilecssurl?: string; // Mobile custom CSS theme. - // eslint-disable-next-line @typescript-eslint/naming-convention - tool_mobile_disabledfeatures?: string; // Disabled features in the app. - identityproviders?: CoreSiteIdentityProvider[]; // Identity providers. - country?: string; // Default site country. - agedigitalconsentverification?: boolean; // Whether age digital consent verification is enabled. - supportname?: string; // Site support contact name (only if age verification is enabled). - supportemail?: string; // Site support contact email (only if age verification is enabled). - supportavailability?: CoreSiteConfigSupportAvailability; - supportpage?: string; // Site support contact url. - autolang?: number; // Whether to detect default language from browser setting. - lang?: string; // Default language for the site. - langmenu?: number; // Whether the language menu should be displayed. - langlist?: string; // Languages on language menu. - locale?: string; // Sitewide locale. - // eslint-disable-next-line @typescript-eslint/naming-convention - tool_mobile_minimumversion?: string; // Minimum required version to access. - // eslint-disable-next-line @typescript-eslint/naming-convention - tool_mobile_iosappid?: string; // IOS app's unique identifier. - // eslint-disable-next-line @typescript-eslint/naming-convention - tool_mobile_androidappid?: string; // Android app's unique identifier. - // eslint-disable-next-line @typescript-eslint/naming-convention - tool_mobile_setuplink?: string; // App download page. - tool_mobile_qrcodetype?: CoreSiteQRCodeType; // eslint-disable-line @typescript-eslint/naming-convention - warnings?: CoreWSExternalWarning[]; -}; - -/** - * Identity provider. - */ -export type CoreSiteIdentityProvider = { - name: string; // The identity provider name. - iconurl: string; // The icon URL for the provider. - url: string; // The URL of the provider. -}; - /** * Result of WS tool_mobile_get_autologin_key. */ @@ -2853,12 +2569,3 @@ enum OngoingRequestType { STANDARD = 0, UPDATE_IN_BACKGROUND = 1, } - -/** - * The type of login. 1 for app, 2 for browser, 3 for embedded. - */ -export enum TypeOfLogin { - APP = 1, - BROWSER = 2, // SSO in browser window is required. - EMBEDDED = 3, // SSO in embedded browser is required. -} diff --git a/src/core/classes/sites/unauthenticated-site.ts b/src/core/classes/sites/unauthenticated-site.ts new file mode 100644 index 000000000..0cc013524 --- /dev/null +++ b/src/core/classes/sites/unauthenticated-site.ts @@ -0,0 +1,409 @@ +// (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 { CoreConstants } from '@/core/constants'; +import { CoreError } from '@classes/errors/error'; +import { CoreLoginHelper } from '@features/login/services/login-helper'; +import { CoreSitesReadingStrategy } from '@services/sites'; +import { CoreTextUtils } from '@services/utils/text'; +import { CoreUrlUtils } from '@services/utils/url'; +import { CoreWS, CoreWSAjaxPreSets, CoreWSExternalWarning } from '@services/ws'; +import { CorePath } from '@singletons/path'; + +/** + * Class that represents a Moodle site where the user still hasn't authenticated. + */ +export class CoreUnauthenticatedSite { + + siteUrl: string; + + /** + * Create a site. + * + * @param siteUrl Site URL. + */ + constructor(siteUrl: string) { + this.siteUrl = CoreUrlUtils.removeUrlParams(siteUrl); // Make sure the URL doesn't have params. + } + + /** + * Get site URL. + * + * @returns Site URL. + */ + getURL(): string { + return this.siteUrl; + } + + /** + * Set site URL. + * + * @param url Site URL. + */ + setURL(url: string): void { + this.siteUrl = url; + } + + /** + * Get site info. + * + * @returns Site info. + */ + getInfo(): CoreSiteInfo | undefined { + // Cannot retrieve info for unauthenticated sites. + return undefined; + } + + /** + * Get site name. + * + * @returns Site name. + */ + async getSiteName(): Promise { + if (this.isDemoModeSite()) { + return CoreConstants.CONFIG.appname; + } + + const siteName = this.getInfo()?.sitename; + if (siteName) { + return siteName; + } + + // Fallback. + const isSingleFixedSite = await CoreLoginHelper.isSingleFixedSite(); + + if (isSingleFixedSite) { + const sites = await CoreLoginHelper.getAvailableSites(); + + return sites[0].name; + } + + return ''; + } + + /** + * Check whether the app should use the local logo instead of the remote one. + * + * @returns Whether local logo is forced. + */ + forcesLocalLogo(): boolean { + return CoreConstants.CONFIG.forceLoginLogo || this.isDemoModeSite(); + } + + /** + * Get logo URL from a site public config. + * + * @param config Site public config. + * @returns Logo URL. + */ + getLogoUrl(config?: CoreSitePublicConfigResponse): string | undefined { + if (!config || this.forcesLocalLogo()) { + return 'assets/img/login_logo.png'; + } + + return config.logourl || config.compactlogourl || 'assets/img/login_logo.png'; + } + + /** + * Returns a url to link an specific page on the site. + * + * @param path Path of the url to go to. + * @param params Object with the params to add. + * @param anchor Anchor text if needed. + * @returns URL with params. + */ + createSiteUrl(path: string, params?: Record, anchor?: string): string { + return CoreUrlUtils.addParamsToUrl(CorePath.concatenatePaths(this.siteUrl, path), params, anchor); + } + + /** + * Check if a URL belongs to this site. + * + * @param url URL to check. + * @returns Whether the URL belongs to this site. + */ + containsUrl(url?: string): boolean { + if (!url) { + return false; + } + + const siteUrl = CoreTextUtils.addEndingSlash(CoreUrlUtils.removeProtocolAndWWW(this.siteUrl)); + url = CoreTextUtils.addEndingSlash(CoreUrlUtils.removeProtocolAndWWW(url)); + + return url.indexOf(siteUrl) == 0; + } + + /** + * Get the public config of this site. + * + * @param options Options. + * @returns Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax. + */ + async getPublicConfig(options: { readingStrategy?: CoreSitesReadingStrategy } = {}): Promise { + if (options.readingStrategy === CoreSitesReadingStrategy.ONLY_CACHE) { + throw new CoreError('Cache not available to read public config'); + } + + return this.requestPublicConfig(); + } + + /** + * Perform a request to the server to get the public config of this site. + * + * @returns Promise resolved with public config. + */ + protected async requestPublicConfig(): Promise { + const preSets: CoreWSAjaxPreSets = { + siteUrl: this.siteUrl, + }; + + let config: CoreSitePublicConfigResponse; + + try { + config = await CoreWS.callAjax('tool_mobile_get_public_config', {}, preSets); + } catch (error) { + if (!error || error.errorcode !== 'codingerror' || (this.getInfo() && !this.isAjaxGetSupported())) { + throw error; + } + + // This error probably means that there is a redirect in the site. Try to use a GET request. + preSets.noLogin = true; + preSets.useGet = true; + + try { + config = await CoreWS.callAjax('tool_mobile_get_public_config', {}, preSets); + } catch (error2) { + if (this.isAjaxGetSupported()) { + // GET is supported, return the second error. + throw error2; + } else { + // GET not supported or we don't know if it's supported. Return first error. + throw error; + } + } + } + + // Use the wwwroot returned by the server. + if (config.httpswwwroot) { + this.siteUrl = CoreUrlUtils.removeUrlParams(config.httpswwwroot); // Make sure the URL doesn't have params. + } + + return config; + } + + /** + * Check if GET method is supported for AJAX calls. + * + * @returns Whether it's supported. + * @since Moodle 3.8 + */ + protected isAjaxGetSupported(): boolean { + // We don't know if it's supported, assume it's not. + return false; + } + + /** + * Check if a URL to a file belongs to the site and uses the pluginfileurl or tokenpluginfileurl endpoints. + * + * @param url File URL to check. + * @returns Whether it's a site file URL. + */ + isSitePluginFileUrl(url: string): boolean { + const isPluginFileUrl = CoreUrlUtils.isPluginFileUrl(url) || CoreUrlUtils.isTokenPluginFileUrl(url); + if (!isPluginFileUrl) { + return false; + } + + return this.containsUrl(url); + } + + /** + * Check if a URL to a file belongs to the site and is a theme image file. + * + * @param url File URL to check. + * @returns Whether it's a site theme image URL. + */ + isSiteThemeImageUrl(url: string): boolean { + if (!CoreUrlUtils.isThemeImageUrl(url)) { + return false; + } + + return this.containsUrl(url); + } + + /** + * Check if the site is a demo mode site. + * + * @returns Whether the site is a demo mode site. + */ + isDemoModeSite(): boolean { + const demoSiteData = CoreLoginHelper.getDemoModeSiteInfo(); + + return this.containsUrl(demoSiteData?.url); + } + + /** + * Check whether informative links should be displayed for this site. + * + * @returns Whether informative links should be displayed. + */ + shouldDisplayInformativeLinks(): boolean { + return !CoreConstants.CONFIG.hideInformativeLinks && !this.isDemoModeSite(); + } + +} + +/** + * Result of WS core_webservice_get_site_info. + */ +export type CoreSiteInfoResponse = { + sitename: string; // Site name. + username: string; // Username. + firstname: string; // First name. + lastname: string; // Last name. + fullname: string; // User full name. + lang: string; // Current language. + userid: number; // User id. + siteurl: string; // Site url. + userpictureurl: string; // The user profile picture. + functions: { + name: string; // Function name. + version: string; // The version number of the component to which the function belongs. + }[]; + downloadfiles?: number; // 1 if users are allowed to download files, 0 if not. + uploadfiles?: number; // 1 if users are allowed to upload files, 0 if not. + release?: string; // Moodle release number. + version?: string; // Moodle version number. + mobilecssurl?: string; // Mobile custom CSS theme. + advancedfeatures?: { // Advanced features availability. + name: string; // Feature name. + value: number; // Feature value. Usually 1 means enabled. + }[]; + usercanmanageownfiles?: boolean; // True if the user can manage his own files. + userquota?: number; // User quota (bytes). 0 means user can ignore the quota. + usermaxuploadfilesize?: number; // User max upload file size (bytes). -1 means the user can ignore the upload file size. + userhomepage?: CoreSiteInfoUserHomepage; // The default home page for the user. + userprivateaccesskey?: string; // Private user access key for fetching files. + siteid?: number; // Site course ID. + sitecalendartype?: string; // Calendar type set in the site. + usercalendartype?: string; // Calendar typed used by the user. + userissiteadmin?: boolean; // Whether the user is a site admin or not. + theme?: string; // Current theme for the user. +}; + +/** + * Site info, including some calculated data. + */ +export type CoreSiteInfo = CoreSiteInfoResponse & { + functionsByName?: { + [name: string]: { + name: string; // Function name. + version: string; // The version number of the component to which the function belongs. + }; + }; +}; + +/** + * Enum constants that define default user home page. + */ +export enum CoreSiteInfoUserHomepage { + HOMEPAGE_SITE = 0, // Site home. + HOMEPAGE_MY = 1, // Dashboard. + HOMEPAGE_MYCOURSES = 3, // My courses. +} + +/** + * Possible values for 'supportavailability' config. + */ +export const enum CoreSiteConfigSupportAvailability { + Disabled = 0, + Authenticated = 1, + Anyone = 2, +} + +/** + * Result of WS tool_mobile_get_public_config. + */ +export type CoreSitePublicConfigResponse = { + wwwroot: string; // Site URL. + httpswwwroot: string; // Site https URL (if httpslogin is enabled). + sitename: string; // Site name. + guestlogin: number; // Whether guest login is enabled. + rememberusername: number; // Values: 0 for No, 1 for Yes, 2 for optional. + authloginviaemail: number; // Whether log in via email is enabled. + registerauth: string; // Authentication method for user registration. + forgottenpasswordurl: string; // Forgotten password URL. + authinstructions: string; // Authentication instructions. + authnoneenabled: number; // Whether auth none is enabled. + enablewebservices: number; // Whether Web Services are enabled. + enablemobilewebservice: number; // Whether the Mobile service is enabled. + maintenanceenabled: number; // Whether site maintenance is enabled. + maintenancemessage: string; // Maintenance message. + logourl?: string; // The site logo URL. + compactlogourl?: string; // The site compact logo URL. + typeoflogin: TypeOfLogin; // The type of login. 1 for app, 2 for browser, 3 for embedded. + launchurl?: string; // SSO login launch URL. + mobilecssurl?: string; // Mobile custom CSS theme. + // eslint-disable-next-line @typescript-eslint/naming-convention + tool_mobile_disabledfeatures?: string; // Disabled features in the app. + identityproviders?: CoreSiteIdentityProvider[]; // Identity providers. + country?: string; // Default site country. + agedigitalconsentverification?: boolean; // Whether age digital consent verification is enabled. + supportname?: string; // Site support contact name (only if age verification is enabled). + supportemail?: string; // Site support contact email (only if age verification is enabled). + supportavailability?: CoreSiteConfigSupportAvailability; + supportpage?: string; // Site support contact url. + autolang?: number; // Whether to detect default language from browser setting. + lang?: string; // Default language for the site. + langmenu?: number; // Whether the language menu should be displayed. + langlist?: string; // Languages on language menu. + locale?: string; // Sitewide locale. + // eslint-disable-next-line @typescript-eslint/naming-convention + tool_mobile_minimumversion?: string; // Minimum required version to access. + // eslint-disable-next-line @typescript-eslint/naming-convention + tool_mobile_iosappid?: string; // IOS app's unique identifier. + // eslint-disable-next-line @typescript-eslint/naming-convention + tool_mobile_androidappid?: string; // Android app's unique identifier. + // eslint-disable-next-line @typescript-eslint/naming-convention + tool_mobile_setuplink?: string; // App download page. + tool_mobile_qrcodetype?: CoreSiteQRCodeType; // eslint-disable-line @typescript-eslint/naming-convention + warnings?: CoreWSExternalWarning[]; +}; + +/** + * QR Code type enumeration. + */ +export enum CoreSiteQRCodeType { + QR_CODE_DISABLED = 0, // QR code disabled value + QR_CODE_URL = 1, // QR code type URL value + QR_CODE_LOGIN = 2, // QR code type login value +} + +/** + * Identity provider. + */ +export type CoreSiteIdentityProvider = { + name: string; // The identity provider name. + iconurl: string; // The icon URL for the provider. + url: string; // The URL of the provider. +}; + +/** + * The type of login. 1 for app, 2 for browser, 3 for embedded. + */ +export enum TypeOfLogin { + APP = 1, + BROWSER = 2, // SSO in browser window is required. + EMBEDDED = 3, // SSO in embedded browser is required. +} diff --git a/src/core/components/sites-list/sites-list.ts b/src/core/components/sites-list/sites-list.ts index b92dd59ca..812cade37 100644 --- a/src/core/components/sites-list/sites-list.ts +++ b/src/core/components/sites-list/sites-list.ts @@ -14,8 +14,9 @@ import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter } from '@angular/core'; -import { CoreSiteBasicInfo, CoreSites } from '@services/sites'; +import { CoreSiteBasicInfo } from '@services/sites'; import { CoreAccountsList } from '@features/login/services/login-helper'; +import { CoreSitesFactory } from '@services/sites-factory'; /** * Component to display a list of sites (accounts). @@ -84,7 +85,7 @@ export class CoreSitesListComponent { * @returns Whether to display URL. */ displaySiteUrl(site: CoreSiteBasicInfo): boolean { - return CoreSites.shouldDisplayInformativeLinks(site.siteUrl); + return CoreSitesFactory.makeSite(site.id, site.siteUrl, '', { info: site.info }).shouldDisplayInformativeLinks(); } } diff --git a/src/core/components/user-avatar/user-avatar.ts b/src/core/components/user-avatar/user-avatar.ts index 6c3e137e5..6b8778014 100644 --- a/src/core/components/user-avatar/user-avatar.ts +++ b/src/core/components/user-avatar/user-avatar.ts @@ -22,7 +22,7 @@ import { CoreNavigator } from '@services/navigator'; import { CoreNetwork } from '@services/network'; import { CoreUserHelper } from '@features/user/services/user-helper'; import { CoreUrlUtils } from '@services/utils/url'; -import { CoreSiteInfo } from '@classes/site'; +import { CoreSiteInfo } from '@classes/sites/unauthenticated-site'; /** * Component to display a "user avatar". diff --git a/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.ts b/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.ts index 1bcbe6325..193b1996b 100644 --- a/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.ts +++ b/src/core/features/contentlinks/components/choose-site-modal/choose-site-modal.ts @@ -20,6 +20,7 @@ import { CoreContentLinksAction } from '../../services/contentlinks-delegate'; import { CoreContentLinksHelper } from '../../services/contentlinks-helper'; import { CoreError } from '@classes/errors/error'; import { CoreNavigator } from '@services/navigator'; +import { CoreSitesFactory } from '@services/sites-factory'; /** * Page to display the list of sites to choose one to perform a content link action. @@ -73,7 +74,7 @@ export class CoreContentLinksChooseSiteModalComponent implements OnInit { this.sites = await CoreSites.getSites(siteIds); // All sites have the same URL, use the first one. - this.displaySiteUrl = CoreSites.shouldDisplayInformativeLinks(this.sites[0].siteUrl); + this.displaySiteUrl = CoreSitesFactory.makeUnauthenticatedSite(this.sites[0].siteUrl).shouldDisplayInformativeLinks(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'core.contentlinks.errornosites', true); this.closeModal(); diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index 8500ecd47..8c908987e 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -101,7 +101,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { } this.displayOptions = Object.assign({ - displayOpenInBrowser: CoreSites.shouldDisplayInformativeLinks(), + displayOpenInBrowser: !!CoreSites.getCurrentSite()?.shouldDisplayInformativeLinks(), displayDescription: true, displayRefresh: true, displayPrefetch: true, diff --git a/src/core/features/course/pages/course-summary/course-summary.page.ts b/src/core/features/course/pages/course-summary/course-summary.page.ts index 73512e822..c1943250d 100644 --- a/src/core/features/course/pages/course-summary/course-summary.page.ts +++ b/src/core/features/course/pages/course-summary/course-summary.page.ts @@ -138,7 +138,7 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy { const currentSiteUrl = CoreSites.getRequiredCurrentSite().getURL(); this.enrolUrl = CorePath.concatenatePaths(currentSiteUrl, 'enrol/index.php?id=' + this.courseId); this.courseUrl = CorePath.concatenatePaths(currentSiteUrl, 'course/view.php?id=' + this.courseId); - this.displayOpenInBrowser = CoreSites.shouldDisplayInformativeLinks(); + this.displayOpenInBrowser = CoreSites.getRequiredCurrentSite().shouldDisplayInformativeLinks(); await this.getCourse(); } diff --git a/src/core/features/course/pages/module-preview/module-preview.ts b/src/core/features/course/pages/module-preview/module-preview.ts index c5192b9d6..bbaf90c66 100644 --- a/src/core/features/course/pages/module-preview/module-preview.ts +++ b/src/core/features/course/pages/module-preview/module-preview.ts @@ -63,7 +63,7 @@ export class CoreCourseModulePreviewPage implements OnInit { return; } - this.displayOpenInBrowser = CoreSites.shouldDisplayInformativeLinks(); + this.displayOpenInBrowser = !!CoreSites.getCurrentSite()?.shouldDisplayInformativeLinks(); this.debouncedUpdateModule = CoreUtils.debounce(() => { this.doRefresh(); }, 10000); diff --git a/src/core/features/courses/services/handlers/my-courses-mainmenu.ts b/src/core/features/courses/services/handlers/my-courses-mainmenu.ts index b962bfb25..110d8f1e7 100644 --- a/src/core/features/courses/services/handlers/my-courses-mainmenu.ts +++ b/src/core/features/courses/services/handlers/my-courses-mainmenu.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreSiteInfoUserHomepage } from '@classes/sites/site'; +import { CoreSiteInfoUserHomepage } from '@classes/sites/unauthenticated-site'; import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreSiteHomeHomeHandler } from '@features/sitehome/services/handlers/sitehome-home'; import { CoreSites } from '@services/sites'; diff --git a/src/core/features/login/components/login-methods/login-methods.ts b/src/core/features/login/components/login-methods/login-methods.ts index 1e3b4d400..f3366b296 100644 --- a/src/core/features/login/components/login-methods/login-methods.ts +++ b/src/core/features/login/components/login-methods/login-methods.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, Input, OnInit } from '@angular/core'; -import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/site'; +import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site'; import { CoreLoginHelper, CoreLoginMethod } from '@features/login/services/login-helper'; import { CoreRedirectPayload } from '@services/navigator'; import { CoreSites } from '@services/sites'; diff --git a/src/core/features/login/pages/credentials/credentials.html b/src/core/features/login/pages/credentials/credentials.html index 300cb545f..f5f3b5db7 100644 --- a/src/core/features/login/pages/credentials/credentials.html +++ b/src/core/features/login/pages/credentials/credentials.html @@ -20,19 +20,17 @@ - +