diff --git a/moodle.config.json b/moodle.config.json index c93ad42ceb6..9a0100bdfeb 100644 --- a/moodle.config.json +++ b/moodle.config.json @@ -98,6 +98,7 @@ "enableonboarding": true, "forceColorScheme": "", "forceLoginLogo": false, + "showTopLogo": "hidden", "ioswebviewscheme": "moodleappfs", "appstores": { "android": "com.moodle.moodlemobile", diff --git a/src/core/classes/sites/unauthenticated-site.ts b/src/core/classes/sites/unauthenticated-site.ts index fc958b0e34e..643c25af1c5 100644 --- a/src/core/classes/sites/unauthenticated-site.ts +++ b/src/core/classes/sites/unauthenticated-site.ts @@ -163,7 +163,7 @@ export class CoreUnauthenticatedSite { } /** - * Check whether the app should use the local logo instead of the remote one. + * Check whether the app should use the local logo instead or the remote one. * * @returns Whether local logo is forced. */ @@ -180,10 +180,34 @@ export class CoreUnauthenticatedSite { getLogoUrl(config?: CoreSitePublicConfigResponse): string | undefined { config = config ?? this.publicConfig; if (!config || this.forcesLocalLogo()) { - return 'assets/img/login_logo.png'; + return; } - return config.logourl || config.compactlogourl || 'assets/img/login_logo.png'; + return config.logourl || config.compactlogourl || undefined; + } + + /** + * Check show top logo mode. + * + * @returns The top logo mode. + */ + getShowTopLogo(): 'online' | 'offline' | 'hidden' { + return this.isDemoModeSite() ? 'hidden' : CoreConstants.CONFIG.showTopLogo; + } + + /** + * Get logo URL from a site public config. + * + * @param config Site public config. + * @returns Logo URL. + */ + getTopLogoUrl(config?: CoreSitePublicConfigResponse): string | undefined { + config = config ?? this.publicConfig; + if (!config || this.getShowTopLogo() !== 'online') { + return; + } + + return config.logourl || config.compactlogourl || undefined; } /** diff --git a/src/core/components/site-logo/site-logo.html b/src/core/components/site-logo/site-logo.html new file mode 100644 index 00000000000..c4fff46bead --- /dev/null +++ b/src/core/components/site-logo/site-logo.html @@ -0,0 +1,14 @@ +
+ + +
+ +

+ +

+ +

+ +

diff --git a/src/core/components/site-logo/site-logo.scss b/src/core/components/site-logo/site-logo.scss new file mode 100644 index 00000000000..ad385ed4bde --- /dev/null +++ b/src/core/components/site-logo/site-logo.scss @@ -0,0 +1,22 @@ +:host { + display: block; +} + +.core-logo-container { + margin-bottom: var(--core-site-logo-margin-bottom, 0px); +} + +img.core-logo { + max-height: var(--core-site-logo-max-height); + max-width: var(--core-site-logo-max-width, 100%); + + + width: var(--core-site-logo-width, auto); + margin: var(--core-site-logo-margin, 0px); +} + +.core-logo-sitename { + display: var(--core-site-logo-sitename-display, block); + font: var(--core-site-logo-sitename-font); + margin-bottom: var(--core-site-logo-sitename-margin-bottom, 0px); +} diff --git a/src/core/components/site-logo/site-logo.ts b/src/core/components/site-logo/site-logo.ts new file mode 100644 index 00000000000..468bdd16667 --- /dev/null +++ b/src/core/components/site-logo/site-logo.ts @@ -0,0 +1,115 @@ +// (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, OnDestroy, Input } from '@angular/core'; +import { CoreSharedModule } from '@/core/shared.module'; +import { CoreSites } from '@services/sites'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; +import { CoreSite } from '@classes/sites/site'; +import { toBoolean } from '@/core/transforms/boolean'; +import { CorePromiseUtils } from '@singletons/promise-utils'; + +/** + * Component to render the site logo. + */ +@Component({ + selector: 'core-site-logo', + templateUrl: 'site-logo.html', + styleUrl: 'site-logo.scss', + standalone: true, + imports: [CoreSharedModule], + +}) +export class CoreSiteLogoComponent implements OnInit, OnDestroy { + + @Input({ transform: toBoolean }) hideOnError = false; + @Input() siteNameMode: CoreSiteLogoSiteNameMode = CoreSiteLogoSiteNameMode.NOTAG; + @Input({ transform: toBoolean }) showLogo = true; + @Input() site?: CoreSite; + @Input() logoType: 'top' | 'login' = 'login'; + + siteName?: string; + siteId?: string; + siteLogo?: string; + logoLoaded = false; + fallbackLogo = ''; + showSiteName = true; + + protected updateSiteObserver: CoreEventObserver; + + constructor() { + this.siteId = this.site?.getId() ?? CoreSites.getCurrentSiteId(); + + this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async () => { + await this.loadInfo(); + }, this.siteId); + } + + /** + * @inheritdoc + */ + async ngOnInit(): Promise { + this.fallbackLogo = this.logoType === 'top' ? 'assets/img/top_logo.png' : 'assets/img/login_logo.png'; + this.showSiteName = this.logoType !== 'top'; + + await this.loadInfo(); + } + + /** + * Function to handle the image error. + */ + imageError(): void { + if (this.hideOnError) { + this.showLogo = false; + } + this.siteLogo = undefined; + } + + /** + * Load the site name and logo. + */ + protected async loadInfo(): Promise { + const site = this.site ?? CoreSites.getRequiredCurrentSite(); + this.siteName = await site.getSiteName() || ''; + + this.showSiteName = this.logoType !== 'top' || site.getShowTopLogo() === 'hidden'; + + if (this.logoType === 'top' && site.getShowTopLogo() === 'hidden') { + this.showLogo = false; + } else { + // Get the public config to avoid race conditions when retrieving the logo. + const siteConfig = await CorePromiseUtils.ignoreErrors(site.getPublicConfig()); + + this.siteLogo = this.logoType === 'top' + ? site.getTopLogoUrl(siteConfig) + : site.getLogoUrl(siteConfig); + } + + this.logoLoaded = true; + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.updateSiteObserver.off(); + } + +} + +export const enum CoreSiteLogoSiteNameMode { + HEADING2 = 'h2', + PARAGRAPH = 'p', + NOTAG = '', +} diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 7257beb714a..842502fb74c 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -122,6 +122,10 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec this.element.addEventListener('click', (event) => this.elementClicked(event)); this.siteId = this.siteId || CoreSites.getCurrentSiteId(); + + if (!this.siteId) { + this.filter = false; + } } /** diff --git a/src/core/features/courses/courses-my-lazy.module.ts b/src/core/features/courses/courses-my-lazy.module.ts index 375c7ae8e90..ba5789efb34 100644 --- a/src/core/features/courses/courses-my-lazy.module.ts +++ b/src/core/features/courses/courses-my-lazy.module.ts @@ -20,6 +20,7 @@ import { CoreBlockComponentsModule } from '@features/block/components/components import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; import { CoreCoursesMyPage } from '@features/courses/pages/my/my'; +import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo'; const routes: Routes = [ { @@ -34,6 +35,7 @@ const routes: Routes = [ CoreSharedModule, CoreBlockComponentsModule, CoreMainMenuComponentsModule, + CoreSiteLogoComponent, ], declarations: [ CoreCoursesMyPage, diff --git a/src/core/features/courses/pages/my/my.html b/src/core/features/courses/pages/my/my.html index 933afcc043c..23e27b2e371 100644 --- a/src/core/features/courses/pages/my/my.html +++ b/src/core/features/courses/pages/my/my.html @@ -5,8 +5,7 @@

- - +

diff --git a/src/core/features/courses/pages/my/my.ts b/src/core/features/courses/pages/my/my.ts index f7a9379aef7..f0e1d978d00 100644 --- a/src/core/features/courses/pages/my/my.ts +++ b/src/core/features/courses/pages/my/my.ts @@ -48,7 +48,6 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { @ViewChild(CoreBlockComponent) block!: CoreBlockComponent; - siteName = ''; downloadCoursesEnabled = false; userId: number; loadedBlock?: Partial; @@ -66,8 +65,6 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { // Refresh the enabled flags if site is updated. this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async () => { this.downloadCoursesEnabled = !CoreCourses.isDownloadCoursesDisabledInSite(); - await this.loadSiteName(); - }, CoreSites.getCurrentSiteId()); this.userId = CoreSites.getCurrentSiteUserId(); @@ -98,8 +95,6 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { CoreSites.loginNavigationFinished(); - await this.loadSiteName(); - this.loadContent(true); } @@ -156,14 +151,6 @@ export class CoreCoursesMyPage implements OnInit, OnDestroy, AsyncDirective { this.logView(); } - /** - * Load the site name. - */ - protected async loadSiteName(): Promise { - const site = CoreSites.getRequiredCurrentSite(); - this.siteName = await site.getSiteName() || ''; - } - /** * Load fallback blocks. */ diff --git a/src/core/features/login/login-credentials-lazy.module.ts b/src/core/features/login/login-credentials-lazy.module.ts index 0827e8b9863..0ecf235e4eb 100644 --- a/src/core/features/login/login-credentials-lazy.module.ts +++ b/src/core/features/login/login-credentials-lazy.module.ts @@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreLoginComponentsModule } from '@features/login/components/components.module'; import { CoreLoginCredentialsPage } from '@features/login/pages/credentials/credentials'; +import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo'; const routes: Routes = [ { @@ -31,6 +32,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, CoreLoginComponentsModule, + CoreSiteLogoComponent, ], declarations: [ CoreLoginCredentialsPage, diff --git a/src/core/features/login/login-reconnect-lazy.module.ts b/src/core/features/login/login-reconnect-lazy.module.ts index 22084638fb7..e0696584ce5 100644 --- a/src/core/features/login/login-reconnect-lazy.module.ts +++ b/src/core/features/login/login-reconnect-lazy.module.ts @@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; import { CoreLoginComponentsModule } from '@features/login/components/components.module'; import { CoreLoginReconnectPage } from '@features/login/pages/reconnect/reconnect'; +import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo'; const routes: Routes = [ { @@ -31,6 +32,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, CoreLoginComponentsModule, + CoreSiteLogoComponent, ], declarations: [ CoreLoginReconnectPage, diff --git a/src/core/features/login/login.scss b/src/core/features/login/login.scss index e02eaea05f1..27a95309d55 100644 --- a/src/core/features/login/login.scss +++ b/src/core/features/login/login.scss @@ -29,20 +29,14 @@ margin-bottom: 32px; .core-login-site { - .core-login-site-logo { - width: 90%; - max-width: 300px; - margin: 0px auto; - - img { - max-width: 100%; - max-height: 104px; - } - } - - .core-sitename { - font-size: 1.2rem; - margin-bottom: 8px; + core-site-logo { + --core-site-logo-max-height: 104px; + --core-site-logo-sitename-margin-bottom: 8px; + --core-site-logo-sitename-font: var(--mdl-typography-subtitle-font-lg); + + --core-site-logo-max-width: 300px; + --core-site-logo-width: 90%; + --core-site-logo-margin: 0 auto; } .core-siteurl { @@ -89,9 +83,12 @@ } @if ($core-fixed-url) { - .core-sitename, .core-siteurl { + .core-siteurl { display: none; } + core-site-logo { + --core-site-logo-sitename-display: none; + } } @if ($core-login-button-outline) { diff --git a/src/core/features/login/pages/credentials/credentials.html b/src/core/features/login/pages/credentials/credentials.html index c6d71047bb9..a54176236fe 100644 --- a/src/core/features/login/pages/credentials/credentials.html +++ b/src/core/features/login/pages/credentials/credentials.html @@ -23,16 +23,7 @@

{{ 'core.login.login' | translate }}