mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
85123: WIP: Support for theme-specific head tags
This commit is contained in:
@@ -31,12 +31,12 @@ import { AuthService } from './core/auth/auth.service';
|
||||
import { CSSVariableService } from './shared/sass-helper/sass-helper.service';
|
||||
import { MenuService } from './shared/menu/menu.service';
|
||||
import { HostWindowService } from './shared/host-window.service';
|
||||
import { ThemeConfig } from '../config/theme.model';
|
||||
import {HeadTagConfig, ThemeConfig} from '../config/theme.model';
|
||||
import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
||||
import { environment } from '../environments/environment';
|
||||
import { models } from './core/core.module';
|
||||
import { LocaleService } from './core/locale/locale.service';
|
||||
import { hasValue, isNotEmpty } from './shared/empty.util';
|
||||
import { hasValue, isEmpty, isNotEmpty } from './shared/empty.util';
|
||||
import { KlaroService } from './shared/cookies/klaro.service';
|
||||
import { GoogleAnalyticsService } from './statistics/google-analytics.service';
|
||||
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
||||
@@ -115,11 +115,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
this.isThemeCSSLoading$.next(true);
|
||||
}
|
||||
if (hasValue(themeName)) {
|
||||
this.setThemeCss(themeName);
|
||||
this.loadGlobalThemeConfig(themeName);
|
||||
} else if (hasValue(DEFAULT_THEME_CONFIG)) {
|
||||
this.setThemeCss(DEFAULT_THEME_CONFIG.name);
|
||||
this.loadGlobalThemeConfig(DEFAULT_THEME_CONFIG.name);
|
||||
} else {
|
||||
this.setThemeCss(BASE_THEME_NAME);
|
||||
this.loadGlobalThemeConfig(BASE_THEME_NAME);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -233,6 +233,11 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
private loadGlobalThemeConfig(themeName: string): void {
|
||||
this.setThemeCss(themeName);
|
||||
this.setHeadTags(themeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the theme css file in <head>
|
||||
*
|
||||
@@ -243,7 +248,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
const head = this.document.getElementsByTagName('head')[0];
|
||||
// Array.from to ensure we end up with an array, not an HTMLCollection, which would be
|
||||
// automatically updated if we add nodes later
|
||||
const currentThemeLinks = Array.from(this.document.getElementsByClassName('theme-css'));
|
||||
const currentThemeLinks = Array.from(head.getElementsByClassName('theme-css'));
|
||||
const link = this.document.createElement('link');
|
||||
link.setAttribute('rel', 'stylesheet');
|
||||
link.setAttribute('type', 'text/css');
|
||||
@@ -265,6 +270,51 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
private setHeadTags(themeName: string): void {
|
||||
const head = this.document.getElementsByTagName('head')[0];
|
||||
|
||||
// clear head tags
|
||||
const currentHeadTags = Array.from(head.getElementsByClassName('theme-head-tag'));
|
||||
if (isNotEmpty(currentHeadTags)) {
|
||||
currentHeadTags.forEach((currentHeadTag: any) => currentHeadTag.remove());
|
||||
}
|
||||
|
||||
// create new head tags (not yet added to DOM)
|
||||
const headTagFragment = document.createDocumentFragment();
|
||||
this.createHeadTags(themeName)
|
||||
.forEach(newHeadTag => headTagFragment.appendChild(newHeadTag));
|
||||
|
||||
// add new head tags to DOM
|
||||
head.appendChild(headTagFragment);
|
||||
}
|
||||
|
||||
private createHeadTags(themeName: string): HTMLElement[] {
|
||||
const themeConfig = this.themeService.getThemeConfigFor(themeName);
|
||||
const headTagConfigs = themeConfig?.headTags;
|
||||
|
||||
// if the current theme does not have head tags, we inherit the head tags of the parent
|
||||
if (isEmpty(headTagConfigs)) {
|
||||
const parentThemeName = themeConfig.extends;
|
||||
return isNotEmpty(parentThemeName) ? this.createHeadTags(parentThemeName) : [];
|
||||
}
|
||||
|
||||
return headTagConfigs.map(this.createHeadTag.bind(this));
|
||||
}
|
||||
|
||||
private createHeadTag(themeHeadTag: HeadTagConfig): HTMLElement {
|
||||
const tag = this.document.createElement(themeHeadTag.tagName);
|
||||
|
||||
if (isNotEmpty(themeHeadTag.attributes)) {
|
||||
Object.entries(themeHeadTag.attributes)
|
||||
.forEach(([key, value]) => tag.setAttribute(key, value));
|
||||
}
|
||||
|
||||
// 'class' attribute should always be 'theme-head-tag' for removal
|
||||
tag.setAttribute('class', 'theme-head-tag');
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
private trackIdleModal() {
|
||||
const isIdle$ = this.authService.isUserIdle();
|
||||
const isAuthenticated$ = this.authService.isAuthenticated();
|
||||
|
@@ -12,6 +12,28 @@ export interface NamedThemeConfig extends Config {
|
||||
* its ancestor theme(s) will be checked recursively before falling back to the default theme.
|
||||
*/
|
||||
extends?: string;
|
||||
|
||||
/**
|
||||
* A list of HTML tags that should be added to the HEAD section of the document, whenever this theme is active.
|
||||
*/
|
||||
headTags?: HeadTagConfig[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that represents a single theme-specific HTML tag in the HEAD section of the page.
|
||||
*/
|
||||
export interface HeadTagConfig extends Config {
|
||||
/**
|
||||
* The name of the HTML tag
|
||||
*/
|
||||
tagName: string;
|
||||
|
||||
/**
|
||||
* The attributes on the HTML tag
|
||||
*/
|
||||
attributes?: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RegExThemeConfig extends NamedThemeConfig {
|
||||
|
@@ -292,7 +292,39 @@ export const environment: GlobalConfig = {
|
||||
|
||||
{
|
||||
// The default dspace theme
|
||||
name: 'dspace'
|
||||
name: 'dspace',
|
||||
headTags: [
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
'rel': 'icon',
|
||||
'href': 'assets/dspace/images/favicons/favicon.ico',
|
||||
'sizes': 'any',
|
||||
}
|
||||
},
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
'rel': 'icon',
|
||||
'href': 'assets/dspace/images/favicons/favicon.svg',
|
||||
'type': 'image/svg+xml',
|
||||
}
|
||||
},
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
'rel': 'apple-touch-icon',
|
||||
'href': 'assets/dspace/images/favicons/apple-touch-icon.png',
|
||||
}
|
||||
},
|
||||
{
|
||||
tagName: 'link',
|
||||
attributes: {
|
||||
'rel': 'manifest',
|
||||
'href': 'assets/dspace/images/favicons/manifest.webmanifest',
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
// Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with "image" or "video").
|
||||
|
Reference in New Issue
Block a user