From 719cd25e6d871ba84bca1021ef98c0a09ea1c59d Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Mon, 6 Jun 2022 14:27:40 +0100 Subject: [PATCH 01/58] Added the fetch of the identity information from the domain associated with the transmission. Enabled the contact DPO, and privacy policy URL icons when identity information is returned. Handled the situation where identity information can not be returned. Modified the data model and fields to support promise resolution to update the UI. Moved the tick and cross icons into the component HTML. --- paf-mvp-audit/src/controller.ts | 50 +++++++++++----- .../components/noresponse.html} | 3 +- .../{provider.html => okresponse.html} | 14 +++-- paf-mvp-audit/src/images/IconTick.svg | 3 - paf-mvp-audit/src/locale.ts | 12 ++++ paf-mvp-audit/src/locales/en-gb.yaml | 29 +++++++++- paf-mvp-audit/src/locales/en-us.yaml | 29 +++++++++- paf-mvp-audit/src/model.ts | 57 +++++++++++++++++-- paf-mvp-audit/src/view.ts | 28 ++++++++- .../src/views/publisher/index.hbs | 10 ++-- 10 files changed, 197 insertions(+), 38 deletions(-) rename paf-mvp-audit/src/{images/IconCross.svg => html/components/noresponse.html} (94%) rename paf-mvp-audit/src/html/components/{provider.html => okresponse.html} (61%) delete mode 100644 paf-mvp-audit/src/images/IconTick.svg diff --git a/paf-mvp-audit/src/controller.ts b/paf-mvp-audit/src/controller.ts index 33431310..2266a196 100644 --- a/paf-mvp-audit/src/controller.ts +++ b/paf-mvp-audit/src/controller.ts @@ -1,14 +1,9 @@ import { Locale } from './locale'; -import { AuditLog, TransmissionResult } from '@core/model/generated-model'; +import { AuditLog, GetIdentityResponse } from '@core/model/generated-model'; import { Log } from '@core/log'; -import { Model } from './model'; +import { Model, TransmissionResultNode } from './model'; import { View } from './view'; import { BindingViewOnly } from '@core/ui/binding'; -import providerComponent from './html/components/provider.html'; -import iconTick from './images/IconTick.svg'; - -// TODO: Add back when full audit information is available. -// import iconCross from './images/iconCross.svg'; /** * Controller class used with the model and views. Uses paf-lib for data access services. @@ -121,11 +116,14 @@ export class Controller { /** * Custom UI binding to display the providers from the audit log. */ -class BindingProviders extends BindingViewOnly { +class BindingProviders extends BindingViewOnly { private readonly locale: Locale; + private readonly auditView: View; + constructor(view: View, id: string, locale: Locale) { super(view, id); + this.auditView = view; this.locale = locale; } @@ -135,14 +133,36 @@ class BindingProviders extends BindingViewOnlydocument.createElement('div'); - item.className = 'ok-ui-provider'; - item.innerHTML = providerComponent({ - ResultSVG: iconTick, - Name: this.field.value.source.domain, - }); - element.appendChild(item); + this.field.value + .getIdentity() + .then((i) => { + this.auditView.addOkResponse(element, { + ...i, + complaint_email_url: this.buildEmailUrl(this.field.value, i), + }); + }) + .catch((e) => { + console.error(e); + this.auditView.addNoResponse(element, { + name: this.field.value.result.source.domain, + }); + }); } return element; } + + private buildEmailUrl(node: TransmissionResultNode, i: GetIdentityResponse): string { + const body = encodeURIComponent( + this.locale.emailBodyText + .replace('[Name]', i.name) + .replace('[TimeStamp]', new Date(node.result.source.timestamp).toUTCString()) + .replace('[PrivacyURL]', i.privacy_policy_url) + // TODO add preferences to the model + .replace('[Preferences]', 'TODO add preferences') + .replace('[Proof]', JSON.stringify(node.result)) + .trim() + ); + const subject = encodeURIComponent(this.locale.emailSubject); + return `mailto:${i.dpo_email}?subject=${subject}&body=${body}`; + } } diff --git a/paf-mvp-audit/src/images/IconCross.svg b/paf-mvp-audit/src/html/components/noresponse.html similarity index 94% rename from paf-mvp-audit/src/images/IconCross.svg rename to paf-mvp-audit/src/html/components/noresponse.html index 916fa0b3..e8e52497 100644 --- a/paf-mvp-audit/src/images/IconCross.svg +++ b/paf-mvp-audit/src/html/components/noresponse.html @@ -1,3 +1,4 @@ - \ No newline at end of file + +

${ _.name }

\ No newline at end of file diff --git a/paf-mvp-audit/src/html/components/provider.html b/paf-mvp-audit/src/html/components/okresponse.html similarity index 61% rename from paf-mvp-audit/src/html/components/provider.html rename to paf-mvp-audit/src/html/components/okresponse.html index 7d02c491..3887c6c8 100644 --- a/paf-mvp-audit/src/html/components/provider.html +++ b/paf-mvp-audit/src/html/components/okresponse.html @@ -1,18 +1,20 @@ -${ _.ResultSVG } -

${ _.Name }

