From 1347932736696ad3f3cb30b88e2dac8a99a22f06 Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Wed, 18 Sep 2024 11:41:48 -0600 Subject: [PATCH 1/7] MWPW-158423 --- acrobat/blocks/unity/unity.js | 12 ++++++++++++ acrobat/blocks/verb-widget/verb-widget.css | 10 ++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/acrobat/blocks/unity/unity.js b/acrobat/blocks/unity/unity.js index 54272e65..3355e475 100644 --- a/acrobat/blocks/unity/unity.js +++ b/acrobat/blocks/unity/unity.js @@ -1,3 +1,5 @@ +import LIMITS from '../verb-widget/limits.js'; + const localeMap = { '': 'en-us', br: 'pt-br', @@ -104,6 +106,16 @@ function getUnityLibs(prodLibs = '/unitylibs') { } export default async function init(el) { + let mobileApp; + if ((/iPad|iPhone|iPod/.test(window.browser?.ua) && !window.MSStream) + || /android/i.test(window.browser?.ua)) { + mobileApp = true; + } + + const element = el.querySelector('span'); + const verb = element.classList[1].replace('icon-', ''); + if (mobileApp && LIMITS[verb].mobileApp) return; + const unitylibs = getUnityLibs(); const langFromPath = window.location.pathname.split('/')[1]; const languageCode = localeMap[langFromPath] ? localeMap[langFromPath].split('-')[0] : 'en'; diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index ef488a2a..057e96f6 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -236,6 +236,13 @@ margin-left: 15px; } +.verb-mobile-cta:hover, +.verb-mobile-cta:active { + background-color: #0054b6; + color: #fff; + text-decoration: none; +} + @media screen and (min-width: 768px) { .verb-container { align-items: center; @@ -246,11 +253,10 @@ } .verb-mobile-cta { - /* display: none; */ background: #1473e6; border-radius: 8px; padding: 11px 27px; - color: white; + color: #fff; white-space: nowrap; font-weight: 700; display: flex; From 18d280f1d848d07856293ddd4fb4d6bdbe7c74ba Mon Sep 17 00:00:00 2001 From: James Tsay Date: Wed, 18 Sep 2024 14:07:44 -0700 Subject: [PATCH 2/7] Add purge action command choice --- .github/workflows/clear-cache.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index b819f57b..b4d0ac2e 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -15,7 +15,7 @@ on: workflow_dispatch: inputs: cpCode: - description: 'CP Code' + description: 'CP Code, 1551836 (prod) / 1551833 (stage)' type: string required: true default: '1551836' @@ -27,6 +27,14 @@ on: options: - 'staging' - 'production' + command: + description: 'Command' + type: choice + required: true + default: 'invalidate' + options: + - 'invalidate' + - 'delete' jobs: clear-cache: @@ -38,7 +46,7 @@ jobs: env: EDGERC: ${{ secrets.EDGERC }} with: - command: 'invalidate' + command: ${{ inputs.command }} type: 'cpcode' - ref: ${{ inputs.cpCode }} + ref: ${{ inputs.cpCode }} network: ${{ inputs.network }} From 5abd28fd77bafe9685d3a2a616409560b6c394da Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Wed, 18 Sep 2024 17:43:15 -0600 Subject: [PATCH 3/7] MWPW-158122 --- acrobat/blocks/verb-widget/verb-widget.js | 24 +++++++-- acrobat/scripts/alloy/verb-widget.js | 65 ++++++++++++++++++++++- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index cc54d9e0..73a766dd 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -1,4 +1,3 @@ - import LIMITS from './limits.js'; import { setLibs, isOldBrowser } from '../../scripts/utils.js'; import verbAnalytics from '../../scripts/alloy/verb-widget.js'; @@ -8,6 +7,10 @@ const { createTag } = await import(`${miloLibs}/utils/utils.js`); const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; +const setUser = () => { + localStorage.setItem('unity.user', 'true'); +}; + // const handleError = (err, errTxt, str, strTwo) => { // err.classList.add('verb-error'); // err.classList.remove('hide'); @@ -27,13 +30,13 @@ const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; // const sendToUnity = async (file, verb, err, errTxt) => { // // Error Check: File Empty // if (file.size < 1) { -// verbAnalytics('error:step01:empty-file', verb); +// verbAnalytics('error:empty_file', verb); // handleError(err, errTxt, 'verb-widget-error-empty'); // } // // Error Check: Supported File Type // if (LIMITS[verb].acceptedFiles.indexOf(file.type) < 0) { -// verbAnalytics('error:step01:unsupported-file-type', verb); +// verbAnalytics('error:unsupported_type', verb); // handleError(err, errTxt, 'verb-widget-error-unsupported'); // return; // } @@ -44,6 +47,8 @@ const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; // handleError(err, errTxt, 'verb-widget-error-large', LIMITS[verb].maxFileSizeFriendly); // } // }; +// Page: Upload Error acrobat:verb-fillsign:error +// Page: Upload Error acrobat:verb-fillsign:error:max_page_count const setDraggingClass = (widget, shouldToggle) => { shouldToggle ? widget.classList.add('dragging') : widget.classList.remove('dragging'); @@ -108,6 +113,10 @@ export default async function init(element) { verbAnalytics('landing:shown', VERB); + widgetMobileButton.addEventListener('click', () => { + verbAnalytics('goto-app:clicked', VERB); + }); + button.addEventListener('click', () => { verbAnalytics('dropzone:choose-file-clicked', VERB); }); @@ -116,6 +125,9 @@ export default async function init(element) { verbAnalytics('choose-file:close', VERB); }); + // Page : File upload events acrobat:verb-fillsign:job:uploaded + // Page : File upload events acrobat:verb-fillsign:job:uploading + widget.addEventListener('dragover', (e) => { e.preventDefault(); setDraggingClass(widget, true); @@ -131,12 +143,14 @@ export default async function init(element) { }); window.addEventListener('unity:track-analytics', (e) => { - if (e.detail.event === 'change') { + if (e.detail?.event === 'change') { verbAnalytics('choose-file:open', VERB); + setUser(); } - if (e.detail.event === 'drop') { + if (e.detail?.event === 'drop') { verbAnalytics('files-dropped', VERB); setDraggingClass(widget, false); + setUser(); } }); } diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js index b36f889b..73624ed8 100644 --- a/acrobat/scripts/alloy/verb-widget.js +++ b/acrobat/scripts/alloy/verb-widget.js @@ -1,3 +1,28 @@ +const params = new Proxy( + // eslint-disable-next-line compat/compat + new URLSearchParams(window.location.search), + { get: (searchParams, prop) => searchParams.get(prop) }, +); + +let appReferrer = params.x_api_client_id || params['x-product'] || ''; +if (params.x_api_client_location || params['x-product-location']) { + appReferrer = `${appReferrer}:${params.x_api_client_location || params['x-product-location']}`; +} +let trackingId = params.trackingid || ''; +if (params.mv) { + trackingId = `${trackingId}:${params.mv}`; +} +if (params.mv2) { + trackingId = `${trackingId}:${params.mv2}`; +} +const appTags = []; +if (params.workflow) { + appTags.push(params.workflow); +} +if (params.dropzone2) { + appTags.push('dropzone2'); +} + export default function init(eventName, verb) { const event = { documentUnloading: true, @@ -12,8 +37,44 @@ export default function init(eventName, verb) { }, _adobe_corpnew: { digitalData: { - dcweb: { event: { pagename: `acrobat:verb-${verb}:${eventName}` } }, - dcweb2: { event: { pagename: `acrobat:verb-${verb}:${eventName}` } }, + dcweb: { + event: { pagename: `acrobat:verb-${verb}:${eventName}` }, + source: { + user_agent: navigator.userAgent, + lang: document.documentElement.lang, + app_name: 'unity:adobe_com', + url: window.location.href, + app_referrer: appReferrer, + tracking_id: trackingId, + }, + user: { + locale: document.documentElement.lang.toLocaleLowerCase(), + id: 'DO WE NEED THIS?', + is_authenticated: false, + user_tags: [ + `${localStorage['unity.user'] ? 'frictionless_return_user' : 'frictionless_new_user'}`, + ], + }, + }, + dcweb2: { + event: { pagename: `acrobat:verb-${verb}:${eventName}` }, + source: { + user_agent: navigator.userAgent, + lang: document.documentElement.lang, + app_name: 'unity:adobe_com', + url: window.location.href, + app_referrer: appReferrer, + tracking_id: trackingId, + }, + user: { + locale: document.documentElement.lang.toLocaleLowerCase(), + id: 'DO WE NEED THIS?', + is_authenticated: false, + user_tags: [ + `${localStorage['unity.user'] ? 'frictionless_return_user' : 'frictionless_new_user'}`, + ], + }, + }, }, }, }, From 6ac8f6e2de56cabb05e700f16b372a8ef6947258 Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Thu, 19 Sep 2024 08:50:18 +0800 Subject: [PATCH 4/7] Eslint fix --- acrobat/blocks/unity/unity.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acrobat/blocks/unity/unity.js b/acrobat/blocks/unity/unity.js index 3355e475..b2d66d1a 100644 --- a/acrobat/blocks/unity/unity.js +++ b/acrobat/blocks/unity/unity.js @@ -100,6 +100,7 @@ function getUnityLibs(prodLibs = '/unitylibs') { && !hostname.includes('localhost')) { return prodLibs; } + // eslint-disable-next-line compat/compat const branch = new URLSearchParams(window.location.search).get('unitylibs') || 'main'; if (branch.indexOf('--') > -1) return `https://${branch}.hlx.live/unitylibs`; return `https://${branch}--unity--adobecom.hlx.live/unitylibs`; From aa541ddcc01f496fc8759c56c1ced490d39f4b1d Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Thu, 19 Sep 2024 13:30:11 -0600 Subject: [PATCH 5/7] updates --- acrobat/blocks/verb-widget/verb-widget.js | 108 +++++++++++++--------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index 73a766dd..bfa3b650 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -11,44 +11,16 @@ const setUser = () => { localStorage.setItem('unity.user', 'true'); }; -// const handleError = (err, errTxt, str, strTwo) => { -// err.classList.add('verb-error'); -// err.classList.remove('hide'); -// errTxt.textContent = `${window.mph[str]} ${strTwo || ''}`; - -// setTimeout(() => { -// err.classList.remove('verb-error'); -// err.classList.add('hide'); -// }, 5000); - -// // Add LANA Logs and AA -// }; - -// This function is no longer needed, I'll remove after we get error event from Unity team. -// handleError() is will need to be uncommented out too. - -// const sendToUnity = async (file, verb, err, errTxt) => { -// // Error Check: File Empty -// if (file.size < 1) { -// verbAnalytics('error:empty_file', verb); -// handleError(err, errTxt, 'verb-widget-error-empty'); -// } - -// // Error Check: Supported File Type -// if (LIMITS[verb].acceptedFiles.indexOf(file.type) < 0) { -// verbAnalytics('error:unsupported_type', verb); -// handleError(err, errTxt, 'verb-widget-error-unsupported'); -// return; -// } - -// // Error Check: File Too Large -// if (file.size > LIMITS[verb].maxFileSize) { -// verbAnalytics('error:step01:file-too-large', verb); -// handleError(err, errTxt, 'verb-widget-error-large', LIMITS[verb].maxFileSizeFriendly); -// } -// }; -// Page: Upload Error acrobat:verb-fillsign:error -// Page: Upload Error acrobat:verb-fillsign:error:max_page_count +const handleError = (err, errTxt, str, strTwo) => { + err.classList.add('verb-error'); + err.classList.remove('hide'); + errTxt.textContent = `${window.mph[str]} ${strTwo || ''}`; + + setTimeout(() => { + err.classList.remove('verb-error'); + err.classList.add('hide'); + }, 5000); +}; const setDraggingClass = (widget, shouldToggle) => { shouldToggle ? widget.classList.add('dragging') : widget.classList.remove('dragging'); @@ -111,6 +83,7 @@ export default async function init(element) { element.append(widget, footer); + // Analytics verbAnalytics('landing:shown', VERB); widgetMobileButton.addEventListener('click', () => { @@ -118,16 +91,13 @@ export default async function init(element) { }); button.addEventListener('click', () => { - verbAnalytics('dropzone:choose-file-clicked', VERB); + verbAnalytics('filepicker:shown', VERB); }); button.addEventListener('cancel', () => { verbAnalytics('choose-file:close', VERB); }); - // Page : File upload events acrobat:verb-fillsign:job:uploaded - // Page : File upload events acrobat:verb-fillsign:job:uploading - widget.addEventListener('dragover', (e) => { e.preventDefault(); setDraggingClass(widget, true); @@ -147,10 +117,62 @@ export default async function init(element) { verbAnalytics('choose-file:open', VERB); setUser(); } + // maybe new event name files-dropped? if (e.detail?.event === 'drop') { - verbAnalytics('files-dropped', VERB); + verbAnalytics('files-dropped', VERB, e.detail?.data); setDraggingClass(widget, false); setUser(); } + if (e.detail?.event === 'choose-file-clicked') { + verbAnalytics('dropzone:choose-file-clicked', VERB, e.detail?.data); + setUser(); + } + + if (e.detail?.event === 'uploading') { + verbAnalytics('job:uploading', VERB, e.detail?.data); + setUser(); + } + + if (e.detail?.event === 'uploaded') { + verbAnalytics('job:uploaded', VERB, e.detail?.data); + setUser(); + } + }); + + // Errors, Analytics & Logging + window.addEventListener('unity:show-error-toast', (e) => { + if (e.detail?.code === 'only_accept_one_file') { + handleError(errorState, errorStateText, 'verb-widget-error-multi'); + verbAnalytics('error', VERB); + } + + if (e.detail?.code === 'unsupported_type') { + handleError(errorState, errorStateText, 'verb-widget-error-unsupported'); + verbAnalytics('error:unsupported_type', VERB); + } + + if (e.detail?.code === 'empty_file') { + handleError(errorState, errorStateText, 'verb-widget-error-empty'); + verbAnalytics('error:empty_file', VERB); + } + + // Code may be wrong. should be 'file_too_large' + if (e.detail?.code === 'file_too_largempty_file') { + handleError(errorState, errorStateText, 'verb-widget-error-large', LIMITS[VERB].maxFileSizeFriendly); + verbAnalytics('error', VERB); + } + + if (e.detail?.code === 'max_page_count') { + handleError(errorState, errorStateText, 'verb-widget-error-max', LIMITS[VERB].maxNumFiles); + verbAnalytics('error:max_page_count', VERB); + } + + // acrobat:verb-fillsign:error:page_count_missing_from_metadata_api + // acrobat:verb-fillsign:error:403 + // acrobat:verb-fillsign:error + // LANA for 403 }); } + +// const ce = (new CustomEvent('unity:show-error-toast', { detail: { code: 'only_accept_one_file', message: 'Error message' } })); +// dispatchEvent(ce) From 86776f3c1eb4daecbd88b23ee20ab5189acfde3e Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Thu, 19 Sep 2024 14:25:34 -0600 Subject: [PATCH 6/7] console logs for ez debug --- acrobat/blocks/verb-widget/verb-widget.js | 3 +++ acrobat/scripts/alloy/verb-widget.js | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index bfa3b650..4b0281b6 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -23,6 +23,7 @@ const handleError = (err, errTxt, str, strTwo) => { }; const setDraggingClass = (widget, shouldToggle) => { + // eslint-disable-next-line chai-friendly/no-unused-expressions shouldToggle ? widget.classList.add('dragging') : widget.classList.remove('dragging'); }; @@ -141,6 +142,8 @@ export default async function init(element) { // Errors, Analytics & Logging window.addEventListener('unity:show-error-toast', (e) => { + console.log(`⛔️ Error Code - ${e.detail?.code}`); + if (e.detail?.code === 'only_accept_one_file') { handleError(errorState, errorStateText, 'verb-widget-error-multi'); verbAnalytics('error', VERB); diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js index 73624ed8..c5829ac9 100644 --- a/acrobat/scripts/alloy/verb-widget.js +++ b/acrobat/scripts/alloy/verb-widget.js @@ -24,6 +24,7 @@ if (params.dropzone2) { } export default function init(eventName, verb) { + console.log(`📡 Event Name - acrobat:verb-${verb}:${eventName}`); const event = { documentUnloading: true, data: { @@ -39,6 +40,12 @@ export default function init(eventName, verb) { digitalData: { dcweb: { event: { pagename: `acrobat:verb-${verb}:${eventName}` }, + content: { + type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + size: '2-5MB', + count: 1, + extension: 'docx', + }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, @@ -58,6 +65,12 @@ export default function init(eventName, verb) { }, dcweb2: { event: { pagename: `acrobat:verb-${verb}:${eventName}` }, + content: { + type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + size: '2-5MB', + count: 1, + extension: 'docx', + }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, From 922880059ed341f4e469cdb406b90717a5b87da5 Mon Sep 17 00:00:00 2001 From: Blaine Gunn Date: Thu, 19 Sep 2024 15:10:11 -0600 Subject: [PATCH 7/7] MWPW-158834 --- acrobat/blocks/verb-widget/verb-widget.js | 10 ++++++++-- acrobat/img/verb-widget/fillsign.png | Bin 0 -> 3224 bytes 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 acrobat/img/verb-widget/fillsign.png diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index cc54d9e0..7e2956b1 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -80,9 +80,12 @@ export default async function init(element) { const widgetButton = createTag('label', { for: 'file-upload', class: 'verb-cta' }, window.mph['verb-widget-cta']); const widgetMobileButton = createTag('a', { class: 'verb-mobile-cta', href: mobileLink }, window.mph['verb-widget-cta-mobile']); const button = createTag('input', { type: 'file', id: 'file-upload', class: 'hide' }); - const widgetImage = createTag('img', { class: 'verb-image', src: children[1].querySelector('img')?.src }); + const widgetImage = createTag('img', { class: 'verb-image', src: `/acrobat/img/verb-widget/${VERB}.png` }); // Since we're using placeholders we need a solution for the hyperlinks - const legal = createTag('p', { class: 'verb-legal' }, window.mph['verb-widget-legal']); + const legal = createTag('p', { class: 'verb-legal' }, `${window.mph['verb-widget-legal']} `); + const terms = createTag('a', { class: 'verb-legal-url', target: '_blank', href: 'https://www.adobe.com/legal/terms.html' }, window.mph.tou); + const and = createTag('span', { class: 'verb-legal-url' }, ` ${window.mph.and} `); + const privacy = createTag('a', { class: 'verb-legal-url', target: '_blank', href: 'https://www.adobe.com/privacy/policy.html' }, `${window.mph.pp}.`); const iconSecurity = createTag('div', { class: 'security-icon' }); const footer = createTag('div', { class: 'verb-footer' }); @@ -102,6 +105,9 @@ export default async function init(element) { } else { widgetLeft.append(widgetHeader, widgetHeading, widgetCopy, errorState, widgetButton, button); } + + legal.append(terms, and, privacy); + footer.append(iconSecurity, legal); element.append(widget, footer); diff --git a/acrobat/img/verb-widget/fillsign.png b/acrobat/img/verb-widget/fillsign.png new file mode 100644 index 0000000000000000000000000000000000000000..7e450f43bf26f6b876da60aedf8cccc7b7d7ceeb GIT binary patch literal 3224 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 K000000000C7(815 literal 0 HcmV?d00001