From 76ba399f40478fbd33b97fa4fcbb0769ea8daec2 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Fri, 30 Aug 2024 12:54:31 -0600 Subject: [PATCH 01/17] sidekick publish button state --- libs/utils/sidekick-decorate.js | 33 ++++++++++++++++++++++++++------- libs/utils/utils.js | 25 +++++++++++++++++++++++++ tools/utils/utils.js | 17 ++++++++++++++++- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index ec125ef650..5922a49e30 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,4 +1,23 @@ +import { miloUserCanPublish } from './utils.js'; + +const PUBLISH_BTN = '.publish.plugin button'; +const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; +const NO_AUTH_MESSAGE = 'This page currently cannot be published'; + export default function stylePublish(sk) { + sk.addEventListener('statusfetched', async (event) => { + const thisPage = event?.detail?.data; + const enablePublish = await miloUserCanPublish(thisPage); + const publishBtn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); + if (publishBtn) { + publishBtn.setAttribute('disabled', !enablePublish); + const message = publishBtn.querySelector('span'); + if (message) { + message.innerText = enablePublish ? CONFIRM_MESSAGE : NO_AUTH_MESSAGE; + } + } + }); + const style = new CSSStyleSheet(); style.replaceSync(` :host { @@ -10,12 +29,14 @@ export default function stylePublish(sk) { order: 100; } .publish.plugin button { + position: relative; + } + .publish.plugin button:not([disabled=true]) { background: var(--bg-color); border-color: #b46157; color: var(--text-color); - position: relative; } - .publish.plugin button:hover { + .publish.plugin button:not([disabled=true]):hover { background-color: var(--hlx-sk-button-hover-bg); border-color: unset; color: var(--hlx-sk-button-hover-color); @@ -51,9 +72,7 @@ export default function stylePublish(sk) { `); sk.shadowRoot.adoptedStyleSheets = [style]; setTimeout(() => { - const btn = sk.shadowRoot.querySelector('.publish.plugin button'); - btn?.insertAdjacentHTML('beforeend', ` - Are you sure? This will publish to production. - `); - }, 500); + const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); + btn?.insertAdjacentHTML('beforeend', `${CONFIRM_MESSAGE}`); + }, 800); } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 52d3b0e190..04d62f50b3 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1,5 +1,7 @@ /* eslint-disable no-console */ +import { getCustomConfig } from '../../tools/utils/utils.js'; + const MILO_TEMPLATES = [ '404', 'featured-story', @@ -1295,3 +1297,26 @@ export function loadLana(options = {}) { } export const reloadPage = () => window.location.reload(); + +export const miloUserCanPublish = async (page) => { + if (!page) return false; + // sidekick status detail + const { live, profile, webPath } = page; + let canPublish = live?.permissions?.includes('write'); + // check custom publish permissions config + const config = await getCustomConfig('/.milo/publish-permissions-config.json'); + if (canPublish && config) { + // supporting only one wildcard(**) + const item = config.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + const publishers = config[item.group]; + if (publishers && profile?.email) { + canPublish = !!publishers.data?.find(({ user }) => user === profile.email); + } + } + } + return canPublish; +}; diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 29d68397d8..3c64129354 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -1,6 +1,7 @@ const IMS_CLIENT_ID = 'milo_ims'; const IMS_PROD_URL = 'https://auth.services.adobe.com/imslib/imslib.min.js'; const STYLE_SHEETS = {}; +const CONFIGS = {}; const getImsToken = async (loadScript) => { window.adobeid = { @@ -25,4 +26,18 @@ const getSheet = async (url) => { return sheet; }; -export { getImsToken, getSheet }; +const getCustomConfig = async (path) => { + if (CONFIGS[path] !== undefined) { + return CONFIGS[path]; + } + let config = null; + const resp = await fetch(path); + if (resp.ok) { + const json = await resp.json(); + config = json; + } + CONFIGS[path] = config; + return CONFIGS[path]; +}; + +export { getImsToken, getSheet, getCustomConfig }; From ed815ba162942f5424d56807e569f51b9a83c09b Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 3 Sep 2024 11:40:49 -0600 Subject: [PATCH 02/17] bulk publish publish config errors --- .../components/bulk-publisher.js | 18 +++++++++----- libs/blocks/bulk-publish-v2/services.js | 24 +++++++++++++++++++ libs/utils/sidekick-decorate.js | 4 ++-- libs/utils/utils.js | 8 +++---- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js index c0c88ae5db..4487d9e9ad 100644 --- a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js +++ b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js @@ -1,7 +1,7 @@ import './job-process.js'; import { LitElement, html } from '../../../deps/lit-all.min.js'; import { getSheet } from '../../../../tools/utils/utils.js'; -import { authenticate, startJob } from '../services.js'; +import { authenticate, getPublishable, startJob } from '../services.js'; import { getConfig } from '../../../utils/utils.js'; import { delay, @@ -95,7 +95,11 @@ class BulkPublish2 extends LitElement { this.validateUrls(); } - setJobErrors(errors) { + setJobErrors(jobErrors, authErrors) { + const errors = [ + ...jobErrors, + ...authErrors.map((href) => ({ href, message: 'Not able to publish' })), + ]; const urls = []; errors.forEach((error) => { const matched = this.urls.filter((url) => { @@ -323,7 +327,8 @@ class BulkPublish2 extends LitElement { class="panel-title" @click=${handleToggle}> - Job Results + ${this.jobs.length ? html`${this.jobs.length}` : ''} + Job Result${this.jobs.length > 1 ? 's' : ''}
{ return newQueue; }; +// publish authentication services +const getPublishable = async ({ urls, process, user }) => { + let publishable = { authorized: [], unauthorized: [] }; + if (!isLive(process)) { + publishable.authorized = urls; + } else { + const { permissions, profile } = user; + const live = { permissions: ['read'] }; + if (permissions?.publish?.canUse) { + live.permissions.push('write'); + } + publishable = await urls.reduce(async (init, url) => { + const result = await init; + const detail = { webPath: new URL(url).pathname, live, profile }; + const auth = await userCanPublishPage(detail); + result[`${auth ? '' : 'un'}authorized`].push(url); + return result; + }, Promise.resolve(publishable)); + } + return publishable; +}; + export { authenticate, + getPublishable, pollJobStatus, startJob, updateRetry, diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index 5922a49e30..dfcaf5612f 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,4 +1,4 @@ -import { miloUserCanPublish } from './utils.js'; +import { userCanPublishPage } from './utils.js'; const PUBLISH_BTN = '.publish.plugin button'; const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; @@ -7,7 +7,7 @@ const NO_AUTH_MESSAGE = 'This page currently cannot be published'; export default function stylePublish(sk) { sk.addEventListener('statusfetched', async (event) => { const thisPage = event?.detail?.data; - const enablePublish = await miloUserCanPublish(thisPage); + const enablePublish = await userCanPublishPage(thisPage); const publishBtn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); if (publishBtn) { publishBtn.setAttribute('disabled', !enablePublish); diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 04d62f50b3..1cf9fb1b86 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1298,15 +1298,15 @@ export function loadLana(options = {}) { export const reloadPage = () => window.location.reload(); -export const miloUserCanPublish = async (page) => { - if (!page) return false; +export const userCanPublishPage = async (detail) => { + if (!detail) return false; // sidekick status detail - const { live, profile, webPath } = page; + const { live, profile, webPath } = detail; let canPublish = live?.permissions?.includes('write'); // check custom publish permissions config const config = await getCustomConfig('/.milo/publish-permissions-config.json'); if (canPublish && config) { - // supporting only one wildcard(**) + // supporting one wildcard (**) const item = config.urls?.data?.find(({ url }) => ( url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath )); From 0d824e6792a39b9f7440737ff4788c2f33d199af Mon Sep 17 00:00:00 2001 From: Sartxi Date: Thu, 5 Sep 2024 10:37:48 -0600 Subject: [PATCH 03/17] dial in functionality add custom messages and denylist --- .../components/bulk-publisher.js | 5 +-- libs/blocks/bulk-publish-v2/services.js | 7 ++-- libs/utils/sidekick-decorate.js | 18 ++++----- libs/utils/utils.js | 37 ++++++++++++------- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js index 4487d9e9ad..1ad498084e 100644 --- a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js +++ b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js @@ -96,10 +96,7 @@ class BulkPublish2 extends LitElement { } setJobErrors(jobErrors, authErrors) { - const errors = [ - ...jobErrors, - ...authErrors.map((href) => ({ href, message: 'Not able to publish' })), - ]; + const errors = [...jobErrors, ...authErrors]; const urls = []; errors.forEach((error) => { const matched = this.urls.filter((url) => { diff --git a/libs/blocks/bulk-publish-v2/services.js b/libs/blocks/bulk-publish-v2/services.js index b2acdcb38d..e6a03679f9 100644 --- a/libs/blocks/bulk-publish-v2/services.js +++ b/libs/blocks/bulk-publish-v2/services.js @@ -247,7 +247,7 @@ const updateRetry = async ({ queue, urls, process }) => { return newQueue; }; -// publish authentication services +// publish authentication service const getPublishable = async ({ urls, process, user }) => { let publishable = { authorized: [], unauthorized: [] }; if (!isLive(process)) { @@ -261,8 +261,9 @@ const getPublishable = async ({ urls, process, user }) => { publishable = await urls.reduce(async (init, url) => { const result = await init; const detail = { webPath: new URL(url).pathname, live, profile }; - const auth = await userCanPublishPage(detail); - result[`${auth ? '' : 'un'}authorized`].push(url); + const { canPublish, message } = await userCanPublishPage(detail); + if (canPublish) result.authorized.push(url); + else result.unauthorized.push({ href: url, message }); return result; }, Promise.resolve(publishable)); } diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index dfcaf5612f..a181141010 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -2,18 +2,17 @@ import { userCanPublishPage } from './utils.js'; const PUBLISH_BTN = '.publish.plugin button'; const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; -const NO_AUTH_MESSAGE = 'This page currently cannot be published'; export default function stylePublish(sk) { sk.addEventListener('statusfetched', async (event) => { - const thisPage = event?.detail?.data; - const enablePublish = await userCanPublishPage(thisPage); - const publishBtn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); - if (publishBtn) { - publishBtn.setAttribute('disabled', !enablePublish); - const message = publishBtn.querySelector('span'); - if (message) { - message.innerText = enablePublish ? CONFIRM_MESSAGE : NO_AUTH_MESSAGE; + const page = event?.detail?.data; + const { canPublish, message } = await userCanPublishPage(page); + const btn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); + if (btn) { + btn.setAttribute('disabled', !canPublish); + const messageText = btn.querySelector('span'); + if (messageText) { + messageText.innerText = canPublish ? CONFIRM_MESSAGE : message; } } }); @@ -73,6 +72,7 @@ export default function stylePublish(sk) { sk.shadowRoot.adoptedStyleSheets = [style]; setTimeout(() => { const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); + btn?.setAttribute('disabled', true); btn?.insertAdjacentHTML('beforeend', `${CONFIRM_MESSAGE}`); }, 800); } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 1cf9fb1b86..db0ea18b14 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1300,23 +1300,32 @@ export const reloadPage = () => window.location.reload(); export const userCanPublishPage = async (detail) => { if (!detail) return false; - // sidekick status detail const { live, profile, webPath } = detail; let canPublish = live?.permissions?.includes('write'); - // check custom publish permissions config - const config = await getCustomConfig('/.milo/publish-permissions-config.json'); - if (canPublish && config) { - // supporting one wildcard (**) - const item = config.urls?.data?.find(({ url }) => ( - url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath - )); - if (item) { - canPublish = false; - const publishers = config[item.group]; - if (publishers && profile?.email) { - canPublish = !!publishers.data?.find(({ user }) => user === profile.email); + let message = 'Publishing is currently disabled for this page'; + if (canPublish) { + const config = await getCustomConfig('/.milo/publish-permissions-config.json'); + if (config) { + const item = config.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + if (item.message) message = item.message; + const group = config[item.group]; + if (group && profile?.email) { + let isDeny; + const user = group.data?.find(({ allow, deny }) => { + if (deny) { + isDeny = true; + return deny === profile.email; + } + return allow === profile.email; + }); + canPublish = isDeny ? !user : !!user; + } } } } - return canPublish; + return { canPublish, message }; }; From abe57af9e46eee4a98a318eefcf9b55ef54ab988 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Fri, 6 Sep 2024 11:11:26 -0600 Subject: [PATCH 04/17] publish permission in bulk pub testing --- .../components/bulk-publisher.js | 2 + test/blocks/bulk-publish-v2/mocks/fetch.js | 3 +- .../mocks/response/permissions.json | 53 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/blocks/bulk-publish-v2/mocks/response/permissions.json diff --git a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js index 1ad498084e..712f663fa0 100644 --- a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js +++ b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js @@ -410,6 +410,7 @@ class BulkPublish2 extends LitElement { renderPromptLoader() { setTimeout(() => { + /* c8 ignore next 4 */ const loader = this.renderRoot.querySelector('.load-indicator'); const message = this.renderRoot.querySelector('.message'); loader?.classList.add('hide'); @@ -430,6 +431,7 @@ class BulkPublish2 extends LitElement { const canUse = Object.values(this.user.permissions).filter((perms) => perms.canUse); if (canUse.length) return html``; message = 'Current user is not authorized to use Bulk Publishing Tool'; + /* c8 ignore next 3 */ } else { message = 'Please sign in to AEM sidekick to continue'; } diff --git a/test/blocks/bulk-publish-v2/mocks/fetch.js b/test/blocks/bulk-publish-v2/mocks/fetch.js index 8ab62d86d3..48d7637f54 100644 --- a/test/blocks/bulk-publish-v2/mocks/fetch.js +++ b/test/blocks/bulk-publish-v2/mocks/fetch.js @@ -11,6 +11,7 @@ const requests = { delstatus: 'https://admin.hlx.page/job/adobecom/milo/main/preview-remove/job-2024-01-24t23-16-20-377z/details', retry: 'https://admin.hlx.page/preview/adobecom/milo/main/tools/bulk-publish-v2-test', index: 'https://admin.hlx.page/index/adobecom/milo/main/tools/bulk-publish-v2-test', + permissions: '/.milo/publish-permissions-config.json', }; const getMocks = async () => { @@ -25,7 +26,7 @@ const getMocks = async () => { export async function mockFetch() { const mocks = await getMocks(); stub(window, 'fetch').callsFake((...args) => { - const headers = args[1].body ?? null; + const headers = args[1]?.body ?? null; const body = headers ? JSON.parse(headers) : false; const [resource] = args; const response = mocks.find((mock) => (body.delete ? mock.request === 'delete' : mock.url === resource)); diff --git a/test/blocks/bulk-publish-v2/mocks/response/permissions.json b/test/blocks/bulk-publish-v2/mocks/response/permissions.json new file mode 100644 index 0000000000..af9e2b0f08 --- /dev/null +++ b/test/blocks/bulk-publish-v2/mocks/response/permissions.json @@ -0,0 +1,53 @@ +{ + "urls": { + "total": 4, + "offset": 0, + "limit": 4, + "data": [ + { + "url": "/drafts/sarchibeque/video-test", + "group": "gwp-US", + "message": "Publishing disabled until September 10th" + }, + { + "url": "/drafts/sarchibeque/aside-variations", + "group": "gwp-FR", + "message": "" + }, + { + "url": "/drafts/sarchibeque/fragments/**", + "group": "gwp-US", + "message": "" + }, + { + "url": "/drafts/sarchibeque/aside-links", + "group": "no-publish", + "message": "" + } + ] + }, + "gwp-US": { + "total": 2, + "offset": 0, + "limit": 2, + "data": [ + { "allow": "testuser@adobe.com" }, + { "allow": "testuser1@adobe.com" } + ] + }, + "no-publish": { + "total": 2, + "offset": 0, + "limit": 2, + "data": [{ "deny": "user@adobe.com" }, { "deny": "thi62185@adobe.com" }] + }, + "gwp-FR": { + "total": 1, + "offset": 0, + "limit": 1, + "data": [{ "allow": "thi62185@adobe.com" }] + }, + ":version": 3, + ":names": ["urls", "gwp-US", "no-publish", "gwp-FR"], + ":type": "multi-sheet" +} From 0155b3f69d84e125c5d74aef8e38e8861197aadf Mon Sep 17 00:00:00 2001 From: Sartxi Date: Fri, 6 Sep 2024 11:35:56 -0600 Subject: [PATCH 05/17] test permissions and utils coverage --- libs/utils/utils.js | 1 + .../bulk-publish-v2/bulk-publish-v2.test.js | 10 ++++++ .../bulk-publish-v2/mocks/authentication.js | 2 +- .../mocks/response/permissions.json | 35 +++---------------- tools/utils/utils.js | 1 + 5 files changed, 17 insertions(+), 32 deletions(-) diff --git a/libs/utils/utils.js b/libs/utils/utils.js index db0ea18b14..432d01a1e1 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1317,6 +1317,7 @@ export const userCanPublishPage = async (detail) => { let isDeny; const user = group.data?.find(({ allow, deny }) => { if (deny) { + /* c8 ignore next 3 */ isDeny = true; return deny === profile.email; } diff --git a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js index 3b634681d2..c8f3ad0ebd 100644 --- a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js +++ b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js @@ -122,6 +122,16 @@ describe('Bulk Publish Tool', () => { bulkPub.clearJobs(); }); + it('can trigger cannot publish config', async () => { + await clock.runAllAsync(); + await setProcess(rootEl, 'publish'); + await setTextArea(rootEl, 'https://error--milo--adobecom.hlx.page/not/a/valid/path'); + await mouseEvent(rootEl.querySelector('#RunProcess')); + const errors = rootEl.querySelector('.errors'); + expect(errors.querySelector('strong').innerText).to.equal('Publishing disabled until the test is over'); + await mouseEvent(rootEl.querySelector('.fix-btn')); + }); + it('can submit valid bulk publish job', async () => { await clock.runAllAsync(); await mouseEvent(rootEl.querySelector('.switch.full')); diff --git a/test/blocks/bulk-publish-v2/mocks/authentication.js b/test/blocks/bulk-publish-v2/mocks/authentication.js index 22d01045ad..ca826fa82d 100644 --- a/test/blocks/bulk-publish-v2/mocks/authentication.js +++ b/test/blocks/bulk-publish-v2/mocks/authentication.js @@ -23,7 +23,7 @@ class MockAuth extends HTMLElement { bubbles: true, detail: { data: { - profile: { name: 'Unit Test' }, + profile: { name: 'Unit Test', email: 'tester@adobe.com' }, preview: { permissions }, live: { permissions }, }, diff --git a/test/blocks/bulk-publish-v2/mocks/response/permissions.json b/test/blocks/bulk-publish-v2/mocks/response/permissions.json index af9e2b0f08..37fe5eaec9 100644 --- a/test/blocks/bulk-publish-v2/mocks/response/permissions.json +++ b/test/blocks/bulk-publish-v2/mocks/response/permissions.json @@ -5,28 +5,13 @@ "limit": 4, "data": [ { - "url": "/drafts/sarchibeque/video-test", - "group": "gwp-US", - "message": "Publishing disabled until September 10th" - }, - { - "url": "/drafts/sarchibeque/aside-variations", - "group": "gwp-FR", - "message": "" - }, - { - "url": "/drafts/sarchibeque/fragments/**", - "group": "gwp-US", - "message": "" - }, - { - "url": "/drafts/sarchibeque/aside-links", - "group": "no-publish", - "message": "" + "url": "/not/a/valid/path", + "group": "gwp-test", + "message": "Publishing disabled until the test is over" } ] }, - "gwp-US": { + "gwp-test": { "total": 2, "offset": 0, "limit": 2, @@ -35,18 +20,6 @@ { "allow": "testuser1@adobe.com" } ] }, - "no-publish": { - "total": 2, - "offset": 0, - "limit": 2, - "data": [{ "deny": "user@adobe.com" }, { "deny": "thi62185@adobe.com" }] - }, - "gwp-FR": { - "total": 1, - "offset": 0, - "limit": 1, - "data": [{ "allow": "thi62185@adobe.com" }] - }, ":version": 3, ":names": ["urls", "gwp-US", "no-publish", "gwp-FR"], ":type": "multi-sheet" diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 3c64129354..b66a76777c 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -27,6 +27,7 @@ const getSheet = async (url) => { }; const getCustomConfig = async (path) => { + /* c8 ignore next 3 */ if (CONFIGS[path] !== undefined) { return CONFIGS[path]; } From 47c2b823e65cbbfc2217c1d08d4fa7a6a08ba4fe Mon Sep 17 00:00:00 2001 From: Sartxi Date: Mon, 9 Sep 2024 10:32:09 -0600 Subject: [PATCH 06/17] remove extra variable assignment --- tools/utils/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/utils/utils.js b/tools/utils/utils.js index b66a76777c..327604793d 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -34,8 +34,7 @@ const getCustomConfig = async (path) => { let config = null; const resp = await fetch(path); if (resp.ok) { - const json = await resp.json(); - config = json; + config = await resp.json(); } CONFIGS[path] = config; return CONFIGS[path]; From e410403afb30981d197548d7a52635409759a8d1 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Mon, 9 Sep 2024 15:37:54 -0600 Subject: [PATCH 07/17] add no signin message --- libs/utils/utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 432d01a1e1..015178f37c 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1302,7 +1302,9 @@ export const userCanPublishPage = async (detail) => { if (!detail) return false; const { live, profile, webPath } = detail; let canPublish = live?.permissions?.includes('write'); - let message = 'Publishing is currently disabled for this page'; + let message = canPublish === undefined + ? 'Please sign into sidekick to publish this page' + : 'Publishing is currently disabled for this page'; if (canPublish) { const config = await getCustomConfig('/.milo/publish-permissions-config.json'); if (config) { From b54329c06c5080603a0237a0608cba40fef21548 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Mon, 9 Sep 2024 15:42:16 -0600 Subject: [PATCH 08/17] put back timout for init sidekick pub button decoration --- libs/utils/sidekick-decorate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index a181141010..b4dc9b06b7 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -74,5 +74,5 @@ export default function stylePublish(sk) { const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); btn?.setAttribute('disabled', true); btn?.insertAdjacentHTML('beforeend', `${CONFIRM_MESSAGE}`); - }, 800); + }, 500); } From 4a4b69d5220ec5f489cd1934dd7060625ff3f1d1 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 10 Sep 2024 10:33:14 -0600 Subject: [PATCH 09/17] fix edge case where sk event isnt fired on refresh --- libs/utils/sidekick-decorate.js | 54 ++++++++++++++++++++++++--------- libs/utils/utils.js | 50 ++++++++++++++---------------- tools/utils/utils.js | 3 +- 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index b4dc9b06b7..aaf1592a28 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,21 +1,21 @@ import { userCanPublishPage } from './utils.js'; const PUBLISH_BTN = '.publish.plugin button'; +const BACKUP_PROFILE = '.profile-email'; const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; export default function stylePublish(sk) { - sk.addEventListener('statusfetched', async (event) => { - const page = event?.detail?.data; - const { canPublish, message } = await userCanPublishPage(page); - const btn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); - if (btn) { - btn.setAttribute('disabled', !canPublish); - const messageText = btn.querySelector('span'); - if (messageText) { - messageText.innerText = canPublish ? CONFIRM_MESSAGE : message; - } + const setupPublishBtn = async (page, btn) => { + const { canPublish, message } = await userCanPublishPage(page, false); + btn.setAttribute('disabled', !canPublish); + const messageText = btn.querySelector('span'); + const text = canPublish ? CONFIRM_MESSAGE : message; + if (messageText) { + messageText.innerText = text; + } else { + btn.insertAdjacentHTML('beforeend', `${text}`); } - }); + }; const style = new CSSStyleSheet(); style.replaceSync(` @@ -42,7 +42,7 @@ export default function stylePublish(sk) { } .publish.plugin button > span { display: none; - background: var(--bg-color); + background: #666; border-radius: 4px; line-height: 1.2rem; padding: 8px 12px; @@ -53,6 +53,9 @@ export default function stylePublish(sk) { width: 150px; white-space: pre-wrap; } + .publish.plugin button:not([disabled=true]) > span { + background: var(--bg-color); + } .publish.plugin button:hover > span { display: block; color: var(--text-color); @@ -61,18 +64,39 @@ export default function stylePublish(sk) { content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; - border-bottom: 6px solid var(--bg-color); + border-bottom: 6px solid #666; position: absolute; text-align: center; top: -6px; left: 50%; transform: translateX(-50%); } + .publish.plugin button:not([disabled=true]) > span:before { + border-bottom: 6px solid var(--bg-color); + } `); + sk.shadowRoot.adoptedStyleSheets = [style]; - setTimeout(() => { + + sk.addEventListener('statusfetched', async (event) => { + const page = event?.detail?.data; + const btn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); + if (page && btn) { + setupPublishBtn(page, btn); + } + }); + + setTimeout(async () => { const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); btn?.setAttribute('disabled', true); - btn?.insertAdjacentHTML('beforeend', `${CONFIRM_MESSAGE}`); + const message = btn?.querySelector('span'); + if (btn && !message) { + const page = { + webPath: window.location.pathname, + // added for edge case where the statusfetched event isnt fired on refresh + profile: { email: sk.shadowRoot.querySelector(BACKUP_PROFILE)?.innerText }, + }; + setupPublishBtn(page, btn); + } }, 500); } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 015178f37c..9e37b01434 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1298,35 +1298,31 @@ export function loadLana(options = {}) { export const reloadPage = () => window.location.reload(); -export const userCanPublishPage = async (detail) => { +export const userCanPublishPage = async (detail, isBulk = true) => { if (!detail) return false; const { live, profile, webPath } = detail; - let canPublish = live?.permissions?.includes('write'); - let message = canPublish === undefined - ? 'Please sign into sidekick to publish this page' - : 'Publishing is currently disabled for this page'; - if (canPublish) { - const config = await getCustomConfig('/.milo/publish-permissions-config.json'); - if (config) { - const item = config.urls?.data?.find(({ url }) => ( - url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath - )); - if (item) { - canPublish = false; - if (item.message) message = item.message; - const group = config[item.group]; - if (group && profile?.email) { - let isDeny; - const user = group.data?.find(({ allow, deny }) => { - if (deny) { - /* c8 ignore next 3 */ - isDeny = true; - return deny === profile.email; - } - return allow === profile.email; - }); - canPublish = isDeny ? !user : !!user; - } + let canPublish = isBulk ? live?.permissions?.includes('write') : true; + let message = 'Publishing is currently disabled for this page'; + const config = await getCustomConfig('/.milo/publish-permissions-config.json'); + if (config) { + const item = config.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + if (item.message) message = item.message; + const group = config[item.group]; + if (group && profile?.email) { + let isDeny; + const user = group.data?.find(({ allow, deny }) => { + if (deny) { + /* c8 ignore next 3 */ + isDeny = true; + return deny === profile.email; + } + return allow === profile.email; + }); + canPublish = isDeny ? !user : !!user; } } } diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 327604793d..b66a76777c 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -34,7 +34,8 @@ const getCustomConfig = async (path) => { let config = null; const resp = await fetch(path); if (resp.ok) { - config = await resp.json(); + const json = await resp.json(); + config = json; } CONFIGS[path] = config; return CONFIGS[path]; From a584d9d87b797ca6152aebc7ae463830118cf259 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 10 Sep 2024 15:14:25 -0600 Subject: [PATCH 10/17] fix unit tests --- .../bulk-publish-v2/bulk-publish-v2.test.js | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js index c8f3ad0ebd..7cd56109b4 100644 --- a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js +++ b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js @@ -114,14 +114,6 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.fix-btn')); }); - it('can validate milo urls and enable form', async () => { - await clock.runAllAsync(); - await setProcess(rootEl, 'publish'); - await setTextArea(rootEl, testPage); - expect(rootEl.querySelector('#RunProcess').getAttribute('disable')).to.equal('false'); - bulkPub.clearJobs(); - }); - it('can trigger cannot publish config', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'publish'); @@ -132,6 +124,14 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.fix-btn')); }); + it('can validate milo urls and enable form', async () => { + await clock.runAllAsync(); + await setProcess(rootEl, 'publish'); + await setTextArea(rootEl, testPage); + expect(rootEl.querySelector('#RunProcess').getAttribute('disable')).to.equal('false'); + bulkPub.clearJobs(); + }); + it('can submit valid bulk publish job', async () => { await clock.runAllAsync(); await mouseEvent(rootEl.querySelector('.switch.full')); @@ -142,6 +142,17 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.switch.half')); }); + it('can toggle job timing flyout', async () => { + await clock.runAllAsync(); + const doneJobProcess = rootEl.querySelector('job-process'); + const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); + const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); + await mouseEvent(timerDetail); + await clock.runAllAsync(); + await mouseEvent(timerDetail); + expect(timerDetail.classList.contains('show-times')).to.be.false; + }); + it('can submit valid bulk preview job', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'preview'); @@ -184,17 +195,6 @@ describe('Bulk Publish Tool', () => { expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(4); }); - it('can toggle job timing flyout', async () => { - await clock.runAllAsync(); - const doneJobProcess = rootEl.querySelector('job-process'); - const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); - const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); - await mouseEvent(timerDetail); - await clock.runAllAsync(); - await mouseEvent(timerDetail); - expect(timerDetail.classList.contains('show-times')).to.be.false; - }); - it('can toggle view mode', async () => { await mouseEvent(rootEl.querySelector('.switch.full')); await clock.runAllAsync(); @@ -228,7 +228,6 @@ describe('Bulk Publish Tool', () => { it('can clear bulk jobs', async () => { await clock.runAllAsync(); await mouseEvent(rootEl.querySelector('.clear-jobs')); - await clock.runAllAsync(); expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(0); }); }); From 4e49a1387af566773a0e6d3da57c0ab52f73e83c Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 10 Sep 2024 15:24:31 -0600 Subject: [PATCH 11/17] put back var assignment --- tools/utils/utils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/utils/utils.js b/tools/utils/utils.js index b66a76777c..327604793d 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -34,8 +34,7 @@ const getCustomConfig = async (path) => { let config = null; const resp = await fetch(path); if (resp.ok) { - const json = await resp.json(); - config = json; + config = await resp.json(); } CONFIGS[path] = config; return CONFIGS[path]; From 62426d401272f7637049c982b9fe242d57fbb1d8 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 10 Sep 2024 16:50:49 -0600 Subject: [PATCH 12/17] dial in user can publish funtion --- libs/utils/utils.js | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 9e37b01434..e41a92123d 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1304,26 +1304,24 @@ export const userCanPublishPage = async (detail, isBulk = true) => { let canPublish = isBulk ? live?.permissions?.includes('write') : true; let message = 'Publishing is currently disabled for this page'; const config = await getCustomConfig('/.milo/publish-permissions-config.json'); - if (config) { - const item = config.urls?.data?.find(({ url }) => ( - url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath - )); - if (item) { - canPublish = false; - if (item.message) message = item.message; - const group = config[item.group]; - if (group && profile?.email) { - let isDeny; - const user = group.data?.find(({ allow, deny }) => { - if (deny) { - /* c8 ignore next 3 */ - isDeny = true; - return deny === profile.email; - } - return allow === profile.email; - }); - canPublish = isDeny ? !user : !!user; - } + const item = config?.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + if (item.message) message = item.message; + const group = config[item.group]; + if (group && profile?.email) { + let isDeny; + const user = group.data?.find(({ allow, deny }) => { + if (deny) { + /* c8 ignore next 3 */ + isDeny = true; + return deny === profile.email; + } + return allow === profile.email; + }); + canPublish = isDeny ? !user : !!user; } } return { canPublish, message }; From 35788f91acbe3cd9b6015fbc10012f269a30c9f5 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Wed, 11 Sep 2024 09:29:19 -0600 Subject: [PATCH 13/17] move publish utility to tools dir --- libs/blocks/bulk-publish-v2/services.js | 2 +- libs/tools/utils/publish.js | 32 +++++++++++++++++++++++++ libs/utils/sidekick-decorate.js | 2 +- libs/utils/utils.js | 31 ------------------------ 4 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 libs/tools/utils/publish.js diff --git a/libs/blocks/bulk-publish-v2/services.js b/libs/blocks/bulk-publish-v2/services.js index e6a03679f9..76af892bf2 100644 --- a/libs/blocks/bulk-publish-v2/services.js +++ b/libs/blocks/bulk-publish-v2/services.js @@ -1,4 +1,4 @@ -import { userCanPublishPage } from '../../utils/utils.js'; +import userCanPublishPage from '../../tools/utils/publish.js'; import { PROCESS_TYPES, getErrorText, diff --git a/libs/tools/utils/publish.js b/libs/tools/utils/publish.js new file mode 100644 index 0000000000..49acf0928d --- /dev/null +++ b/libs/tools/utils/publish.js @@ -0,0 +1,32 @@ +import { getCustomConfig } from '../../../tools/utils/utils.js'; + +const userCanPublishPage = async (detail, isBulk = true) => { + if (!detail) return false; + const { live, profile, webPath } = detail; + let canPublish = isBulk ? live?.permissions?.includes('write') : true; + let message = 'Publishing is currently disabled for this page'; + const config = await getCustomConfig('/.milo/publish-permissions-config.json'); + const item = config?.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + if (item.message) message = item.message; + const group = config[item.group]; + if (group && profile?.email) { + let isDeny; + const user = group.data?.find(({ allow, deny }) => { + if (deny) { + /* c8 ignore next 3 */ + isDeny = true; + return deny === profile.email; + } + return allow === profile.email; + }); + canPublish = isDeny ? !user : !!user; + } + } + return { canPublish, message }; +}; + +export default userCanPublishPage; diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index aaf1592a28..acaa428716 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,4 +1,4 @@ -import { userCanPublishPage } from './utils.js'; +import userCanPublishPage from '../tools/utils/publish.js'; const PUBLISH_BTN = '.publish.plugin button'; const BACKUP_PROFILE = '.profile-email'; diff --git a/libs/utils/utils.js b/libs/utils/utils.js index e41a92123d..52d3b0e190 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1,7 +1,5 @@ /* eslint-disable no-console */ -import { getCustomConfig } from '../../tools/utils/utils.js'; - const MILO_TEMPLATES = [ '404', 'featured-story', @@ -1297,32 +1295,3 @@ export function loadLana(options = {}) { } export const reloadPage = () => window.location.reload(); - -export const userCanPublishPage = async (detail, isBulk = true) => { - if (!detail) return false; - const { live, profile, webPath } = detail; - let canPublish = isBulk ? live?.permissions?.includes('write') : true; - let message = 'Publishing is currently disabled for this page'; - const config = await getCustomConfig('/.milo/publish-permissions-config.json'); - const item = config?.urls?.data?.find(({ url }) => ( - url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath - )); - if (item) { - canPublish = false; - if (item.message) message = item.message; - const group = config[item.group]; - if (group && profile?.email) { - let isDeny; - const user = group.data?.find(({ allow, deny }) => { - if (deny) { - /* c8 ignore next 3 */ - isDeny = true; - return deny === profile.email; - } - return allow === profile.email; - }); - canPublish = isDeny ? !user : !!user; - } - } - return { canPublish, message }; -}; From 5932770441e92fc532223a03e5e56d9844bc779c Mon Sep 17 00:00:00 2001 From: Sartxi Date: Mon, 16 Sep 2024 10:37:09 -0600 Subject: [PATCH 14/17] fix disabled false on button --- libs/utils/sidekick-decorate.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index acaa428716..89a87911ae 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,13 +1,17 @@ import userCanPublishPage from '../tools/utils/publish.js'; const PUBLISH_BTN = '.publish.plugin button'; -const BACKUP_PROFILE = '.profile-email'; +const PROFILE = '.profile-email'; const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; export default function stylePublish(sk) { const setupPublishBtn = async (page, btn) => { const { canPublish, message } = await userCanPublishPage(page, false); - btn.setAttribute('disabled', !canPublish); + if (canPublish) { + btn.removeAttribute('disabled'); + } else { + btn.setAttribute('disabled', true); + } const messageText = btn.querySelector('span'); const text = canPublish ? CONFIRM_MESSAGE : message; if (messageText) { @@ -94,7 +98,7 @@ export default function stylePublish(sk) { const page = { webPath: window.location.pathname, // added for edge case where the statusfetched event isnt fired on refresh - profile: { email: sk.shadowRoot.querySelector(BACKUP_PROFILE)?.innerText }, + profile: { email: sk.shadowRoot.querySelector(PROFILE)?.innerText }, }; setupPublishBtn(page, btn); } From dc5c2004b17faa17b08803ab481dd69e227e0005 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 17 Sep 2024 19:01:08 -0600 Subject: [PATCH 15/17] preflight publishing disabling --- libs/blocks/preflight/panels/general.js | 53 ++++++++---- libs/blocks/preflight/preflight.css | 104 +++++++++++++++++++----- 2 files changed, 121 insertions(+), 36 deletions(-) diff --git a/libs/blocks/preflight/panels/general.js b/libs/blocks/preflight/panels/general.js index 53d8fcda3c..1805ce4bc4 100644 --- a/libs/blocks/preflight/panels/general.js +++ b/libs/blocks/preflight/panels/general.js @@ -1,4 +1,5 @@ import { html, signal, useEffect } from '../../../deps/htm-preact.js'; +import userCanPublishPage from '../../../tools/utils/publish.js'; const NOT_FOUND = { preview: { lastModified: 'Not found' }, live: { lastModified: 'Not found' } }; @@ -16,19 +17,19 @@ async function getStatus(url) { const resp = await fetch(adminUrl); if (!resp.ok) return {}; const json = await resp.json(); + const publish = await userCanPublishPage(json, false); const preview = json.preview.lastModified || 'Never'; const live = json.live.lastModified || 'Never'; const edit = json.edit.url; - return { url, edit, preview, live }; + return { url, edit, preview, live, publish }; } -function getStatuses() { - Object.keys(content.value).forEach((key) => { - content.value[key].items.forEach((item, idx) => { - getStatus(item.url).then((status) => { - content.value[key].items[idx] = status; - content.value = { ...content.value }; - }); +async function getStatuses() { + Object.keys(content.value).forEach(async (key) => { + content.value[key].items.forEach(async (item, idx) => { + const status = await getStatus(item.url); + content.value[key].items[idx] = status; + content.value = { ...content.value }; }); }); } @@ -73,7 +74,8 @@ async function setContent() { async function handleAction(action) { Object.keys(content.value).map(async (key) => { content.value[key].items.forEach(async (item, idx) => { - if (!item.checked) return; + const checkPublish = action === 'live' ? (item.publish && !item.publish.canPublish) : false; + if (!item.checked || checkPublish) return; content.value[key].items[idx].action = action; content.value = { ...content.value }; const adminUrl = getAdminUrl(item.url, action); @@ -128,7 +130,19 @@ function prettyPath(url) { return url.hash ? `${url.pathname} (${url.hash})` : url.pathname; } +function usePublishProps(item) { + let disablePublish; + if (item.publish && !item.publish.canPublish) { + disablePublish = html`${item.publish.message}`; + } + return { + publishText: html`${item.action === 'live' ? 'Publishing' : prettyDate(item.live)}`, + disablePublish, + }; +} + function Item({ name, item, idx }) { + const { publishText, disablePublish } = usePublishProps(item); const isChecked = item.checked ? ' is-checked' : ''; const isFetching = item.edit ? '' : ' is-fetching'; if (!item.url) return undefined; @@ -139,7 +153,9 @@ function Item({ name, item, idx }) {

${prettyPath(item.url)}

${item.edit && html`EDIT`}

${item.action === 'preview' ? 'Previewing' : prettyDate(item.preview)}

-

${item.action === 'live' ? 'Publishing' : prettyDate(item.live)}

+

+ ${isChecked && disablePublish ? html`${disablePublish}` : publishText} +

`; } @@ -167,12 +183,18 @@ function ContentGroup({ name, group }) { export default function General() { useEffect(() => { setContent(); }, []); - const checked = Object.keys(content.value) - .find((key) => content.value[key].items.find((item) => item.checked)); + const allChecked = Object.values(content.value) + .flatMap((item) => item.items).filter((item) => item.checked); + + const checked = !!allChecked.length; + const publishable = allChecked + .filter((item) => item.checked && !!item.publish?.canPublish).length; const hasPage = content.value.page; const selectStyle = checked ? 'Select none' : 'Select all'; + const tooltip = allChecked.length !== publishable && 'Puplishing disabled pages will be ignored'; + return html`
${Object.keys(content.value).map((key) => html`<${ContentGroup} name=${key} group=${content.value[key]} />`)} @@ -188,8 +210,11 @@ export default function General() {
-
- +
+ ${!!publishable && html` + `}
`}
diff --git a/libs/blocks/preflight/preflight.css b/libs/blocks/preflight/preflight.css index 40210aa579..91266545b1 100644 --- a/libs/blocks/preflight/preflight.css +++ b/libs/blocks/preflight/preflight.css @@ -89,7 +89,7 @@ p.preflight-content-heading { } p.preflight-content-heading-edit { - padding-left: 4px; + padding-left: 4px; } .preflight-group-row { @@ -200,6 +200,10 @@ p.preflight-date-wrapper { margin: 0; } +p.preflight-date-wrapper .disabled-publish { + font-size: 0.8em; +} + .preflight-group-row p { margin: 0; line-height: 1; @@ -238,6 +242,9 @@ span.preflight-time { } .preflight-action { + display: flex; + align-items: center; + cursor: pointer; background: var(--action-color); color: #FFF; font-weight: 700; @@ -251,6 +258,51 @@ span.preflight-time { clip-path: polygon(0% 0%, var(--notch-size) 0%, calc(100% - var(--notch-size)) 0%, 100% var(--notch-size), 100% calc(100% - var(--notch-size)), 100% 100%, 0% 100%, 0% 100%); } +.tooltip { + position: relative; +} + +.tooltip::before { + content: attr(data-tooltip); + position: absolute; + right: 74%; + transform: translateX(50%); + bottom: 130%; + margin-top: 15px; + width: max-content; + max-width: 157px; + padding: 10px; + border-radius: 4px; + background: white; + color: var(--action-color); + text-align: center; + font-size: 14px; + font-weight: 400; + opacity: 0; + transition: opacity 0.3s, visibility 0.3s; + visibility: hidden; + line-height: 19px; +} + +.tooltip::after { + content: ''; + bottom: 103%; + right: 69%; + position: absolute; + margin: 12px 1px 0; + border: 5px solid white; + border-color: white transparent transparent; + opacity: 0; + transition: 200ms; + visibility: hidden; +} + +.tooltip:hover::before, +.tooltip:hover::after { + opacity: 1; + visibility: visible; + z-index: 9; +} /* SEO */ .seo-columns { @@ -282,11 +334,11 @@ span.preflight-time { animation: spin 2s linear infinite; } -@keyframes spin { - 100% { - -webkit-transform: rotate(360deg); - transform:rotate(360deg); - } +@keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } } .result-icon.green { @@ -346,7 +398,7 @@ span.preflight-time { .dialog-modal#preflight .problem-links table td a { color: #fff; display: inline-block; - overflow-x: scroll; + overflow-x: scroll; position: absolute; top: 50%; left: 0; @@ -387,19 +439,19 @@ span.preflight-time { } .problem-link { - border: 3px solid #000!important; - color: #fff!important; - font-size: larger!important; - font-weight: bold!important; - padding-top: 12px!important; - padding-bottom: 12px!important; + border: 3px solid #000 !important; + color: #fff !important; + font-size: larger !important; + font-weight: bold !important; + padding-top: 12px !important; + padding-bottom: 12px !important; animation: pulse 1.5s ease-out infinite; } .problem-link:hover { - background-color: transparent!important; - color: inherit!important; - border-color:rgb(255 0 0)!important; + background-color: transparent !important; + color: inherit !important; + border-color: rgb(255 0 0) !important; } .problem-link::after { @@ -410,9 +462,17 @@ span.preflight-time { } @keyframes pulse { - 0% {background-color: rgb(255 0 0);} - 50% {background-color: rgb(150 0 0);} - 100% {background-color: rgb(255 0 0);} + 0% { + background-color: rgb(255 0 0); + } + + 50% { + background-color: rgb(150 0 0); + } + + 100% { + background-color: rgb(255 0 0); + } } /* Accessibility css */ @@ -425,7 +485,7 @@ span.preflight-time { margin-bottom: 20px; } -.access-columns .grid-heading + .access-columns .grid-heading { +.access-columns .grid-heading+.access-columns .grid-heading { margin-top: 20px; } @@ -471,7 +531,7 @@ span.preflight-time { margin-bottom: 0; } -.access-columns .grid-heading.is-closed + .access-image-grid { +.access-columns .grid-heading.is-closed+.access-image-grid { display: none; } @@ -566,4 +626,4 @@ img[data-alt-check]::after { .dialog-modal#preflight table td h3 { font-size: 16px; -} +} \ No newline at end of file From 19d2443e73596f65a4c96381802fa8d4e2df3d53 Mon Sep 17 00:00:00 2001 From: Sartxi Date: Tue, 17 Sep 2024 19:11:19 -0600 Subject: [PATCH 16/17] watch for sidekick logins --- libs/blocks/preflight/panels/general.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/blocks/preflight/panels/general.js b/libs/blocks/preflight/panels/general.js index 1805ce4bc4..70c22b5553 100644 --- a/libs/blocks/preflight/panels/general.js +++ b/libs/blocks/preflight/panels/general.js @@ -69,6 +69,10 @@ async function setContent() { }; getStatuses(); + const sk = document.querySelector('helix-sidekick'); + sk?.addEventListener('statusfetched', async () => { + getStatuses(); + }); } async function handleAction(action) { @@ -210,12 +214,13 @@ export default function General() {
-
- ${!!publishable && html` + ${!!publishable && html` +
`} -
+ +
+ `} `}
`; From 57e00ab626636d947348e76896f2fdb15396ae4c Mon Sep 17 00:00:00 2001 From: Sartxi Date: Wed, 18 Sep 2024 11:13:58 -0600 Subject: [PATCH 17/17] remove css formatting updates --- libs/blocks/preflight/preflight.css | 50 ++++++++++++----------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/libs/blocks/preflight/preflight.css b/libs/blocks/preflight/preflight.css index 91266545b1..394b09519b 100644 --- a/libs/blocks/preflight/preflight.css +++ b/libs/blocks/preflight/preflight.css @@ -89,7 +89,7 @@ p.preflight-content-heading { } p.preflight-content-heading-edit { - padding-left: 4px; + padding-left: 4px; } .preflight-group-row { @@ -334,11 +334,11 @@ span.preflight-time { animation: spin 2s linear infinite; } -@keyframes spin { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } +@keyframes spin { + 100% { + -webkit-transform: rotate(360deg); + transform:rotate(360deg); + } } .result-icon.green { @@ -398,7 +398,7 @@ span.preflight-time { .dialog-modal#preflight .problem-links table td a { color: #fff; display: inline-block; - overflow-x: scroll; + overflow-x: scroll; position: absolute; top: 50%; left: 0; @@ -439,19 +439,19 @@ span.preflight-time { } .problem-link { - border: 3px solid #000 !important; - color: #fff !important; - font-size: larger !important; - font-weight: bold !important; - padding-top: 12px !important; - padding-bottom: 12px !important; + border: 3px solid #000!important; + color: #fff!important; + font-size: larger!important; + font-weight: bold!important; + padding-top: 12px!important; + padding-bottom: 12px!important; animation: pulse 1.5s ease-out infinite; } .problem-link:hover { - background-color: transparent !important; - color: inherit !important; - border-color: rgb(255 0 0) !important; + background-color: transparent!important; + color: inherit!important; + border-color:rgb(255 0 0)!important; } .problem-link::after { @@ -462,17 +462,9 @@ span.preflight-time { } @keyframes pulse { - 0% { - background-color: rgb(255 0 0); - } - - 50% { - background-color: rgb(150 0 0); - } - - 100% { - background-color: rgb(255 0 0); - } + 0% {background-color: rgb(255 0 0);} + 50% {background-color: rgb(150 0 0);} + 100% {background-color: rgb(255 0 0);} } /* Accessibility css */ @@ -485,7 +477,7 @@ span.preflight-time { margin-bottom: 20px; } -.access-columns .grid-heading+.access-columns .grid-heading { +.access-columns .grid-heading + .access-columns .grid-heading { margin-top: 20px; } @@ -531,7 +523,7 @@ span.preflight-time { margin-bottom: 0; } -.access-columns .grid-heading.is-closed+.access-image-grid { +.access-columns .grid-heading.is-closed + .access-image-grid { display: none; }