- - \ No newline at end of file + \ No newline at end of file diff --git a/paf-mvp-audit/src/images/IconTick.svg b/paf-mvp-audit/src/images/IconTick.svg deleted file mode 100644 index 66ea6bb4..00000000 --- a/paf-mvp-audit/src/images/IconTick.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/paf-mvp-audit/src/locale.ts b/paf-mvp-audit/src/locale.ts index 3df2416f..68484294 100644 --- a/paf-mvp-audit/src/locale.ts +++ b/paf-mvp-audit/src/locale.ts @@ -7,6 +7,10 @@ export class Values { auditBody: string[]; auditFooter = 'NOT SET'; + // Email text + emailSubject: string; + emailBody: string[]; + // Button text download = 'NOT SET'; cancel = 'NOT SET'; @@ -14,6 +18,7 @@ export class Values { export class Locale extends Values { public readonly auditBodyHTML: string; + public readonly emailBodyText: string; /** * Logo to use with the templates. @@ -31,12 +36,19 @@ export class Locale extends Values { // Extract the arrays into paragraph HTML element strings. this.auditBodyHTML = this.toHtml(this.auditBody); + + // Extract the email array to a single text string. + this.emailBodyText = this.toText(this.emailBody); } private toHtml(list: string[]): string { return `

${list.join('

')}

`; } + private toText(list: string[]): string { + return list.join('\r\n'); + } + private getLocale(locales: readonly string[]): Values { for (const locale of locales) { switch (locale) { diff --git a/paf-mvp-audit/src/locales/en-gb.yaml b/paf-mvp-audit/src/locales/en-gb.yaml index 62a05461..b083ce4c 100644 --- a/paf-mvp-audit/src/locales/en-gb.yaml +++ b/paf-mvp-audit/src/locales/en-gb.yaml @@ -7,4 +7,31 @@ auditFooter: If you believe an organization didn't honour your preferences, clic # Button text download: Download text file cancel: Cancel -open: Ad audit by OneKey \ No newline at end of file +open: Ad audit by OneKey + +# DPO email +emailSubject: OneKey Data Protection Request +emailBody: + - To whom it may concern, + - + - I believe that [Name] used my personal information without a legal basis on [TimeStamp]. + - + - I provided you the following permissions for use of this data. + - + - '[Preferences]' + - + - You cryptographically signed this information which is shown at the bottom of this email. We therefore agree that you were in possession of the information. + - + - You are bound by the privacy policy here. + - + - "[PrivacyURL]" + - + - I would be grateful if you can respond by email to this address within 7 working days. + - + - Regards, + - + - INSERT YOU NAME + - + - --- DO NOT CHANGE THE TEXT BELOW THIS LINE --- + - '[Proof]' + - --- DO NOT CHANGE THE TEXT ABOVE THIS LINE --- diff --git a/paf-mvp-audit/src/locales/en-us.yaml b/paf-mvp-audit/src/locales/en-us.yaml index bd5eece6..f60015f3 100644 --- a/paf-mvp-audit/src/locales/en-us.yaml +++ b/paf-mvp-audit/src/locales/en-us.yaml @@ -7,4 +7,31 @@ auditFooter: If you believe an organization didn't honour your preferences, clic # Button text download: Download text file cancel: Cancel -open: Ad audit by OneKey \ No newline at end of file +open: Ad audit by OneKey + +# DPO email +emailSubject: OneKey Data Protection Request +emailBody: + - To whom it may concern, + - + - I believe that [Name] used my personal information without a legal basis on [TimeStamp]. + - + - I provided you the following permissions for use of this data. + - + - '[Preferences]' + - + - You cryptographically signed this information which is shown at the bottom of this email. We therefore agree that you were in possession of the information. + - + - You are bound by the privacy policy here. + - + - "[PrivacyURL]" + - + - I would be grateful if you can respond by email to this address within 7 working days. + - + - Regards, + - + - INSERT YOU NAME + - + - --- DO NOT CHANGE THE TEXT BELOW THIS LINE --- + - '[Proof]' + - --- DO NOT CHANGE THE TEXT ABOVE THIS LINE --- diff --git a/paf-mvp-audit/src/model.ts b/paf-mvp-audit/src/model.ts index 62338083..82dc1942 100644 --- a/paf-mvp-audit/src/model.ts +++ b/paf-mvp-audit/src/model.ts @@ -1,10 +1,57 @@ -import { Field, IFieldBind, IModel } from '@core/ui/fields'; +import { Field, FieldReadOnly, IFieldBind, IModel } from '@core/ui/fields'; import { AuditLog, TransmissionResult } from '@core/model/generated-model'; +import { GetIdentityResponse } from '@core/model/generated-model'; +import { FileExtensionInfo } from 'typescript'; /** - * Field represents the transmission result from the audit log. + * Represents a transmission result from the audit log with additional features to support a hierarchy, fetching + * identity data, and verifying the signature. */ -export class FieldTransmissionResult extends Field implements IFieldBind {} +export class TransmissionResultNode { + /** + * Cached copy of the identity information for the domain associated with the result. + */ + private identity: GetIdentityResponse = undefined; + + /** + * The result returned in the audit log. + */ + public readonly result: TransmissionResult; + + /** + * Constructs a new instance of TransmissionResultNode for the audit log record provided. + */ + constructor(result: TransmissionResult) { + this.result = result; + } + + /** + * Identity of the transmission result. + * @remarks this method gets the identity and all the public keys in a single method to avoid fetching the public key + * and the other identity information over multiple requests to the same endpoint. + * @returns promise that when resolved will provide the identity associated with the transmission result or an error. + */ + public getIdentity() { + if (this.identity === undefined) { + return fetch(`https://${this.result.source.domain}/paf/v1/identity`, { + method: 'GET', + mode: 'cors', + cache: 'default', + }) + .then((response) => { + if (response) { + return response.json() as Promise; + } + throw new Error(response.statusText); + }) + .then((identityResponse) => { + this.identity = identityResponse; + return identityResponse; + }); + } + return Promise.resolve(this.identity); + } +} /** * The model used in the module. @@ -19,7 +66,7 @@ export class Model implements IModel { /** * The data fields that relate to each transmission result to be displayed. */ - readonly results: FieldTransmissionResult[] = []; + readonly results: Field[] = []; /** * All the fields that need to be bound. @@ -32,7 +79,7 @@ export class Model implements IModel { */ constructor(audit: AuditLog) { audit.transmissions?.forEach((t) => { - const field = new FieldTransmissionResult(this, t); + const field = new Field(this, new TransmissionResultNode(t)); this.results.push(field); this.allFields.push(field); }); diff --git a/paf-mvp-audit/src/view.ts b/paf-mvp-audit/src/view.ts index dd2993af..a40ed182 100644 --- a/paf-mvp-audit/src/view.ts +++ b/paf-mvp-audit/src/view.ts @@ -1,6 +1,5 @@ /** * Resources used by the controller for HTML views and CSS. - * TODO: fix the warning associated with can't find module or type. */ import logoSvg from './images/OneKey.svg'; import css from './css/ok-ui.css'; @@ -9,6 +8,8 @@ import buttonTemplate from './html/components/button.html'; import { Locale } from './locale'; import { IView } from '@core/ui/binding'; import { Log } from '@core/log'; +import okResponse from './html/components/okResponse.html'; +import noResponse from './html/components/noresponse.html'; export class View implements IView { // The shadow root for the UI. @@ -50,6 +51,24 @@ export class View implements IView { this.setContainerCard(card); } + /** + * Adds the good data to the view. + * @param element that the component should be added to + * @param data data to be displayed by the component + */ + public addOkResponse(element: HTMLDivElement, data: unknown) { + View.addComponent(element, okResponse, data); + } + + /** + * Adds the no response data to the view. + * @param element that the component should be added to + * @param data data to be displayed by the component + */ + public addNoResponse(element: HTMLDivElement, data: unknown) { + View.addComponent(element, noResponse, data); + } + /** * Used to get an array of action elements from the current view. * @returns array of HTMLElements that can have events added to them @@ -122,4 +141,11 @@ export class View implements IView { this.root.appendChild(style); this.root.appendChild(this.auditContainer); } + + private static addComponent(element: HTMLDivElement, component: Component, data: unknown) { + const item = document.createElement('div'); + item.className = 'ok-ui-provider'; + item.innerHTML = component(data); + element.appendChild(item); + } } diff --git a/paf-mvp-demo-express/src/views/publisher/index.hbs b/paf-mvp-demo-express/src/views/publisher/index.hbs index 1072adb6..9c2eb1a0 100644 --- a/paf-mvp-demo-express/src/views/publisher/index.hbs +++ b/paf-mvp-demo-express/src/views/publisher/index.hbs @@ -932,11 +932,11 @@ From c62be723bc579d20c58a05b1fa8ac68ff76cc52f Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:07:03 +0100 Subject: [PATCH 02/58] Removed the CMP folder. --- paf-mvp-demo-express/public/assets/cmp/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 paf-mvp-demo-express/public/assets/cmp/.gitignore diff --git a/paf-mvp-demo-express/public/assets/cmp/.gitignore b/paf-mvp-demo-express/public/assets/cmp/.gitignore deleted file mode 100644 index f49098ac..00000000 --- a/paf-mvp-demo-express/public/assets/cmp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ok-ui*.* \ No newline at end of file From 49a1115a2f7c5862d49e7657cd02e04bbd427e76 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:09:38 +0100 Subject: [PATCH 03/58] Move the locale and loader capabilities to the core so that they can be shared across the CMP and the Audit modules. --- paf-mvp-audit/src/locale.ts | 63 ---------------- paf-mvp-audit/src/main.ts | 24 ------ paf-mvp-cmp/src/loader.ts | 55 -------------- .../src => paf-mvp-core-js/src/ui}/ILocale.ts | 0 paf-mvp-core-js/src/ui/loader.ts | 73 +++++++++++++++++++ 5 files changed, 73 insertions(+), 142 deletions(-) delete mode 100644 paf-mvp-audit/src/locale.ts delete mode 100644 paf-mvp-audit/src/main.ts delete mode 100644 paf-mvp-cmp/src/loader.ts rename {paf-mvp-cmp/src => paf-mvp-core-js/src/ui}/ILocale.ts (100%) create mode 100644 paf-mvp-core-js/src/ui/loader.ts diff --git a/paf-mvp-audit/src/locale.ts b/paf-mvp-audit/src/locale.ts deleted file mode 100644 index 68484294..00000000 --- a/paf-mvp-audit/src/locale.ts +++ /dev/null @@ -1,63 +0,0 @@ -import en_us from './locales/en-us.yaml'; -import en_gb from './locales/en-gb.yaml'; - -export class Values { - // Audit text - auditHeading = 'NOT SET'; - auditBody: string[]; - auditFooter = 'NOT SET'; - - // Email text - emailSubject: string; - emailBody: string[]; - - // Button text - download = 'NOT SET'; - cancel = 'NOT SET'; -} - -export class Locale extends Values { - public readonly auditBodyHTML: string; - public readonly emailBodyText: string; - - /** - * Logo to use with the templates. - */ - public Logo = ''; - - constructor(languages: readonly string[]) { - super(); - - // Use US english as the default locale. - Object.assign(this, en_us); - - // Replace any values with the users chosen locale. - Object.assign(this, this.getLocale(languages)); - - // Extract the arrays into paragraph HTML element strings. - this.auditBodyHTML = this.toHtml(this.auditBody); - - // Extract the email array to a single text string. - this.emailBodyText = this.toText(this.emailBody); - } - - private toHtml(list: string[]): string { - return `

${list.join('

')}

`; - } - - private toText(list: string[]): string { - return list.join('\r\n'); - } - - private getLocale(locales: readonly string[]): Values { - for (const locale of locales) { - switch (locale) { - case 'en-GB': - return en_gb; - case 'en-US': - return en_us; - } - } - return en_us; - } -} diff --git a/paf-mvp-audit/src/main.ts b/paf-mvp-audit/src/main.ts deleted file mode 100644 index 912064fb..00000000 --- a/paf-mvp-audit/src/main.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Locale } from './locale'; -import { Controller } from './controller'; -import { Log } from '@core/log'; - -const log = new Log('audit', '#18a9e1'); - -class MonitoredElement extends HTMLDivElement { - public timer: NodeJS.Timer; -} - -document.querySelectorAll('[auditLog]').forEach((e) => { - if (e instanceof HTMLDivElement) { - log.Message('register', e.id); - const content = e.innerHTML; - (e).timer = setInterval(() => { - log.Message('check', e.id); - if (content !== e.innerHTML) { - log.Message('adding', e.id); - clearInterval((e).timer); - new Controller(new Locale(window.navigator.languages), e, log); - } - }, 1000); - } -}); diff --git a/paf-mvp-cmp/src/loader.ts b/paf-mvp-cmp/src/loader.ts deleted file mode 100644 index da154887..00000000 --- a/paf-mvp-cmp/src/loader.ts +++ /dev/null @@ -1,55 +0,0 @@ -// The available language codes calculated at build time. -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore this value is populated at build time in rollup.config.js. -const available = __Locales__; - -// The default language to use if the users preferences are not available. -const defaultLang = 'en-us'; - -// Record the current script so that the language script can be inserted. -const thisScript = document.currentScript; - -// The base URL that should be used to find the language specific script. -const baseUrl = thisScript.getAttribute('src'); - -/** - * Gets the URL for the language code provided. - * @param language required - * @returns URL for the language - */ -const getUrlForLanguage = (language: string) => baseUrl.replace('ok-ui', `ok-ui-${language}`); - -/** - * Insert the JavaScript at the provided URL into the document next to this loader script copying all the configuration - * from the loader to the language specific script. - * @param url - */ -const insertScript = (url: string) => { - const script = document.createElement('script'); - const attrNames = thisScript.getAttributeNames(); - for (let i = 0; i < attrNames.length; i++) { - const qn = attrNames[i]; - script.setAttribute(qn, thisScript.getAttribute(qn)); - } - script.setAttribute('src', url); - thisScript.insertAdjacentElement('afterend', script); -}; - -/** - * Checks if there is a bundle for the next language in the list. If so then this is inserted into the document, - * otherwise if there are more languages remaining then these are loaded. If there are no more languages then use the - * default. - */ -const insertLanguage = () => { - for (let i = 0; i < window.navigator.languages.length; i++) { - const language = window.navigator.languages[i].toLowerCase(); - if (available.indexOf(language) >= 0) { - insertScript(getUrlForLanguage(language)); - return; - } - } - insertScript(getUrlForLanguage(defaultLang)); -}; - -// Find the best language and insert it after this script. -insertLanguage(); diff --git a/paf-mvp-cmp/src/ILocale.ts b/paf-mvp-core-js/src/ui/ILocale.ts similarity index 100% rename from paf-mvp-cmp/src/ILocale.ts rename to paf-mvp-core-js/src/ui/ILocale.ts diff --git a/paf-mvp-core-js/src/ui/loader.ts b/paf-mvp-core-js/src/ui/loader.ts new file mode 100644 index 00000000..dca07adc --- /dev/null +++ b/paf-mvp-core-js/src/ui/loader.ts @@ -0,0 +1,73 @@ +class Loader { + // The available language codes calculated at build time. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this value is populated at build time in rollup.config.js. + private readonly available = __Locales__; + + // The default language to use if the users preferences are not available. + private readonly defaultLang = 'en-us'; + + // Record the current script so that the language script can be inserted. + private readonly currentScript: HTMLOrSVGScriptElement; + + // The base URL that should be used to find the language specific script. + private readonly baseUrl: string; + + // The first dot in the file name. + private readonly dot: number; + + /** + * Constructs a new instance of the loader. + */ + constructor(currentScript: HTMLOrSVGScriptElement) { + this.currentScript = currentScript; + this.baseUrl = this.currentScript.getAttribute('src'); + const lastSlash = this.baseUrl.lastIndexOf('/'); + this.dot = this.baseUrl.indexOf('.', lastSlash); + } + + /** + * Gets the URL for the language code provided. + * @param language required + * @returns URL for the language + */ + private getUrlForLanguage(language: string): string { + return this.baseUrl.substring(0, this.dot) + '-' + language + this.baseUrl.substring(this.dot); + } + + /** + * Insert the JavaScript at the provided URL into the document next to this loader script copying all the configuration + * from the loader to the language specific script. + * @param url + */ + private insertScript(url: string) { + const script = document.createElement('script'); + const attrNames = this.currentScript.getAttributeNames(); + for (let i = 0; i < attrNames.length; i++) { + const qn = attrNames[i]; + script.setAttribute(qn, this.currentScript.getAttribute(qn)); + } + script.setAttribute('src', url); + this.currentScript.insertAdjacentElement('afterend', script); + } + + /** + * Checks if there is a bundle for the next language in the list. If so then this is inserted into the document, + * otherwise if there are more languages remaining then these are loaded. If there are no more languages then use the + * default. + */ + public insertLanguage() { + for (let i = 0; i < window.navigator.languages.length; i++) { + const language = window.navigator.languages[i].toLowerCase(); + if (this.available.indexOf(language) >= 0) { + const languageUrl = this.getUrlForLanguage(language); + this.insertScript(languageUrl); + return; + } + } + this.insertScript(this.getUrlForLanguage(this.defaultLang)); + } +} + +// Find the best language and insert it after this script. +new Loader(document.currentScript).insertLanguage(); From d4e7a2fb8e6f4210324aa9b52299cc8fc15449d7 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:12:05 +0100 Subject: [PATCH 04/58] The output from the CMP module now has "cmp" in the resource file name rather than a folder. Uses the shared core loader and locale features. --- paf-mvp-cmp/README.md | 6 ++--- paf-mvp-cmp/rollup.config.js | 44 +++++++++++++++++------------------ paf-mvp-cmp/src/controller.ts | 2 +- paf-mvp-cmp/src/view.ts | 3 +-- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/paf-mvp-cmp/README.md b/paf-mvp-cmp/README.md index 4be1df2d..b3ae5871 100644 --- a/paf-mvp-cmp/README.md +++ b/paf-mvp-cmp/README.md @@ -12,7 +12,7 @@ proxy-host-name domain. A list of CMP providers is available in the page. ```HTML - ``` @@ -127,14 +127,14 @@ particularly noticeable when HTTPS/2 is used. For example to preload the loader the following then following would be used. ```html - + ``` If the server has already worked out the correct language bundle the following would be used. ```html - + ``` If this link is not added, then the functionality will still be provided by page diff --git a/paf-mvp-cmp/rollup.config.js b/paf-mvp-cmp/rollup.config.js index 9e77d7c0..85375a3f 100644 --- a/paf-mvp-cmp/rollup.config.js +++ b/paf-mvp-cmp/rollup.config.js @@ -27,6 +27,9 @@ import * as yaml from 'js-yaml'; // Used to get the TCF core string from the environment. import { env } from 'process'; +// File name prefix for the bundle files. +const namePrefix = 'ok-ui-cmp'; + const DEV = process.env.ROLLUP_WATCH; // Options to pass to terser. @@ -131,11 +134,12 @@ function getLocaleCodes() { // Builds the loader working out from the locales directory the various options that will be available. function buildLoader() { + const loader = '../paf-mvp-core-js/src/ui/loader.ts'; return { - input: './src/loader.ts', + input: loader, plugins: [ replace({ - include: './src/loader.ts', + include: loader, preventAssignment: true, __Locales__: '[' + getLocaleCodes().join(',') + ']' }), @@ -150,25 +154,25 @@ function buildLoader() { treeshake: true, output: [ { - file: `./dist/ok-ui.js`, - sourcemap: true, + file: `./dist/${namePrefix}.js`, + sourcemap: false, format: 'iife' }, { - file: `./dist/ok-ui.min.js`, + file: `./dist/${namePrefix}.min.js`, format: 'iife', - sourcemap: true, + sourcemap: false, plugins: [terser(terserOptions)] }, { - file: `../paf-mvp-demo-express/public/assets/cmp/ok-ui.js`, - sourcemap: true, + file: `../paf-mvp-demo-express/public/assets/${namePrefix}.js`, + sourcemap: false, format: 'iife' }, { - file: `../paf-mvp-demo-express/public/assets/cmp/ok-ui.min.js`, + file: `../paf-mvp-demo-express/public/assets/${namePrefix}.min.js`, format: 'iife', - sourcemap: true, + sourcemap: false, plugins: [terser(terserOptions)] }, ] @@ -193,10 +197,6 @@ function buildLocaleConfig(localeCode, localeContent, tcfCoreTemplate) { preventAssignment: true, 'process.env.NODE_ENV': JSON.stringify(DEV ? 'development' : 'production') }), - replace({ - preventAssignment: true, - 'process.env.NODE_ENV': JSON.stringify(DEV ? 'development' : 'production') - }), postHTML({ template: true }), minifyHTML({ // Include string literals that contain the div or section element as well as the default check. @@ -216,25 +216,25 @@ function buildLocaleConfig(localeCode, localeContent, tcfCoreTemplate) { treeshake: true, output: [ { - file: `./dist/ok-ui-${localeCode}.js`, - sourcemap: true, + file: `./dist/${namePrefix}-${localeCode}.js`, + sourcemap: false, format: 'iife', }, { - file: `./dist/ok-ui-${localeCode}.min.js`, + file: `./dist/${namePrefix}-${localeCode}.min.js`, format: 'iife', - sourcemap: true, + sourcemap: false, plugins: [terser(terserOptions)] }, { - file: `../paf-mvp-demo-express/public/assets/cmp/ok-ui-${localeCode}.js`, - sourcemap: true, + file: `../paf-mvp-demo-express/public/assets/${namePrefix}-${localeCode}.js`, + sourcemap: false, format: 'iife', }, { - file: `../paf-mvp-demo-express/public/assets/cmp/ok-ui-${localeCode}.min.js`, + file: `../paf-mvp-demo-express/public/assets/${namePrefix}-${localeCode}.min.js`, format: 'iife', - sourcemap: true, + sourcemap: false, plugins: [terser(terserOptions)] }, ] diff --git a/paf-mvp-cmp/src/controller.ts b/paf-mvp-cmp/src/controller.ts index 91f4ec0c..9386895e 100644 --- a/paf-mvp-cmp/src/controller.ts +++ b/paf-mvp-cmp/src/controller.ts @@ -19,7 +19,7 @@ import { View } from './view'; import { getCookieValue } from '@frontend/utils/cookie'; import { Cookies, getPrebidDataCacheExpiration } from '@core/cookies'; import { TcfCore } from './tcfcore'; -import { ILocale } from './ILocale'; +import { ILocale } from '@core/ui/ILocale'; /** * Controller class used with the model and views. Uses paf-lib for data access services. diff --git a/paf-mvp-cmp/src/view.ts b/paf-mvp-cmp/src/view.ts index f7fc8749..42f677b6 100644 --- a/paf-mvp-cmp/src/view.ts +++ b/paf-mvp-cmp/src/view.ts @@ -1,6 +1,5 @@ /** * Resources used by the controller for HTML views and CSS. - * TODO: fix the warning associated with can't find module or type. */ import css from './css/ok-ui.css'; import introTemplate from './html/cards/intro.html'; @@ -12,7 +11,7 @@ import snackbarTemplate from './html/cards/snackbar.html'; import popupTemplate from './html/containers/popup.html'; import { Config } from './config'; import { IView } from '@core/ui/binding'; -import { ILocale } from './ILocale'; +import { ILocale } from '@core/ui/ILocale'; import { Tooltip } from './tooltip'; export class View implements IView { From 6b679f5a513ec99b83d50572df1a098b905ff4c7 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:21:30 +0100 Subject: [PATCH 05/58] Contoller now supports the AuditHandler interface. --- paf-mvp-audit/rollup.config.js | 268 ++++++++++++++++++++++++++------ paf-mvp-audit/src/controller.ts | 59 +++---- paf-mvp-audit/src/view.ts | 12 +- 3 files changed, 259 insertions(+), 80 deletions(-) diff --git a/paf-mvp-audit/rollup.config.js b/paf-mvp-audit/rollup.config.js index 80688978..6b2fd237 100644 --- a/paf-mvp-audit/rollup.config.js +++ b/paf-mvp-audit/rollup.config.js @@ -6,54 +6,234 @@ import { nodeResolve } from '@rollup/plugin-node-resolve'; // Needed to minimize the resulting bundle. import { terser } from 'rollup-plugin-terser'; -// Used to get the locales for embedding into the bundle. -import yaml from '@rollup/plugin-yaml'; - // HTML templates used to add language text. import postHTML from 'rollup-plugin-posthtml-template'; +// Reduces the size of the HTML. +import minifyHTML from 'rollup-plugin-minify-html-literals'; +import { defaultShouldMinify } from 'minify-html-literals'; + // Embed the CSS into the bundle. import { string } from 'rollup-plugin-string'; -export default { - input: './src/main.ts', - plugins: [ - string({ include: ['**/*.css', '**/*.svg'] }), - postHTML({ template: true }), - yaml(), - nodeResolve(), - commonjs(), - typescript({ - tsconfig: '../tsconfig.json' - }) - ], - treeshake: true, - output: [ - { - file: './dist/ok-audit.js', - format: 'iife', - sourcemap: true - }, - { - file: './dist/ok-audit.min.js', - format: 'iife', - sourcemap: true, - plugins: [ - terser() - ] - }, - { - file: '../paf-mvp-demo-express/public/assets/ok-audit.js', - format: 'iife', - sourcemap: true - }, - { - file: '../paf-mvp-demo-express/public/assets/ok-audit.min.js', - format: 'iife', - sourcemap: true, - plugins: [ - terser() - ] - } - ] +// Used to set the locale for each of the bundles. +import replace from '@rollup/plugin-replace'; + +// Used to get the locales available for each of the bundles. +import * as fs from 'fs'; +import * as path from 'path'; +import * as yaml from 'js-yaml'; + +// File name prefix for the bundle files. +const namePrefix = 'ok-ui-audit'; + +// The name of the root namespace. +const globalName = 'OKA'; + +const DEV = process.env.ROLLUP_WATCH; + +// Options to pass to terser. +const terserOptions = { + toplevel: true, + format: { + comments: false + }, + compress: true, + mangle: true }; + +// Adds the SVG images to the local. Could be expanded to support other images in the future. +function addImages(locale) { + const directory = './src/images'; + fs.readdirSync('./src/images').forEach(imageFile => { + if (path.extname(imageFile) === '.svg') { + const imageName = imageFile.split('.')[0]; + locale[imageName] = fs.readFileSync(path.join(directory, imageFile), 'utf8') + } + }); + return locale; +} + +// Converts the list of strings to paragraph tags. +function toHtml(list) { return `

${list.join('

')}

`; } + +// Converts the list of strings to a text block. +function toText(list) { return list.join('\r\n'); } + +// Converts the source locale into one with HTML properties. +function addHTML(locale) { + locale.emailBodyHTML = toText(locale.emailBody); + locale.auditBodyHTML = toHtml(locale.auditBody); + delete locale['emailBody']; + delete locale['auditBody']; + return locale; +} + +// Converts the source locale into one with HTML and image properties. +function buildLocale(locale) { + locale = addHTML(locale); + locale = addImages(locale); + return locale; +} + +// Gets all the locale files as an array. +function getLocaleFiles() { + const directory = './src/locales'; + const localeFiles = []; + fs.readdirSync(directory).forEach(localeFile => { + if (path.extname(localeFile) === '.yaml') { + localeFiles.push(localeFile); + } + }); + return localeFiles; +} + +// Get the locale and the text as JSON. +async function getLocales() { + const directory = './src/locales'; + const locales = new Map(); + getLocaleFiles().forEach(localeFile => { + const language = localeFile.split('.')[0]; + const locale = buildLocale(yaml.load(fs.readFileSync(path.join(directory, localeFile), 'utf8'))); + const text = JSON.stringify(locale); + locales.set(language, text); + }); + if (locales.size == 0) { + throw 'No locale files found. Check the "/src/locales" folder.'; + } + return locales; +} + +// Gets the available locale codes for use with the loader. +function getLocaleCodes() { + const locales = []; + getLocaleFiles().forEach(localeFile => { + const locale = localeFile.split('.')[0]; + locales.push('\'' + locale.toLowerCase() + '\''); + }); + return locales; +} + +// Builds the loader working out from the locales directory the various options that will be available. +function buildLoader() { + const loader = '../paf-mvp-core-js/src/ui/loader.ts'; + return { + input: loader, + plugins: [ + replace({ + include: loader, + preventAssignment: true, + __Locales__: '[' + getLocaleCodes().join(',') + ']' + }), + typescript({ + tsconfig: '../tsconfig.json' + }), + commonjs(), + nodeResolve({ + browser: true + }) + ], + treeshake: true, + output: [ + { + file: `./dist/${namePrefix}.js`, + sourcemap: true, + format: 'iife' + }, + { + file: `./dist/${namePrefix}.min.js`, + format: 'iife', + sourcemap: false, + plugins: [terser(terserOptions)] + }, + { + file: `../paf-mvp-demo-express/public/assets/${namePrefix}.js`, + sourcemap: true, + format: 'iife' + }, + { + file: `../paf-mvp-demo-express/public/assets/${namePrefix}.min.js`, + format: 'iife', + sourcemap: false, + plugins: [terser(terserOptions)] + }, + ] + } +} + +// Returns configuration for a specific locale. +// localeCode the code of the locale being built for. i.e. en-GB +// localeContent the JSON object with the content +function buildLocaleConfig(localeCode, localeContent) { + return { + input: './src/controller.ts', + plugins: [ + replace({ + include: './src/controller.ts', + preventAssignment: true, + __Locale__: localeContent + }), + replace({ + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify(DEV ? 'development' : 'production') + }), + postHTML({ template: true }), + minifyHTML({ + // Include string literals that contain the div or section element as well as the default check. + options: { + shouldMinify: (t) => defaultShouldMinify(t) || t.parts.some(p => + p.text.includes(' { + configs.push(buildLocaleConfig(locale, value)); + }); + return configs; +} + +// Finally export the configurations for each of the locales. +export default getLocales().then(getConfigs); \ No newline at end of file diff --git a/paf-mvp-audit/src/controller.ts b/paf-mvp-audit/src/controller.ts index 2266a196..89e84d34 100644 --- a/paf-mvp-audit/src/controller.ts +++ b/paf-mvp-audit/src/controller.ts @@ -1,53 +1,56 @@ -import { Locale } from './locale'; +import { ILocale } from '@core/ui/ILocale'; import { AuditLog, GetIdentityResponse } from '@core/model/generated-model'; import { Log } from '@core/log'; import { Model, TransmissionResultNode } from './model'; import { View } from './view'; import { BindingViewOnly } from '@core/ui/binding'; +import { Window } from '@frontend/global'; +import { AuditHandler } from '@frontend/lib/paf-lib'; /** * Controller class used with the model and views. Uses paf-lib for data access services. */ -export class Controller { - // The locale that the UI should adopt. - private readonly locale: Locale; +export class Controller implements AuditHandler { + /** + * The language text object populated by rollup.config.js at build time based on the YAML resource language files. + */ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore rollup replaces this with the JS object for the language. + private readonly locale = __Locale__; + + // Logger. + private readonly log = new Log('audit', '#18a9e1'); // The view associated with the controller. - private readonly view: View; + private view: View; // The model that wraps the audit log. - private readonly model: Model; + private model: Model; // The HTML element the audit module instance is listening to. - private readonly element: HTMLElement; + private element: HTMLElement; // The HTML element that will open the audit view if selected. private readonly button: HTMLElement; - // Logger. - private readonly log: Log; + constructor() { + this.log.Message('OKA Controller'); + } /** - * Constructs a new instance of Controller and displays the audit popup. - * @param locale the language file to use with the UI - * @param advert to bind the audit viewer to - * @param log + * Binds the controller to the element with the advert. + * @advertElement that contains the advert and audit log. */ - constructor(locale: Locale, advert: HTMLElement, log: Log) { - this.locale = locale; - this.element = advert; - this.log = log; - + public bind(advertElement: HTMLElement) { + this.element = advertElement; // TODO: Replace this with a fetch for the real audit log once available. - const auditLog = JSON.parse(advert.getAttribute('auditLog')); - + const auditLog = JSON.parse(advertElement.getAttribute('auditLog')); this.model = new Model(auditLog); - this.view = new View(advert, locale, log); + this.view = new View(advertElement, this.locale, this.log); this.mapFieldsToUI(); this.view.display('button'); this.bindActions(); - - log.Info('Audit registered', advert.id); + this.log.Info('Audit registered', advertElement.id); } /** @@ -92,7 +95,7 @@ export class Controller { case 'settings': this.view.display('button'); this.bindActions(); - window.PAFUI.promptConsent(); + (window).PAFUI.promptConsent(); break; case 'audit': this.view.display('audit'); @@ -117,11 +120,11 @@ export class Controller { * Custom UI binding to display the providers from the audit log. */ class BindingProviders extends BindingViewOnly { - private readonly locale: Locale; + private readonly locale: ILocale; private readonly auditView: View; - constructor(view: View, id: string, locale: Locale) { + constructor(view: View, id: string, locale: ILocale) { super(view, id); this.auditView = view; this.locale = locale; @@ -153,7 +156,7 @@ class BindingProviders extends BindingViewOnlythis.locale.emailBodyText) .replace('[Name]', i.name) .replace('[TimeStamp]', new Date(node.result.source.timestamp).toUTCString()) .replace('[PrivacyURL]', i.privacy_policy_url) @@ -162,7 +165,7 @@ class BindingProviders extends BindingViewOnlythis.locale.emailSubject); return `mailto:${i.dpo_email}?subject=${subject}&body=${body}`; } } diff --git a/paf-mvp-audit/src/view.ts b/paf-mvp-audit/src/view.ts index a40ed182..17ea8646 100644 --- a/paf-mvp-audit/src/view.ts +++ b/paf-mvp-audit/src/view.ts @@ -1,11 +1,10 @@ /** * Resources used by the controller for HTML views and CSS. */ -import logoSvg from './images/OneKey.svg'; import css from './css/ok-ui.css'; import auditTemplate from './html/containers/audit.html'; import buttonTemplate from './html/components/button.html'; -import { Locale } from './locale'; +import { ILocale } from '@core/ui/ILocale'; import { IView } from '@core/ui/binding'; import { Log } from '@core/log'; import okResponse from './html/components/okResponse.html'; @@ -28,20 +27,17 @@ export class View implements IView { private readonly log: Log; // The locale that the UI should adopt. - public readonly locale: Locale; + public readonly locale: ILocale; /** * Constructs a new instance of View. * @param advert element the module relates to * @param locale the language file to use with the UI */ - constructor(advert: HTMLElement, locale: Locale, log: Log) { + constructor(advert: HTMLElement, locale: ILocale, log: Log) { this.advert = advert; - this.log = log; - - // Setup the locale with the text and images to use. this.locale = locale; - this.locale.Logo = logoSvg; + this.log = log; } /** From b198c27871f0380c16bcb2b3beb0fd17fde32f15 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:22:57 +0100 Subject: [PATCH 06/58] Modified the gitignore to ignore all the OK-UI assets. --- paf-mvp-demo-express/public/assets/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paf-mvp-demo-express/public/assets/.gitignore b/paf-mvp-demo-express/public/assets/.gitignore index 8239118d..f49098ac 100644 --- a/paf-mvp-demo-express/public/assets/.gitignore +++ b/paf-mvp-demo-express/public/assets/.gitignore @@ -1 +1 @@ -ok-audit*.* \ No newline at end of file +ok-ui*.* \ No newline at end of file From 156e58e7cff0300163bbca4ed7cdda0f0b8df7e3 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Wed, 15 Jun 2022 13:25:13 +0100 Subject: [PATCH 07/58] Updated the marketing template to use the new CMP file. --- paf-mvp-demo-express/src/views/advertiser/index.hbs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paf-mvp-demo-express/src/views/advertiser/index.hbs b/paf-mvp-demo-express/src/views/advertiser/index.hbs index dfabb904..0a4ad239 100644 --- a/paf-mvp-demo-express/src/views/advertiser/index.hbs +++ b/paf-mvp-demo-express/src/views/advertiser/index.hbs @@ -5,7 +5,7 @@ {{#if useCmpUI}} - + {{else}} @@ -30,8 +30,8 @@ - - - + {{title}} {{#if metaRedirect}} - + {{/if}} + -{{#if useCmpUI}} - - - - - -{{else}} - - - -{{/if}} - - - -
- - - -
- + {{#if useCmpUI}} + + + + + + {{else}} + + + + {{/if}} -
-
-