|
| 1 | +import Log from 'sap/base/Log'; |
| 2 | +import type AppLifeCycle from 'sap/ushell/services/AppLifeCycle'; |
| 3 | +import IconPool from 'sap/ui/core/IconPool'; |
| 4 | +import ResourceBundle from 'sap/base/i18n/ResourceBundle'; |
| 5 | +import UriParameters from 'sap/base/util/UriParameters'; |
| 6 | + |
| 7 | +/** |
| 8 | + * SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap |
| 9 | + */ |
| 10 | +const UI5_LIBS = [ |
| 11 | + 'sap.apf', |
| 12 | + 'sap.base', |
| 13 | + 'sap.chart', |
| 14 | + 'sap.collaboration', |
| 15 | + 'sap.f', |
| 16 | + 'sap.fe', |
| 17 | + 'sap.fileviewer', |
| 18 | + 'sap.gantt', |
| 19 | + 'sap.landvisz', |
| 20 | + 'sap.m', |
| 21 | + 'sap.ndc', |
| 22 | + 'sap.ovp', |
| 23 | + 'sap.rules', |
| 24 | + 'sap.suite', |
| 25 | + 'sap.tnt', |
| 26 | + 'sap.ui', |
| 27 | + 'sap.uiext', |
| 28 | + 'sap.ushell', |
| 29 | + 'sap.uxap', |
| 30 | + 'sap.viz', |
| 31 | + 'sap.webanalytics', |
| 32 | + 'sap.zen' |
| 33 | +]; |
| 34 | + |
| 35 | +/** |
| 36 | + * Check whether a specific dependency is a custom library, and if yes, add it to the map. |
| 37 | + * |
| 38 | + * @param dependency dependency from the manifest |
| 39 | + * @param customLibs map containing the required custom libraries |
| 40 | + */ |
| 41 | +function addKeys(dependency: Record<string, unknown>, customLibs: Record<string, true>): void { |
| 42 | + Object.keys(dependency).forEach(function (key) { |
| 43 | + // ignore libs or Components that start with SAPUI5 delivered namespaces |
| 44 | + if ( |
| 45 | + !UI5_LIBS.some(function (substring) { |
| 46 | + return key === substring || key.startsWith(substring + '.'); |
| 47 | + }) |
| 48 | + ) { |
| 49 | + customLibs[key] = true; |
| 50 | + } |
| 51 | + }); |
| 52 | +} |
| 53 | + |
| 54 | +/** |
| 55 | + * Fetch the manifest for all the given application urls and generate a string containing all required custom library ids. |
| 56 | + * |
| 57 | + * @param appUrls urls pointing to included applications |
| 58 | + * @returns Promise of a comma separated list of all required libraries. |
| 59 | + */ |
| 60 | +function getManifestLibs(appUrls: string[]): Promise<string> { |
| 61 | + const result = {} as Record<string, true>; |
| 62 | + const promises = []; |
| 63 | + for (const url of appUrls) { |
| 64 | + promises.push( |
| 65 | + fetch(`${url}/manifest.json`).then(async (resp) => { |
| 66 | + const manifest = await resp.json(); |
| 67 | + if (manifest) { |
| 68 | + if (manifest['sap.ui5'] && manifest['sap.ui5'].dependencies) { |
| 69 | + if (manifest['sap.ui5'].dependencies.libs) { |
| 70 | + addKeys(manifest['sap.ui5'].dependencies.libs, result); |
| 71 | + } |
| 72 | + if (manifest['sap.ui5'].dependencies.components) { |
| 73 | + addKeys(manifest['sap.ui5'].dependencies.components, result); |
| 74 | + } |
| 75 | + } |
| 76 | + if (manifest['sap.ui5'] && manifest['sap.ui5'].componentUsages) { |
| 77 | + addKeys(manifest['sap.ui5'].componentUsages, result); |
| 78 | + } |
| 79 | + } |
| 80 | + }) |
| 81 | + ); |
| 82 | + } |
| 83 | + return Promise.all(promises).then(() => Object.keys(result).join(',')); |
| 84 | +} |
| 85 | + |
| 86 | +/** |
| 87 | + * Register the custom libraries and their url with the UI5 loader. |
| 88 | + * |
| 89 | + * @param dataFromAppIndex data returned from the app index service |
| 90 | + */ |
| 91 | +function registerModules( |
| 92 | + dataFromAppIndex: Record< |
| 93 | + string, |
| 94 | + { |
| 95 | + dependencies?: { |
| 96 | + url?: string; |
| 97 | + type?: string; |
| 98 | + componentId: string; |
| 99 | + }[]; |
| 100 | + } |
| 101 | + > |
| 102 | +) { |
| 103 | + Object.keys(dataFromAppIndex).forEach(function (moduleDefinitionKey) { |
| 104 | + const moduleDefinition = dataFromAppIndex[moduleDefinitionKey]; |
| 105 | + if (moduleDefinition && moduleDefinition.dependencies) { |
| 106 | + moduleDefinition.dependencies.forEach(function (dependency) { |
| 107 | + if (dependency.url && dependency.url.length > 0 && dependency.type === 'UI5LIB') { |
| 108 | + Log.info('Registering Library ' + dependency.componentId + ' from server ' + dependency.url); |
| 109 | + const compId = dependency.componentId.replace(/\./g, '/'); |
| 110 | + const config = { |
| 111 | + paths: {} as Record<string, string> |
| 112 | + }; |
| 113 | + config.paths[compId] = dependency.url; |
| 114 | + sap.ui.loader.config(config); |
| 115 | + } |
| 116 | + }); |
| 117 | + } |
| 118 | + }); |
| 119 | +} |
| 120 | + |
| 121 | +/** |
| 122 | + * Fetch the manifest from the given application urls, then parse them for custom libs, and finally request their urls. |
| 123 | + * |
| 124 | + * @param appUrls application urls |
| 125 | + * @returns returns a promise when the registration is completed. |
| 126 | + */ |
| 127 | +export async function registerComponentDependencyPaths(appUrls: string[]): Promise<void> { |
| 128 | + const libs = await getManifestLibs(appUrls); |
| 129 | + if (libs && libs.length > 0) { |
| 130 | + let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs; |
| 131 | + const sapClient = UriParameters.fromQuery(window.location.search).get('sap-client'); |
| 132 | + if (sapClient && sapClient.length === 3) { |
| 133 | + url = url + '&sap-client=' + sapClient; |
| 134 | + } |
| 135 | + const response = await fetch(url); |
| 136 | + registerModules(await response.json()); |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +/** |
| 141 | + * Register SAP fonts that are also registered in a productive Fiori launchpad. |
| 142 | + */ |
| 143 | +export function registerSAPFonts() { |
| 144 | + //Fiori Theme font family and URI |
| 145 | + const fioriTheme = { |
| 146 | + fontFamily: 'SAP-icons-TNT', |
| 147 | + fontURI: sap.ui.require.toUrl('sap/tnt/themes/base/fonts/') |
| 148 | + }; |
| 149 | + //Registering to the icon pool |
| 150 | + IconPool.registerFont(fioriTheme); |
| 151 | + //SAP Business Suite Theme font family and URI |
| 152 | + const bSuiteTheme = { |
| 153 | + fontFamily: 'BusinessSuiteInAppSymbols', |
| 154 | + fontURI: sap.ui.require.toUrl('sap/ushell/themes/base/fonts/') |
| 155 | + }; |
| 156 | + //Registering to the icon pool |
| 157 | + IconPool.registerFont(bSuiteTheme); |
| 158 | +} |
| 159 | + |
| 160 | +/** |
| 161 | + * Read the application title from the resource bundle and set it as document title. |
| 162 | + */ |
| 163 | +export function setI18nTitle() { |
| 164 | + const locale = sap.ui.getCore().getConfiguration().getLanguage(); |
| 165 | + const resourceBundle = ResourceBundle.create({ |
| 166 | + url: 'i18n/i18n.properties', |
| 167 | + locale |
| 168 | + }) as ResourceBundle; |
| 169 | + document.title = resourceBundle.getText('appTitle') ?? document.title; |
| 170 | +} |
| 171 | + |
| 172 | +/** |
| 173 | + * Apply additional configuration. |
| 174 | + * |
| 175 | + * @param params init parameters read from the script tag |
| 176 | + * @param params.appUrls JSON containing a string array of application urls |
| 177 | + * @param params.flex JSON containing the flex configuration |
| 178 | + * @returns promise |
| 179 | + */ |
| 180 | +export function configure({ appUrls, flex }: { appUrls?: string | null; flex?: string | null }): Promise<void> { |
| 181 | + // Register RTA if configured |
| 182 | + if (flex) { |
| 183 | + sap.ushell.Container.attachRendererCreatedEvent(async function () { |
| 184 | + const serviceInstance = await sap.ushell.Container.getServiceAsync<AppLifeCycle>('AppLifeCycle'); |
| 185 | + serviceInstance.attachAppLoaded(event => { |
| 186 | + const oView = event.getParameter('componentInstance'); |
| 187 | + sap.ui.require(['sap/ui/rta/api/startAdaptation'], function (startAdaptation: (opts: object) => void) { |
| 188 | + const options = { |
| 189 | + rootControl: oView, |
| 190 | + validateAppVersion: false, |
| 191 | + flexSettings: JSON.parse(flex) |
| 192 | + }; |
| 193 | + startAdaptation(options); |
| 194 | + }); |
| 195 | + }); |
| 196 | + }); |
| 197 | + } |
| 198 | + |
| 199 | + // Load custom library paths if configured |
| 200 | + if (appUrls) { |
| 201 | + return registerComponentDependencyPaths(JSON.parse(appUrls)); |
| 202 | + } else { |
| 203 | + return Promise.resolve(); |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/** |
| 208 | + * Initialize the FLP sandbox. |
| 209 | + */ |
| 210 | +export function init() { |
| 211 | + setI18nTitle(); |
| 212 | + registerSAPFonts(); |
| 213 | + sap.ushell.Container.createRenderer().placeAt('content'); |
| 214 | +} |
| 215 | + |
| 216 | +const bootstrapConfig = document.getElementById('sap-ui-bootstrap'); |
| 217 | +if (bootstrapConfig) { |
| 218 | + configure({ |
| 219 | + appUrls: bootstrapConfig.getAttribute('data-open-ux-preview-libs-manifests'), |
| 220 | + flex: bootstrapConfig.getAttribute('data-open-ux-preview-flex-settings') |
| 221 | + }) |
| 222 | + .then(init) |
| 223 | + .catch(() => Log.error('Sandbox initialization failed.')); |
| 224 | +} |
0 commit comments