From 2cace2987d5ae0dc651225eab2332c0a18eca7d2 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 11 Jul 2024 14:02:55 +0530 Subject: [PATCH 001/164] Added test for hooks - useSortableIndexedList, usePagination (admin-x-design-system) Ref ENG-1351 ENG-1373 --- apps/admin-x-design-system/package.json | 5 +- .../{hello.test.ts => unit/hello.test.js} | 2 +- .../test/unit/hooks/usePagination.test.ts | 116 ++++++++++++++ .../unit/hooks/useSortableIndexedList.test.ts | 150 ++++++++++++++++++ yarn.lock | 15 ++ 5 files changed, 286 insertions(+), 2 deletions(-) rename apps/admin-x-design-system/test/{hello.test.ts => unit/hello.test.js} (81%) create mode 100644 apps/admin-x-design-system/test/unit/hooks/usePagination.test.ts create mode 100644 apps/admin-x-design-system/test/unit/hooks/useSortableIndexedList.test.ts diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 413d2a26b9..052a57d1d4 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -11,7 +11,8 @@ "scripts": { "build": "concurrently \"vite build\" \"tsc -p tsconfig.declaration.json\"", "prepare": "yarn build", - "test": "yarn test:types", + "test": "yarn test:unit && yarn test:types", + "test:unit": "yarn nx build && vitest run", "test:types": "tsc --noEmit", "lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache", "lint": "yarn lint:code && yarn lint:test", @@ -36,6 +37,7 @@ "@storybook/react-vite": "7.6.4", "@storybook/testing-library": "0.2.2", "@testing-library/react": "14.1.0", + "@testing-library/react-hooks" : "8.0.1", "@vitejs/plugin-react": "4.2.1", "c8": "8.0.1", "eslint-plugin-react-hooks": "4.6.0", @@ -43,6 +45,7 @@ "eslint-plugin-tailwindcss": "3.13.0", "jsdom": "24.1.0", "mocha": "10.2.0", + "chai": "4.3.8", "react": "18.3.1", "react-dom": "18.3.1", "rollup-plugin-node-builtins": "2.1.2", diff --git a/apps/admin-x-design-system/test/hello.test.ts b/apps/admin-x-design-system/test/unit/hello.test.js similarity index 81% rename from apps/admin-x-design-system/test/hello.test.ts rename to apps/admin-x-design-system/test/unit/hello.test.js index e66b88fad4..926450737b 100644 --- a/apps/admin-x-design-system/test/hello.test.ts +++ b/apps/admin-x-design-system/test/unit/hello.test.js @@ -3,6 +3,6 @@ import assert from 'assert/strict'; describe('Hello world', function () { it('Runs a test', function () { // TODO: Write me! - assert.ok(require('../')); + assert.equal(1, 1); }); }); diff --git a/apps/admin-x-design-system/test/unit/hooks/usePagination.test.ts b/apps/admin-x-design-system/test/unit/hooks/usePagination.test.ts new file mode 100644 index 0000000000..85ed5af4a7 --- /dev/null +++ b/apps/admin-x-design-system/test/unit/hooks/usePagination.test.ts @@ -0,0 +1,116 @@ +import {expect} from 'chai'; +import {renderHook, act} from '@testing-library/react-hooks'; +import {usePagination, PaginationMeta, PaginationData} from '../../../src/hooks/usePagination'; + +describe('usePagination', function () { + const initialMeta: PaginationMeta = { + limit: 10, + pages: 5, + total: 50, + next: null, + prev: null + }; + + it('should initialize with the given meta and page', function () { + const {result} = renderHook(() => usePagination({ + meta: initialMeta, + limit: 10, + page: 1, + setPage: () => {} + }) + ); + + const expectedData: PaginationData = { + page: 1, + pages: initialMeta.pages, + total: initialMeta.total, + limit: initialMeta.limit, + setPage: result.current.setPage, + nextPage: result.current.nextPage, + prevPage: result.current.prevPage + }; + + expect(result.current).to.deep.equal(expectedData); + }); + + it('should update page correctly when nextPage and prevPage are called', function () { + let currentPage = 1; + const setPage = (newPage: number) => { + currentPage = newPage; + }; + + const {result} = renderHook(() => usePagination({ + meta: initialMeta, + limit: 10, + page: currentPage, + setPage + }) + ); + + act(() => { + result.current.nextPage(); + }); + + expect(currentPage).to.equal(2); + + act(() => { + result.current.prevPage(); + }); + + expect(currentPage).to.equal(1); + }); + + it('should update page correctly when setPage is called', function () { + let currentPage = 3; + const setPage = (newPage: number) => { + currentPage = newPage; + }; + + const {result} = renderHook(() => usePagination({ + meta: initialMeta, + limit: 10, + page: currentPage, + setPage + }) + ); + + const newPage = 5; + + act(() => { + result.current.setPage(newPage); + }); + + expect(currentPage).to.equal(newPage); + }); + + it('should handle edge cases where meta.pages < page when setting meta', function () { + let currentPage = 5; + const setPage = (newPage: number) => { + currentPage = newPage; + }; + + const {rerender} = renderHook( + ({meta}) => usePagination({ + meta, + limit: 10, + page: currentPage, + setPage + }), + {initialProps: {meta: initialMeta}} + ); + + const updatedMeta: PaginationMeta = { + limit: 10, + pages: 4, + total: 40, + next: null, + prev: null + }; + + act(() => { + rerender({meta: updatedMeta}); + }); + + expect(currentPage).to.equal(4); + }); +}); diff --git a/apps/admin-x-design-system/test/unit/hooks/useSortableIndexedList.test.ts b/apps/admin-x-design-system/test/unit/hooks/useSortableIndexedList.test.ts new file mode 100644 index 0000000000..9a8f444915 --- /dev/null +++ b/apps/admin-x-design-system/test/unit/hooks/useSortableIndexedList.test.ts @@ -0,0 +1,150 @@ +import {expect} from 'chai'; +import {renderHook, act} from '@testing-library/react-hooks'; +import useSortableIndexedList from '../../../src/hooks/useSortableIndexedList'; +import sinon from 'sinon'; + +describe('useSortableIndexedList', function () { + // Mock initial items and blank item + const initialItems = [{name: 'Item 1'}, {name: 'Item 2'}]; + const blankItem = {name: ''}; + + // Mock canAddNewItem function + const canAddNewItem = (item: { name: string }) => !!item.name; + + it('should initialize with the given items', function () { + const setItems = sinon.spy(); + + const {result} = renderHook(() => useSortableIndexedList({ + items: initialItems, + setItems, + blank: blankItem, + canAddNewItem + }) + ); + + // Assert initial items setup correctly + expect(result.current.items).to.deep.equal(initialItems.map((item, index) => ({item, id: index.toString()}))); + }); + + it('should add a new item', function () { + let items = initialItems; + const setItems = (newItems: any[]) => { + items = newItems; + }; + + const {result} = renderHook(() => useSortableIndexedList({ + items, + setItems, + blank: blankItem, + canAddNewItem + }) + ); + + act(() => { + result.current.setNewItem({name: 'New Item'}); + result.current.addItem(); + }); + + // Assert items updated correctly after adding new item + expect(items).to.deep.equal([...initialItems, {name: 'New Item'}]); + }); + + it('should update an item', function () { + let items = initialItems; + const setItems = (newItems: any[]) => { + items = newItems; + }; + + const {result} = renderHook(() => useSortableIndexedList({ + items, + setItems, + blank: blankItem, + canAddNewItem + }) + ); + + act(() => { + result.current.updateItem('0', {name: 'Updated Item 1'}); + }); + + // Assert item updated correctly + expect(items[0]).to.deep.equal({name: 'Updated Item 1'}); + }); + + it('should remove an item', function () { + let items = initialItems; + const setItems = (newItems: any[]) => { + items = newItems; + }; + + const {result} = renderHook(() => useSortableIndexedList({ + items, + setItems, + blank: blankItem, + canAddNewItem + }) + ); + + act(() => { + result.current.removeItem('0'); + }); + + // Assert item removed correctly + expect(items).to.deep.equal([initialItems[1]]); + }); + + it('should move an item', function () { + let items = initialItems; + const setItems = (newItems: any[]) => { + items = newItems; + }; + + const {result} = renderHook(() => useSortableIndexedList({ + items, + setItems, + blank: blankItem, + canAddNewItem + }) + ); + + act(() => { + result.current.moveItem('0', '1'); + }); + + // Assert item moved correctly + expect(items).to.deep.equal([initialItems[1], initialItems[0]]); + }); + + it('should not setItems for deeply equal items regardless of property order', function () { + const setItems = sinon.spy(); + const initialItem = [{name: 'Item 1', url: 'http://example.com'}]; + const blankItem1 = {name: '', url: ''}; + + const {rerender} = renderHook( + // eslint-disable-next-line + ({items, setItems}) => useSortableIndexedList({ + items, + setItems, + blank: blankItem1, + canAddNewItem + }), + { + initialProps: { + items: initialItem, + setItems + } + } + ); + + expect(setItems.callCount).to.equal(0); + + // Re-render with items in different order but same content + rerender({ + items: [{url: 'http://example.com', name: 'Item 1'}], + setItems + }); + + // Expect no additional calls because the items are deeply equal + expect(setItems.callCount).to.equal(0); + }); +}); diff --git a/yarn.lock b/yarn.lock index 1ab878fa60..1dbd6d4252 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7810,6 +7810,14 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/react-hooks@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" + integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g== + dependencies: + "@babel/runtime" "^7.12.5" + react-error-boundary "^3.1.0" + "@testing-library/react@12.1.5": version "12.1.5" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" @@ -27785,6 +27793,13 @@ react-element-to-jsx-string@^15.0.0: is-plain-object "5.0.0" react-is "18.1.0" +react-error-boundary@^3.1.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" + integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== + dependencies: + "@babel/runtime" "^7.12.5" + react-hot-toast@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994" From 1d21612ceb28f02cceda1491844a0fde3fd8d068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Winden?= Date: Thu, 11 Jul 2024 11:50:06 +0200 Subject: [PATCH 002/164] Improved text-wrapping for Resource headers (#20590) Adds text-wrap: pretty to Resource H3 on Post Analytics page, to avoid orphans in typography. --- ghost/admin/app/styles/layouts/content.css | 1 + 1 file changed, 1 insertion(+) diff --git a/ghost/admin/app/styles/layouts/content.css b/ghost/admin/app/styles/layouts/content.css index 01d06e03e9..a4e6f6aa5c 100644 --- a/ghost/admin/app/styles/layouts/content.css +++ b/ghost/admin/app/styles/layouts/content.css @@ -865,6 +865,7 @@ .gh-post-analytics-resource h3 { font-size: 1.8rem; font-weight: 700; + text-wrap: pretty; } .gh-post-analytics-box h4.gh-main-section-header.small { From 74e4ee340a9204bae1edc7db3d1be8d021a9b7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Winden?= Date: Thu, 11 Jul 2024 15:15:40 +0200 Subject: [PATCH 003/164] Fixes searchbar breakpoint issue (#20593) Fixes https://linear.app/tryghost/issue/DES-323/settings-has-a-bad-breakpoint-with-search-bar On smaller resolutions, the `X` of the searchbar was overlapping with the `X` of Settings. Setting the right media queries resolved that. --- apps/admin-x-settings/src/components/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-x-settings/src/components/Sidebar.tsx b/apps/admin-x-settings/src/components/Sidebar.tsx index 1105c9c630..08f14de477 100644 --- a/apps/admin-x-settings/src/components/Sidebar.tsx +++ b/apps/admin-x-settings/src/components/Sidebar.tsx @@ -142,7 +142,7 @@ const Sidebar: React.FC = () => { unstyled onChange={updateSearch} /> - {filter ? - - - - {{else}} - - - - {{svg-jar "dotdotdot"}} - - - - -
  • - - View Stripe customer - -
  • -
  • -
  • - - View Stripe subscription - -
  • -
  • - {{#if (not-eq sub.status "canceled")}} - {{#if sub.cancel_at_period_end}} - - {{else}} - +
  • +
    +
    + {{else}} + + + + {{svg-jar "dotdotdot"}} + + + + +
  • + + View Stripe customer + +
  • +
  • +
  • + + View Stripe subscription + +
  • +
  • + {{#if (not-eq sub.status "canceled")}} + {{#if sub.cancel_at_period_end}} + + {{else}} + + {{/if}} {{/if}} - {{/if}} -
  • -
    -
    - {{/if}} - + + + + {{/if}} + {{/each}} {{#if (eq tier.subscriptions.length 0)}} diff --git a/ghost/admin/app/utils/subscription-data.js b/ghost/admin/app/utils/subscription-data.js index 347265ace4..9a396963d9 100644 --- a/ghost/admin/app/utils/subscription-data.js +++ b/ghost/admin/app/utils/subscription-data.js @@ -24,7 +24,8 @@ export function getSubscriptionData(sub) { trialUntil: trialUntil(sub) }; - data.validityDetails = validityDetails(data); + data.priceLabel = priceLabel(data); + data.validityDetails = validityDetails(data, !!data.priceLabel); return data; } @@ -77,22 +78,39 @@ export function trialUntil(sub) { return undefined; } -export function validityDetails(data) { - if (data.isComplimentary && data.compExpiry) { - return `Expires ${data.compExpiry}`; +export function validityDetails(data, separatorNeeded = false) { + const separator = separatorNeeded ? ' – ' : ''; + const space = data.validUntil ? ' ' : ''; + + if (data.isComplimentary) { + if (data.compExpiry) { + return `${separator}Expires ${data.compExpiry}`; + } else { + return ''; + } } if (data.hasEnded) { - return `Ended ${data.validUntil}`; + return `${separator}Ended${space}${data.validUntil}`; } if (data.willEndSoon) { - return `Has access until ${data.validUntil}`; + return `${separator}Has access until${space}${data.validUntil}`; } if (data.trialUntil) { - return `Ends ${data.trialUntil}`; + return `${separator}Ends ${data.trialUntil}`; } - return `Renews ${data.validUntil}`; + return `${separator}Renews${space}${data.validUntil}`; +} + +export function priceLabel(data) { + if (data.trialUntil) { + return 'Free trial'; + } + + if (data.price.nickname && data.price.nickname.length > 0 && data.price.nickname !== 'Monthly' && data.price.nickname !== 'Yearly') { + return data.price.nickname; + } } diff --git a/ghost/admin/tests/unit/utils/subscription-data-test.js b/ghost/admin/tests/unit/utils/subscription-data-test.js index 5b396b20d8..660b4d72c4 100644 --- a/ghost/admin/tests/unit/utils/subscription-data-test.js +++ b/ghost/admin/tests/unit/utils/subscription-data-test.js @@ -1,5 +1,5 @@ import moment from 'moment-timezone'; -import {compExpiry, getSubscriptionData, isActive, isCanceled, isComplimentary, isSetToCancel, trialUntil, validUntil, validityDetails} from 'ghost-admin/utils/subscription-data'; +import {compExpiry, getSubscriptionData, isActive, isCanceled, isComplimentary, isSetToCancel, priceLabel, trialUntil, validUntil, validityDetails} from 'ghost-admin/utils/subscription-data'; import {describe, it} from 'mocha'; import {expect} from 'chai'; @@ -165,6 +165,26 @@ describe('Unit: Util: subscription-data', function () { }); }); + describe('priceLabel', function () { + it('returns "Free trial" for trial subscriptions', function () { + let data = {trialUntil: '31 May 2021'}; + expect(priceLabel(data)).to.equal('Free trial'); + }); + + it('returns nothing if the price nickname is the default "monthly" or "yearly"', function () { + let data = {price: {nickname: 'Monthly'}}; + expect(priceLabel(data)).to.be.undefined; + + data = {price: {nickname: 'Yearly'}}; + expect(priceLabel(data)).to.be.undefined; + }); + + it('returns the price nickname for non-default prices', function () { + let data = {price: {nickname: 'Custom'}}; + expect(priceLabel(data)).to.equal('Custom'); + }); + }); + describe('validityDetails', function () { it('returns "Expires {compExpiry}" for expired complimentary subscriptions', function () { let data = { @@ -174,6 +194,14 @@ describe('Unit: Util: subscription-data', function () { expect(validityDetails(data)).to.equal('Expires 31 May 2021'); }); + it('returns "" for forever complimentary subscriptions', function () { + let data = { + isComplimentary: true, + compExpiry: undefined + }; + expect(validityDetails(data)).to.equal(''); + }); + it('returns "Ended {validUntil}" for canceled subscriptions', function () { let data = { hasEnded: true, @@ -228,6 +256,7 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: false, trialUntil: undefined, + priceLabel: undefined, validityDetails: 'Renews 31 May 2021' }); }); @@ -254,7 +283,8 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2222', willEndSoon: false, trialUntil: '31 May 2222', - validityDetails: 'Ends 31 May 2222' + priceLabel: 'Free trial', + validityDetails: ' – Ends 31 May 2222' }); }); @@ -280,7 +310,8 @@ describe('Unit: Util: subscription-data', function () { validUntil: '', willEndSoon: false, trialUntil: undefined, - validityDetails: 'Ended ' + priceLabel: undefined, + validityDetails: 'Ended' }); }); @@ -306,11 +337,42 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: true, trialUntil: undefined, + priceLabel: undefined, validityDetails: 'Has access until 31 May 2021' }); }); - it('returns the correct data for a complimentary subscription', function () { + it('returns the correct data for a complimentary subscription active forever', function () { + let sub = { + id: null, + status: 'active', + cancel_at_period_end: false, + current_period_end: '2021-05-31', + trial_end_at: null, + tier: { + expiry_at: null + }, + price: { + currency: 'usd', + amount: 0, + nickname: 'Complimentary' + } + }; + let data = getSubscriptionData(sub); + + expect(data).to.include({ + isComplimentary: true, + compExpiry: undefined, + hasEnded: false, + validUntil: '31 May 2021', + willEndSoon: false, + trialUntil: undefined, + priceLabel: 'Complimentary', + validityDetails: '' + }); + }); + + it('returns the correct data for a complimentary subscription with an expiration date', function () { let sub = { id: null, status: 'active', @@ -322,7 +384,8 @@ describe('Unit: Util: subscription-data', function () { }, price: { currency: 'usd', - amount: 0 + amount: 0, + nickname: 'Complimentary' } }; let data = getSubscriptionData(sub); @@ -334,7 +397,8 @@ describe('Unit: Util: subscription-data', function () { validUntil: '31 May 2021', willEndSoon: false, trialUntil: undefined, - validityDetails: 'Expires 31 May 2021' + priceLabel: 'Complimentary', + validityDetails: ' – Expires 31 May 2021' }); }); }); From 7de7d3326613773700be6dbab8e2d9af752ce366 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:17:19 +0000 Subject: [PATCH 029/164] Update dependency mailgun.js to v10.2.3 --- ghost/mailgun-client/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ghost/mailgun-client/package.json b/ghost/mailgun-client/package.json index 5744e22323..f8c09d69be 100644 --- a/ghost/mailgun-client/package.json +++ b/ghost/mailgun-client/package.json @@ -29,6 +29,6 @@ "@tryghost/metrics": "1.0.34", "form-data": "4.0.0", "lodash": "4.17.21", - "mailgun.js": "10.2.1" + "mailgun.js": "10.2.3" } } diff --git a/yarn.lock b/yarn.lock index 3ca02245ef..96d09af5a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10403,12 +10403,12 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios@^1.0.0, axios@^1.3.3, axios@^1.6.0: - version "1.6.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" - integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== +axios@^1.0.0, axios@^1.3.3, axios@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== dependencies: - follow-redirects "^1.15.4" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -18200,10 +18200,10 @@ focus-trap@^6.7.2: dependencies: tabbable "^5.3.3" -follow-redirects@^1.0.0, follow-redirects@^1.15.4: - version "1.15.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-each@^0.3.3: version "0.3.3" @@ -22944,12 +22944,12 @@ magic-string@^0.30.0, magic-string@^0.30.1: dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" -mailgun.js@10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/mailgun.js/-/mailgun.js-10.2.1.tgz#411fc39c9d2bd26fcb2445416deab81ebd87044f" - integrity sha512-lslXpLdwtLyavJGHsPKlpXLnd3XEuwWY5X/KaTAvv2UvHiOWSlUzqkGW21vVgcQQ9AKw0s5ayS5V9Cv8Ze2OIw== +mailgun.js@10.2.3: + version "10.2.3" + resolved "https://registry.yarnpkg.com/mailgun.js/-/mailgun.js-10.2.3.tgz#77a0602340383e5340aa6ab1a6c704ab3839e7e3" + integrity sha512-7Mcw5IFtzN21i+qFQoWI+aQFDpLYSMUIWvDUXKLlpGFVVGfYVL8GIiveS+LIXpEJTQcF1hoNhOhDwenFqNSKmw== dependencies: - axios "^1.6.0" + axios "^1.7.2" base-64 "^1.0.0" url-join "^4.0.1" From 7488e2e7b2faaebd60b1f73896791e39366548b0 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 18 Jul 2024 11:51:46 +0100 Subject: [PATCH 030/164] Enforced nested `

    ` when rendering `

    ` in email closes https://linear.app/tryghost/issue/ENG-1432 - bumps `kg-lexical-html-render` package with required rendering change - bumps `koenig-lexical` with TypeError fix and improved handling of failed image uploads - bumps other packages that were missed from previous bumps, fixes split versions of underlying lexical packages --- ghost/admin/package.json | 4 +- ghost/core/package.json | 6 +- .../__snapshots__/cards.test.js.snap | 4 +- yarn.lock | 300 +++++++++--------- 4 files changed, 164 insertions(+), 150 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 645da47894..fb4e541d6d 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.1", "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.9", + "@tryghost/koenig-lexical": "1.3.10", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", @@ -205,4 +205,4 @@ } } } -} \ No newline at end of file +} diff --git a/ghost/core/package.json b/ghost/core/package.json index 8e1150dbf8..c67a825a3b 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -111,9 +111,9 @@ "@tryghost/kg-converters": "1.0.5", "@tryghost/kg-default-atoms": "5.0.3", "@tryghost/kg-default-cards": "10.0.6", - "@tryghost/kg-default-nodes": "1.1.7", - "@tryghost/kg-html-to-lexical": "1.1.8", - "@tryghost/kg-lexical-html-renderer": "1.1.8", + "@tryghost/kg-default-nodes": "1.1.8", + "@tryghost/kg-html-to-lexical": "1.1.9", + "@tryghost/kg-lexical-html-renderer": "1.1.10", "@tryghost/kg-mobiledoc-html-renderer": "7.0.4", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap index c65a0863d1..848d459b4a 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap @@ -1811,7 +1811,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing -

    This is just a simple paragraph, no frills.

    This is block quote
    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    and a paragraph (in markdown!)

    @@ -2018,7 +2018,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing -

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    +

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    diff --git a/yarn.lock b/yarn.lock index 96d09af5a5..cdf1455347 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3736,92 +3736,76 @@ "@keyvhq/core" "^2.0.0" mimic-fn "~3.0.0" -"@lexical/clipboard@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/clipboard/-/clipboard-0.14.3.tgz#c759fddb384fbda7ecfd1a2e9dd9a304ee08ee76" - integrity sha512-kMasHJQCNSSdD6US8XF/GJEZAgdmIUIoqwcV/7Q8jVUICYT53bcr+Rh7RxL+1c7ZpJE2rXg5KTELsUPGjs0uwA== +"@lexical/clipboard@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/clipboard/-/clipboard-0.13.1.tgz#ca132306129974ea2c9e51d6a8637f8fcffcdb3d" + integrity sha512-gMSbVeqb7S+XAi/EMMlwl+FCurLPugN2jAXcp5k5ZaUd7be8B+iupbYdoKkjt4qBhxmvmfe9k46GoC0QOPl/nw== dependencies: - "@lexical/html" "0.14.3" - "@lexical/list" "0.14.3" - "@lexical/selection" "0.14.3" - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/html" "0.13.1" + "@lexical/list" "0.13.1" + "@lexical/selection" "0.13.1" + "@lexical/utils" "0.13.1" -"@lexical/code@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/code/-/code-0.14.3.tgz#be7b7ebef5de9db3a88d939492084cda99f6f7c2" - integrity sha512-eBhs+TsJ5z7Vg/0e77bau86lN7R5nqO7effkPNNndn0XV2VSDpjMF+PTj4Cd1peenFlfqVivBr9gdewDrvPQng== +"@lexical/code@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/code/-/code-0.13.1.tgz#e13688390582a4b63a639daff1f16bcb82aa854d" + integrity sha512-QK77r3QgEtJy96ahYXNgpve8EY64BQgBSnPDOuqVrLdl92nPzjqzlsko2OZldlrt7gjXcfl9nqfhZ/CAhStfOg== dependencies: - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/utils" "0.13.1" prismjs "^1.27.0" -"@lexical/headless@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/headless/-/headless-0.14.3.tgz#f649a6925282fe58996d90f9a7097dc2a86f3a13" - integrity sha512-/fNeDHm0WvnlquKgjJDJ0G9Di8TJU6gw0AaLmNLI1Iz+8HvMZ8P6R8OW0l+uRtJMC0TlFt6iLxira6rntQ2K2g== - dependencies: - lexical "0.14.3" +"@lexical/headless@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/headless/-/headless-0.13.1.tgz#61e475495de49a4c5732e383e1d5f75fc5e39664" + integrity sha512-W2mLUuWPrsyf2n73NWM8nKiBI11lEpVVzKE0OzMsjTskv5+AAMaeu1wQ7M1508vKdCcUZwA6AOh3To/hstLEpw== -"@lexical/html@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/html/-/html-0.14.3.tgz#fe960afdc94232d5cec13a070bbb965b6d4bce66" - integrity sha512-ID4RdHdOXv2qIg6cqNhbYiqgcV5aEJFAV+zZ14CMpxPlW71tiRlmy/Pp4WqCFgjnZ2GZRq34+kag+cT2H69ILQ== +"@lexical/html@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/html/-/html-0.13.1.tgz#e56035d0c6528ffb932390e0d3d357c82f69253a" + integrity sha512-XkZrnCSHIUavtpMol6aG8YsJ5KqC9hMxEhAENf3HTGi3ocysCByyXOyt1EhEYpjJvgDG4wRqt25xGDbLjj1/sA== dependencies: - "@lexical/selection" "0.14.3" - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/selection" "0.13.1" + "@lexical/utils" "0.13.1" -"@lexical/link@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/link/-/link-0.14.3.tgz#7320f5eba82f451da9449a4b8c57fa60341938cb" - integrity sha512-txhuzcx2OfOtZ/fy9cgauDGW1gi2vSU0iQdde4i0UP2KK4ltioA9eFkjqAacGiPvwJ8w2CZV9q5Ck4DgFAKQ7w== +"@lexical/link@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/link/-/link-0.13.1.tgz#f1c4c12c828c0251e5d7fb4fb336f2d62380fc57" + integrity sha512-7E3B2juL2UoMj2n+CiyFZ7tlpsdViAoIE7MpegXwfe/VQ66wFwk/VxGTa/69ng2EoF7E0kh+SldvGQDrWAWb1g== dependencies: - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/utils" "0.13.1" -"@lexical/list@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/list/-/list-0.14.3.tgz#1b587e2c807465d1b50d0f09aedda58b7591a958" - integrity sha512-d9ZiEkZ34DpzBNq2GkedJpXF8sIxSQvHOGhNbVvTuBvgDcCwbmXL0KY4k+xu+jMScRO/3oR7C6YZpZT3GaUO+Q== +"@lexical/list@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/list/-/list-0.13.1.tgz#461cb989157bdf4a43eaa8596fdb09df60d114ee" + integrity sha512-6U1pmNZcKLuOWiWRML8Raf9zSEuUCMlsOye82niyF6I0rpPgYo5UFghAAbGISDsyqzM1B2L4BgJ6XrCk/dJptg== dependencies: - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/utils" "0.13.1" -"@lexical/rich-text@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/rich-text/-/rich-text-0.14.3.tgz#92a26e5092af387c550c094df7a8353a8318f95b" - integrity sha512-o8wGvRDyPSRcfb6bauF5lzK5u/kzCW+hAQq0ExM1e8p4GHDb0vwz9DA6NH5D0BPHb2fUgknwClHOoJX95WUA8A== - dependencies: - "@lexical/clipboard" "0.14.3" - "@lexical/selection" "0.14.3" - "@lexical/utils" "0.14.3" - lexical "0.14.3" +"@lexical/rich-text@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/rich-text/-/rich-text-0.13.1.tgz#8251e81a3985a4d76bef027cf6c0dc90c661e4ec" + integrity sha512-HliB9Ync06mv9DBg/5j0lIsTJp+exLHlaLJe+n8Zq1QNTzZzu2LsIT/Crquk50In7K/cjtlaQ/d5RB0LkjMHYg== -"@lexical/selection@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/selection/-/selection-0.14.3.tgz#79b81dd8a9afeb442e180644dcde82d708b2c069" - integrity sha512-43EmqG6flLqFJJNZ7GCxFlx3qXy7osB3AQBgxKTthWtQeBrJPdgacctL1jhO7etTIQWP5C1DExy3opDLVKyDjg== - dependencies: - lexical "0.14.3" +"@lexical/selection@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/selection/-/selection-0.13.1.tgz#466d7cd0ee1b04680bd949112f1f5cb6a6618efa" + integrity sha512-Kt9eSwjxPznj7yzIYipu9yYEgmRJhHiq3DNxHRxInYcZJWWNNHum2xKyxwwcN8QYBBzgfPegfM/geqQEJSV1lQ== -"@lexical/table@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/table/-/table-0.14.3.tgz#84df456c0565db2d18ef4e5fe2aa65cc1bff1b19" - integrity sha512-9btpU2lfAE34ucIqlMu5RiSVlxREXY7Zp+s26oFsXNoNPhW57iND96TrqwYo9FJl/6zXXfvqYxnUEcUD2dLgwQ== +"@lexical/table@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/table/-/table-0.13.1.tgz#814d3b8a2afb821aff151c92cce831809f9d67a1" + integrity sha512-VQzgkfkEmnvn6C64O/kvl0HI3bFoBh3WA/U67ALw+DS11Mb5CKjbt0Gzm/258/reIxNMpshjjicpWMv9Miwauw== dependencies: - "@lexical/utils" "0.14.3" - lexical "0.14.3" + "@lexical/utils" "0.13.1" -"@lexical/utils@0.14.3": - version "0.14.3" - resolved "https://registry.npmjs.org/@lexical/utils/-/utils-0.14.3.tgz#dc5fe87282f77ad40b46b5d4aee148a9843ad939" - integrity sha512-coqG2AO7QhJCM0xBlYvtETjl0il9u4HQRuc8ye3j8jMfNadVvVVWO3Fodmm/8FTPyJuxIij1Ruma9zqhlAbN6Q== +"@lexical/utils@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@lexical/utils/-/utils-0.13.1.tgz#f2a72f71c859933781294830b38b25b5b33122a9" + integrity sha512-AtQQKzYymkbOaQxaBXjRBS8IPxF9zWQnqwHTUTrJqJ4hX71aIQd/thqZbfQETAFJfC8pNBZw5zpxN6yPHk23dQ== dependencies: - "@lexical/list" "0.14.3" - "@lexical/selection" "0.14.3" - "@lexical/table" "0.14.3" - lexical "0.14.3" + "@lexical/list" "0.13.1" + "@lexical/selection" "0.13.1" + "@lexical/table" "0.13.1" "@lezer/common@^1.0.0", "@lezer/common@^1.0.2": version "1.0.3" @@ -7975,65 +7959,65 @@ lodash "^4.17.21" luxon "^3.0.0" -"@tryghost/kg-default-nodes@1.1.7": - version "1.1.7" - resolved "https://registry.npmjs.org/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.7.tgz#f792cf56ded918a4b826b8f8c5dfee8ec7368cda" - integrity sha512-VHw6zVLnoXn5AoubjNOivBhiF8gSbL7OoiMTiLaA1V39HgqYuq2YTMZx5d5KlPap3GXqQOSfe3wHT6YGCYUzFA== +"@tryghost/kg-default-nodes@1.1.8": + version "1.1.8" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.8.tgz#4a528041dce9256484110162e805e799c77b37e9" + integrity sha512-fAttomZj55uXeZcRhz3clkyFkFtrW/ZvsjY9GRjCGtOsKPGOIInjizwJFPj2lOWpeimPrBNMxRs1NLEgWjpr1Q== dependencies: - "@lexical/clipboard" "0.14.3" - "@lexical/rich-text" "0.14.3" - "@lexical/selection" "0.14.3" - "@lexical/utils" "0.14.3" + "@lexical/clipboard" "0.13.1" + "@lexical/rich-text" "0.13.1" + "@lexical/selection" "0.13.1" + "@lexical/utils" "0.13.1" "@tryghost/kg-clean-basic-html" "4.1.1" "@tryghost/kg-markdown-html-renderer" "7.0.5" html-minifier "^4.0.0" jsdom "^24.0.0" - lexical "0.14.3" + lexical "0.13.1" lodash "^4.17.21" luxon "^3.3.0" -"@tryghost/kg-default-transforms@1.1.8": - version "1.1.8" - resolved "https://registry.npmjs.org/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.8.tgz#6b20afdd6ace6322806eb6651a499b6594f589de" - integrity sha512-VOBqjJxjFibxorua+6D91MwjP28oYjNNy8DBVnWxx4zGYjf9NN0omRvZU2fca1EVmJHqx2Hqunu70V6SI+pKjA== +"@tryghost/kg-default-transforms@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.9.tgz#92a4e0386c73dfd704822e4cae61e1643f368bd4" + integrity sha512-Z3IHmDOEsBGbIQtbbVc0NSVuB5yLxkQK1hcl65DA2dg8Qqf4p54xazqVB+0JWj3wTFVnqfHyY6Rpag9VW7UOPQ== dependencies: - "@lexical/list" "0.14.3" - "@lexical/rich-text" "0.14.3" - "@lexical/utils" "0.14.3" - "@tryghost/kg-default-nodes" "1.1.7" - lexical "0.14.3" + "@lexical/list" "0.13.1" + "@lexical/rich-text" "0.13.1" + "@lexical/utils" "0.13.1" + "@tryghost/kg-default-nodes" "1.1.8" + lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.8": - version "1.1.8" - resolved "https://registry.npmjs.org/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.8.tgz#91abedaa12c118f1181f6726e72ffa9c55bae365" - integrity sha512-c1e+0QOZxhTazICWZWSVC1Ph0nkNNdePcm5w73T1CkxXlFN/RZzKOXVcNll68T6i/vqjJ37HJSsnARZgW8ePkQ== +"@tryghost/kg-html-to-lexical@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.9.tgz#10767d5ad90efaad4ab48f2aacabb4b752e98831" + integrity sha512-IbOicRX/Mf4qaS+rZ1SqT4KISYEsCGCbSvBWEqKOoEQhWrjl5cjEccijUkLmzKIQdEten1Ds9UgETRX4u1Odfg== dependencies: - "@lexical/clipboard" "0.14.3" - "@lexical/headless" "0.14.3" - "@lexical/html" "0.14.3" - "@lexical/link" "0.14.3" - "@lexical/list" "0.14.3" - "@lexical/rich-text" "0.14.3" - "@tryghost/kg-default-nodes" "1.1.7" - "@tryghost/kg-default-transforms" "1.1.8" + "@lexical/clipboard" "0.13.1" + "@lexical/headless" "0.13.1" + "@lexical/html" "0.13.1" + "@lexical/link" "0.13.1" + "@lexical/list" "0.13.1" + "@lexical/rich-text" "0.13.1" + "@tryghost/kg-default-nodes" "1.1.8" + "@tryghost/kg-default-transforms" "1.1.9" jsdom "^24.0.0" - lexical "0.14.3" + lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.8": - version "1.1.8" - resolved "https://registry.npmjs.org/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.8.tgz#a3d840cec6f2e1e8bd4760917ab883a990630f75" - integrity sha512-V7CXYY8cPaBXVWtoM0JCwXeM4j5tfLOJK5+gbjoS6iBb1nOa25AQGGxvuqljVUkKYeEpYKwSXopkpB5ZrqRRMQ== +"@tryghost/kg-lexical-html-renderer@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.10.tgz#01de3ef020b2054c125b1c39bcf6920306a7e3eb" + integrity sha512-tL1mFm8uM1fo2g72KU9+hCKIcDPA6xGOMlGBUVcTSfUIpzVCGc5rWbRzdOjFgZbH7qOYWwDnnj8xUfI6LkM7Vg== dependencies: - "@lexical/clipboard" "0.14.3" - "@lexical/code" "0.14.3" - "@lexical/headless" "0.14.3" - "@lexical/link" "0.14.3" - "@lexical/list" "0.14.3" - "@lexical/rich-text" "0.14.3" - "@tryghost/kg-default-nodes" "1.1.7" - "@tryghost/kg-default-transforms" "1.1.8" + "@lexical/clipboard" "0.13.1" + "@lexical/code" "0.13.1" + "@lexical/headless" "0.13.1" + "@lexical/link" "0.13.1" + "@lexical/list" "0.13.1" + "@lexical/rich-text" "0.13.1" + "@tryghost/kg-default-nodes" "1.1.8" + "@tryghost/kg-default-transforms" "1.1.9" jsdom "^24.0.0" - lexical "0.14.3" + lexical "0.13.1" "@tryghost/kg-markdown-html-renderer@7.0.5": version "7.0.5" @@ -8078,10 +8062,10 @@ dependencies: semver "^7.3.5" -"@tryghost/koenig-lexical@1.3.9": - version "1.3.9" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.9.tgz#0266a53c9aecd77e2b8297e9c026aaad939a71c6" - integrity sha512-GTA0FCkuBGO61dAR7MdRCK+G5XWzqffSVjGkLy00vi4MNAKqvgoAxf/Lnc71nkut7c1z2Y6z+ad0XahFgEc2oA== +"@tryghost/koenig-lexical@1.3.10": + version "1.3.10" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.10.tgz#4591cadd8926eec0e0f4a8a57dc64911599a714f" + integrity sha512-5jw+2eCfBuETvcS2twtLcSTrKjGqVpsfZsRcDV4y9R5YmAA3DiOJLKMOpKb6CPekh71cLp6m0UR4MAow4qc2DQ== "@tryghost/limit-service@1.2.14": version "1.2.14" @@ -9747,9 +9731,9 @@ agent-base@6, agent-base@^6.0.2: debug "4" agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" - integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== dependencies: debug "^4.3.4" @@ -14395,10 +14379,10 @@ debug@3.2.7, debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" @@ -14409,6 +14393,13 @@ debug@4.1.1: dependencies: ms "^2.1.1" +debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -19733,7 +19724,7 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" -https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.4: +https-proxy-agent@^7.0.1: version "7.0.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== @@ -19741,6 +19732,14 @@ https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.4: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.4: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + https@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4" @@ -22155,10 +22154,10 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lexical@0.14.3: - version "0.14.3" - resolved "https://registry.npmjs.org/lexical/-/lexical-0.14.3.tgz#81c41a4c585100192f6d330e81cc6013bf326f5e" - integrity sha512-LaWSKj6OpvJ+bdfQA2AybEzho0YoWfAdRGkuCtPNYd/uf7IHyoEwCFQsIBvWCQF23saDgE1NONR4uiwl6iaJ9g== +lexical@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/lexical/-/lexical-0.13.1.tgz#0abffe9bc05a7a9da8a6128ea478bf08c11654db" + integrity sha512-jaqRYzVEfBKbX4FwYpd/g+MyOjRaraAel0iQsTrwvx3hyN0bswUZuzb6H6nGlFSjcdrc77wKpyKwoWj4aUd+Bw== lib0@0.2.94, lib0@^0.2.85, lib0@^0.2.86: version "0.2.94" @@ -23129,16 +23128,16 @@ markdown-it@^12.0.2: uc.micro "^1.0.5" markdown-it@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.0.0.tgz#b4b2ddeb0f925e88d981f84c183b59bac9e3741b" - integrity sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw== + version "14.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== dependencies: argparse "^2.0.1" entities "^4.4.0" linkify-it "^5.0.0" mdurl "^2.0.0" punycode.js "^2.3.1" - uc.micro "^2.0.0" + uc.micro "^2.1.0" markdown-it@^8.3.1: version "8.4.2" @@ -24642,11 +24641,16 @@ numbered@^1.1.0: resolved "https://registry.yarnpkg.com/numbered/-/numbered-1.1.0.tgz#9fcd79564c73a84b9574e8370c3d8e58fe3c133c" integrity sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g== -nwsapi@^2.2.0, nwsapi@^2.2.10: +nwsapi@^2.2.0: version "2.2.10" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.10.tgz#0b77a68e21a0b483db70b11fad055906e867cda8" integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== +nwsapi@^2.2.10: + version "2.2.12" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" + integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== + nx@16.8.1: version "16.8.1" resolved "https://registry.yarnpkg.com/nx/-/nx-16.8.1.tgz#b3b084da5f880c638debbefbf33eeccb96633595" @@ -28317,9 +28321,9 @@ rrweb-cssom@^0.6.0: integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== rrweb-cssom@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.0.tgz#51cc1e7f4c20dd81218545b5092939bc6fd81bcd" - integrity sha512-KlSv0pm9kgQSRxXEMgtivPJ4h826YHsuob8pSHcfSZsSXGtvpEAie8S0AnXuObEJ7nhikOb4ahwxDm0H2yW17g== + version "0.7.1" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" + integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== rss@1.2.2: version "1.2.2" @@ -28581,7 +28585,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.6.2, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.6.2, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -28591,6 +28595,11 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.3.5: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + semver@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" @@ -30892,21 +30901,26 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uc.micro@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.0.0.tgz#84b3c335c12b1497fd9e80fcd3bfa7634c363ff1" - integrity sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig== +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== ufo@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== -uglify-js@^3.1.4, uglify-js@^3.5.1: +uglify-js@^3.1.4: version "3.17.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== +uglify-js@^3.5.1: + version "3.19.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.0.tgz#6d45f1cad2c54117fa2fabd87fc2713a83e3bf7b" + integrity sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q== + uid-safe@~2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" From 7d2787aa32a9e12cca3adf86defb93ca90836283 Mon Sep 17 00:00:00 2001 From: Sodbileg Gansukh Date: Thu, 18 Jul 2024 20:10:29 +0800 Subject: [PATCH 031/164] Fixed blockquote spacing on iOS Mail app (#20621) ref DES-571 - iOS Mail app ignores spacing on the \ element, but will respect spacing on the \ element inside it - for that reason, we started to enforce always rendering \ inside \ for emails - these changes move the spacing related styles from blockquote to p inside --- .../__snapshots__/email-previews.test.js.snap | 72 ++++--- .../__snapshots__/batch-sending.test.js.snap | 192 ++++++++++++------ .../__snapshots__/cards.test.js.snap | 40 ++-- .../lib/email-templates/partials/styles.hbs | 24 ++- 4 files changed, 216 insertions(+), 112 deletions(-) diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap index da380bd059..2cf9c0d045 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap @@ -417,19 +417,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -772,7 +776,7 @@ exports[`Email Preview API Read can read post email preview with email card and Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "24500", + "content-length": "24519", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -1103,19 +1107,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -1272,7 +1280,7 @@ table.body h2 span { @@ -1480,7 +1488,7 @@ exports[`Email Preview API Read can read post email preview with fields 4: [head Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "29289", + "content-length": "29301", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -1842,19 +1850,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -2204,7 +2216,7 @@ exports[`Email Preview API Read has custom content transformations for email com Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "24254", + "content-length": "24273", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -2895,19 +2907,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -3271,7 +3287,7 @@ exports[`Email Preview API Read uses the newsletter provided through ?newsletter Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "25037", + "content-length": "25056", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -3988,19 +4004,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -4364,7 +4384,7 @@ exports[`Email Preview API Read uses the posts newsletter by default 4: [headers Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "25037", + "content-length": "25056", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap index 206592330f..939df78f6f 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap @@ -310,19 +310,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -985,19 +989,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -1636,19 +1644,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -2287,19 +2299,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -2938,19 +2954,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -3537,19 +3557,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -4188,19 +4212,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -4933,19 +4961,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -6998,19 +7030,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -7794,19 +7830,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -8500,19 +8540,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -9206,19 +9250,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -9912,19 +9960,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -10618,19 +10670,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -11324,19 +11380,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -11977,19 +12037,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap index 848d459b4a..773a39967a 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap @@ -310,19 +310,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -961,19 +965,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -1612,19 +1620,23 @@ table.body h2 span { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } @@ -1811,7 +1823,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing
    -

    HTML Ipsum Presents

    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

    Header Level 2

    1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    2. Aliquam tincidunt mauris eu risus.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

    Header Level 3

    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    • Aliquam tincidunt mauris eu risus.
    #header h1 a{display: block;width: 300px;height: 80px;}
    +

    HTML Ipsum Presents

    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

    Header Level 2

    1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    2. Aliquam tincidunt mauris eu risus.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

    Header Level 3

    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    • Aliquam tincidunt mauris eu risus.
    #header h1 a{display: block;width: 300px;height: 80px;}
    -

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    and a paragraph (in markdown!)

    @@ -2018,7 +2030,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing -

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    +

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    diff --git a/ghost/email-service/lib/email-templates/partials/styles.hbs b/ghost/email-service/lib/email-templates/partials/styles.hbs index 13bd04d798..4731420ea8 100644 --- a/ghost/email-service/lib/email-templates/partials/styles.hbs +++ b/ghost/email-service/lib/email-templates/partials/styles.hbs @@ -157,8 +157,8 @@ dd { } blockquote { - margin: 2em 0 2em 0; - padding: 0 25px 0 25px; + margin: 0; + padding: 0; border-left: {{accentColor}} 2px solid; font-size: 17px; font-weight: 500; @@ -168,7 +168,6 @@ blockquote { blockquote.kg-blockquote-alt { border-left: 0 none; - padding: 0 50px 0 50px; text-align: center; font-size: 1.2em; font-style: italic; @@ -176,10 +175,15 @@ blockquote.kg-blockquote-alt { } blockquote p { - margin: 0.8em 0; + margin: 2em 0; + padding: 0 25px; font-size: 1em; } +blockquote.kg-blockquote-alt p { + padding: 0 50px; +} + blockquote small { display: inline-block; margin: 0.8em 0 0.8em 1.5em; @@ -1574,19 +1578,23 @@ a[data-flickr-embed] img { font-size: 16px !important; line-height: 1.6em; margin-bottom: 0; - padding-left: 15px; + } + + table.body blockquote p { + padding: 0 15px !important; } table.body blockquote.kg-blockquote-alt { border-left: 0 none !important; margin: 0 !important; - padding-left: 20px !important; - padding-right: 20px !important; - padding-bottom: 1.5em !important; font-size: 18px !important; line-height: 1.4em !important; } + table.body blockquote.kg-blockquote-alt p { + padding: 0 20px !important; + } + table.body hr { margin: 2em 0 !important; } From 170fba096268943f23c89d7fe595a063ce6b9464 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 18 Jul 2024 13:19:39 +0100 Subject: [PATCH 032/164] Enforced nested `

    ` when rendering `

    ` for Aside nodes in email closes https://linear.app/tryghost/issue/ENG-1432 - bumps `kg-lexical-html-render` package with required rendering change --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- .../__snapshots__/cards.test.js.snap | 2 +- yarn.lock | 16 ++++++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index fb4e541d6d..1aba9ce4f8 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.1", "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.10", + "@tryghost/koenig-lexical": "1.3.11", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/package.json b/ghost/core/package.json index c67a825a3b..6ff1fa9629 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -113,7 +113,7 @@ "@tryghost/kg-default-cards": "10.0.6", "@tryghost/kg-default-nodes": "1.1.8", "@tryghost/kg-html-to-lexical": "1.1.9", - "@tryghost/kg-lexical-html-renderer": "1.1.10", + "@tryghost/kg-lexical-html-renderer": "1.1.11", "@tryghost/kg-mobiledoc-html-renderer": "7.0.4", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap index 773a39967a..cbe7d94376 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap @@ -1823,7 +1823,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing
    @@ -1488,7 +1492,7 @@ exports[`Email Preview API Read can read post email preview with fields 4: [head Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "29301", + "content-length": "29375", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -1853,7 +1857,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1864,7 +1869,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -2216,7 +2222,7 @@ exports[`Email Preview API Read has custom content transformations for email com Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "24273", + "content-length": "24349", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -2910,7 +2916,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -2921,7 +2928,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -3287,7 +3295,7 @@ exports[`Email Preview API Read uses the newsletter provided through ?newsletter Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "25056", + "content-length": "25132", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -4007,7 +4015,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -4018,7 +4027,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -4384,7 +4394,7 @@ exports[`Email Preview API Read uses the posts newsletter by default 4: [headers Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "25056", + "content-length": "25132", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap index 939df78f6f..d7b0bb87d4 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap @@ -313,7 +313,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -324,7 +325,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -992,7 +994,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1003,7 +1006,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -1647,7 +1651,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1658,7 +1663,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -2302,7 +2308,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -2313,7 +2320,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -2957,7 +2965,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -2968,7 +2977,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -3560,7 +3570,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -3571,7 +3582,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -4215,7 +4227,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -4226,7 +4239,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -4964,7 +4978,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -4975,7 +4990,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -7033,7 +7049,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -7044,7 +7061,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -7833,7 +7851,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -7844,7 +7863,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -8543,7 +8563,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -8554,7 +8575,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -9253,7 +9275,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -9264,7 +9287,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -9963,7 +9987,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -9974,7 +9999,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -10673,7 +10699,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -10684,7 +10711,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -11383,7 +11411,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -11394,7 +11423,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -12040,7 +12070,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -12051,7 +12082,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap index cbe7d94376..a30d6ed8ee 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap @@ -313,7 +313,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -324,7 +325,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -968,7 +970,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -979,7 +982,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -1623,7 +1627,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1634,7 +1639,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -1823,7 +1829,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing
    -

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    and a paragraph (in markdown!)

    diff --git a/yarn.lock b/yarn.lock index cdf1455347..2c6cb5d103 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8003,10 +8003,10 @@ jsdom "^24.0.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.10.tgz#01de3ef020b2054c125b1c39bcf6920306a7e3eb" - integrity sha512-tL1mFm8uM1fo2g72KU9+hCKIcDPA6xGOMlGBUVcTSfUIpzVCGc5rWbRzdOjFgZbH7qOYWwDnnj8xUfI6LkM7Vg== +"@tryghost/kg-lexical-html-renderer@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.11.tgz#5fe1d7ea5f55b665285bad5ac41a71e025152221" + integrity sha512-q5oWbxp2Uun7xt+S+qI7zCaDlHvL6qkD5+Tzk4/UaZEojxbcWnVVeQe574cQwHAjvZN4H65aVRyRE4poo1ZoAw== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -8062,10 +8062,10 @@ dependencies: semver "^7.3.5" -"@tryghost/koenig-lexical@1.3.10": - version "1.3.10" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.10.tgz#4591cadd8926eec0e0f4a8a57dc64911599a714f" - integrity sha512-5jw+2eCfBuETvcS2twtLcSTrKjGqVpsfZsRcDV4y9R5YmAA3DiOJLKMOpKb6CPekh71cLp6m0UR4MAow4qc2DQ== +"@tryghost/koenig-lexical@1.3.11": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.11.tgz#fe7f5add77303f8e884d120f606d0228e6e731ff" + integrity sha512-h6uVmcPCGMbmVmh+TuORMnYcN5U/1RCAL/LgeizphufPUVD6bXi+icxJQ5qpY1yIH1iNABSc6lPcwFgajM4COA== "@tryghost/limit-service@1.2.14": version "1.2.14" From 87c12b511337d5ce09867228eb836188a6b66a26 Mon Sep 17 00:00:00 2001 From: Sodbileg Gansukh Date: Thu, 18 Jul 2024 21:01:43 +0800 Subject: [PATCH 033/164] Fixed blockquote horizontal spacing on Outlook (#20625) ref DES-571 - padding does not work well with paragraph inside blockquote as horizontal spacing on Outlook - using margin instead of padding makes sure the spacing is consistent across Outlook versions --- .../__snapshots__/email-previews.test.js.snap | 42 ++++---- .../__snapshots__/batch-sending.test.js.snap | 96 ++++++++++++------- .../__snapshots__/cards.test.js.snap | 22 +++-- .../lib/email-templates/partials/styles.hbs | 13 ++- 4 files changed, 112 insertions(+), 61 deletions(-) diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap index 2cf9c0d045..ab36fdd179 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/email-previews.test.js.snap @@ -420,7 +420,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -431,7 +432,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -776,7 +778,7 @@ exports[`Email Preview API Read can read post email preview with email card and Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "24519", + "content-length": "24595", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, @@ -1110,7 +1112,8 @@ table.body h2 span { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1121,7 +1124,8 @@ table.body h2 span { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { @@ -1280,7 +1284,7 @@ table.body h2 span {
    -

    HTML Ipsum Presents

    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

    Header Level 2

    1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    2. Aliquam tincidunt mauris eu risus.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

    Header Level 3

    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    • Aliquam tincidunt mauris eu risus.
    #header h1 a{display: block;width: 300px;height: 80px;}
    +

    HTML Ipsum Presents

    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

    Header Level 2

    1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    2. Aliquam tincidunt mauris eu risus.

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

    Header Level 3

    • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    • Aliquam tincidunt mauris eu risus.
    #header h1 a{display: block;width: 300px;height: 80px;}
    -

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    and a paragraph (in markdown!)

    @@ -2030,7 +2036,7 @@ Ghost: Independent technology for modern publishingBeautiful, modern publishing -

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    +

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    diff --git a/ghost/email-service/lib/email-templates/partials/styles.hbs b/ghost/email-service/lib/email-templates/partials/styles.hbs index 4731420ea8..4f2b39ea48 100644 --- a/ghost/email-service/lib/email-templates/partials/styles.hbs +++ b/ghost/email-service/lib/email-templates/partials/styles.hbs @@ -175,13 +175,14 @@ blockquote.kg-blockquote-alt { } blockquote p { - margin: 2em 0; - padding: 0 25px; + margin: 2em 25px; + padding: 0 !important; font-size: 1em; } blockquote.kg-blockquote-alt p { - padding: 0 50px; + margin-right: 50px; + margin-left: 50px; } blockquote small { @@ -1581,7 +1582,8 @@ a[data-flickr-embed] img { } table.body blockquote p { - padding: 0 15px !important; + margin-right: 15px !important; + margin-left: 15px !important; } table.body blockquote.kg-blockquote-alt { @@ -1592,7 +1594,8 @@ a[data-flickr-embed] img { } table.body blockquote.kg-blockquote-alt p { - padding: 0 20px !important; + margin-right: 20px !important; + margin-left: 20px !important; } table.body hr { From 836b6e7be94f1d402a762330e41b77a646857c95 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 18 Jul 2024 19:14:18 +0530 Subject: [PATCH 034/164] Fixed publishing issue when site has no active newsletters Ref ENG-1253 Not allowing users to archive their last active newsletter --- .../settings/email/newsletters/NewsletterDetailModal.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx index a762d5a707..0b73777146 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx @@ -106,8 +106,15 @@ const Sidebar: React.FC<{ const {localSettings} = useSettingGroup(); const [siteTitle] = getSettingValues(localSettings, ['title']) as string[]; const handleError = useHandleError(); + const {data: {newsletters: apiNewsletters} = {}} = useBrowseNewsletters(); let newsletterAddress = renderSenderEmail(newsletter, config, defaultEmailAddress); + const [newsletters, setNewsletters] = useState(apiNewsletters || []); + const activeNewsletters = newsletters.filter(n => n.status === 'active'); + + useEffect(() => { + setNewsletters(apiNewsletters || []); + }, [apiNewsletters]); const fontOptions: SelectOption[] = [ {value: 'serif', label: 'Elegant serif', className: 'font-serif'}, @@ -252,7 +259,7 @@ const Sidebar: React.FC<{ />
    - {newsletter.status === 'active' ? (!onlyOne &&
    }, From f05ba155b8b14ac2091c75d0d8551c0efbc3648e Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 18 Jul 2024 19:25:49 +0530 Subject: [PATCH 035/164] Revert fixed publishing issue when site has no active newsletters --- .../settings/email/newsletters/NewsletterDetailModal.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx index 0b73777146..a762d5a707 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx @@ -106,15 +106,8 @@ const Sidebar: React.FC<{ const {localSettings} = useSettingGroup(); const [siteTitle] = getSettingValues(localSettings, ['title']) as string[]; const handleError = useHandleError(); - const {data: {newsletters: apiNewsletters} = {}} = useBrowseNewsletters(); let newsletterAddress = renderSenderEmail(newsletter, config, defaultEmailAddress); - const [newsletters, setNewsletters] = useState(apiNewsletters || []); - const activeNewsletters = newsletters.filter(n => n.status === 'active'); - - useEffect(() => { - setNewsletters(apiNewsletters || []); - }, [apiNewsletters]); const fontOptions: SelectOption[] = [ {value: 'serif', label: 'Elegant serif', className: 'font-serif'}, @@ -259,7 +252,7 @@ const Sidebar: React.FC<{ />
    - {newsletter.status === 'active' ? (!onlyOne &&
    }, From 7c1a732a300e367388fe028daa18a918c0df6b7e Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 18 Jul 2024 19:33:15 +0530 Subject: [PATCH 036/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20publishing=20iss?= =?UTF-8?q?ue=20when=20site=20has=20no=20active=20newsletters=20(#20627)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref [ENG-1253](https://linear.app/tryghost/issue/ENG-1253/) Not allowing users to archive their last active newsletter --- .../settings/email/newsletters/NewsletterDetailModal.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx index a762d5a707..0b73777146 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModal.tsx @@ -106,8 +106,15 @@ const Sidebar: React.FC<{ const {localSettings} = useSettingGroup(); const [siteTitle] = getSettingValues(localSettings, ['title']) as string[]; const handleError = useHandleError(); + const {data: {newsletters: apiNewsletters} = {}} = useBrowseNewsletters(); let newsletterAddress = renderSenderEmail(newsletter, config, defaultEmailAddress); + const [newsletters, setNewsletters] = useState(apiNewsletters || []); + const activeNewsletters = newsletters.filter(n => n.status === 'active'); + + useEffect(() => { + setNewsletters(apiNewsletters || []); + }, [apiNewsletters]); const fontOptions: SelectOption[] = [ {value: 'serif', label: 'Elegant serif', className: 'font-serif'}, @@ -252,7 +259,7 @@ const Sidebar: React.FC<{ />
    - {newsletter.status === 'active' ? (!onlyOne &&
    }, From 6840c3a6b3e8ea442e45bd48ba85b97b88585081 Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Thu, 18 Jul 2024 12:18:32 -0500 Subject: [PATCH 037/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20admin=20crashing?= =?UTF-8?q?=20when=20deleting=20a=20tier=20benefit=20that=20was=20last=20m?= =?UTF-8?q?oved=20(#20628)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/ENG-1433/ - in the tiers details modal, if a tier was deleted after being moved it would crash admin The SortableList component calls renderItem for the drag overlay on the dragged item. However, if that item was deleted, the draggingId never got reset causing the drag overlay to try to call renderItem on a nonexistent item. As a note, we don't actually use this overlay... it could be best to strip it out to prevent causing issues. --- apps/admin-x-design-system/src/global/SortableList.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/admin-x-design-system/src/global/SortableList.tsx b/apps/admin-x-design-system/src/global/SortableList.tsx index 9209f5e319..43247d0339 100644 --- a/apps/admin-x-design-system/src/global/SortableList.tsx +++ b/apps/admin-x-design-system/src/global/SortableList.tsx @@ -139,7 +139,10 @@ const SortableList = ({
    onMove(event.active.id as string, event.over?.id as string)} + onDragEnd={(event) => { + onMove(event.active.id as string, event.over?.id as string); + setDraggingId(null); + }} onDragStart={event => setDraggingId(event.active.id as string)} > From 4a59e300504d99389ccf20537b6a4c4687740e78 Mon Sep 17 00:00:00 2001 From: Michael Barrett Date: Fri, 19 Jul 2024 09:26:59 +0100 Subject: [PATCH 038/164] Published new versions of apps to support BS locale (#20629) refs [ONC-150](https://linear.app/tryghost/issue/ONC-150/support-escalation-re-trouble-with-new-languages) Published new versions of apps to support BS locale - comments-ui@0.17.3 - signup-form@0.1.5 - portal@2.37.10 --- apps/comments-ui/package.json | 2 +- apps/portal/package.json | 2 +- apps/signup-form/package.json | 2 +- ghost/i18n/lib/i18n.js | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 49c0796dd0..e26fda67d9 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/comments-ui", - "version": "0.17.1", + "version": "0.17.3", "license": "MIT", "repository": "git@github.com:TryGhost/comments-ui.git", "author": "Ghost Foundation", diff --git a/apps/portal/package.json b/apps/portal/package.json index 669702417b..75b3fcccd2 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/portal", - "version": "2.37.8", + "version": "2.37.10", "license": "MIT", "repository": { "type": "git", diff --git a/apps/signup-form/package.json b/apps/signup-form/package.json index be36380994..6f1a205d16 100644 --- a/apps/signup-form/package.json +++ b/apps/signup-form/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/signup-form", - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "repository": { "type": "git", diff --git a/ghost/i18n/lib/i18n.js b/ghost/i18n/lib/i18n.js index 3ff0925525..42aa92e685 100644 --- a/ghost/i18n/lib/i18n.js +++ b/ghost/i18n/lib/i18n.js @@ -3,6 +3,7 @@ const i18next = require('i18next'); const SUPPORTED_LOCALES = [ 'af', // Afrikaans 'bg', // Bulgarian + 'bs', // Bosnian 'ca', // Catalan 'cs', // Czech 'da', // Danish From db9bf510912198595d9a9d54422d083e19d850e0 Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:04:20 +0000 Subject: [PATCH 039/164] =?UTF-8?q?=F0=9F=8E=A8=20Updated=20Source=20to=20?= =?UTF-8?q?v1.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ghost/core/content/themes/source | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/core/content/themes/source b/ghost/core/content/themes/source index d09d421326..61a511b229 160000 --- a/ghost/core/content/themes/source +++ b/ghost/core/content/themes/source @@ -1 +1 @@ -Subproject commit d09d42132688f8fef8ced49fddb676888f61b339 +Subproject commit 61a511b229f3005856dadf625a24d697d5b07986 From d0db527b8d895965ed6c1bfc8efd2116a6ca7f75 Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:04:20 +0000 Subject: [PATCH 040/164] v5.88.0 --- ghost/admin/package.json | 4 ++-- ghost/core/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 1aba9ce4f8..bfa155e23f 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.87.3", + "version": "5.88.0", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", @@ -205,4 +205,4 @@ } } } -} +} \ No newline at end of file diff --git a/ghost/core/package.json b/ghost/core/package.json index 6ff1fa9629..bcace9d231 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.87.3", + "version": "5.88.0", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From 8ea1dfb957334da2bf6eb380ba4c6f1022547847 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Sat, 20 Jul 2024 21:49:21 +0100 Subject: [PATCH 041/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20listing=20pages?= =?UTF-8?q?=20in=20Admin=20(#20633)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/TryGhost/Ghost/issues/20632 - Revert "✨ Improved performance loading the posts list in admin (#20618)" --- ghost/admin/app/components/gh-context-menu.js | 2 +- .../app/components/posts-list/context-menu.js | 40 ++- .../admin/app/components/posts-list/list.hbs | 41 +-- ghost/admin/app/controllers/posts.js | 2 +- ghost/admin/app/routes/posts.js | 55 ++-- ghost/admin/app/templates/posts.hbs | 23 +- .../posts-list => utils}/selection-list.js | 96 +++---- ghost/admin/mirage/config/posts.js | 25 +- ghost/admin/tests/acceptance/content-test.js | 246 ++++++++---------- 9 files changed, 202 insertions(+), 328 deletions(-) rename ghost/admin/app/{components/posts-list => utils}/selection-list.js (67%) diff --git a/ghost/admin/app/components/gh-context-menu.js b/ghost/admin/app/components/gh-context-menu.js index 205e488bb1..8b3de5a54b 100644 --- a/ghost/admin/app/components/gh-context-menu.js +++ b/ghost/admin/app/components/gh-context-menu.js @@ -1,5 +1,5 @@ import Component from '@glimmer/component'; -import SelectionList from './posts-list/selection-list'; +import SelectionList from '../utils/selection-list'; import {action} from '@ember/object'; import {inject as service} from '@ember/service'; import {task} from 'ember-concurrency'; diff --git a/ghost/admin/app/components/posts-list/context-menu.js b/ghost/admin/app/components/posts-list/context-menu.js index 0e9b248661..f945cfda40 100644 --- a/ghost/admin/app/components/posts-list/context-menu.js +++ b/ghost/admin/app/components/posts-list/context-menu.js @@ -216,14 +216,11 @@ export default class PostsContextMenu extends Component { yield this.performBulkDestroy(); this.notifications.showNotification(this.#getToastMessage('deleted'), {type: 'success'}); - for (const key in this.selectionList.infinityModel) { - const remainingModels = this.selectionList.infinityModel[key].content.filter((model) => { - return !deletedModels.includes(model); - }); - // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this - this.infinity.replace(this.selectionList.infinityModel[key], remainingModels); - } - + const remainingModels = this.selectionList.infinityModel.content.filter((model) => { + return !deletedModels.includes(model); + }); + // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this + this.infinity.replace(this.selectionList.infinityModel, remainingModels); this.selectionList.clearSelection({force: true}); return true; } @@ -250,7 +247,9 @@ export default class PostsContextMenu extends Component { } } + // Remove posts that no longer match the filter this.updateFilteredPosts(); + return true; } @@ -283,17 +282,14 @@ export default class PostsContextMenu extends Component { ] }); - // TODO: something is wrong in here - for (const key in this.selectionList.infinityModel) { - const remainingModels = this.selectionList.infinityModel[key].content.filter((model) => { - if (!updatedModels.find(u => u.id === model.id)) { - return true; - } - return filterNql.queryJSON(model.serialize({includeId: true})); - }); - // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this - this.infinity.replace(this.selectionList.infinityModel[key], remainingModels); - } + const remainingModels = this.selectionList.infinityModel.content.filter((model) => { + if (!updatedModels.find(u => u.id === model.id)) { + return true; + } + return filterNql.queryJSON(model.serialize({includeId: true})); + }); + // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this + this.infinity.replace(this.selectionList.infinityModel, remainingModels); this.selectionList.clearUnavailableItems(); } @@ -390,10 +386,8 @@ export default class PostsContextMenu extends Component { const data = result[this.type === 'post' ? 'posts' : 'pages'][0]; const model = this.store.peekRecord(this.type, data.id); - // Update infinity draft posts content - copied posts are always drafts - if (this.selectionList.infinityModel.draftPosts) { - this.selectionList.infinityModel.draftPosts.content.unshiftObject(model); - } + // Update infinity list + this.selectionList.infinityModel.content.unshiftObject(model); // Show notification this.notifications.showNotification(this.#getToastMessage('duplicated'), {type: 'success'}); diff --git a/ghost/admin/app/components/posts-list/list.hbs b/ghost/admin/app/components/posts-list/list.hbs index b1ab3acd99..4755c76d62 100644 --- a/ghost/admin/app/components/posts-list/list.hbs +++ b/ghost/admin/app/components/posts-list/list.hbs @@ -1,39 +1,14 @@ - {{!-- always order as scheduled, draft, remainder --}} - {{#if (or @model.scheduledPosts (or @model.draftPosts @model.publishedAndSentPosts))}} - {{#if @model.scheduledPosts}} - {{#each @model.scheduledPosts as |post|}} - - - - {{/each}} - {{/if}} - {{#if (and @model.draftPosts (or (not @model.scheduledPosts) (and @model.scheduledPosts @model.scheduledPosts.reachedInfinity)))}} - {{#each @model.draftPosts as |post|}} - - - - {{/each}} - {{/if}} - {{#if (and @model.publishedAndSentPosts (and (or (not @model.scheduledPosts) @model.scheduledPosts.reachedInfinity) (or (not @model.draftPosts) @model.draftPosts.reachedInfinity)))}} - {{#each @model.publishedAndSentPosts as |post|}} - - - - {{/each}} - {{/if}} + {{#each @model as |post|}} + + + {{else}} {{yield}} - {{/if}} + {{/each}} {{!-- The currently selected item or items are passed to the context menu --}} diff --git a/ghost/admin/app/controllers/posts.js b/ghost/admin/app/controllers/posts.js index 1a8dd66e2a..014cad0f47 100644 --- a/ghost/admin/app/controllers/posts.js +++ b/ghost/admin/app/controllers/posts.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -import SelectionList from 'ghost-admin/components/posts-list/selection-list'; +import SelectionList from 'ghost-admin/utils/selection-list'; import {DEFAULT_QUERY_PARAMS} from 'ghost-admin/helpers/reset-query-params'; import {action} from '@ember/object'; import {inject} from 'ghost-admin/decorators/inject'; diff --git a/ghost/admin/app/routes/posts.js b/ghost/admin/app/routes/posts.js index 1df44770ec..93e7d5d4ab 100644 --- a/ghost/admin/app/routes/posts.js +++ b/ghost/admin/app/routes/posts.js @@ -1,5 +1,4 @@ import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; -import RSVP from 'rsvp'; import {action} from '@ember/object'; import {assign} from '@ember/polyfills'; import {isBlank} from '@ember/utils'; @@ -40,53 +39,43 @@ export default class PostsRoute extends AuthenticatedRoute { model(params) { const user = this.session.user; + let queryParams = {}; let filterParams = {tag: params.tag, visibility: params.visibility}; let paginationParams = { perPageParam: 'limit', totalPagesParam: 'meta.pagination.pages' }; - - // type filters are actually mapping statuses + assign(filterParams, this._getTypeFilters(params.type)); - + if (params.type === 'featured') { filterParams.featured = true; } - - // authors and contributors can only view their own posts + if (user.isAuthor) { + // authors can only view their own posts filterParams.authors = user.slug; } else if (user.isContributor) { + // Contributors can only view their own draft posts filterParams.authors = user.slug; - // otherwise we need to filter by author if present + // filterParams.status = 'draft'; } else if (params.author) { filterParams.authors = params.author; } - - let perPage = this.perPage; - - const filterStatuses = filterParams.status; - let queryParams = {allFilter: this._filterString({...filterParams})}; // pass along the parent filter so it's easier to apply the params filter to each infinity model - let models = {}; - if (filterStatuses.includes('scheduled')) { - let scheduledPostsParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: 'scheduled'})}; - models.scheduledPosts = this.infinity.model('post', assign({perPage, startingPage: 1}, paginationParams, scheduledPostsParams)); - } - if (filterStatuses.includes('draft')) { - let draftPostsParams = {...queryParams, order: params.order || 'updated_at desc', filter: this._filterString({...filterParams, status: 'draft'})}; - models.draftPosts = this.infinity.model('post', assign({perPage, startingPage: 1}, paginationParams, draftPostsParams)); - } - if (filterStatuses.includes('published') || filterStatuses.includes('sent')) { - let publishedAndSentPostsParams; - if (filterStatuses.includes('published') && filterStatuses.includes('sent')) { - publishedAndSentPostsParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: '[published,sent]'})}; - } else { - publishedAndSentPostsParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: filterStatuses.includes('published') ? 'published' : 'sent'})}; - } - models.publishedAndSentPosts = this.infinity.model('post', assign({perPage, startingPage: 1}, paginationParams, publishedAndSentPostsParams)); + + let filter = this._filterString(filterParams); + if (!isBlank(filter)) { + queryParams.filter = filter; } - return RSVP.hash(models); + if (!isBlank(params.order)) { + queryParams.order = params.order; + } + + let perPage = this.perPage; + let paginationSettings = assign({perPage, startingPage: 1}, paginationParams, queryParams); + + return this.infinity.model(this.modelName, paginationSettings); } // trigger a background load of all tags and authors for use in filter dropdowns @@ -131,12 +120,6 @@ export default class PostsRoute extends AuthenticatedRoute { }; } - /** - * Returns an object containing the status filter based on the given type. - * - * @param {string} type - The type of filter to generate (draft, published, scheduled, sent). - * @returns {Object} - An object containing the status filter. - */ _getTypeFilters(type) { let status = '[draft,scheduled,published,sent]'; diff --git a/ghost/admin/app/templates/posts.hbs b/ghost/admin/app/templates/posts.hbs index 4187b57418..f0d0b6bbe8 100644 --- a/ghost/admin/app/templates/posts.hbs +++ b/ghost/admin/app/templates/posts.hbs @@ -30,7 +30,7 @@
  • @@ -43,7 +43,7 @@ {{else}}

    No posts match the current filter

    - + Show all posts {{/if}} @@ -51,26 +51,11 @@
  • - {{!-- only show one infinity loader wheel at a time - always order as scheduled, draft, remainder --}} - {{#if @model.scheduledPosts}} - {{/if}} - {{#if (and @model.draftPosts (or (not @model.scheduledPosts) (and @model.scheduledPosts @model.scheduledPosts.reachedInfinity)))}} - - {{/if}} - {{#if (and @model.publishedAndSentPosts (and (or (not @model.scheduledPosts) @model.scheduledPosts.reachedInfinity) (or (not @model.draftPosts) @model.draftPosts.reachedInfinity)))}} - - {{/if}} -
    + {{outlet}} diff --git a/ghost/admin/app/components/posts-list/selection-list.js b/ghost/admin/app/utils/selection-list.js similarity index 67% rename from ghost/admin/app/components/posts-list/selection-list.js rename to ghost/admin/app/utils/selection-list.js index c389cd9545..b409d6da4b 100644 --- a/ghost/admin/app/components/posts-list/selection-list.js +++ b/ghost/admin/app/utils/selection-list.js @@ -18,11 +18,7 @@ export default class SelectionList { #clearOnNextUnfreeze = false; constructor(infinityModel) { - this.infinityModel = infinityModel ?? { - draftPosts: { - content: [] - } - }; + this.infinityModel = infinityModel ?? {content: []}; } freeze() { @@ -45,12 +41,7 @@ export default class SelectionList { * Returns an NQL filter for all items, not the selection */ get allFilter() { - const models = this.infinityModel; - // grab filter from the first key in the infinityModel object (they should all be identical) - for (const key in models) { - return models[key].extraParams?.allFilter ?? ''; - } - return ''; + return this.infinityModel.extraParams?.filter ?? ''; } /** @@ -90,13 +81,10 @@ export default class SelectionList { * Keep in mind that when using CMD + A, we don't have all items in memory! */ get availableModels() { - const models = this.infinityModel; const arr = []; - for (const key in models) { - for (const item of models[key].content) { - if (this.isSelected(item.id)) { - arr.push(item); - } + for (const item of this.infinityModel.content) { + if (this.isSelected(item.id)) { + arr.push(item); } } return arr; @@ -114,13 +102,7 @@ export default class SelectionList { if (!this.inverted) { return this.selectedIds.size; } - - const models = this.infinityModel; - let total; - for (const key in models) { - total += models[key].meta?.pagination?.total; - } - return Math.max((total ?? 0) - this.selectedIds.size, 1); + return Math.max((this.infinityModel.meta?.pagination?.total ?? 0) - this.selectedIds.size, 1); } isSelected(id) { @@ -165,12 +147,9 @@ export default class SelectionList { clearUnavailableItems() { const newSelection = new Set(); - const models = this.infinityModel; - for (const key in models) { - for (const item of models[key].content) { - if (this.selectedIds.has(item.id)) { - newSelection.add(item.id); - } + for (const item of this.infinityModel.content) { + if (this.selectedIds.has(item.id)) { + newSelection.add(item.id); } } this.selectedIds = newSelection; @@ -202,40 +181,37 @@ export default class SelectionList { // todo let running = false; - const models = this.infinityModel; - for (const key in models) { - for (const item of this.models[key].content) { - // Exlusing the last selected item - if (item.id === this.lastSelectedId || item.id === id) { - if (!running) { - running = true; + for (const item of this.infinityModel.content) { + // Exlusing the last selected item + if (item.id === this.lastSelectedId || item.id === id) { + if (!running) { + running = true; - // Skip last selected on its own - if (item.id === this.lastSelectedId) { - continue; - } - } else { - // Still include id - if (item.id === id) { - this.lastShiftSelectionGroup.add(item.id); - - if (this.inverted) { - this.selectedIds.delete(item.id); - } else { - this.selectedIds.add(item.id); - } - } - break; + // Skip last selected on its own + if (item.id === this.lastSelectedId) { + continue; } + } else { + // Still include id + if (item.id === id) { + this.lastShiftSelectionGroup.add(item.id); + + if (this.inverted) { + this.selectedIds.delete(item.id); + } else { + this.selectedIds.add(item.id); + } + } + break; } + } - if (running) { - this.lastShiftSelectionGroup.add(item.id); - if (this.inverted) { - this.selectedIds.delete(item.id); - } else { - this.selectedIds.add(item.id); - } + if (running) { + this.lastShiftSelectionGroup.add(item.id); + if (this.inverted) { + this.selectedIds.delete(item.id); + } else { + this.selectedIds.add(item.id); } } } diff --git a/ghost/admin/mirage/config/posts.js b/ghost/admin/mirage/config/posts.js index 2836e0613d..a12863bfe7 100644 --- a/ghost/admin/mirage/config/posts.js +++ b/ghost/admin/mirage/config/posts.js @@ -23,6 +23,7 @@ function extractTags(postAttrs, tags) { }); } +// TODO: handle authors filter export function getPosts({posts}, {queryParams}) { let {filter, page, limit} = queryParams; @@ -30,27 +31,15 @@ export function getPosts({posts}, {queryParams}) { limit = +limit || 15; let statusFilter = extractFilterParam('status', filter); - let authorsFilter = extractFilterParam('authors', filter); - let visibilityFilter = extractFilterParam('visibility', filter); let collection = posts.all().filter((post) => { let matchesStatus = true; - let matchesAuthors = true; - let matchesVisibility = true; if (!isEmpty(statusFilter)) { matchesStatus = statusFilter.includes(post.status); } - if (!isEmpty(authorsFilter)) { - matchesAuthors = authorsFilter.includes(post.authors.models[0].slug); - } - - if (!isEmpty(visibilityFilter)) { - matchesVisibility = visibilityFilter.includes(post.visibility); - } - - return matchesStatus && matchesAuthors && matchesVisibility; + return matchesStatus; }); return paginateModelCollection('posts', collection, page, limit); @@ -70,6 +59,7 @@ export default function mockPosts(server) { return posts.create(attrs); }); + // TODO: handle authors filter server.get('/posts/', getPosts); server.get('/posts/:id/', function ({posts}, {params}) { @@ -110,13 +100,6 @@ export default function mockPosts(server) { posts.find(ids).destroy(); }); - server.post('/posts/:id/copy/', function ({posts}, {params}) { - let post = posts.find(params.id); - let attrs = post.attrs; - - return posts.create(attrs); - }); - server.put('/posts/bulk/', function ({tags}, {requestBody}) { const bulk = JSON.parse(requestBody).bulk; const action = bulk.action; @@ -132,7 +115,7 @@ export default function mockPosts(server) { tags.create(tag); } }); - // TODO: update the actual posts in the mock db if wanting to write tests where we navigate around (refresh model) + // TODO: update the actual posts in the mock db // const postsToUpdate = posts.find(ids); // getting the posts is fine, but within this we CANNOT manipulate them (???) not even iterate with .forEach } diff --git a/ghost/admin/tests/acceptance/content-test.js b/ghost/admin/tests/acceptance/content-test.js index 0fa898abab..5fbbf3f743 100644 --- a/ghost/admin/tests/acceptance/content-test.js +++ b/ghost/admin/tests/acceptance/content-test.js @@ -17,15 +17,11 @@ const findButton = (text, buttons) => { return Array.from(buttons).find(button => button.innerText.trim() === text); }; -// NOTE: With accommodations for faster loading of posts in the UI, the requests to fetch the posts have been split into separate requests based -// on the status of the post. This means that the tests for filtering by status will have multiple requests to check against. describe('Acceptance: Content', function () { let hooks = setupApplicationTest(); setupMirage(hooks); beforeEach(async function () { - // console.log(`this.server`, this.server); - // console.log(`this.server.db`, this.server.db); this.server.loadFixtures('configs'); }); @@ -36,70 +32,6 @@ describe('Acceptance: Content', function () { expect(currentURL()).to.equal('/signin'); }); - describe('as contributor', function () { - beforeEach(async function () { - let contributorRole = this.server.create('role', {name: 'Contributor'}); - this.server.create('user', {roles: [contributorRole]}); - - return await authenticateSession(); - }); - - // NOTE: This test seems to fail if run AFTER the 'can change access' test in the 'as admin' section; router seems to fail, did not look into it further - it('shows posts list and allows post creation', async function () { - await visit('/posts'); - - // has an empty state - expect(findAll('[data-test-post-id]')).to.have.length(0); - expect(find('[data-test-no-posts-box]')).to.exist; - expect(find('[data-test-link="write-a-new-post"]')).to.exist; - - await click('[data-test-link="write-a-new-post"]'); - - expect(currentURL()).to.equal('/editor/post'); - - await fillIn('[data-test-editor-title-input]', 'First contributor post'); - await blur('[data-test-editor-title-input]'); - - expect(currentURL()).to.equal('/editor/post/1'); - - await click('[data-test-link="posts"]'); - - expect(findAll('[data-test-post-id]')).to.have.length(1); - expect(find('[data-test-no-posts-box]')).to.not.exist; - }); - }); - - describe('as author', function () { - let author, authorPost; - - beforeEach(async function () { - let authorRole = this.server.create('role', {name: 'Author'}); - author = this.server.create('user', {roles: [authorRole]}); - let adminRole = this.server.create('role', {name: 'Administrator'}); - let admin = this.server.create('user', {roles: [adminRole]}); - - // create posts - authorPost = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'}); - this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'}); - - return await authenticateSession(); - }); - - it('only fetches the author\'s posts', async function () { - await visit('/posts'); - // trigger a filter request so we can grab the posts API request easily - await selectChoose('[data-test-type-select]', 'Published posts'); - - // API request includes author filter - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter).to.have.string(`authors:${author.slug}`); - - // only author's post is shown - expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1); - expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist; - }); - }); - describe('as admin', function () { let admin, editor, publishedPost, scheduledPost, draftPost, authorPost; @@ -109,10 +41,11 @@ describe('Acceptance: Content', function () { let editorRole = this.server.create('role', {name: 'Editor'}); editor = this.server.create('user', {roles: [editorRole]}); - publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post'}); scheduledPost = this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'}); + // draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post', visibility: 'paid'}); draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'}); - authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'}); + authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post', visibiity: 'paid'}); // pages shouldn't appear in the list this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'}); @@ -128,17 +61,7 @@ describe('Acceptance: Content', function () { // displays all posts by default (all statuses) [no pages] expect(posts.length, 'all posts count').to.equal(4); - // make sure display is scheduled > draft > published/sent - expect(posts[0].querySelector('.gh-content-entry-title').textContent, 'post 1 title').to.contain('Scheduled Post'); - expect(posts[1].querySelector('.gh-content-entry-title').textContent, 'post 2 title').to.contain('Draft Post'); - expect(posts[2].querySelector('.gh-content-entry-title').textContent, 'post 3 title').to.contain('Published Post'); - expect(posts[3].querySelector('.gh-content-entry-title').textContent, 'post 4 title').to.contain('Editor Published Post'); - - // check API requests - let lastRequests = this.server.pretender.handledRequests.filter(request => request.url.includes('/posts/')); - expect(lastRequests[0].queryParams.filter, 'scheduled request filter').to.have.string('status:scheduled'); - expect(lastRequests[1].queryParams.filter, 'drafts request filter').to.have.string('status:draft'); - expect(lastRequests[2].queryParams.filter, 'published request filter').to.have.string('status:[published,sent]'); + // note: atm the mirage backend doesn't support ordering of the results set }); it('can filter by status', async function () { @@ -174,6 +97,13 @@ describe('Acceptance: Content', function () { // Displays scheduled post expect(findAll('[data-test-post-id]').length, 'scheduled count').to.equal(1); expect(find(`[data-test-post-id="${scheduledPost.id}"]`), 'scheduled post').to.exist; + + // show all posts + await selectChoose('[data-test-type-select]', 'All posts'); + + // API request is correct + [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:[draft,scheduled,published,sent]'); }); it('can filter by author', async function () { @@ -184,31 +114,20 @@ describe('Acceptance: Content', function () { // API request is correct let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.allFilter, '"editor" request status filter') + expect(lastRequest.queryParams.filter, '"editor" request status filter') .to.have.string('status:[draft,scheduled,published,sent]'); - expect(lastRequest.queryParams.allFilter, '"editor" request filter param') + expect(lastRequest.queryParams.filter, '"editor" request filter param') .to.have.string(`authors:${editor.slug}`); - - // Displays editor post - expect(findAll('[data-test-post-id]').length, 'editor count').to.equal(1); }); it('can filter by visibility', async function () { await visit('/posts'); await selectChoose('[data-test-visibility-select]', 'Paid members-only'); + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.allFilter, '"visibility" request filter param') - .to.have.string('visibility:[paid,tiers]'); - let posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(1); - - await selectChoose('[data-test-visibility-select]', 'Public'); - [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.allFilter, '"visibility" request filter param') - .to.have.string('visibility:public'); - posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(3); + expect(lastRequest.queryParams.filter, '"visibility" request filter param') + .to.have.string('visibility:[paid,tiers]+status:[draft,scheduled,published,sent]'); }); it('can filter by tag', async function () { @@ -231,13 +150,14 @@ describe('Acceptance: Content', function () { await selectChoose('[data-test-tag-select]', 'B - Second'); // affirm request let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.allFilter, '"tag" request filter param').to.have.string('tag:second'); + expect(lastRequest.queryParams.filter, 'request filter').to.have.string('tag:second'); }); }); describe('context menu actions', function () { describe('single post', function () { - it('can duplicate a post', async function () { + // has a duplicate option + it.skip('can duplicate a post', async function () { await visit('/posts'); // get the post @@ -245,11 +165,13 @@ describe('Acceptance: Content', function () { expect(post, 'post').to.exist; await triggerEvent(post, 'contextmenu'); + // await this.pauseTest(); let contextMenu = find('.gh-posts-context-menu'); // this is a
      element let buttons = contextMenu.querySelectorAll('button'); + // should have three options for a published post expect(contextMenu, 'context menu').to.exist; expect(buttons.length, 'context menu buttons').to.equal(5); expect(buttons[0].innerText.trim(), 'context menu button 1').to.contain('Unpublish'); @@ -261,15 +183,19 @@ describe('Acceptance: Content', function () { // duplicate the post await click(buttons[3]); - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(5); - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.url, 'request url').to.match(new RegExp(`/posts/${publishedPost.id}/copy/`)); + // API request is correct + // POST /ghost/api/admin/posts/{id}/copy/?formats=mobiledoc,lexical + + // TODO: probably missing endpoint in mirage... + + // let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + // console.log(`lastRequest`, lastRequest); + // expect(lastRequest.url, 'request url').to.match(new RegExp(`/posts/${publishedPost.id}/copy/`)); }); }); describe('multiple posts', function () { - it('can feature and unfeature', async function () { + it('can feature and unfeature posts', async function () { await visit('/posts'); // get all posts @@ -300,7 +226,7 @@ describe('Acceptance: Content', function () { // API request is correct - note, we don't mock the actual model updates let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'feature request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.queryParams.filter, 'feature request id').to.equal(`id:['3','4']`); expect(JSON.parse(lastRequest.requestBody).bulk.action, 'feature request action').to.equal('feature'); // ensure ui shows these are now featured @@ -321,7 +247,7 @@ describe('Acceptance: Content', function () { // API request is correct - note, we don't mock the actual model updates [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'unfeature request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.queryParams.filter, 'unfeature request id').to.equal(`id:['3','4']`); expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unfeature request action').to.equal('unfeature'); // ensure ui shows these are now unfeatured @@ -329,7 +255,7 @@ describe('Acceptance: Content', function () { expect(postFourContainer.querySelector('.gh-featured-post'), 'postFour featured').to.not.exist; }); - it('can add a tag', async function () { + it('can add a tag to multiple posts', async function () { await visit('/posts'); // get all posts @@ -369,12 +295,13 @@ describe('Acceptance: Content', function () { // API request is correct - note, we don't mock the actual model updates let [lastRequest] = this.server.pretender.handledRequests.slice(-2); - expect(lastRequest.queryParams.filter, 'add tag request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.queryParams.filter, 'add tag request id').to.equal(`id:['3','4']`); expect(JSON.parse(lastRequest.requestBody).bulk.action, 'add tag request action').to.equal('addTag'); }); - // TODO: Skip for now. This causes the member creation test to fail ('New member' text doesn't show... ???). - it.skip('can change access', async function () { + // NOTE: we do not seem to be loading the settings properly into the membersutil service, such that the members + // service doesn't think members are enabled + it.skip('can change access to multiple posts', async function () { await visit('/posts'); // get all posts @@ -390,38 +317,26 @@ describe('Acceptance: Content', function () { expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + // NOTE: right clicks don't seem to work in these tests + // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event await triggerEvent(postFourContainer, 'contextmenu'); + let contextMenu = find('.gh-posts-context-menu'); // this is a
        element expect(contextMenu, 'context menu').to.exist; + + // TODO: the change access button is not showing; need to debug the UI to see what field it expects + // change access to the posts let buttons = contextMenu.querySelectorAll('button'); let changeAccessButton = findButton('Change access', buttons); - - expect(changeAccessButton, 'change access button').not.to.exist; - - const settingsService = this.owner.lookup('service:settings'); - await settingsService.set('membersEnabled', true); - - await triggerEvent(postFourContainer, 'contextmenu'); - contextMenu = find('.gh-posts-context-menu'); // this is a
          element - expect(contextMenu, 'context menu').to.exist; - buttons = contextMenu.querySelectorAll('button'); - changeAccessButton = findButton('Change access', buttons); expect(changeAccessButton, 'change access button').to.exist; await click(changeAccessButton); - + const changeAccessModal = find('[data-test-modal="edit-posts-access"]'); - const selectElement = changeAccessModal.querySelector('select'); - await fillIn(selectElement, 'members'); - await click('[data-test-button="confirm"]'); - - // check API request - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'change access request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); - expect(JSON.parse(lastRequest.requestBody).bulk.action, 'change access request action').to.equal('access'); + expect(changeAccessModal, 'change access modal').to.exist; }); - it('can unpublish', async function () { + it('can unpublish posts', async function () { await visit('/posts'); // get all posts @@ -457,7 +372,7 @@ describe('Acceptance: Content', function () { // API request is correct - note, we don't mock the actual model updates let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'unpublish request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.queryParams.filter, 'unpublish request id').to.equal(`id:['3','4']`); expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unpublish request action').to.equal('unpublish'); // ensure ui shows these are now unpublished @@ -465,7 +380,7 @@ describe('Acceptance: Content', function () { expect(postFourContainer.querySelector('.gh-content-entry-status').textContent, 'postThree status').to.contain('Draft'); }); - it('can delete', async function () { + it('can delete posts', async function () { await visit('/posts'); // get all posts @@ -501,7 +416,7 @@ describe('Acceptance: Content', function () { // API request is correct - note, we don't mock the actual model updates let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'delete request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.queryParams.filter, 'delete request id').to.equal(`id:['3','4']`); expect(lastRequest.method, 'delete request method').to.equal('DELETE'); // ensure ui shows these are now deleted @@ -593,4 +508,67 @@ describe('Acceptance: Content', function () { expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/); }); }); + + describe('as author', function () { + let author, authorPost; + + beforeEach(async function () { + let authorRole = this.server.create('role', {name: 'Author'}); + author = this.server.create('user', {roles: [authorRole]}); + let adminRole = this.server.create('role', {name: 'Administrator'}); + let admin = this.server.create('user', {roles: [adminRole]}); + + // create posts + authorPost = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'}); + this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'}); + + return await authenticateSession(); + }); + + it('only fetches the author\'s posts', async function () { + await visit('/posts'); + // trigger a filter request so we can grab the posts API request easily + await selectChoose('[data-test-type-select]', 'Published posts'); + + // API request includes author filter + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter).to.have.string(`authors:${author.slug}`); + + // only author's post is shown + expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1); + expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist; + }); + }); + + describe('as contributor', function () { + beforeEach(async function () { + let contributorRole = this.server.create('role', {name: 'Contributor'}); + this.server.create('user', {roles: [contributorRole]}); + + return await authenticateSession(); + }); + + it('shows posts list and allows post creation', async function () { + await visit('/posts'); + + // has an empty state + expect(findAll('[data-test-post-id]')).to.have.length(0); + expect(find('[data-test-no-posts-box]')).to.exist; + expect(find('[data-test-link="write-a-new-post"]')).to.exist; + + await click('[data-test-link="write-a-new-post"]'); + + expect(currentURL()).to.equal('/editor/post'); + + await fillIn('[data-test-editor-title-input]', 'First contributor post'); + await blur('[data-test-editor-title-input]'); + + expect(currentURL()).to.equal('/editor/post/1'); + + await click('[data-test-link="posts"]'); + + expect(findAll('[data-test-post-id]')).to.have.length(1); + expect(find('[data-test-no-posts-box]')).to.not.exist; + }); + }); }); \ No newline at end of file From f55d1e90c36e4719d3edf6b7310b11da8df7e6bd Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:18:14 +0000 Subject: [PATCH 042/164] v5.88.1 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index bfa155e23f..335333ccf7 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.88.0", + "version": "5.88.1", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index bcace9d231..891ca74e74 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.88.0", + "version": "5.88.1", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From cbd994d0333dd96c755fcb5d3d35def23d4faa88 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 00:12:53 +0000 Subject: [PATCH 043/164] Update dependency jsdom to v24.1.1 --- apps/admin-x-design-system/package.json | 2 +- apps/admin-x-framework/package.json | 2 +- apps/announcement-bar/package.json | 2 +- apps/comments-ui/package.json | 2 +- apps/portal/package.json | 2 +- apps/signup-form/package.json | 2 +- apps/sodo-search/package.json | 2 +- yarn.lock | 52 ++++++++----------------- 8 files changed, 24 insertions(+), 42 deletions(-) diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 052a57d1d4..bd6b079415 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -43,7 +43,7 @@ "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-refresh": "0.4.3", "eslint-plugin-tailwindcss": "3.13.0", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "mocha": "10.2.0", "chai": "4.3.8", "react": "18.3.1", diff --git a/apps/admin-x-framework/package.json b/apps/admin-x-framework/package.json index 5381205abd..f8ccf034da 100644 --- a/apps/admin-x-framework/package.json +++ b/apps/admin-x-framework/package.json @@ -73,7 +73,7 @@ "c8": "8.0.1", "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-refresh": "0.4.3", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "mocha": "10.2.0", "react": "18.3.1", "react-dom": "18.3.1", diff --git a/apps/announcement-bar/package.json b/apps/announcement-bar/package.json index 921f414f54..f5b40b466e 100644 --- a/apps/announcement-bar/package.json +++ b/apps/announcement-bar/package.json @@ -80,7 +80,7 @@ }, "devDependencies": { "@vitejs/plugin-react": "4.2.1", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "vite": "4.5.3", "vite-plugin-svgr": "3.3.0", "vitest": "0.34.3" diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index e26fda67d9..aada799b8f 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -73,7 +73,7 @@ "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-refresh": "0.4.3", "eslint-plugin-tailwindcss": "3.13.0", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "postcss": "8.4.39", "tailwindcss": "3.4.4", "vite": "4.5.3", diff --git a/apps/portal/package.json b/apps/portal/package.json index 75b3fcccd2..395f38a128 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -91,7 +91,7 @@ "concurrently": "8.2.2", "cross-fetch": "4.0.0", "eslint-plugin-i18next": "6.0.3", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "react": "17.0.2", "react-dom": "17.0.2", "vite": "4.5.3", diff --git a/apps/signup-form/package.json b/apps/signup-form/package.json index 6f1a205d16..c4b8e0a039 100644 --- a/apps/signup-form/package.json +++ b/apps/signup-form/package.json @@ -58,7 +58,7 @@ "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-refresh": "0.4.3", "eslint-plugin-tailwindcss": "3.13.0", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "postcss": "8.4.39", "postcss-import": "16.1.0", "prop-types": "15.8.1", diff --git a/apps/sodo-search/package.json b/apps/sodo-search/package.json index f6c90a7f2c..f7c29adc72 100644 --- a/apps/sodo-search/package.json +++ b/apps/sodo-search/package.json @@ -85,7 +85,7 @@ "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "12.1.5", "@vitejs/plugin-react": "4.2.1", - "jsdom": "24.1.0", + "jsdom": "24.1.1", "nock": "13.3.3", "vite": "4.5.3", "vite-plugin-svgr": "3.3.0", diff --git a/yarn.lock b/yarn.lock index 2c6cb5d103..795db278f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14379,7 +14379,7 @@ debug@3.2.7, debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@4, debug@^4.3.4: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== @@ -14393,7 +14393,7 @@ debug@4.1.1: dependencies: ms "^2.1.1" -debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: +debug@4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -19724,15 +19724,7 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" -https-proxy-agent@^7.0.1: - version "7.0.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" - integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== - dependencies: - agent-base "^7.0.2" - debug "4" - -https-proxy-agent@^7.0.4: +https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== @@ -21543,10 +21535,10 @@ jscodeshift@^0.15.1: temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@24.1.0, jsdom@^24.0.0, jsdom@~24.1.0: - version "24.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.1.0.tgz#0cffdabd42c506788bfecd160e8ac22d4387f971" - integrity sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA== +jsdom@24.1.1, jsdom@^24.0.0, jsdom@~24.1.0: + version "24.1.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.1.1.tgz#f41df8f4f3b2fbfa7e1bdc5df62c9804fd14a9d0" + integrity sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ== dependencies: cssstyle "^4.0.1" data-urls "^5.0.0" @@ -21554,11 +21546,11 @@ jsdom@24.1.0, jsdom@^24.0.0, jsdom@~24.1.0: form-data "^4.0.0" html-encoding-sniffer "^4.0.0" http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.4" + https-proxy-agent "^7.0.5" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.10" + nwsapi "^2.2.12" parse5 "^7.1.2" - rrweb-cssom "^0.7.0" + rrweb-cssom "^0.7.1" saxes "^6.0.0" symbol-tree "^3.2.4" tough-cookie "^4.1.4" @@ -21567,7 +21559,7 @@ jsdom@24.1.0, jsdom@^24.0.0, jsdom@~24.1.0: whatwg-encoding "^3.1.1" whatwg-mimetype "^4.0.0" whatwg-url "^14.0.0" - ws "^8.17.0" + ws "^8.18.0" xml-name-validator "^5.0.0" jsdom@^16.4.0: @@ -24641,12 +24633,7 @@ numbered@^1.1.0: resolved "https://registry.yarnpkg.com/numbered/-/numbered-1.1.0.tgz#9fcd79564c73a84b9574e8370c3d8e58fe3c133c" integrity sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g== -nwsapi@^2.2.0: - version "2.2.10" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.10.tgz#0b77a68e21a0b483db70b11fad055906e867cda8" - integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== - -nwsapi@^2.2.10: +nwsapi@^2.2.0, nwsapi@^2.2.12: version "2.2.12" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== @@ -28320,7 +28307,7 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== -rrweb-cssom@^0.7.0: +rrweb-cssom@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== @@ -28585,7 +28572,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.6.2, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.6.2: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -28595,7 +28582,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5: +semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -30911,12 +30898,7 @@ ufo@^1.1.2: resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76" integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ== -uglify-js@^3.1.4: - version "3.17.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" - integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== - -uglify-js@^3.5.1: +uglify-js@^3.1.4, uglify-js@^3.5.1: version "3.19.0" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.0.tgz#6d45f1cad2c54117fa2fabd87fc2713a83e3bf7b" integrity sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q== @@ -32153,7 +32135,7 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@8.18.0, ws@^8.17.0, ws@^8.2.3: +ws@8.18.0, ws@^8.18.0, ws@^8.2.3: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== From c360fe5a2f738519b7a0db8aa969ca6ead6b6102 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 00:50:06 +0000 Subject: [PATCH 044/164] Update dependency rimraf to v5.0.8 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1aa8b05595..d28d5e4db5 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "husky": "8.0.3", "lint-staged": "15.2.7", "nx": "16.8.1", - "rimraf": "5.0.7", + "rimraf": "5.0.8", "ts-node": "10.9.2", "typescript": "5.4.5", "inquirer": "8.2.6" diff --git a/yarn.lock b/yarn.lock index 795db278f6..d019cca1e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28194,10 +28194,10 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== -rimraf@5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" - integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg== +rimraf@5.0.8: + version "5.0.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.8.tgz#9d4d0ef5106817102b14fdbbf01cf29545e99a6c" + integrity sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw== dependencies: glob "^10.3.7" From ec019f6a701d1087c27ae6fed23be38cfb35be53 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 09:30:21 +0000 Subject: [PATCH 045/164] Update dependency cssnano to v7.0.4 --- ghost/core/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ghost/core/package.json b/ghost/core/package.json index 891ca74e74..adbe112d23 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -242,7 +242,7 @@ "@types/common-tags": "1.8.4", "c8": "8.0.1", "cli-progress": "3.12.0", - "cssnano": "7.0.3", + "cssnano": "7.0.4", "detect-indent": "6.1.0", "detect-newline": "3.1.0", "expect": "29.3.1", diff --git a/yarn.lock b/yarn.lock index d019cca1e7..ee5b36e0af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14147,17 +14147,17 @@ cssnano-preset-default@^4.0.7: postcss-svgo "^4.0.3" postcss-unique-selectors "^4.0.1" -cssnano-preset-default@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-7.0.3.tgz#dd554e4d5bef3da50e40d725c7ba0b49053a993c" - integrity sha512-dQ3Ba1p/oewICp/szF1XjFFgql8OlOBrI2YNBUUwhHQnJNoMOcQTa+Bi7jSJN8r/eM1egW0Ud1se/S7qlduWKA== +cssnano-preset-default@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-7.0.4.tgz#9cfcd25f85bfedc84367b881dad56b75a0f976b5" + integrity sha512-jQ6zY9GAomQX7/YNLibMEsRZguqMUGuupXcEk2zZ+p3GUxwCAsobqPYE62VrJ9qZ0l9ltrv2rgjwZPBIFIjYtw== dependencies: browserslist "^4.23.1" css-declaration-sorter "^7.2.0" cssnano-utils "^5.0.0" postcss-calc "^10.0.0" postcss-colormin "^7.0.1" - postcss-convert-values "^7.0.1" + postcss-convert-values "^7.0.2" postcss-discard-comments "^7.0.1" postcss-discard-duplicates "^7.0.0" postcss-discard-empty "^7.0.0" @@ -14220,12 +14220,12 @@ cssnano@4.1.10: is-resolvable "^1.0.0" postcss "^7.0.0" -cssnano@7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-7.0.3.tgz#f7ea770931c84ffd4e7c07fbd1993cee602e4dd1" - integrity sha512-lsekJctOTqdCn4cNrtrSwsuMR/fHC+oiVMHkp/OugBWtwjH8XJag1/OtGaYJGtz0un1fQcRy4ryfYTQsfh+KSQ== +cssnano@7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-7.0.4.tgz#13a4fb4dd14f3b1ee0cd51e6404ae4656f8ad9a0" + integrity sha512-rQgpZra72iFjiheNreXn77q1haS2GEy69zCMbu4cpXCFPMQF+D4Ik5V7ktMzUF/sA7xCIgcqHwGPnCD+0a1vHg== dependencies: - cssnano-preset-default "^7.0.3" + cssnano-preset-default "^7.0.4" lilconfig "^3.1.2" csso@5.0.5, csso@^5.0.5: @@ -25740,10 +25740,10 @@ postcss-convert-values@^4.0.1: postcss "^7.0.0" postcss-value-parser "^3.0.0" -postcss-convert-values@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-7.0.1.tgz#df84dab80a03b30a4b4dba68b348fb07e649a3c3" - integrity sha512-9x2ofb+hYPwHWMlWAzyWys2yMDZYGfkX9LodbaVTmLdlupmtH2AGvj8Up95wzzNPRDEzPIxQIkUaPJew3bT6xA== +postcss-convert-values@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-7.0.2.tgz#8a33265f5f1decfc93328e2a23e03e8491a3d9ae" + integrity sha512-MuZIF6HJ4izko07Q0TgW6pClalI4al6wHRNPkFzqQdwAwG7hPn0lA58VZdxyb2Vl5AYjJ1piO+jgF9EnTjQwQQ== dependencies: browserslist "^4.23.1" postcss-value-parser "^4.2.0" From e740cef863a5a15fa8cda0e290745f1d32783b10 Mon Sep 17 00:00:00 2001 From: Sag Date: Mon, 22 Jul 2024 09:55:37 +0200 Subject: [PATCH 046/164] Fixed regex to ignore AbortError in Sentry (#20639) fixes https://linear.app/tryghost/issue/SLO-175 --- ghost/admin/app/routes/application.js | 2 +- ghost/admin/app/utils/sentry.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/app/routes/application.js b/ghost/admin/app/routes/application.js index 5eb27060f1..24a5f7ad57 100644 --- a/ghost/admin/app/routes/application.js +++ b/ghost/admin/app/routes/application.js @@ -186,7 +186,7 @@ export default Route.extend(ShortcutsRoute, { beforeSend, ignoreErrors: [ // Browser autoplay policies (this regex covers a few) - /The play() request was interrupted/, + /^The play\(\) request was interrupted.*/, /The request is not allowed by the user agent or the platform in the current context/, // Network errors that we don't control diff --git a/ghost/admin/app/utils/sentry.js b/ghost/admin/app/utils/sentry.js index 1056046b2f..4677f68fa2 100644 --- a/ghost/admin/app/utils/sentry.js +++ b/ghost/admin/app/utils/sentry.js @@ -34,7 +34,7 @@ export function beforeSend(event, hint) { delete event.tags.ajax_url; } - // Do not report poshog-js errors to Sentry + // Do not report posthog-js errors to Sentry if (hint && hint.originalException && hint.originalException.stack) { if (hint.originalException.stack.includes('/posthog-js/')) { return null; From 74a67855f4f858c97dd064ef1d8ed312766c076b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 07:16:03 +0000 Subject: [PATCH 047/164] Update dependency rimraf to v5.0.9 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d28d5e4db5..45db669c69 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "husky": "8.0.3", "lint-staged": "15.2.7", "nx": "16.8.1", - "rimraf": "5.0.8", + "rimraf": "5.0.9", "ts-node": "10.9.2", "typescript": "5.4.5", "inquirer": "8.2.6" diff --git a/yarn.lock b/yarn.lock index ee5b36e0af..0549ad4773 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28194,10 +28194,10 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== -rimraf@5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.8.tgz#9d4d0ef5106817102b14fdbbf01cf29545e99a6c" - integrity sha512-XSh0V2/yNhDEi8HwdIefD8MLgs4LQXPag/nEJWs3YUc3Upn+UHa1GyIkEg9xSSNt7HnkO5FjTvmcRzgf+8UZuw== +rimraf@5.0.9: + version "5.0.9" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.9.tgz#c3baa1b886eadc2ec7981a06a593c3d01134ffe9" + integrity sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA== dependencies: glob "^10.3.7" From f493ce9abe819c8b6908257edcd50e9fb9ddf387 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:49:59 +0000 Subject: [PATCH 048/164] Update tiptap monorepo to v2.5.5 --- apps/comments-ui/package.json | 20 +++---- yarn.lock | 100 +++++++++++++++++----------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index aada799b8f..4cf4624c63 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.4", - "@tiptap/extension-blockquote": "2.5.4", - "@tiptap/extension-document": "2.5.4", - "@tiptap/extension-hard-break": "2.5.4", - "@tiptap/extension-link": "2.5.4", - "@tiptap/extension-paragraph": "2.5.4", - "@tiptap/extension-placeholder": "2.5.4", - "@tiptap/extension-text": "2.5.4", - "@tiptap/pm": "2.5.4", - "@tiptap/react": "2.5.4", + "@tiptap/core": "2.5.5", + "@tiptap/extension-blockquote": "2.5.5", + "@tiptap/extension-document": "2.5.5", + "@tiptap/extension-hard-break": "2.5.5", + "@tiptap/extension-link": "2.5.5", + "@tiptap/extension-paragraph": "2.5.5", + "@tiptap/extension-placeholder": "2.5.5", + "@tiptap/extension-text": "2.5.5", + "@tiptap/pm": "2.5.5", + "@tiptap/react": "2.5.5", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index 0549ad4773..652a346600 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7563,66 +7563,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== -"@tiptap/core@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.4.tgz#8b08113512dd8dc9d98268ad6f982d62c85433d2" - integrity sha512-Zs/hShr4+W02+0nOlpmr5cS2YjDRLqd+XMt+jsiQH0QNr3s1Lc82pfF6C3CjgLEZtdUzImZrW2ABtLlpvbogaA== +"@tiptap/core@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.5.tgz#67301af399644a24a9f62b4309a9875794740a66" + integrity sha512-VnAnyWnsqN65QijtUFHbe7EPSJCkhNEAwlatsG/HvrZvUv9KmoWWbMsHAU73wozKzPXR3nHRbCxN+LuxP5bADg== -"@tiptap/extension-blockquote@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.4.tgz#7b0bbf2bed91533a73887b0217bf8000681b8b5a" - integrity sha512-UqeJunZM3IiCQGZE0X5YNUOWYkuIieqrwPgOEghAIjnhDcQizQcouRQ5R7cwwv/scNr2JvZHncOTLrALV3Janw== +"@tiptap/extension-blockquote@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.5.tgz#71102d76eb859991d25c58ce54ab06001bc578ad" + integrity sha512-K+fc++ASlgDRHN6i3j3JBGzWiDhhoZv0jCUB/l7Jzut4UfjIoWqKhmJajnp95Qu9tmwQUy9LMzHqG4G5wUsIsQ== -"@tiptap/extension-bubble-menu@^2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.4.tgz#eaff01a21869a9d65b8fd6e29043008c8ad1b03c" - integrity sha512-GHwef912K1yd75pp9JGDnKSp1DvdOHH8BcHQv0no+a3q2ePFPYcgaSwVRR59jHRX9WzdVfoLcqDSAeoNGOrISw== +"@tiptap/extension-bubble-menu@^2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.5.tgz#b6e72bba8e216ac48e0741a2af16733348567ad8" + integrity sha512-7k0HqrnhQGVZk86MEc5vt8stNRxIY65AMjZfszY/mQw0Dza7EQig/9b/AEmi9n+TNW5/8Qu+OMJD9ln92d/Eog== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.4.tgz#99279433ddd1572bd8e92f43cb40aa72ab576574" - integrity sha512-4RDrhASxCTOZETYhIhEW1TfZqx3Tm+LQxouvBMFyODmT1PSgsg5Xz1FYpDPr+J49bGAK0Pr9ae0XcGW011L3sA== +"@tiptap/extension-document@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.5.tgz#e69929f5d28e8ca659ab98075c5dd742ff805e5d" + integrity sha512-MIjYO63JepcJW37PQuKVmYuZFqkQOZ/12tV0YLU4o6gmGVdqJS0+3md9CdnyUFUDIo7x6TBh8r5i5L2xQpm3Sg== -"@tiptap/extension-floating-menu@^2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.4.tgz#a80e54c685c24c320ef389b8d13f7382bc7e8f8f" - integrity sha512-EqD4rgi3UhnDcV3H1+ndAS4Ue2zpsU7hFKoevOIV6GS7xVnWN70AGt6swH24QzuHKKISFtWoLpKjrwRORNIxuA== +"@tiptap/extension-floating-menu@^2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.5.tgz#3685b3dad59408177720ae78fc069b6f138e67ec" + integrity sha512-1mgpxZGfy1ziNSvWz6m1nGb9ZF9fVVz4X4XwrIqwGw1Vqt9oXflm6puglnzwVLDeaMDT014VUfczJ4My3wDZzA== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.4.tgz#df6e70930e6b426b527d1150ff0bde8118ea6d41" - integrity sha512-nLn6HP9tqgdGGwbMORXVtcY30DTGctYFaWADRthvBjVgacYSeKlhUcsSu3YgaxtbxZp6BhfRvD2kKrxyQsSjnQ== +"@tiptap/extension-hard-break@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.5.tgz#1d85247c26bc8c90ba75516b710a98695707b408" + integrity sha512-VtrwKU0LYS/0rfH5rGz8ztKwA0bsHRyBF53G7aP2FS4BiN8aOEu8t7VkvBZAewXDITDah9K6rqfXk+MNwoul2Q== -"@tiptap/extension-link@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.4.tgz#a256652198cc0abb662608802c7bb98ee1914067" - integrity sha512-xTB/+T6SHHCXInJni8WdqOfF40a/MiFUf5OoWW9cPrApx3I7TzJ9j8/WDshM0BOnDDw80w1bl9F2zkUQjC0Y2A== +"@tiptap/extension-link@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.5.tgz#a1de5308146a9be658b24c78be3dbce824917299" + integrity sha512-zVpNvMD8R9uW1SX1PJoj3fLyOHwuFWqiqEHN2KWfLbEnbL/KXNnpIyKdpHnI9lqFrsMf2dmyZCS3R6xIrynviQ== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.4.tgz#6aa8da578161ecedfb12e37cc515147d4bd5b476" - integrity sha512-pC1YIkkRPXoU0eDrhfAf8ZrFJQzvw2ftP6KRhLnnSw/Ot1DOjT1r95l7zsFefS9oCDMT/L4HghTAiPZ4rcpPbg== +"@tiptap/extension-paragraph@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.5.tgz#13e73750ff78b7206e0eb31d7d5b31f8f0e76dd5" + integrity sha512-XZO1rqsU1vlt9qeG2pVVAt2gXjD0twl2D+uxy4Nw6gxqbhSgfbNq3RP72mmtcS4KyFJi7ETANpcRpb8ZNvXfmg== -"@tiptap/extension-placeholder@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.4.tgz#6841b137011d7873725c99ff07074b0f21af847b" - integrity sha512-mcj4j2Z/L1H5dzWHbbWChuAdJK9F2p06fcjqL4iyJtVx38QQFzCdVmGaTAim8CLp/EynbAOYJ5gk9w2PTdv7+w== +"@tiptap/extension-placeholder@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.5.tgz#1883994be2e4dfb5b7a271dd39bbdd3e680b6521" + integrity sha512-SwWLYdyrMeoVUQdivkIJ4kkAcb38pykxSetlrXitfUmnkwv0/fi+p76Rickf+roudWPsfzqvgvJ4gT6OAOJrGA== -"@tiptap/extension-text@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.4.tgz#235638ad08975a321dae4f2a2c1e495f38bd3adb" - integrity sha512-+3x/hYqhmCYbvedCcQzQHFtZ5MAcMOlKuczomZtygf8AfDfuQVrG1m4GoJyNzJdqxjN80/xq4e2vDVvqQxYTCw== +"@tiptap/extension-text@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.5.tgz#3675a1ace24869c3664137054a3b2bc2d25ac2c7" + integrity sha512-8c/hxcw7t/S3iKGSFwGNxC2I6AkKpRiySQJ95ML2miwSOAxWhnltoYYV7gobWCRgm25lnvzX/Z6BdpFzXBrBKA== -"@tiptap/pm@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.4.tgz#6d66cd8ab3bd696958a4a62f34d2032a82a28910" - integrity sha512-oFIsuniptdUXn93x4aM2sVN3hYKo9Fj55zAkYrWhwxFYUYcPxd5ibra2we+wRK5TaiPu098wpC+yMSTZ/KKMpA== +"@tiptap/pm@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.5.tgz#3c67224f9aa7460f05c4979aa0faab60b97dd503" + integrity sha512-ppePiLaeG6IKkm8Yq+mRENT4LIAS4qQyLT8EnKadznaTL6SNj/72mm0MjD44URkM38ySzIyvt/vqHDapNK0Hww== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7643,13 +7643,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.8" -"@tiptap/react@2.5.4": - version "2.5.4" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.4.tgz#8007a33e23557743bbaeb96dc89a180261718adf" - integrity sha512-2HPHt2lEK6Z4jOV3HHVTee8hD4NS6eEj0zRZWSFjt1zDzXtFqX8VIv7qC1iDYsQgyiFnFnOucOQtAlDewBb23A== +"@tiptap/react@2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.5.tgz#6a20dfac2e80bcd314dd628621f73760826d25c6" + integrity sha512-V3cfrDWMtOViheMkIQ3qRnKLs0CZaouoB6LBtw96vo8G7IFKAvtgvmau0fjdJKRtHJp5xYiecP6RHt3lHulu+g== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.4" - "@tiptap/extension-floating-menu" "^2.5.4" + "@tiptap/extension-bubble-menu" "^2.5.5" + "@tiptap/extension-floating-menu" "^2.5.5" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From 00d2cc9f44e1756b4653bb552b74a54f305a6a2e Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 3 Jul 2024 10:53:26 +0200 Subject: [PATCH 049/164] Improved speed of monobundle script - right now, it loops through all packages serially, which isn't effectively using multi-core machines - by using `concurrently`, we can rely on it to use all the cores it can, so this should dramatically speed up the bundling step --- ghost/core/monobundle.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ghost/core/monobundle.js b/ghost/core/monobundle.js index 7d471749a0..72157fbf56 100755 --- a/ghost/core/monobundle.js +++ b/ghost/core/monobundle.js @@ -4,9 +4,8 @@ const fs = require('fs'); const path = require('path'); -const util = require('util'); -const exec = util.promisify(require('node:child_process').exec); +const concurrently = require('concurrently'); const detectIndent = require('detect-indent'); const detectNewline = require('detect-newline'); const findRoot = require('find-root'); @@ -111,6 +110,8 @@ function getWorkspaces(from) { console.log('workspaces', workspaces); console.log('\n-------------------------\n'); + const packagesToPack = []; + for (const w of workspaces) { const workspacePkgInfo = JSONFile.for(path.join(w, 'package.json')); @@ -118,7 +119,7 @@ function getWorkspaces(from) { continue; } - console.log(`packaging ${w}\n`); + console.log(`packaging ${w}`); workspacePkgInfo.pkg.version = pkgInfo.pkg.version; workspacePkgInfo.write(); @@ -144,17 +145,24 @@ function getWorkspaces(from) { console.log(`- resolution override for ${workspacePkgInfo.pkg.name} to ${packedFilename}\n`); pkgInfo.pkg.resolutions[workspacePkgInfo.pkg.name] = packedFilename; - const command = `npm pack --pack-destination ../core/components`; - console.log(`running '${command}' in ${w}\n`); - - const {stdout, stderr} = await exec(command, {cwd: w}); - console.log('stdout', stdout); - console.log('stderr', stderr); - console.log('\n-------------------------\n'); + packagesToPack.push(w); } pkgInfo.write(); + const {result} = concurrently(packagesToPack.map(w => ({ + name: w, + cwd: w, + command: 'npm pack --pack-destination ../core/components' + }))); + + try { + await result; + } catch (e) { + console.error(e); + throw e; + } + const filesToCopy = [ 'README.md', 'LICENSE', From 4465e09f2fda54bf524682a58e4df1010e922bf8 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Tue, 23 Jul 2024 14:58:48 +0100 Subject: [PATCH 050/164] Fixed missing space after account name when disconnecting Stripe no issue - line wrap immediately after the variable meant whitespace was being ignored --- .../settings/membership/stripe/StripeConnectModal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/admin-x-settings/src/components/settings/membership/stripe/StripeConnectModal.tsx b/apps/admin-x-settings/src/components/settings/membership/stripe/StripeConnectModal.tsx index 00b909402e..524aa4eb83 100644 --- a/apps/admin-x-settings/src/components/settings/membership/stripe/StripeConnectModal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/stripe/StripeConnectModal.tsx @@ -162,8 +162,7 @@ const Connected: React.FC<{onClose?: () => void}> = ({onClose}) => { // this.ghostPaths.url.api('/members/') + '?filter=status:paid&limit=0'; NiceModal.show(ConfirmationModal, { title: 'Disconnect Stripe', - prompt: (hasActiveStripeSubscriptions ? 'Cannot disconnect while there are members with active Stripe subscriptions.' : <>You‘re about to disconnect your Stripe account {stripeConnectAccountName} - from this site. This will automatically turn off paid memberships on this site.), + prompt: (hasActiveStripeSubscriptions ? 'Cannot disconnect while there are members with active Stripe subscriptions.' : <>You‘re about to disconnect your Stripe account {stripeConnectAccountName} from this site. This will automatically turn off paid memberships on this site.), okLabel: hasActiveStripeSubscriptions ? '' : 'Disconnect', onOk: async (modal) => { try { From fb71b03c2861067f4a399b5f7cd8518b47b0bf4e Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 23 Jul 2024 18:03:21 +0200 Subject: [PATCH 051/164] Updated dependency koenig-lexical to v1.3.12 (#20645) ref https://linear.app/tryghost/issue/SLO-181 - this version fixes copy/pasting images from Slack to the editor --- ghost/admin/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 335333ccf7..183c7d6858 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.1", "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.11", + "@tryghost/koenig-lexical": "1.3.12", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/yarn.lock b/yarn.lock index 652a346600..0884cf7c79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8062,10 +8062,10 @@ dependencies: semver "^7.3.5" -"@tryghost/koenig-lexical@1.3.11": - version "1.3.11" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.11.tgz#fe7f5add77303f8e884d120f606d0228e6e731ff" - integrity sha512-h6uVmcPCGMbmVmh+TuORMnYcN5U/1RCAL/LgeizphufPUVD6bXi+icxJQ5qpY1yIH1iNABSc6lPcwFgajM4COA== +"@tryghost/koenig-lexical@1.3.12": + version "1.3.12" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.12.tgz#2a398f66b48e03cab5aaf58edd03c6d3294940cb" + integrity sha512-taAz1XOQTrQKvviiBLfO/bIyDM/B+3ghH+EUITWOY+5JRDhbk2TnVrFD8VABsKnRWgdzSFXE/ToiXlIW8np4pg== "@tryghost/limit-service@1.2.14": version "1.2.14" From 63261d7d88cef2bedab9df6b062025d016bb1f5a Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Mon, 22 Jul 2024 14:43:49 +0700 Subject: [PATCH 052/164] Updated vite config to allow sibling test files --- apps/admin-x-activitypub/vite.config.mjs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/admin-x-activitypub/vite.config.mjs b/apps/admin-x-activitypub/vite.config.mjs index ae5b996d87..2521075119 100644 --- a/apps/admin-x-activitypub/vite.config.mjs +++ b/apps/admin-x-activitypub/vite.config.mjs @@ -5,6 +5,14 @@ import {resolve} from 'path'; export default (function viteConfig() { return adminXViteConfig({ packageName: pkg.name, - entry: resolve(__dirname, 'src/index.tsx') + entry: resolve(__dirname, 'src/index.tsx'), + overrides: { + test: { + include: [ + './test/unit/**/*', + './src/**/*.test.ts' + ] + } + } }); }); From bef58e1069302ab64d41f385a0949900032a8165 Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Mon, 22 Jul 2024 14:54:19 +0700 Subject: [PATCH 053/164] Removed `nx build` from `test:unit` script This slows down TDD workflows way too much --- apps/admin-x-activitypub/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json index 1911c4b190..0782704d0b 100644 --- a/apps/admin-x-activitypub/package.json +++ b/apps/admin-x-activitypub/package.json @@ -25,7 +25,7 @@ "lint": "yarn run lint:code && yarn run lint:test", "lint:code": "eslint --ext .js,.ts,.cjs,.tsx --cache src", "lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx --cache test", - "test:unit": "yarn nx build && vitest run", + "test:unit": "vitest run", "test:acceptance": "NODE_OPTIONS='--experimental-specifier-resolution=node --no-warnings' VITE_TEST=true playwright test", "test:acceptance:slowmo": "TIMEOUT=100000 PLAYWRIGHT_SLOWMO=100 yarn test:acceptance --headed", "test:acceptance:full": "ALL_BROWSERS=1 yarn test:acceptance", From c6e407fb7e2f1206db28753ab7047f13d995b4a0 Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Mon, 22 Jul 2024 14:44:47 +0700 Subject: [PATCH 054/164] Created ActivityPubAPI module ref https://linear.app/tryghost/issue/MOM-288 Instead of having all of our network code inside of admin-x-framework, we're moving it into activitypub to make it easier to work with. We've also added support for using identity tokens for authentication with the ActivityPub API. --- .../src/api/activitypub.test.ts | 366 ++++++++++++++++++ .../src/api/activitypub.ts | 109 ++++++ 2 files changed, 475 insertions(+) create mode 100644 apps/admin-x-activitypub/src/api/activitypub.test.ts create mode 100644 apps/admin-x-activitypub/src/api/activitypub.ts diff --git a/apps/admin-x-activitypub/src/api/activitypub.test.ts b/apps/admin-x-activitypub/src/api/activitypub.test.ts new file mode 100644 index 0000000000..3e742b65f4 --- /dev/null +++ b/apps/admin-x-activitypub/src/api/activitypub.test.ts @@ -0,0 +1,366 @@ +import {Activity, ActivityPubAPI} from './activitypub'; + +function NotFound() { + return new Response(null, { + status: 404 + }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function JSONResponse(data: any, contentType = 'application/json', status = 200) { + return new Response(JSON.stringify(data), { + status, + headers: { + 'Content-Type': contentType + } + }); +} + +type Spec = { + response: Response, + assert?: (resource: URL, init?: RequestInit) => Promise +}; + +function Fetch(specs: Record) { + return async function (resource: URL, init?: RequestInit): Promise { + const spec = specs[resource.href]; + if (!spec) { + return NotFound(); + } + if (spec.assert) { + await spec.assert(resource, init); + } + return spec.response; + }; +} + +describe('ActivityPubAPI', function () { + describe('getInbox', function () { + test('It passes the token to the inbox endpoint', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/inbox/index': { + async assert(_resource, init) { + const headers = new Headers(init?.headers); + expect(headers.get('Authorization')).toContain('fake-token'); + }, + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + await api.getInbox(); + }); + + test('Returns an empty array when the inbox is empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/inbox/index': { + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getInbox(); + const expected: never[] = []; + + expect(actual).toEqual(expected); + }); + + test('Returns an the items array when the inbox is not empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/inbox/index': { + response: + JSONResponse({ + type: 'Collection', + items: [{ + type: 'Create', + object: { + type: 'Note' + } + }] + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getInbox(); + const expected: Activity[] = [ + { + type: 'Create', + object: { + type: 'Note' + } + } + ]; + + expect(actual).toEqual(expected); + }); + }); + + describe('getFollowing', function () { + test('It passes the token to the following endpoint', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/following/index': { + async assert(_resource, init) { + const headers = new Headers(init?.headers); + expect(headers.get('Authorization')).toContain('fake-token'); + }, + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + await api.getFollowing(); + }); + + test('Returns an empty array when the following is empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/following/index': { + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowing(); + const expected: never[] = []; + + expect(actual).toEqual(expected); + }); + + test('Returns an the items array when the following is not empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/following/index': { + response: + JSONResponse({ + type: 'Collection', + items: [{ + type: 'Person' + }] + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowing(); + const expected: Activity[] = [ + { + type: 'Person' + } + ]; + + expect(actual).toEqual(expected); + }); + }); + + describe('getFollowers', function () { + test('It passes the token to the followers endpoint', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/followers/index': { + async assert(_resource, init) { + const headers = new Headers(init?.headers); + expect(headers.get('Authorization')).toContain('fake-token'); + }, + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + await api.getFollowers(); + }); + + test('Returns an empty array when the followers is empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/followers/index': { + response: JSONResponse({ + type: 'Collection', + items: [] + }) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowers(); + const expected: never[] = []; + + expect(actual).toEqual(expected); + }); + + test('Returns an the items array when the followers is not empty', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/followers/index': { + response: + JSONResponse({ + type: 'Collection', + items: [{ + type: 'Person' + }] + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowers(); + const expected: Activity[] = [ + { + type: 'Person' + } + ]; + + expect(actual).toEqual(expected); + }); + }); + + describe('follow', function () { + test('It passes the token to the follow endpoint', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/actions/follow/@user@domain.com': { + async assert(_resource, init) { + const headers = new Headers(init?.headers); + expect(headers.get('Authorization')).toContain('fake-token'); + }, + response: JSONResponse({}) + } + }); + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + await api.follow('@user@domain.com'); + }); + }); +}); diff --git a/apps/admin-x-activitypub/src/api/activitypub.ts b/apps/admin-x-activitypub/src/api/activitypub.ts new file mode 100644 index 0000000000..fb7a185131 --- /dev/null +++ b/apps/admin-x-activitypub/src/api/activitypub.ts @@ -0,0 +1,109 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type Actor = any; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type Activity = any; + +export class ActivityPubAPI { + constructor( + private readonly apiUrl: URL, + private readonly authApiUrl: URL, + private readonly handle: string, + private readonly fetch: (resource: URL, init?: RequestInit) => Promise = window.fetch.bind(window) + ) {} + + private async getToken(): Promise { + try { + const response = await this.fetch(this.authApiUrl); + const json = await response.json(); + return json?.identities?.[0]?.token || null; + } catch (err) { + // TODO: Ping sentry? + return null; + } + } + + private async fetchJSON(url: URL, method: 'GET' | 'POST' = 'GET'): Promise { + const token = await this.getToken(); + const response = await this.fetch(url, { + method, + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/activity+json' + } + }); + const json = await response.json(); + return json; + } + + get inboxApiUrl() { + return new URL(`.ghost/activitypub/inbox/${this.handle}`, this.apiUrl); + } + + async getInbox(): Promise { + const json = await this.fetchJSON(this.inboxApiUrl); + if (json === null) { + return []; + } + if ('items' in json) { + return Array.isArray(json?.items) ? json.items : []; + } + return []; + } + + get followingApiUrl() { + return new URL(`.ghost/activitypub/following/${this.handle}`, this.apiUrl); + } + + async getFollowing(): Promise { + const json = await this.fetchJSON(this.followingApiUrl); + if (json === null) { + return []; + } + if ('items' in json) { + return Array.isArray(json?.items) ? json.items : []; + } + return []; + } + + async getFollowingCount(): Promise { + const json = await this.fetchJSON(this.followingApiUrl); + if (json === null) { + return 0; + } + if ('totalItems' in json && typeof json.totalItems === 'number') { + return json.totalItems; + } + return 0; + } + + get followersApiUrl() { + return new URL(`.ghost/activitypub/followers/${this.handle}`, this.apiUrl); + } + + async getFollowers(): Promise { + const json = await this.fetchJSON(this.followersApiUrl); + if (json === null) { + return []; + } + if ('items' in json) { + return Array.isArray(json?.items) ? json.items : []; + } + return []; + } + + async getFollowersCount(): Promise { + const json = await this.fetchJSON(this.followersApiUrl); + if (json === null) { + return 0; + } + if ('totalItems' in json && typeof json.totalItems === 'number') { + return json.totalItems; + } + return 0; + } + + async follow(username: string): Promise { + const url = new URL(`.ghost/activitypub/actions/follow/${username}`, this.apiUrl); + await this.fetchJSON(url, 'POST'); + } +} From bfd3ee1209c0c5b13957131428062ffc93aa02f2 Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Mon, 22 Jul 2024 18:18:45 +0700 Subject: [PATCH 055/164] Wired up ActivityPubAPI w/ ReactQuery This is a bit of a stopgap, we'll want to eventually pull these hooks out into a shared file, but for now this is fine. This almost decouples us from admin-x-framework, but we're still using it to get the site url as well as some types that we can pull out later. --- .../src/components/FollowSite.tsx | 81 ++++++++----------- .../src/components/ListIndex.tsx | 62 ++++++++++++-- .../src/components/ViewFollowers.tsx | 47 ++++++++--- .../src/components/ViewFollowing.tsx | 31 ++++--- 4 files changed, 148 insertions(+), 73 deletions(-) diff --git a/apps/admin-x-activitypub/src/components/FollowSite.tsx b/apps/admin-x-activitypub/src/components/FollowSite.tsx index c76fb6c64a..55f613d18a 100644 --- a/apps/admin-x-activitypub/src/components/FollowSite.tsx +++ b/apps/admin-x-activitypub/src/components/FollowSite.tsx @@ -1,65 +1,48 @@ import NiceModal from '@ebay/nice-modal-react'; +import {ActivityPubAPI} from '../api/activitypub'; import {Modal, TextField, showToast} from '@tryghost/admin-x-design-system'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; -import {useFollow} from '@tryghost/admin-x-framework/api/activitypub'; -import {useQueryClient} from '@tryghost/admin-x-framework'; +import {useMutation} from '@tanstack/react-query'; import {useRouting} from '@tryghost/admin-x-framework/routing'; import {useState} from 'react'; -// const sleep = (ms: number) => ( -// new Promise((resolve) => { -// setTimeout(resolve, ms); -// }) -// ); +function useFollow(handle: string, onSuccess: () => void, onError: () => void) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useMutation({ + async mutationFn(username: string) { + return api.follow(username); + }, + onSuccess, + onError + }); +} const FollowSite = NiceModal.create(() => { const {updateRoute} = useRouting(); const modal = NiceModal.useModal(); - const mutation = useFollow(); - const client = useQueryClient(); - const site = useBrowseSite(); - const siteData = site.data?.site; - const siteUrl = siteData?.url ?? window.location.origin; - - // mutation.isPending - // mutation.isError - // mutation.isSuccess - // mutation.mutate({username: '@index@site.com'}) - // mutation.reset(); - - // State to manage the text field value const [profileName, setProfileName] = useState(''); - // const [success, setSuccess] = useState(false); const [errorMessage, setError] = useState(null); - const handleFollow = async () => { - try { - const url = new URL(`.ghost/activitypub/actions/follow/${profileName}`, siteUrl); - await fetch(url, { - method: 'POST' - }); - // Perform the mutation - // If successful, set the success state to true - // setSuccess(true); - showToast({ - message: 'Site followed', - type: 'success' - }); + async function onSuccess() { + showToast({ + message: 'Site followed', + type: 'success' + }); - // // Because we don't return the new follower data from the API, we need to wait a bit to let it process and then update the query. - // // This is a dirty hack and should be replaced with a better solution. - // await sleep(2000); - - modal.remove(); - // Refetch the following data. - // At this point it might not be updated yet, but it will be eventually. - await client.refetchQueries({queryKey: ['FollowingResponseData'], type: 'active'}); - updateRoute(''); - } catch (error) { - // If there's an error, set the error state - setError(errorMessage); - } - }; + modal.remove(); + updateRoute(''); + } + async function onError() { + setError(errorMessage); + } + const mutation = useFollow('index', onSuccess, onError); return ( { okLabel='Follow' size='sm' title='Follow a Ghost site' - onOk={handleFollow} + onOk={() => mutation.mutate(profileName)} >
          void; } +function useBrowseInboxForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`inbox:${handle}`], + async queryFn() { + return api.getInbox(); + } + }); +} + +function useFollowersCountForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`followersCount:${handle}`], + async queryFn() { + return api.getFollowersCount(); + } + }); +} + +function useFollowingCountForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`followingCount:${handle}`], + async queryFn() { + return api.getFollowingCount(); + } + }); +} + const ActivityPubComponent: React.FC = () => { const {updateRoute} = useRouting(); // TODO: Replace with actual user ID - const {data: {items: activities = []} = {}} = useBrowseInboxForUser('index'); - const {data: {totalItems: followingCount = 0} = {}} = useBrowseFollowingForUser('index'); - const {data: {totalItems: followersCount = 0} = {}} = useBrowseFollowersForUser('index'); + const {data: activities = []} = useBrowseInboxForUser('index'); + const {data: followersCount = 0} = useFollowersCountForUser('index'); + const {data: followingCount = 0} = useFollowingCountForUser('index'); const [articleContent, setArticleContent] = useState(null); const [, setArticleActor] = useState(null); diff --git a/apps/admin-x-activitypub/src/components/ViewFollowers.tsx b/apps/admin-x-activitypub/src/components/ViewFollowers.tsx index bff12ef78c..99cc96fa17 100644 --- a/apps/admin-x-activitypub/src/components/ViewFollowers.tsx +++ b/apps/admin-x-activitypub/src/components/ViewFollowers.tsx @@ -1,21 +1,50 @@ -import {} from '@tryghost/admin-x-framework/api/activitypub'; import NiceModal from '@ebay/nice-modal-react'; import getUsername from '../utils/get-username'; +import {ActivityPubAPI} from '../api/activitypub'; import {Avatar, Button, List, ListItem, Modal} from '@tryghost/admin-x-design-system'; -import {FollowingResponseData, useBrowseFollowersForUser, useFollow} from '@tryghost/admin-x-framework/api/activitypub'; import {RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; +import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; +import {useMutation, useQuery} from '@tanstack/react-query'; -interface ViewFollowersModalProps { - following: FollowingResponseData[], - animate?: boolean +function useFollowersForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`followers:${handle}`], + async queryFn() { + return api.getFollowers(); + } + }); } -const ViewFollowersModal: React.FC = ({}) => { +function useFollow(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useMutation({ + async mutationFn(username: string) { + return api.follow(username); + } + }); +} + +const ViewFollowersModal: React.FC = ({}) => { const {updateRoute} = useRouting(); // const modal = NiceModal.useModal(); - const mutation = useFollow(); + const mutation = useFollow('index'); - const {data: {items = []} = {}} = useBrowseFollowersForUser('inbox'); + const {data: items = []} = useFollowersForUser('index'); const followers = Array.isArray(items) ? items : [items]; return ( @@ -34,7 +63,7 @@ const ViewFollowersModal: React.FC
          {followers.map(item => ( - mutation.mutate({username: getUsername(item)})} />} avatar={} detail={getUsername(item)} id='list-item' title={item.name}> + mutation.mutate(getUsername(item))} />} avatar={} detail={getUsername(item)} id='list-item' title={item.name}> ))}
          diff --git a/apps/admin-x-activitypub/src/components/ViewFollowing.tsx b/apps/admin-x-activitypub/src/components/ViewFollowing.tsx index 4a5bc82cd6..4fc3de36c4 100644 --- a/apps/admin-x-activitypub/src/components/ViewFollowing.tsx +++ b/apps/admin-x-activitypub/src/components/ViewFollowing.tsx @@ -1,26 +1,37 @@ -import {} from '@tryghost/admin-x-framework/api/activitypub'; import NiceModal from '@ebay/nice-modal-react'; import getUsername from '../utils/get-username'; +import {ActivityPubAPI} from '../api/activitypub'; import {Avatar, Button, List, ListItem, Modal} from '@tryghost/admin-x-design-system'; -import {FollowingResponseData, useBrowseFollowingForUser, useUnfollow} from '@tryghost/admin-x-framework/api/activitypub'; import {RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; +import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; +import {useQuery} from '@tanstack/react-query'; -interface ViewFollowingModalProps { - following: FollowingResponseData[], - animate?: boolean +function useFollowingForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`following:${handle}`], + async queryFn() { + return api.getFollowing(); + } + }); } -const ViewFollowingModal: React.FC = ({}) => { +const ViewFollowingModal: React.FC = ({}) => { const {updateRoute} = useRouting(); - const mutation = useUnfollow(); - const {data: {items = []} = {}} = useBrowseFollowingForUser('inbox'); + const {data: items = []} = useFollowingForUser('index'); const following = Array.isArray(items) ? items : [items]; return ( { - mutation.reset(); updateRoute(''); }} cancelLabel='' @@ -33,7 +44,7 @@ const ViewFollowingModal: React.FC
          {following.map(item => ( - mutation.mutate({username: getUsername(item)})} />} avatar={} detail={getUsername(item)} id='list-item' title={item.name}> + } avatar={} detail={getUsername(item)} id='list-item' title={item.name}> ))} {/*
    From b3b9c895442cd48309ed8f4522fc0012c76d147d Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Wed, 10 Jul 2024 11:57:09 +0700 Subject: [PATCH 056/164] Added role to identity token Right now identity tokens can only be fetched by the Owner, which means they implicitly have the Owner role, but we want to expand that. The first step is adding the role to the token, and then we need to update each place which uses the token and add an assertion that the role is correct. --- .../core/server/api/endpoints/identities.js | 18 ++++++++++++++++-- .../regression/api/admin/identities.test.js | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ghost/core/core/server/api/endpoints/identities.js b/ghost/core/core/server/api/endpoints/identities.js index ad0c3a15ca..165674f491 100644 --- a/ghost/core/core/server/api/endpoints/identities.js +++ b/ghost/core/core/server/api/endpoints/identities.js @@ -1,3 +1,4 @@ +const logging = require('@tryghost/logging'); const settings = require('../../../shared/settings-cache'); const urlUtils = require('../../../shared/url-utils'); const jwt = require('jsonwebtoken'); @@ -13,7 +14,7 @@ const getKeyID = async () => { return key.kid; }; -const sign = async (claims, options) => { +const sign = async (claims, options = {}) => { const kid = await getKeyID(); return jwt.sign(claims, dangerousPrivateKey, Object.assign({ issuer, @@ -32,7 +33,20 @@ const controller = { }, permissions: true, async query(frame) { - const token = await sign({sub: frame.user.get('email')}); + let role = null; + try { + await frame.user.load(['roles']); + role = frame.user.relations.roles.toJSON()[0].name; + } catch (err) { + logging.warn('Could not load role for identity'); + } + const claims = { + sub: frame.user.get('email') + }; + if (typeof role === 'string') { + claims.role = role; + } + const token = await sign(claims); return {token}; } } diff --git a/ghost/core/test/regression/api/admin/identities.test.js b/ghost/core/test/regression/api/admin/identities.test.js index c338fb1ea6..6fc5429203 100644 --- a/ghost/core/test/regression/api/admin/identities.test.js +++ b/ghost/core/test/regression/api/admin/identities.test.js @@ -60,6 +60,7 @@ describe('Identities API', function () { }) .then((decoded) => { decoded.sub.should.equal('jbloggs@example.com'); + decoded.role.should.equal('Owner'); }); }); }); From 806fce191d1de67ebf8cffa91f083408c1c65394 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:26:29 +0200 Subject: [PATCH 057/164] Updated Tips & Donations settings design (#20649) REF MOM-315 - Changed to column layout - Fixed broken currency dropdown - Included a link to Stripe terms & conditions - Renamed from "Tips or donations" to "Tips & donations" --- .../src/components/Sidebar.tsx | 2 +- .../settings/growth/GrowthSettings.tsx | 6 +- .../settings/growth/TipsAndDonations.tsx | 154 ++++++++++++++++++ .../settings/growth/TipsOrDonations.tsx | 136 ---------------- .../test/e2e-browser/portal/donations.spec.js | 2 +- 5 files changed, 159 insertions(+), 141 deletions(-) create mode 100644 apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx delete mode 100644 apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx diff --git a/apps/admin-x-settings/src/components/Sidebar.tsx b/apps/admin-x-settings/src/components/Sidebar.tsx index 08f14de477..46d18a238a 100644 --- a/apps/admin-x-settings/src/components/Sidebar.tsx +++ b/apps/admin-x-settings/src/components/Sidebar.tsx @@ -186,7 +186,7 @@ const Sidebar: React.FC = () => { {hasStripeEnabled && } - {hasTipsAndDonations && } + {hasTipsAndDonations && } diff --git a/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx b/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx index 5d5f3d625f..4d74d27237 100644 --- a/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/GrowthSettings.tsx @@ -3,13 +3,13 @@ import Offers from './Offers'; import React from 'react'; import Recommendations from './Recommendations'; import SearchableSection from '../../SearchableSection'; -import TipsOrDonations from './TipsOrDonations'; +import TipsAndDonations from './TipsAndDonations'; import useFeatureFlag from '../../../hooks/useFeatureFlag'; import {checkStripeEnabled} from '@tryghost/admin-x-framework/api/settings'; import {useGlobalData} from '../../providers/GlobalDataProvider'; export const searchKeywords = { - tips: ['growth', 'tip', 'donation', 'one time', 'payment'], + tips: ['growth', 'tips', 'donations', 'one time', 'payment'], embedSignupForm: ['growth', 'embeddable signup form', 'embeddable form', 'embeddable sign up form', 'embeddable sign up'], recommendations: ['growth', 'recommendations', 'recommend', 'blogroll'], offers: ['growth', 'offers', 'discounts', 'coupons', 'promotions'] @@ -25,7 +25,7 @@ const GrowthSettings: React.FC = () => { {hasStripeEnabled && } - {hasTipsAndDonations && } + {hasTipsAndDonations && } ); }; diff --git a/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx new file mode 100644 index 0000000000..10cbf0ed67 --- /dev/null +++ b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx @@ -0,0 +1,154 @@ +import React, {useEffect, useState} from 'react'; +import TopLevelGroup from '../../TopLevelGroup'; +import useSettingGroup from '../../../hooks/useSettingGroup'; +import {Button, CurrencyField, Heading, Select, SettingGroupContent, confirmIfDirty, withErrorBoundary} from '@tryghost/admin-x-design-system'; +import {currencySelectGroups, getSymbol, validateCurrencyAmount} from '../../../utils/currency'; +import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; + +// Stripe doesn't allow amounts over 10,000 as a preset amount +const MAX_AMOUNT = 10_000; + +const TipsAndDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { + const { + localSettings, + siteData, + updateSetting, + isEditing, + saveState, + handleSave, + handleCancel, + focusRef, + handleEditingChange, + errors, + validate, + clearError + } = useSettingGroup({ + onValidate: () => { + return { + donationsSuggestedAmount: validateCurrencyAmount(suggestedAmountInCents, donationsCurrency, {maxAmount: MAX_AMOUNT}) + }; + } + }); + + const [donationsCurrency = 'USD', donationsSuggestedAmount = '0'] = getSettingValues( + localSettings, + ['donations_currency', 'donations_suggested_amount'] + ); + + const suggestedAmountInCents = parseInt(donationsSuggestedAmount); + const suggestedAmountInDollars = suggestedAmountInCents / 100; + const donateUrl = `${siteData?.url.replace(/\/$/, '')}/#/portal/support`; + + useEffect(() => { + validate(); + }, [donationsCurrency]); // eslint-disable-line react-hooks/exhaustive-deps + + const [copied, setCopied] = useState(false); + + const copyDonateUrl = () => { + navigator.clipboard.writeText(donateUrl); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + const openPreview = () => { + confirmIfDirty(saveState === 'unsaved', () => window.open(donateUrl, '_blank')); + }; + + const values = ( + +
    + Shareable link +
    +
    + {donateUrl} +
    +
    +
    + + ) + } + ]} + /> + ); + + const inputFields = ( + +
    + group.options).find(option => option.value === donationsCurrency)} + title='Currency' + hideTitle + isSearchable + onSelect={option => updateSetting('donations_currency', option?.value || 'USD')} + /> + )} + title='Suggested amount' + valueInCents={parseInt(donationsSuggestedAmount)} + onBlur={validate} + onChange={cents => updateSetting('donations_suggested_amount', cents.toString())} + onKeyDown={() => clearError('donationsSuggestedAmount')} + /> +
    +
    +
    + Shareable link +
    +
    + {donateUrl} +
    +
    +
    +
    +
    + ); + + return ( + + {isEditing ? inputFields : values} +
    + All tips and donations are subject to Stripe's tipping policy. +
    +
    + ); +}; + +export default withErrorBoundary(TipsAndDonations, 'Tips & donations'); diff --git a/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx b/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx deleted file mode 100644 index 64726daddb..0000000000 --- a/apps/admin-x-settings/src/components/settings/growth/TipsOrDonations.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, {useEffect, useState} from 'react'; -import TopLevelGroup from '../../TopLevelGroup'; -import useSettingGroup from '../../../hooks/useSettingGroup'; -import {Button, CurrencyField, Heading, Select, SettingGroupContent, confirmIfDirty, withErrorBoundary} from '@tryghost/admin-x-design-system'; -import {currencySelectGroups, getSymbol, validateCurrencyAmount} from '../../../utils/currency'; -import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; - -// Stripe doesn't allow amounts over 10,000 as a preset amount -const MAX_AMOUNT = 10_000; - -const TipsOrDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { - const { - localSettings, - siteData, - updateSetting, - isEditing, - saveState, - handleSave, - handleCancel, - focusRef, - handleEditingChange, - errors, - validate, - clearError - } = useSettingGroup({ - onValidate: () => { - return { - donationsSuggestedAmount: validateCurrencyAmount(suggestedAmountInCents, donationsCurrency, {maxAmount: MAX_AMOUNT}) - }; - } - }); - - const [donationsCurrency = 'USD', donationsSuggestedAmount = '0'] = getSettingValues( - localSettings, - ['donations_currency', 'donations_suggested_amount'] - ); - - const suggestedAmountInCents = parseInt(donationsSuggestedAmount); - const suggestedAmountInDollars = suggestedAmountInCents / 100; - const donateUrl = `${siteData?.url.replace(/\/$/, '')}/#/portal/support`; - - useEffect(() => { - validate(); - }, [donationsCurrency]); // eslint-disable-line react-hooks/exhaustive-deps - - const [copied, setCopied] = useState(false); - - const copyDonateUrl = () => { - navigator.clipboard.writeText(donateUrl); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - const openPreview = () => { - confirmIfDirty(saveState === 'unsaved', () => window.open(donateUrl, '_blank')); - }; - - const values = ( - -
    - Shareable link — - -
    -
    - {donateUrl} -
    -
    -
    - - ) - } - ]} - /> - ); - - const inputFields = ( - - group.options).find(option => option.value === donationsCurrency)} - title='Currency' - hideTitle - isSearchable - onSelect={option => updateSetting('donations_currency', option?.value || 'USD')} - /> - )} - title='Suggested amount' - valueInCents={parseInt(donationsSuggestedAmount)} - onBlur={validate} - onChange={cents => updateSetting('donations_suggested_amount', cents.toString())} - onKeyDown={() => clearError('donationsSuggestedAmount')} - /> - - ); - - return ( - - {isEditing ? inputFields : values} - - ); -}; - -export default withErrorBoundary(TipsOrDonations, 'Tips or donations'); diff --git a/ghost/core/test/e2e-browser/portal/donations.spec.js b/ghost/core/test/e2e-browser/portal/donations.spec.js index e772e65367..93f170ea02 100644 --- a/ghost/core/test/e2e-browser/portal/donations.spec.js +++ b/ghost/core/test/e2e-browser/portal/donations.spec.js @@ -46,7 +46,7 @@ test.describe('Portal', () => { test('Can donate with a fixed amount set and different currency', async ({sharedPage}) => { await sharedPage.goto('/ghost/#/settings'); - const section = sharedPage.getByTestId('tips-or-donations'); + const section = sharedPage.getByTestId('tips-and-donations'); await section.getByRole('button', {name: 'Edit'}).click(); await section.getByLabel('Suggested amount').fill('98'); From b7fd885a08e6c154340d0c1a1b641bc6252e5359 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 07:16:32 +0000 Subject: [PATCH 058/164] Update dependency semver to v7.6.3 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- ghost/mw-error-handler/package.json | 2 +- ghost/mw-version-match/package.json | 2 +- yarn.lock | 13 ++++--------- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 183c7d6858..cba2008d27 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -143,7 +143,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "reframe.js": "4.0.2", - "semver": "7.6.2", + "semver": "7.6.3", "testem": "3.15.0", "tracked-built-ins": "3.3.0", "util": "0.12.5", diff --git a/ghost/core/package.json b/ghost/core/package.json index adbe112d23..d2c914552a 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -221,7 +221,7 @@ "probe-image-size": "7.2.3", "rss": "1.2.2", "sanitize-html": "2.13.0", - "semver": "7.6.2", + "semver": "7.6.3", "stoppable": "1.1.0", "uuid": "9.0.1", "ws": "8.18.0", diff --git a/ghost/mw-error-handler/package.json b/ghost/mw-error-handler/package.json index 20add15dd8..208718369f 100644 --- a/ghost/mw-error-handler/package.json +++ b/ghost/mw-error-handler/package.json @@ -28,6 +28,6 @@ "@tryghost/http-cache-utils": "0.1.15", "@tryghost/tpl": "0.1.30", "lodash": "4.17.21", - "semver": "7.6.2" + "semver": "7.6.3" } } diff --git a/ghost/mw-version-match/package.json b/ghost/mw-version-match/package.json index eae367058d..890676ee29 100644 --- a/ghost/mw-version-match/package.json +++ b/ghost/mw-version-match/package.json @@ -25,6 +25,6 @@ "dependencies": { "@tryghost/errors": "1.3.2", "@tryghost/tpl": "0.1.30", - "semver": "7.6.2" + "semver": "7.6.3" } } diff --git a/yarn.lock b/yarn.lock index 0884cf7c79..bfc5d0680c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28572,21 +28572,16 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.6.2: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +semver@7.6.3, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - semver@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" From 59d4ad91d278415ba673c3629174ff44ed249d91 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:42:57 +0000 Subject: [PATCH 059/164] Update dependency tailwindcss to v3.4.5 --- apps/admin-x-design-system/package.json | 2 +- apps/comments-ui/package.json | 2 +- apps/signup-form/package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index bd6b079415..4d1841c4fc 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -71,7 +71,7 @@ "react-colorful": "5.6.1", "react-hot-toast": "2.4.1", "react-select": "5.8.0", - "tailwindcss": "3.4.4" + "tailwindcss": "3.4.5" }, "peerDependencies": { "react": "^18.2.0", diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 4cf4624c63..77902bed6c 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -75,7 +75,7 @@ "eslint-plugin-tailwindcss": "3.13.0", "jsdom": "24.1.1", "postcss": "8.4.39", - "tailwindcss": "3.4.4", + "tailwindcss": "3.4.5", "vite": "4.5.3", "vite-plugin-css-injected-by-js": "3.3.0", "vite-plugin-svgr": "3.3.0", diff --git a/apps/signup-form/package.json b/apps/signup-form/package.json index c4b8e0a039..e1e608d751 100644 --- a/apps/signup-form/package.json +++ b/apps/signup-form/package.json @@ -65,7 +65,7 @@ "rollup-plugin-node-builtins": "2.1.2", "storybook": "7.6.20", "stylelint": "15.10.3", - "tailwindcss": "3.4.4", + "tailwindcss": "3.4.5", "vite": "4.5.3", "vite-plugin-commonjs": "0.10.1", "vite-plugin-svgr": "3.3.0", diff --git a/yarn.lock b/yarn.lock index bfc5d0680c..43ed7125d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30064,10 +30064,10 @@ table@^6.0.9, table@^6.8.1: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@3.4.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.4.tgz#351d932273e6abfa75ce7d226b5bf3a6cb257c05" - integrity sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A== +tailwindcss@3.4.5: + version "3.4.5" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.5.tgz#0de2e92ed4d00fb015feb962fa0781605761724d" + integrity sha512-DlTxttYcogpDfx3tf/8jfnma1nfAYi2cBUYV2YNoPPecwmO3YGiFlOX9D8tGAu+EDF38ryBzvrDKU/BLMsUwbw== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" From 4dc503e9e5184446354469fe141dc39be51bc902 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Wed, 24 Jul 2024 17:28:32 +0800 Subject: [PATCH 060/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20broken=20link=20?= =?UTF-8?q?to=20subscription=20settings=20(#20651)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no issue - Fixed a broken link when navigating from Members list to subscription access settings. --- ghost/admin/app/components/gh-members-no-members.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/admin/app/components/gh-members-no-members.hbs b/ghost/admin/app/components/gh-members-no-members.hbs index 32926e99f8..e7563464ca 100644 --- a/ghost/admin/app/components/gh-members-no-members.hbs +++ b/ghost/admin/app/components/gh-members-no-members.hbs @@ -9,7 +9,7 @@

    Have members already? Add them manually or import from CSV

    {{else}}

    Memberships have been disabled. Adjust your Subscription Access settings to start adding members.

    - + Membership settings {{/if}} From 18c5c3bba8e4257037a0227526cde0eff029157e Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:57:34 +0200 Subject: [PATCH 061/164] Improved one-time payment notification email design (#20653) REF MOM-317 --- .../lib/email-templates/donation.hbs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ghost/staff-service/lib/email-templates/donation.hbs b/ghost/staff-service/lib/email-templates/donation.hbs index 71eadb7bab..88b1e07a8f 100644 --- a/ghost/staff-service/lib/email-templates/donation.hbs +++ b/ghost/staff-service/lib/email-templates/donation.hbs @@ -23,19 +23,18 @@
    From 54449ac98e2312d605914ef3d6e66d3bed3c04d6 Mon Sep 17 00:00:00 2001 From: Sag Date: Wed, 24 Jul 2024 15:09:53 +0200 Subject: [PATCH 062/164] Fixed members list when scrolled down too quickly (#20654) ref https://linear.app/tryghost/issue/SLO-160 - in the Members list, we were using VerticalCollection with `@bufferSize` set to 20, which means that 20 additional items before and after the visible items in the viewport were pre-loaded - however, scrolling down too quickly (e.g. dragging the scrollbar thumb to the bottom) breaks the list - with this fix, we adjust `@estimateHeight` parameter to the correct item size, and reduce the `@bufferSize` --- ghost/admin/app/templates/members.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/admin/app/templates/members.hbs b/ghost/admin/app/templates/members.hbs index 9bd9c98cdc..c2671358d6 100644 --- a/ghost/admin/app/templates/members.hbs +++ b/ghost/admin/app/templates/members.hbs @@ -143,7 +143,7 @@ {{/each}} - + {{#if member.is_loading}} Date: Wed, 24 Jul 2024 16:28:59 +0000 Subject: [PATCH 063/164] Update tiptap monorepo to v2.5.6 --- apps/comments-ui/package.json | 20 ++--- yarn.lock | 140 +++++++++++++++++----------------- 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 77902bed6c..0284f2536b 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.5", - "@tiptap/extension-blockquote": "2.5.5", - "@tiptap/extension-document": "2.5.5", - "@tiptap/extension-hard-break": "2.5.5", - "@tiptap/extension-link": "2.5.5", - "@tiptap/extension-paragraph": "2.5.5", - "@tiptap/extension-placeholder": "2.5.5", - "@tiptap/extension-text": "2.5.5", - "@tiptap/pm": "2.5.5", - "@tiptap/react": "2.5.5", + "@tiptap/core": "2.5.6", + "@tiptap/extension-blockquote": "2.5.6", + "@tiptap/extension-document": "2.5.6", + "@tiptap/extension-hard-break": "2.5.6", + "@tiptap/extension-link": "2.5.6", + "@tiptap/extension-paragraph": "2.5.6", + "@tiptap/extension-placeholder": "2.5.6", + "@tiptap/extension-text": "2.5.6", + "@tiptap/pm": "2.5.6", + "@tiptap/react": "2.5.6", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index 43ed7125d0..f354ca5e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7563,66 +7563,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== -"@tiptap/core@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.5.tgz#67301af399644a24a9f62b4309a9875794740a66" - integrity sha512-VnAnyWnsqN65QijtUFHbe7EPSJCkhNEAwlatsG/HvrZvUv9KmoWWbMsHAU73wozKzPXR3nHRbCxN+LuxP5bADg== +"@tiptap/core@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.6.tgz#b8ed9d7398db11ef4d42c3d41cafc0680718273c" + integrity sha512-sE6UgY4TChKbtKATZuEhfFdvDHxsBZ2u7+0W4IYvBP/ldlE+2Pq2tUjgekUZElhnr2Qmy7TlSNjbHkUeOTJjew== -"@tiptap/extension-blockquote@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.5.tgz#71102d76eb859991d25c58ce54ab06001bc578ad" - integrity sha512-K+fc++ASlgDRHN6i3j3JBGzWiDhhoZv0jCUB/l7Jzut4UfjIoWqKhmJajnp95Qu9tmwQUy9LMzHqG4G5wUsIsQ== +"@tiptap/extension-blockquote@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.6.tgz#cb502222c14c795f0b840546422fb7c908682bd7" + integrity sha512-nrGWCo/EMDFhViZB7LKW2oS+CmFezHGr3+CFbsl5tkK/Ro682kT0TQCfy9S1RGyGhUhZC6VkVaAmDpeppanDGA== -"@tiptap/extension-bubble-menu@^2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.5.tgz#b6e72bba8e216ac48e0741a2af16733348567ad8" - integrity sha512-7k0HqrnhQGVZk86MEc5vt8stNRxIY65AMjZfszY/mQw0Dza7EQig/9b/AEmi9n+TNW5/8Qu+OMJD9ln92d/Eog== +"@tiptap/extension-bubble-menu@^2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.6.tgz#e9451229d98187d9271f80eb9f9376acb4038884" + integrity sha512-jFfMyaVkA7gSqS8CZyzdAUhMw5lMhBCoNOZE9oOIttWoE2wssSsGnprmIzTOR+g65kF8yxxzAdNOYjfv/xQ1pw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.5.tgz#e69929f5d28e8ca659ab98075c5dd742ff805e5d" - integrity sha512-MIjYO63JepcJW37PQuKVmYuZFqkQOZ/12tV0YLU4o6gmGVdqJS0+3md9CdnyUFUDIo7x6TBh8r5i5L2xQpm3Sg== +"@tiptap/extension-document@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.6.tgz#19163a1d25773a105a674d9815416808b41c2bc7" + integrity sha512-0XNAFpCKHtTd0WCEhkg4zFCII2HV5/L7lQniHlVW/EaDsibkDtIZklSGKIRn+/xaS1bVrzcIq4Z/2hE52a9H0A== -"@tiptap/extension-floating-menu@^2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.5.tgz#3685b3dad59408177720ae78fc069b6f138e67ec" - integrity sha512-1mgpxZGfy1ziNSvWz6m1nGb9ZF9fVVz4X4XwrIqwGw1Vqt9oXflm6puglnzwVLDeaMDT014VUfczJ4My3wDZzA== +"@tiptap/extension-floating-menu@^2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.6.tgz#71bd52246e13345d11a84adc8abd9f35e829dbb3" + integrity sha512-UORk6CDtECqLoaOVx0oTwidrjdHEWaPaY/pYGnCC4Kptv6zEqIsZXidFcYBM1wTt44AESgUXDTnZF8U7M/YtEA== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.5.tgz#1d85247c26bc8c90ba75516b710a98695707b408" - integrity sha512-VtrwKU0LYS/0rfH5rGz8ztKwA0bsHRyBF53G7aP2FS4BiN8aOEu8t7VkvBZAewXDITDah9K6rqfXk+MNwoul2Q== +"@tiptap/extension-hard-break@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.6.tgz#44b8e58cadcc429466c12586cb58eb50c59f4d6a" + integrity sha512-pBKRIziToH/0OB0Q6jOyCZcBLa1OdY8UrwRPWXOWIXSQOSgxHpzYhpwuN+s341c8U8l13mqgo0Ths5IS7/vp3g== -"@tiptap/extension-link@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.5.tgz#a1de5308146a9be658b24c78be3dbce824917299" - integrity sha512-zVpNvMD8R9uW1SX1PJoj3fLyOHwuFWqiqEHN2KWfLbEnbL/KXNnpIyKdpHnI9lqFrsMf2dmyZCS3R6xIrynviQ== +"@tiptap/extension-link@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.6.tgz#5899815619b6e703bd39bee2c783f35071c1b220" + integrity sha512-sLcrVKB6qfnRQ66YQ48/hX8/69fvHgcPjq+k3dpqvi8CohmcYn8CuTbIh4+x/fgmIb6esqUkamDsGdU+70bTwg== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.5.tgz#13e73750ff78b7206e0eb31d7d5b31f8f0e76dd5" - integrity sha512-XZO1rqsU1vlt9qeG2pVVAt2gXjD0twl2D+uxy4Nw6gxqbhSgfbNq3RP72mmtcS4KyFJi7ETANpcRpb8ZNvXfmg== +"@tiptap/extension-paragraph@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.6.tgz#92c3073f230da7d86dad04d7262013c5dd4a68c9" + integrity sha512-ZPoerYEZLHlOCsfEfagX38ahXwegZaAB/ZUlcff9ehp5v8uO4L64mWyQRoTDF74A9cGnKxE3enokz8tOA47fkA== -"@tiptap/extension-placeholder@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.5.tgz#1883994be2e4dfb5b7a271dd39bbdd3e680b6521" - integrity sha512-SwWLYdyrMeoVUQdivkIJ4kkAcb38pykxSetlrXitfUmnkwv0/fi+p76Rickf+roudWPsfzqvgvJ4gT6OAOJrGA== +"@tiptap/extension-placeholder@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.6.tgz#c1bfdf3a9fbe2cee510666d36657a31544f18fa4" + integrity sha512-ULmR146/l1zGTkzyda/9Y7yDSz3Pr6hE+ccBDiJ0OMgf7ukL+iwAuZJz3R1EbDz6x2FfrspVnt2/ieCYDKe7Hw== -"@tiptap/extension-text@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.5.tgz#3675a1ace24869c3664137054a3b2bc2d25ac2c7" - integrity sha512-8c/hxcw7t/S3iKGSFwGNxC2I6AkKpRiySQJ95ML2miwSOAxWhnltoYYV7gobWCRgm25lnvzX/Z6BdpFzXBrBKA== +"@tiptap/extension-text@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.6.tgz#f1b7b41ef36fd4507bd62a75dc21fde5392ae491" + integrity sha512-dJFARnhuxaLrUP/3PhSE7Wfq/OBRKKW73ZMVrIFhGbVxOdmTZxA12d95U0e+5q6mbPSQFNx8ZYhqnStztRAjBw== -"@tiptap/pm@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.5.tgz#3c67224f9aa7460f05c4979aa0faab60b97dd503" - integrity sha512-ppePiLaeG6IKkm8Yq+mRENT4LIAS4qQyLT8EnKadznaTL6SNj/72mm0MjD44URkM38ySzIyvt/vqHDapNK0Hww== +"@tiptap/pm@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.6.tgz#320e24950cdcd81d18339ec3528094e4d60dd795" + integrity sha512-0ysx5eXyF5XXfBmo/krMWKvZh6iBnqJ0YVPO/NsGzZcdjBix9lYYDh6nswLvv2mjhPbMEmcMW1ctj3myOwoUbw== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7634,22 +7634,22 @@ prosemirror-keymap "^1.2.2" prosemirror-markdown "^1.13.0" prosemirror-menu "^1.2.4" - prosemirror-model "^1.22.1" + prosemirror-model "^1.22.2" prosemirror-schema-basic "^1.2.3" prosemirror-schema-list "^1.4.1" prosemirror-state "^1.4.3" - prosemirror-tables "^1.3.7" - prosemirror-trailing-node "^2.0.8" + prosemirror-tables "^1.4.0" + prosemirror-trailing-node "^2.0.9" prosemirror-transform "^1.9.0" - prosemirror-view "^1.33.8" + prosemirror-view "^1.33.9" -"@tiptap/react@2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.5.tgz#6a20dfac2e80bcd314dd628621f73760826d25c6" - integrity sha512-V3cfrDWMtOViheMkIQ3qRnKLs0CZaouoB6LBtw96vo8G7IFKAvtgvmau0fjdJKRtHJp5xYiecP6RHt3lHulu+g== +"@tiptap/react@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.6.tgz#b1ee9843eb8c2cc00d85f229c539b815e238c88f" + integrity sha512-n6R5wVug1cGzE0DYWKSkRNBwDJ5ZPwOb21w9PjlEaD2A7yw6JuUBkWaCyfwl3bCzGj8ctZQ5hd9hhnWyJ1UPOg== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.5" - "@tiptap/extension-floating-menu" "^2.5.5" + "@tiptap/extension-bubble-menu" "^2.5.6" + "@tiptap/extension-floating-menu" "^2.5.6" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" @@ -26902,10 +26902,10 @@ prosemirror-menu@^1.2.4: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.19.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0, prosemirror-model@^1.22.1, prosemirror-model@^1.8.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.22.1.tgz#2ed7d7840e710172c559d5a9950e92b870d1e764" - integrity sha512-gMrxal+F3higDFxCkBK5iQXckRVYvIu/3dopERJ6b20xfwZ9cbYvQvuldqaN+v/XytNPGyURYUpUU23kBRxWCQ== +prosemirror-model@^1.0.0, prosemirror-model@^1.19.0, prosemirror-model@^1.20.0, prosemirror-model@^1.21.0, prosemirror-model@^1.22.2, prosemirror-model@^1.8.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.22.2.tgz#9bae923c4c4149eee59ff61a5b390c26011a26b4" + integrity sha512-I4lS7HHIW47D0Xv/gWmi4iUWcQIDYaJKd8Hk4+lcSps+553FlQrhmxtItpEvTr75iAruhzVShVp6WUwsT6Boww== dependencies: orderedmap "^2.0.0" @@ -26934,10 +26934,10 @@ prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, pr prosemirror-transform "^1.0.0" prosemirror-view "^1.27.0" -prosemirror-tables@^1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.3.7.tgz#9d296bd432d2bc7dca90f14e5c3b5c5f61277f7a" - integrity sha512-oEwX1wrziuxMtwFvdDWSFHVUWrFJWt929kVVfHvtTi8yvw+5ppxjXZkMG/fuTdFo+3DXyIPSKfid+Be1npKXDA== +prosemirror-tables@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.4.0.tgz#59c3dc241e03fc4ba8c093995b130d2980f0ffdc" + integrity sha512-fxryZZkQG12fSCNuZDrYx6Xvo2rLYZTbKLRd8rglOPgNJGMKIS8uvTt6gGC38m7UCu/ENnXIP9pEz5uDaPc+cA== dependencies: prosemirror-keymap "^1.1.2" prosemirror-model "^1.8.1" @@ -26945,10 +26945,10 @@ prosemirror-tables@^1.3.7: prosemirror-transform "^1.2.1" prosemirror-view "^1.13.3" -prosemirror-trailing-node@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.8.tgz#233ddcbda72de06f9b5d758d2a65a8cac482ea10" - integrity sha512-ujRYhSuhQb1Jsarh1IHqb2KoSnRiD7wAMDGucP35DN7j5af6X7B18PfdPIrbwsPTqIAj0fyOvxbuPsWhNvylmA== +prosemirror-trailing-node@^2.0.9: + version "2.0.9" + resolved "https://registry.yarnpkg.com/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.9.tgz#a087e6d1372e888cd3e57c977507b6b85dc658e4" + integrity sha512-YvyIn3/UaLFlFKrlJB6cObvUhmwFNZVhy1Q8OpW/avoTbD/Y7H5EcjK4AZFKhmuS6/N6WkGgt7gWtBWDnmFvHg== dependencies: "@remirror/core-constants" "^2.0.2" escape-string-regexp "^4.0.0" @@ -26960,10 +26960,10 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor dependencies: prosemirror-model "^1.21.0" -prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.33.8: - version "1.33.8" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.33.8.tgz#cfd76dff421730cbca0b6ea40ce36994daaeda41" - integrity sha512-4PhMr/ufz2cdvFgpUAnZfs+0xij3RsFysreeG9V/utpwX7AJtYCDVyuRxzWoMJIEf4C7wVihuBNMPpFLPCiLQw== +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.33.9: + version "1.33.9" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.33.9.tgz#0ed61ae42405cfc9799bde4db86badbb1ad99b08" + integrity sha512-xV1A0Vz9cIcEnwmMhKKFAOkfIp8XmJRnaZoPqNXrPS7EK5n11Ov8V76KhR0RsfQd/SIzmWY+bg+M44A2Lx/Nnw== dependencies: prosemirror-model "^1.20.0" prosemirror-state "^1.0.0" From 0947b047efd6b56cb03536e5fa3f988d903cecd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 08:39:08 +0000 Subject: [PATCH 064/164] Update dependency testem to v3.15.1 --- ghost/admin/package.json | 2 +- yarn.lock | 33 +++++++-------------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index cba2008d27..598963f7b7 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -144,7 +144,7 @@ "react-dom": "18.3.1", "reframe.js": "4.0.2", "semver": "7.6.3", - "testem": "3.15.0", + "testem": "3.15.1", "tracked-built-ins": "3.3.0", "util": "0.12.5", "validator": "7.2.0", diff --git a/yarn.lock b/yarn.lock index f354ca5e5a..e068cfd2cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22490,7 +22490,7 @@ lodash.assign@^3.2.0: lodash._createassigner "^3.0.0" lodash.keys "^3.0.0" -lodash.assignin@^4.0.9, lodash.assignin@^4.1.0: +lodash.assignin@^4.0.9: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" integrity sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg== @@ -22505,12 +22505,7 @@ lodash.camelcase@4.3.0, lodash.camelcase@^4.1.1, lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== -lodash.castarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" - integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== - -lodash.clonedeep@^4.4.1, lodash.clonedeep@^4.5.0: +lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== @@ -22547,11 +22542,6 @@ lodash.filter@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" integrity sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ== -lodash.find@^4.5.1: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" - integrity sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg== - lodash.flatten@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-3.0.2.tgz#de1cf57758f8f4479319d35c3e9cc60c4501938c" @@ -22722,11 +22712,6 @@ lodash.uniqby@4.5.0: lodash._baseiteratee "~4.7.0" lodash._baseuniq "~4.6.0" -lodash.uniqby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" - integrity sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww== - lodash.upperfirst@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" @@ -30271,10 +30256,10 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -testem@3.15.0, testem@^3.2.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/testem/-/testem-3.15.0.tgz#c1f66ff2f6d0e9606c77c4de6b62bca088632d1a" - integrity sha512-vI1oQsjJW4QdVaH6ZmfNErzH7nzs0KzHJluocnfvbz1XRYGJKkIMGKWfsbD8MGGJOg+uzXcEek0/2W7BmGR4ug== +testem@3.15.1, testem@^3.2.0: + version "3.15.1" + resolved "https://registry.yarnpkg.com/testem/-/testem-3.15.1.tgz#bb39b41b6928bf347fbb9ea1bfe1e085b15ecb7e" + integrity sha512-532OJy6VVsnDFEGZBdWVywvceNWdjdxJruC6qCDzLH586+eh1duWk5ju6QSRqQdP6gZB7zNiUJU9dSQdHPzFuA== dependencies: "@xmldom/xmldom" "^0.8.0" backbone "^1.1.2" @@ -30289,11 +30274,7 @@ testem@3.15.0, testem@^3.2.0: glob "^7.0.4" http-proxy "^1.13.1" js-yaml "^3.2.5" - lodash.assignin "^4.1.0" - lodash.castarray "^4.4.0" - lodash.clonedeep "^4.4.1" - lodash.find "^4.5.1" - lodash.uniqby "^4.7.0" + lodash "^4.17.21" mkdirp "^3.0.1" mustache "^4.2.0" node-notifier "^10.0.0" From b54e1ad6e7f522b7b28164bbc18d0fa1f596d268 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 11:18:17 +0000 Subject: [PATCH 065/164] Update Koenig packages (#20545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@tryghost/kg-default-nodes](https://togithub.com/TryGhost/Koenig/tree/main#readme) ([source](https://togithub.com/TryGhost/Koenig)) | [`1.1.8` -> `1.1.9`](https://renovatebot.com/diffs/npm/@tryghost%2fkg-default-nodes/1.1.8/1.1.9) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@tryghost%2fkg-default-nodes/1.1.9?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tryghost%2fkg-default-nodes/1.1.9?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tryghost%2fkg-default-nodes/1.1.8/1.1.9?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tryghost%2fkg-default-nodes/1.1.8/1.1.9?slim=true)](https://docs.renovatebot.com/merge-confidence/) | | [@tryghost/kg-html-to-lexical](https://togithub.com/TryGhost/Koenig/tree/main#readme) ([source](https://togithub.com/TryGhost/Koenig)) | [`1.1.9` -> `1.1.10`](https://renovatebot.com/diffs/npm/@tryghost%2fkg-html-to-lexical/1.1.9/1.1.10) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@tryghost%2fkg-html-to-lexical/1.1.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tryghost%2fkg-html-to-lexical/1.1.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tryghost%2fkg-html-to-lexical/1.1.9/1.1.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tryghost%2fkg-html-to-lexical/1.1.9/1.1.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | | [@tryghost/kg-lexical-html-renderer](https://togithub.com/TryGhost/Koenig/tree/main#readme) ([source](https://togithub.com/TryGhost/Koenig)) | [`1.1.11` -> `1.1.12`](https://renovatebot.com/diffs/npm/@tryghost%2fkg-lexical-html-renderer/1.1.11/1.1.12) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@tryghost%2fkg-lexical-html-renderer/1.1.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tryghost%2fkg-lexical-html-renderer/1.1.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tryghost%2fkg-lexical-html-renderer/1.1.11/1.1.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tryghost%2fkg-lexical-html-renderer/1.1.11/1.1.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | | [@tryghost/koenig-lexical](https://togithub.com/TryGhost/Koenig/tree/master#readme) ([source](https://togithub.com/TryGhost/Koenig)) | [`1.3.12` -> `1.3.13`](https://renovatebot.com/diffs/npm/@tryghost%2fkoenig-lexical/1.3.12/1.3.13) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@tryghost%2fkoenig-lexical/1.3.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tryghost%2fkoenig-lexical/1.3.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tryghost%2fkoenig-lexical/1.3.12/1.3.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tryghost%2fkoenig-lexical/1.3.12/1.3.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
    TryGhost/Koenig (@​tryghost/kg-default-nodes) ### [`v1.1.9`](https://togithub.com/TryGhost/Koenig/compare/@tryghost/kg-default-nodes@1.1.8...@tryghost/kg-default-nodes@1.1.9) [Compare Source](https://togithub.com/TryGhost/Koenig/compare/@tryghost/kg-default-nodes@1.1.8...@tryghost/kg-default-nodes@1.1.9)
    --- ### Configuration 📅 **Schedule**: Branch creation - "every weekday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View the [repository job log](https://developer.mend.io/github/TryGhost/Ghost). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ronald Langeveld --- ghost/admin/package.json | 2 +- ghost/core/package.json | 6 +-- .../utils/serializers/input/posts.test.js | 2 +- yarn.lock | 50 +++++++++---------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 598963f7b7..74ad283c0b 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.1", "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.12", + "@tryghost/koenig-lexical": "1.3.13", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/package.json b/ghost/core/package.json index d2c914552a..af71ee7059 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -111,9 +111,9 @@ "@tryghost/kg-converters": "1.0.5", "@tryghost/kg-default-atoms": "5.0.3", "@tryghost/kg-default-cards": "10.0.6", - "@tryghost/kg-default-nodes": "1.1.8", - "@tryghost/kg-html-to-lexical": "1.1.9", - "@tryghost/kg-lexical-html-renderer": "1.1.11", + "@tryghost/kg-default-nodes": "1.1.9", + "@tryghost/kg-html-to-lexical": "1.1.10", + "@tryghost/kg-lexical-html-renderer": "1.1.12", "@tryghost/kg-mobiledoc-html-renderer": "7.0.4", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js b/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js index 3ec4b6f5c1..1328116f8b 100644 --- a/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js +++ b/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js @@ -353,7 +353,7 @@ describe('Unit: endpoints/utils/serializers/input/posts', function () { serializers.input.posts.edit(apiConfig, frame); let postData = frame.data.posts[0]; - postData.lexical.should.equal('{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"this is great feature","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1},{"type":"html","version":1,"html":"
    My Custom HTML
    "},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"custom html preserved!","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'); + postData.lexical.should.equal('{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"this is great feature","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1},{"type":"html","version":1,"html":"
    My Custom HTML
    ","visibility":{"emailOnly":false,"segment":""}},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"custom html preserved!","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'); }); it('throws error when HTML conversion fails', function () { diff --git a/yarn.lock b/yarn.lock index e068cfd2cc..48005958e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7959,10 +7959,10 @@ lodash "^4.17.21" luxon "^3.0.0" -"@tryghost/kg-default-nodes@1.1.8": - version "1.1.8" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.8.tgz#4a528041dce9256484110162e805e799c77b37e9" - integrity sha512-fAttomZj55uXeZcRhz3clkyFkFtrW/ZvsjY9GRjCGtOsKPGOIInjizwJFPj2lOWpeimPrBNMxRs1NLEgWjpr1Q== +"@tryghost/kg-default-nodes@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.9.tgz#2f8e0851735ad9daf8ba103fe3d42d9119a366eb" + integrity sha512-xTIkOfusnTHub/pU/Pdw8S5rQ8GLf5ONfS1y8x5v2cqXln1e08lV8wa5gv44WSxuyDzKjVHKKceGkT3rbdVBXg== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" @@ -7976,21 +7976,21 @@ lodash "^4.17.21" luxon "^3.3.0" -"@tryghost/kg-default-transforms@1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.9.tgz#92a4e0386c73dfd704822e4cae61e1643f368bd4" - integrity sha512-Z3IHmDOEsBGbIQtbbVc0NSVuB5yLxkQK1hcl65DA2dg8Qqf4p54xazqVB+0JWj3wTFVnqfHyY6Rpag9VW7UOPQ== +"@tryghost/kg-default-transforms@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.10.tgz#7f4f648e2a8eec8d4af43028111e766feb3975ea" + integrity sha512-T9OZau2npHwtxKw77hXqRWNEErxdM/WKldWtmLKGJiKT7Czx8v9eEpsNmGgJYnXenkyzehJmU1okJlAUqMbcsA== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.8" + "@tryghost/kg-default-nodes" "1.1.9" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.9.tgz#10767d5ad90efaad4ab48f2aacabb4b752e98831" - integrity sha512-IbOicRX/Mf4qaS+rZ1SqT4KISYEsCGCbSvBWEqKOoEQhWrjl5cjEccijUkLmzKIQdEten1Ds9UgETRX4u1Odfg== +"@tryghost/kg-html-to-lexical@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.10.tgz#89dcd98e3933485bb0f33ab725dac4080f5a01fe" + integrity sha512-ja0DRLEzQhhOzK4n7HQqUttr0dbDsMaueyXb6+bxovHyog3HFo3A5NYz0DX9UU3qdUhAKBLwrgY5SwrtJ/ysVw== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7998,15 +7998,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.8" - "@tryghost/kg-default-transforms" "1.1.9" + "@tryghost/kg-default-nodes" "1.1.9" + "@tryghost/kg-default-transforms" "1.1.10" jsdom "^24.0.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.11.tgz#5fe1d7ea5f55b665285bad5ac41a71e025152221" - integrity sha512-q5oWbxp2Uun7xt+S+qI7zCaDlHvL6qkD5+Tzk4/UaZEojxbcWnVVeQe574cQwHAjvZN4H65aVRyRE4poo1ZoAw== +"@tryghost/kg-lexical-html-renderer@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.12.tgz#3234331f18b0dfe65e52e8b5821ef2bbc4a7909a" + integrity sha512-AEV+A1ZxSSVjTse7YonMz8AF9pqppEpfAHnwKH6BarSTJAVL8Gbvv/zcISO03UVrUqfqCZnnmmXS2h8iOBrbSA== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -8014,8 +8014,8 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.8" - "@tryghost/kg-default-transforms" "1.1.9" + "@tryghost/kg-default-nodes" "1.1.9" + "@tryghost/kg-default-transforms" "1.1.10" jsdom "^24.0.0" lexical "0.13.1" @@ -8062,10 +8062,10 @@ dependencies: semver "^7.3.5" -"@tryghost/koenig-lexical@1.3.12": - version "1.3.12" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.12.tgz#2a398f66b48e03cab5aaf58edd03c6d3294940cb" - integrity sha512-taAz1XOQTrQKvviiBLfO/bIyDM/B+3ghH+EUITWOY+5JRDhbk2TnVrFD8VABsKnRWgdzSFXE/ToiXlIW8np4pg== +"@tryghost/koenig-lexical@1.3.13": + version "1.3.13" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.13.tgz#7ffa158d1f28f4f75d0fce763c0a884f98a23f13" + integrity sha512-tjLouQMCPPAXdvBYWVtHp4SeDylT3tF5nh0cy3JPOmG0eJRYGdGwxiEabk6jmzOEjjhkD0kwT+6m3G+vv70dcw== "@tryghost/limit-service@1.2.14": version "1.2.14" From 42398ce525fb9393282b31b0a91b4e2d0b0df062 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 25 Jul 2024 17:08:51 +0530 Subject: [PATCH 066/164] Added 90 days filter to Mrr Admin Dashboard query (#20661) Ref https://linear.app/tryghost/issue/SLO-186/add-90-days-filter-to-mrr-admin-dashboard-query --- .../services/stats/mrr-stats-service.test.js | 48 +++++++++++++++---- ghost/stats-service/lib/MrrStatsService.js | 12 ++++- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js b/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js index ae79d127d8..b387a0d462 100644 --- a/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js +++ b/ghost/core/test/e2e-server/services/stats/mrr-stats-service.test.js @@ -162,31 +162,59 @@ describe('MRR Stats Service', function () { }); describe('fetchAllDeltas', function () { - it('Returns deltas in ascending order', async function () { + it('Returns deltas for mrr', async function () { + const ninetyDaysAgo = moment().subtract(90, 'days').startOf('day').format('YYYY-MM-DD'); + const ninetyFiveDaysAgo = moment().subtract(95, 'days').startOf('day').format('YYYY-MM-DD'); + const eightyNineDaysAgo = moment().subtract(89, 'days').startOf('day').format('YYYY-MM-DD'); + const today = moment().startOf('day').format('YYYY-MM-DD'); + + await createMemberWithSubscription('month', 500, 'eur', moment(ninetyDaysAgo).toISOString()); + await createMemberWithSubscription('month', 500, 'eur', moment(ninetyFiveDaysAgo).toISOString()); + await createMemberWithSubscription('month', 1, 'usd', moment(eightyNineDaysAgo).toISOString()); + await createMemberWithSubscription('month', 2, 'usd', moment(today).toISOString()); + const results = await statsService.api.mrr.fetchAllDeltas(); - results.length.should.equal(4); + results.length.should.equal(3); results.should.match([ { - date: '2000-01-10', + date: ninetyDaysAgo, delta: 500, currency: 'EUR' }, { - date: '2000-01-10', + date: eightyNineDaysAgo, delta: 1, currency: 'USD' }, { - date: '2000-01-11', - delta: 1, - currency: 'USD' - }, - { - date: '2000-01-12', + date: today, delta: 2, currency: 'USD' } ]); }); + + it('Returns deltas for the last 90 days', async function () { + const ninetyDaysAgo = moment().subtract(90, 'days').startOf('day'); + const ninetyFiveDaysAgo = moment().subtract(95, 'days').startOf('day'); + const eightyNineDaysAgo = moment().subtract(89, 'days').startOf('day'); + const today = moment().startOf('day'); + + await createMemberWithSubscription('month', 500, 'eur', ninetyDaysAgo.toISOString()); + await createMemberWithSubscription('month', 500, 'eur', ninetyFiveDaysAgo.toISOString()); + await createMemberWithSubscription('month', 1, 'usd', eightyNineDaysAgo.toISOString()); + await createMemberWithSubscription('month', 2, 'usd', today.toISOString()); + + const results = await statsService.api.mrr.fetchAllDeltas(); + + // Check that results are within the last 90 days + const isWithinLast90Days = (date) => { + return moment(date).isBetween(ninetyDaysAgo, today, null, '[]'); + }; + results.length.should.be.above(0); + results.forEach((result) => { + isWithinLast90Days(result.date).should.equal(true); + }); + }); }); }); diff --git a/ghost/stats-service/lib/MrrStatsService.js b/ghost/stats-service/lib/MrrStatsService.js index 29d7112a1e..4434b0ee7b 100644 --- a/ghost/stats-service/lib/MrrStatsService.js +++ b/ghost/stats-service/lib/MrrStatsService.js @@ -38,14 +38,15 @@ class MrrStatsService { */ async fetchAllDeltas() { const knex = this.knex; + const ninetyDaysAgo = moment.utc().subtract(90, 'days').startOf('day').utc().format('YYYY-MM-DD HH:mm:ss'); const rows = await knex('members_paid_subscription_events') .select('currency') // In SQLite, DATE(created_at) would map to a string value, while DATE(created_at) would map to a JSDate object in MySQL // That is why we need the cast here (to have some consistency) .select(knex.raw('CAST(DATE(created_at) as CHAR) as date')) .select(knex.raw(`SUM(mrr_delta) as delta`)) - .groupByRaw('CAST(DATE(created_at) as CHAR), currency') - .orderByRaw('CAST(DATE(created_at) as CHAR), currency'); + .where('created_at', '>=', ninetyDaysAgo) + .groupByRaw('CAST(DATE(created_at) as CHAR), currency'); return rows; } @@ -60,6 +61,13 @@ class MrrStatsService { const rows = await this.fetchAllDeltas(); + rows.sort((rowA, rowB) => { + const dateA = new Date(rowA.date); + const dateB = new Date(rowB.date); + + return dateA - dateB || rowA.currency.localeCompare(rowB.currency); + }); + // Get today in UTC (default timezone) const today = moment().format('YYYY-MM-DD'); From 1422ad5e6f10d06742caccc2a571fcde185bdf0e Mon Sep 17 00:00:00 2001 From: Sag Date: Thu, 25 Jul 2024 14:45:23 +0200 Subject: [PATCH 067/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20error=20in=20tra?= =?UTF-8?q?nslate=20helper=20when=20invalid=20parameters=20are=20passed=20?= =?UTF-8?q?(#20663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/SLO-182 ref https://github.com/TryGhost/Ghost/issues/15500 - when the {{ t }} helper is used with no parameter or an empty string, it now returns an empty string - when the {{ t }} helper is used without options, it now does not throw an error --- ghost/core/core/frontend/helpers/t.js | 15 ++++-------- .../services/theme-engine/i18n/I18n.js | 2 +- .../core/test/unit/frontend/helpers/t.test.js | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/ghost/core/core/frontend/helpers/t.js b/ghost/core/core/frontend/helpers/t.js index 57fa2c40cf..281c18c08c 100644 --- a/ghost/core/core/frontend/helpers/t.js +++ b/ghost/core/core/frontend/helpers/t.js @@ -11,18 +11,11 @@ // {{tags prefix=(t " on ")}} const {themeI18n} = require('../services/handlebars'); -const errors = require('@tryghost/errors'); -const tpl = require('@tryghost/tpl'); -const messages = { - oopsErrorTemplateHasError: 'Oops, seems there is an error in the template.' -}; - -module.exports = function t(text, options) { - if (text === undefined && options === undefined) { - throw new errors.IncorrectUsageError({ - message: tpl(messages.oopsErrorTemplateHasError) - }); +module.exports = function t(text, options = {}) { + if (!text || text.length === 0) { + // no-op: translation key is missing, return an empty string + return ''; } const bindings = {}; diff --git a/ghost/core/core/frontend/services/theme-engine/i18n/I18n.js b/ghost/core/core/frontend/services/theme-engine/i18n/I18n.js index e369d034ae..a6287fc47b 100644 --- a/ghost/core/core/frontend/services/theme-engine/i18n/I18n.js +++ b/ghost/core/core/frontend/services/theme-engine/i18n/I18n.js @@ -165,7 +165,7 @@ class I18n { let matchingString; // no path? no string - if (msgPath.length === 0 || !isString(msgPath)) { + if (!msgPath || msgPath.length === 0 || !isString(msgPath)) { this._handleEmptyKeyError(); return ''; } diff --git a/ghost/core/test/unit/frontend/helpers/t.test.js b/ghost/core/test/unit/frontend/helpers/t.test.js index 433365936c..6fcb736d1f 100644 --- a/ghost/core/test/unit/frontend/helpers/t.test.js +++ b/ghost/core/test/unit/frontend/helpers/t.test.js @@ -53,4 +53,28 @@ describe('{{t}} helper', function () { rendered.should.eql('Top left Button'); }); + + it('returns an empty string if translation key is an empty string', function () { + let rendered = t.call({}, '', { + hash: {} + }); + + rendered.should.eql(''); + }); + + it('returns an empty string if translation key is missing', function () { + let rendered = t.call({}, undefined, { + hash: {} + }); + + rendered.should.eql(''); + }); + + it('returns a translated string even if no options are passed', function () { + themeI18n.init({activeTheme: 'locale-theme', locale: 'en'}); + + let rendered = t.call({}, 'Top left Button'); + + rendered.should.eql('Left Button on Top'); + }); }); From 74b5677e3d787eccef908a129a072a63d3029132 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 17:25:54 +0200 Subject: [PATCH 068/164] Update dependency gscan to v4.43.2 (#20664) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [gscan](https://ghost.org/) ([source](https://togithub.com/TryGhost/gscan)) | [`4.43.1` -> `4.43.2`](https://renovatebot.com/diffs/npm/gscan/4.43.1/4.43.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/gscan/4.43.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/gscan/4.43.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/gscan/4.43.1/4.43.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/gscan/4.43.1/4.43.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
    TryGhost/gscan (gscan) ### [`v4.43.2`](https://togithub.com/TryGhost/gscan/compare/v4.43.1...v4.43.2) [Compare Source](https://togithub.com/TryGhost/gscan/compare/v4.43.1...v4.43.2)
    --- ### Configuration 📅 **Schedule**: Branch creation - "every weekday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Never, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View the [repository job log](https://developer.mend.io/github/TryGhost/Ghost). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- ghost/core/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/core/package.json b/ghost/core/package.json index af71ee7059..58f0456af1 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -196,7 +196,7 @@ "ghost-storage-base": "1.0.0", "glob": "8.1.0", "got": "11.8.6", - "gscan": "4.43.1", + "gscan": "4.43.2", "human-number": "2.0.4", "image-size": "1.1.1", "intl": "1.2.5", diff --git a/yarn.lock b/yarn.lock index 48005958e9..71be7a534b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19140,10 +19140,10 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw== -gscan@4.43.1: - version "4.43.1" - resolved "https://registry.yarnpkg.com/gscan/-/gscan-4.43.1.tgz#82e7ad14be753143313b8487f7846e1c85762468" - integrity sha512-++xxksUOrIljzNYoSzEMsKUDSnXVQJmaFcaWtenEP0xzKnga+i1UR1q3UF3Wf1jD+R/8poCBDcfmu13h+HA54Q== +gscan@4.43.2: + version "4.43.2" + resolved "https://registry.yarnpkg.com/gscan/-/gscan-4.43.2.tgz#72601fe61e763b9dac605a4804b6ebab27e199a6" + integrity sha512-raMKNb8gmMThjEZfffsTbLd/eWx86rM17SuFxvyPEpp949GXYdh1oaBYgTS3yWQ7Ydw6ZabwZPuctU0zirMZxg== dependencies: "@sentry/node" "^7.73.0" "@tryghost/config" "^0.2.18" From 23458c664a93bf6381d119255d675873ba929e60 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 25 Jul 2024 23:15:13 +0530 Subject: [PATCH 069/164] Added 90 days filter to Member Count Admin Dashboard query (#20665) Ref https://linear.app/tryghost/issue/SLO-192/add-90-days-filter-to-member-count-admin-dashboard-query This API used to fetch the data for all time. We need data for only 90 days for our dashboard. This will optimise the performance of this API. Also, sorting the rows in memory is lot more efficient than orderBy in db in this case. --- ghost/stats-service/lib/MembersStatsService.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ghost/stats-service/lib/MembersStatsService.js b/ghost/stats-service/lib/MembersStatsService.js index ce46b16e04..9cfad8d4c0 100644 --- a/ghost/stats-service/lib/MembersStatsService.js +++ b/ghost/stats-service/lib/MembersStatsService.js @@ -36,6 +36,7 @@ class MembersStatsService { */ async fetchAllStatusDeltas() { const knex = this.knex; + const ninetyDaysAgo = moment.utc().subtract(91, 'days').startOf('day').utc().format('YYYY-MM-DD HH:mm:ss'); const rows = await knex('members_status_events') .select(knex.raw('DATE(created_at) as date')) .select(knex.raw(`SUM( @@ -56,8 +57,8 @@ class MembersStatsService { WHEN from_status='free' THEN -1 ELSE 0 END ) as free_delta`)) - .groupByRaw('DATE(created_at)') - .orderByRaw('DATE(created_at)'); + .where('created_at', '>=', ninetyDaysAgo) + .groupByRaw('DATE(created_at)'); return rows; } @@ -77,6 +78,7 @@ class MembersStatsService { const cumulativeResults = []; + rows.sort((a, b) => new Date(a.date) - new Date(b.date)); // Loop in reverse order (needed to have correct sorted result) for (let i = rows.length - 1; i >= 0; i -= 1) { const row = rows[i]; From 71d830e1c9c8840ef6ea4125814b658fcea4d21b Mon Sep 17 00:00:00 2001 From: Chris Raible Date: Thu, 25 Jul 2024 15:40:12 -0700 Subject: [PATCH 070/164] Removed cache buster from client extensions script (#20668) ref https://linear.app/tryghost/issue/PA-71/remove-cache-bust-from-projs-in-admin ref https://github.com/TryGhost/Ghost/commit/15ed2eb245d880a292375a18c61444eff7da66f0 - This cache buster was added in March to mitigate a client side error in pro.js, to effectively force browsers to redownload the fixed version of the file. - It's not needed anymore, as the error has been fixed for a few months now, so we can safely remove it. --- ghost/admin/app/controllers/application.js | 4 ---- ghost/admin/app/templates/application.hbs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/ghost/admin/app/controllers/application.js b/ghost/admin/app/controllers/application.js index 6c15e2840d..c014d571aa 100644 --- a/ghost/admin/app/controllers/application.js +++ b/ghost/admin/app/controllers/application.js @@ -27,10 +27,6 @@ export default class ApplicationController extends Controller { return this.config.clientExtensions?.script; } - get cacheBuster() { - return Date.now(); - } - get showNavMenu() { let {router, session, ui} = this; diff --git a/ghost/admin/app/templates/application.hbs b/ghost/admin/app/templates/application.hbs index dbc514172e..fe955babf1 100644 --- a/ghost/admin/app/templates/application.hbs +++ b/ghost/admin/app/templates/application.hbs @@ -40,7 +40,7 @@ {{#if this.showScriptExtension}} {{{this.showScriptExtension.container}}} {{!-- template-lint-disable no-forbidden-elements --}} - + {{/if}} {{#if this.settings.accentColor}} From 09c59a65697f36afa9d01833f3a4570d4d8ae7b5 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Fri, 26 Jul 2024 09:41:34 +0200 Subject: [PATCH 071/164] Lazyloaded several required dependencies - these dependencies can be pushed to be loaded later than upon boot, where boot time is critical - this commit makes boot about 4% faster --- ghost/core/core/frontend/helpers/comment_count.js | 4 +++- ghost/core/ghost.js | 4 +--- ghost/email-content-generator/lib/EmailContentGenerator.js | 4 ++-- ghost/oembed-service/lib/OEmbedService.js | 4 +++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ghost/core/core/frontend/helpers/comment_count.js b/ghost/core/core/frontend/helpers/comment_count.js index 337b0dcd2c..99e5c0b244 100644 --- a/ghost/core/core/frontend/helpers/comment_count.js +++ b/ghost/core/core/frontend/helpers/comment_count.js @@ -1,7 +1,9 @@ const {SafeString} = require('../services/handlebars'); -const {html} = require('common-tags'); module.exports = function commentCount(options) { + // Lazy require the dependency to keep boot fast + const {html} = require('common-tags'); + const empty = options.hash.empty === undefined ? '' : options.hash.empty; const singular = options.hash.singular === undefined ? 'comment' : options.hash.singular; const plural = options.hash.plural === undefined ? 'comments' : options.hash.plural; diff --git a/ghost/core/ghost.js b/ghost/core/ghost.js index 709916cd35..0bcf73bddc 100644 --- a/ghost/core/ghost.js +++ b/ghost/core/ghost.js @@ -12,15 +12,13 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; const argv = process.argv; const mode = argv[2]; -const command = require('./core/cli/command'); - // Switch between boot modes switch (mode) { case 'repl': case 'timetravel': case 'generate-data': case 'record-test': - command.run(mode); + require('./core/cli/command').run(mode); break; default: // New boot sequence diff --git a/ghost/email-content-generator/lib/EmailContentGenerator.js b/ghost/email-content-generator/lib/EmailContentGenerator.js index 7edbb43953..92480ab754 100644 --- a/ghost/email-content-generator/lib/EmailContentGenerator.js +++ b/ghost/email-content-generator/lib/EmailContentGenerator.js @@ -1,7 +1,6 @@ const _ = require('lodash').runInContext(); const fs = require('fs-extra'); const path = require('path'); -const htmlToText = require('html-to-text'); _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; @@ -41,7 +40,8 @@ class EmailContentGenerator { const compiled = _.template(content); const htmlContent = compiled(data); - // generate a plain-text version of the same email + // lazyload the lib, and generate a plain-text version of the same email + const htmlToText = require('html-to-text'); const textContent = htmlToText.fromString(htmlContent); return { diff --git a/ghost/oembed-service/lib/OEmbedService.js b/ghost/oembed-service/lib/OEmbedService.js index d33c8f417d..c9876027bb 100644 --- a/ghost/oembed-service/lib/OEmbedService.js +++ b/ghost/oembed-service/lib/OEmbedService.js @@ -1,7 +1,6 @@ const errors = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const logging = require('@tryghost/logging'); -const cheerio = require('cheerio'); const _ = require('lodash'); const charset = require('charset'); const iconv = require('iconv-lite'); @@ -297,6 +296,9 @@ class OEmbedService { * @returns {Promise} */ async fetchOembedData(url, html, cardType) { + // Lazy require the library to keep boot quick + const cheerio = require('cheerio'); + // check for element let oembedUrl; try { From 6da911e5781d5b3b2b69652fc5be225a72c59afa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:01:37 +0000 Subject: [PATCH 072/164] Update tiptap monorepo to v2.5.7 --- apps/comments-ui/package.json | 20 +++---- yarn.lock | 100 +++++++++++++++++----------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 0284f2536b..3f89a3258c 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.6", - "@tiptap/extension-blockquote": "2.5.6", - "@tiptap/extension-document": "2.5.6", - "@tiptap/extension-hard-break": "2.5.6", - "@tiptap/extension-link": "2.5.6", - "@tiptap/extension-paragraph": "2.5.6", - "@tiptap/extension-placeholder": "2.5.6", - "@tiptap/extension-text": "2.5.6", - "@tiptap/pm": "2.5.6", - "@tiptap/react": "2.5.6", + "@tiptap/core": "2.5.7", + "@tiptap/extension-blockquote": "2.5.7", + "@tiptap/extension-document": "2.5.7", + "@tiptap/extension-hard-break": "2.5.7", + "@tiptap/extension-link": "2.5.7", + "@tiptap/extension-paragraph": "2.5.7", + "@tiptap/extension-placeholder": "2.5.7", + "@tiptap/extension-text": "2.5.7", + "@tiptap/pm": "2.5.7", + "@tiptap/react": "2.5.7", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index 71be7a534b..88f33df5c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7563,66 +7563,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== -"@tiptap/core@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.6.tgz#b8ed9d7398db11ef4d42c3d41cafc0680718273c" - integrity sha512-sE6UgY4TChKbtKATZuEhfFdvDHxsBZ2u7+0W4IYvBP/ldlE+2Pq2tUjgekUZElhnr2Qmy7TlSNjbHkUeOTJjew== +"@tiptap/core@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.7.tgz#681eefd198f9b7b8ad543ca29c56d46aab4919cf" + integrity sha512-8fBW+yBRSc2rEDOs6P+53kF0EAmSv17M4ruQBABo18Nt5qIyr/Uo4p+/E4NkV30bKgKI1zyq1dPeznDplSseqQ== -"@tiptap/extension-blockquote@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.6.tgz#cb502222c14c795f0b840546422fb7c908682bd7" - integrity sha512-nrGWCo/EMDFhViZB7LKW2oS+CmFezHGr3+CFbsl5tkK/Ro682kT0TQCfy9S1RGyGhUhZC6VkVaAmDpeppanDGA== +"@tiptap/extension-blockquote@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.7.tgz#378921028a25f39d6a1dcebc86efb73fc4f19cce" + integrity sha512-cSnk5ViQgG6SgKnvJ5qaW47jl5qTN0oADXdcfyaY5XrbCPBGCVq1yRZlUtPU/J0YocZpjNLRRSMPVQ3wya5vtQ== -"@tiptap/extension-bubble-menu@^2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.6.tgz#e9451229d98187d9271f80eb9f9376acb4038884" - integrity sha512-jFfMyaVkA7gSqS8CZyzdAUhMw5lMhBCoNOZE9oOIttWoE2wssSsGnprmIzTOR+g65kF8yxxzAdNOYjfv/xQ1pw== +"@tiptap/extension-bubble-menu@^2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.7.tgz#24c3f41d93022ed4bebe3610124bb114333c834d" + integrity sha512-gkuBuVGm5YPDRUG5Bscj6IYjDbzM7iJ2aXBGCM1rzuIiwT04twY51dKMIeseXa49uk/AQs/mqt3kGQjgSdSFAw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.6.tgz#19163a1d25773a105a674d9815416808b41c2bc7" - integrity sha512-0XNAFpCKHtTd0WCEhkg4zFCII2HV5/L7lQniHlVW/EaDsibkDtIZklSGKIRn+/xaS1bVrzcIq4Z/2hE52a9H0A== +"@tiptap/extension-document@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.7.tgz#cfc608656ab9173334247baf8ddb93135b024260" + integrity sha512-tcK6aleya6pmC/ForF/y2PiwPhN5hK8JSm07pcWV9FmP2Qemx26GWS+1u1EzPDeTTbRBvk+9txHGcq9NYZem0Q== -"@tiptap/extension-floating-menu@^2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.6.tgz#71bd52246e13345d11a84adc8abd9f35e829dbb3" - integrity sha512-UORk6CDtECqLoaOVx0oTwidrjdHEWaPaY/pYGnCC4Kptv6zEqIsZXidFcYBM1wTt44AESgUXDTnZF8U7M/YtEA== +"@tiptap/extension-floating-menu@^2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.7.tgz#b97101d82629335f47663bb4ddbc9231985a2b80" + integrity sha512-tQjNNx0gPb7GxMiozcQ4R1Tl1znmlx/ZkbCF9rqxTzPTD4fnCliqBQAWjtHl98+D8+yEJBcB2DimtP7ztkv2mg== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.6.tgz#44b8e58cadcc429466c12586cb58eb50c59f4d6a" - integrity sha512-pBKRIziToH/0OB0Q6jOyCZcBLa1OdY8UrwRPWXOWIXSQOSgxHpzYhpwuN+s341c8U8l13mqgo0Ths5IS7/vp3g== +"@tiptap/extension-hard-break@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.7.tgz#a832460a610f3ff6c3c4260562c8555e0d0734ec" + integrity sha512-Ki1JV2cz74wo4am8vIY6KWnfiFoE68RVQDIL0/29fNz1oZI46R4VV2Q5IvoVhetXcx7Qe9nTJVqy1vRS//Kcvg== -"@tiptap/extension-link@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.6.tgz#5899815619b6e703bd39bee2c783f35071c1b220" - integrity sha512-sLcrVKB6qfnRQ66YQ48/hX8/69fvHgcPjq+k3dpqvi8CohmcYn8CuTbIh4+x/fgmIb6esqUkamDsGdU+70bTwg== +"@tiptap/extension-link@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.7.tgz#91326304a3f4f5fdeb23a8f630d5ae4a1ab64287" + integrity sha512-rxvcdV8H/TiRhR2SZfLHp7hUp5hwBAhkc6PsXEWj8lekG4/5lXGwPSPxLtHMBRtOyeJpXTv9DY6nsCGZuz9x6A== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.6.tgz#92c3073f230da7d86dad04d7262013c5dd4a68c9" - integrity sha512-ZPoerYEZLHlOCsfEfagX38ahXwegZaAB/ZUlcff9ehp5v8uO4L64mWyQRoTDF74A9cGnKxE3enokz8tOA47fkA== +"@tiptap/extension-paragraph@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.7.tgz#7ce35b365e8222fb8e93f5e7bcdc18ef73c32ac5" + integrity sha512-7zmDE43jv+GMTLuWrztA6oAnYLdUki5fUjYFn0h5FPRHQTuDoxsCD+hX0N/sGQVlc8zl1yn7EYbPNn9rHi7ECw== -"@tiptap/extension-placeholder@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.6.tgz#c1bfdf3a9fbe2cee510666d36657a31544f18fa4" - integrity sha512-ULmR146/l1zGTkzyda/9Y7yDSz3Pr6hE+ccBDiJ0OMgf7ukL+iwAuZJz3R1EbDz6x2FfrspVnt2/ieCYDKe7Hw== +"@tiptap/extension-placeholder@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.7.tgz#b01f695d370d72ce2efd81a2b9b61868290b1f36" + integrity sha512-Xjl0sCUlNyVq8HDrf+6n62gPEM3ymPr5d5t0zXjE+NPzfOeOARfiMXW2VB5QYFOsxnCd2MbZAeZ4+RY2sSVaZg== -"@tiptap/extension-text@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.6.tgz#f1b7b41ef36fd4507bd62a75dc21fde5392ae491" - integrity sha512-dJFARnhuxaLrUP/3PhSE7Wfq/OBRKKW73ZMVrIFhGbVxOdmTZxA12d95U0e+5q6mbPSQFNx8ZYhqnStztRAjBw== +"@tiptap/extension-text@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.7.tgz#bc436206ba16214383064672a6ebe3dd0464f368" + integrity sha512-vukhh2K/MsaIIs/UzIAwp44IVxTHPJcAhSsDnmJd4iPlkpjLt1eph77dfxv5awq78bj6mGvnCM0/0F6fW1C6/w== -"@tiptap/pm@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.6.tgz#320e24950cdcd81d18339ec3528094e4d60dd795" - integrity sha512-0ysx5eXyF5XXfBmo/krMWKvZh6iBnqJ0YVPO/NsGzZcdjBix9lYYDh6nswLvv2mjhPbMEmcMW1ctj3myOwoUbw== +"@tiptap/pm@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.7.tgz#9661d508fe34f7616b1078becc049baeff75d677" + integrity sha512-4Eb4vA4e4vesBAUmZgx+n3xjgJ58uRKKtnhFDJ3Gg+dfpXvtF8FcEwSIjHJsTlNJ8mSrzX/I7S157qPc5wZXVw== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7643,13 +7643,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.9" -"@tiptap/react@2.5.6": - version "2.5.6" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.6.tgz#b1ee9843eb8c2cc00d85f229c539b815e238c88f" - integrity sha512-n6R5wVug1cGzE0DYWKSkRNBwDJ5ZPwOb21w9PjlEaD2A7yw6JuUBkWaCyfwl3bCzGj8ctZQ5hd9hhnWyJ1UPOg== +"@tiptap/react@2.5.7": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.7.tgz#0de723c468a37cf69bb1a9d7a38815137f156c71" + integrity sha512-QRMbo6eDtYHBwZ7ATFgKFWLlRZ/Q7NJrBS/Z6FW2lFhr1eM8UhOG6HMEMt/kibMJDJVi1FpXEavgaT75oe2BJg== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.6" - "@tiptap/extension-floating-menu" "^2.5.6" + "@tiptap/extension-bubble-menu" "^2.5.7" + "@tiptap/extension-floating-menu" "^2.5.7" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From c8df04de1bfda6712e46baec02b7eec682f6250a Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:05:23 +0000 Subject: [PATCH 073/164] v5.88.2 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 74ad283c0b..21dc8328ed 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.88.1", + "version": "5.88.2", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index 58f0456af1..d9551825f9 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.88.1", + "version": "5.88.2", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From 1f05a7890f52573b83578944456fc3579917c09a Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Fri, 26 Jul 2024 21:20:13 -0500 Subject: [PATCH 074/164] Added test coverage over newsletter flows (#20672) no ref - while reviewing the newsletter flows, it was apparent that we were missing test coverage Some of the tests in Portal are a bit redundant with tests added for child components, but it didn't seem worth removing them after getting them to work. There was a bug in our Portal fixture data that requires a few changes, as well as some small adjustments for making tests easier (testing-lib-react has `getByTestId` and simply a `querySelector` to use alternate test attributes). --- .../components/common/NewsletterManagement.js | 4 +- .../components/pages/AccountEmailPage.test.js | 117 ++++++++++++++++++ .../AccountHomePage/AccountHomePage.test.js | 4 +- .../pages/NewsletterSelectionPage.js | 4 +- .../src/tests/EmailSubscriptionsFlow.test.js | 31 ++++- apps/portal/src/utils/fixtures-generator.js | 2 +- .../server/services/members/middleware.js | 2 +- .../shared/config/env/config.development.json | 12 ++ .../e2e-browser/portal/member-actions.spec.js | 2 +- .../services/members/middleware.test.js | 93 +++++++++++++- 10 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 apps/portal/src/components/pages/AccountEmailPage.test.js diff --git a/apps/portal/src/components/common/NewsletterManagement.js b/apps/portal/src/components/common/NewsletterManagement.js index b36406ce90..3bc9782de2 100644 --- a/apps/portal/src/components/common/NewsletterManagement.js +++ b/apps/portal/src/components/common/NewsletterManagement.js @@ -46,7 +46,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed const [showUpdated, setShowUpdated] = useState(false); const [timeoutId, setTimeoutId] = useState(null); return ( -
    +

    {newsletter.name}

    {newsletter?.description}

    @@ -95,7 +95,7 @@ function CommentsSection({updateCommentNotifications, isCommentsEnabled, enableC } return ( -
    +

    {t('Comments')}

    {t('Get notified when someone replies to your comment')}

    diff --git a/apps/portal/src/components/pages/AccountEmailPage.test.js b/apps/portal/src/components/pages/AccountEmailPage.test.js new file mode 100644 index 0000000000..87c5f8ea57 --- /dev/null +++ b/apps/portal/src/components/pages/AccountEmailPage.test.js @@ -0,0 +1,117 @@ +import {getSiteData, getNewslettersData, getMemberData} from '../../utils/fixtures-generator'; +import {render, fireEvent} from '../../utils/test-utils'; +import AccountEmailPage from './AccountEmailPage'; + +const setup = (overrides) => { + const {mockOnActionFn, context, ...utils} = render( + , + { + overrideContext: { + ...overrides + } + } + ); + const unsubscribeAllBtn = utils.getByText('Unsubscribe from all emails'); + const closeBtn = utils.getByTestId('close-popup'); + + return { + unsubscribeAllBtn, + closeBtn, + mockOnActionFn, + context, + ...utils + }; +}; + +describe('Account Email Page', () => { + test('renders', () => { + const newsletterData = getNewslettersData({numOfNewsletters: 2}); + const siteData = getSiteData({ + newsletters: newsletterData, + member: getMemberData({newsletters: newsletterData}) + }); + const {unsubscribeAllBtn, getAllByTestId, getByText} = setup({site: siteData}); + const unsubscribeBtns = getAllByTestId(`toggle-wrapper`); + expect(getByText('Email preferences')).toBeInTheDocument(); + // one for each newsletter and one for comments + expect(unsubscribeBtns).toHaveLength(3); + expect(unsubscribeAllBtn).toBeInTheDocument(); + }); + + test('can unsubscribe from all emails', async () => { + const newsletterData = getNewslettersData({numOfNewsletters: 2}); + const siteData = getSiteData({ + newsletters: newsletterData + }); + const {mockOnActionFn, unsubscribeAllBtn, getAllByTestId} = setup({site: siteData, member: getMemberData({newsletters: newsletterData})}); + let checkmarkContainers = getAllByTestId('checkmark-container'); + // each newsletter should have the checked class (this is how we know they're enabled/subscribed to) + expect(checkmarkContainers[0]).toHaveClass('gh-portal-toggle-checked'); + expect(checkmarkContainers[1]).toHaveClass('gh-portal-toggle-checked'); + + fireEvent.click(unsubscribeAllBtn); + expect(mockOnActionFn).toHaveBeenCalledTimes(2); + expect(mockOnActionFn).toHaveBeenCalledWith('showPopupNotification', {action: 'updated:success', message: 'Unsubscribed from all emails.'}); + expect(mockOnActionFn).toHaveBeenLastCalledWith('updateNewsletterPreference', {newsletters: [], enableCommentNotifications: false}); + + checkmarkContainers = getAllByTestId('checkmark-container'); + expect(checkmarkContainers).toHaveLength(3); + checkmarkContainers.forEach((newsletter) => { + // each newsletter htmlElement should not have the checked class + expect(newsletter).not.toHaveClass('gh-portal-toggle-checked'); + }); + }); + + test('unsubscribe all is disabled when no newsletters are subscribed to', async () => { + const siteData = getSiteData({ + newsletters: getNewslettersData({numOfNewsletters: 2}) + }); + const {unsubscribeAllBtn} = setup({site: siteData, member: getMemberData()}); + expect(unsubscribeAllBtn).toBeDisabled(); + }); + + test('can update newsletter preferences', async () => { + const newsletterData = getNewslettersData({numOfNewsletters: 2}); + const siteData = getSiteData({ + newsletters: newsletterData + }); + const {mockOnActionFn, getAllByTestId} = setup({site: siteData, member: getMemberData({newsletters: newsletterData})}); + let checkmarkContainers = getAllByTestId('checkmark-container'); + // each newsletter should have the checked class (this is how we know they're enabled/subscribed to) + expect(checkmarkContainers[0]).toHaveClass('gh-portal-toggle-checked'); + let subscriptionToggles = getAllByTestId('switch-input'); + fireEvent.click(subscriptionToggles[0]); + expect(mockOnActionFn).toHaveBeenCalledWith('updateNewsletterPreference', {newsletters: [{id: newsletterData[1].id}]}); + fireEvent.click(subscriptionToggles[0]); + expect(mockOnActionFn).toHaveBeenCalledWith('updateNewsletterPreference', {newsletters: [{id: newsletterData[1].id}, {id: newsletterData[0].id}]}); + }); + + test('can update comment notifications', async () => { + const siteData = getSiteData(); + const {mockOnActionFn, getAllByTestId} = setup({site: siteData, member: getMemberData()}); + let subscriptionToggles = getAllByTestId('switch-input'); + fireEvent.click(subscriptionToggles[0]); + expect(mockOnActionFn).toHaveBeenCalledWith('updateNewsletterPreference', {enableCommentNotifications: true}); + fireEvent.click(subscriptionToggles[0]); + expect(mockOnActionFn).toHaveBeenCalledWith('updateNewsletterPreference', {enableCommentNotifications: false}); + }); + + test('displays help for members with email suppressions', async () => { + const newsletterData = getNewslettersData({numOfNewsletters: 2}); + const siteData = getSiteData({ + newsletters: newsletterData + }); + const {getByText} = setup({site: siteData, member: getMemberData({newsletters: newsletterData, email_suppressions: {suppressed: false}})}); + expect(getByText('Not receiving emails?')).toBeInTheDocument(); + expect(getByText('Get help →')).toBeInTheDocument(); + }); + + test('redirects to signin page if no member', async () => { + const newsletterData = getNewslettersData({numOfNewsletters: 2}); + const siteData = getSiteData({ + newsletters: newsletterData + }); + const {mockOnActionFn} = setup({site: siteData, member: null}); + expect(mockOnActionFn).toHaveBeenCalledWith('switchPage', {page: 'signin'}); + }); +}); diff --git a/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js b/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js index 98ab7bebcd..ac6679bf47 100644 --- a/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js +++ b/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js @@ -1,6 +1,7 @@ import {render, fireEvent} from '../../../utils/test-utils'; import AccountHomePage from './AccountHomePage'; import {site} from '../../../utils/fixtures'; +import {getSiteData} from '../../../utils/fixtures-generator'; const setup = (overrides) => { const {mockOnActionFn, ...utils} = render( @@ -21,7 +22,8 @@ const setup = (overrides) => { describe('Account Home Page', () => { test('renders', () => { - const {logoutBtn, utils} = setup(); + const siteData = getSiteData({commentsEnabled: 'off'}); + const {logoutBtn, utils} = setup({site: siteData}); expect(logoutBtn).toBeInTheDocument(); expect(utils.queryByText('You\'re currently not receiving emails')).not.toBeInTheDocument(); expect(utils.queryByText('Email newsletter')).toBeInTheDocument(); diff --git a/apps/portal/src/components/pages/NewsletterSelectionPage.js b/apps/portal/src/components/pages/NewsletterSelectionPage.js index 4707fa8bae..067c9aa2e5 100644 --- a/apps/portal/src/components/pages/NewsletterSelectionPage.js +++ b/apps/portal/src/components/pages/NewsletterSelectionPage.js @@ -11,7 +11,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed }); if (newsletter.paid) { return ( -
    +

    {newsletter.name}

    {newsletter.description}

    @@ -23,7 +23,7 @@ function NewsletterPrefSection({newsletter, subscribedNewsletters, setSubscribed ); } return ( -
    +

    {newsletter.name}

    {newsletter.description}

    diff --git a/apps/portal/src/tests/EmailSubscriptionsFlow.test.js b/apps/portal/src/tests/EmailSubscriptionsFlow.test.js index 0ecace4b9c..960a240858 100644 --- a/apps/portal/src/tests/EmailSubscriptionsFlow.test.js +++ b/apps/portal/src/tests/EmailSubscriptionsFlow.test.js @@ -160,7 +160,7 @@ describe('Newsletter Subscriptions', () => { fireEvent.click(unsubscribeAllButton); - expect(ghostApi.member.update).toHaveBeenCalledWith({newsletters: []}); + expect(ghostApi.member.update).toHaveBeenCalledWith({newsletters: [], enableCommentNotifications: false}); // Verify the local state shows the newsletter as unsubscribed let newsletterToggles = within(popupIframeDocument).queryAllByTestId('checkmark-container'); let newsletter1Toggle = newsletterToggles[0]; @@ -254,4 +254,33 @@ describe('Newsletter Subscriptions', () => { expect(newsletter2Toggle).toHaveClass('gh-portal-toggle-checked'); }); }); + + // describe('navigating straight to /portal/account/newsletters', () => { + // it('shows the newsletter management page when signed in', async () => { + // const {popupFrame, triggerButton, queryAllByText, popupIframeDocument} = await setup({ + // site: FixtureSite.singleTier.onlyFreePlanWithoutStripe, + // member: FixtureMember.subbedToNewsletter, + // newsletters: Newsletters + // }); + + // const manageSubscriptionsButton = within(popupIframeDocument).queryByRole('button', {name: 'Manage'}); + // await userEvent.click(manageSubscriptionsButton); + + // const newsletter1 = within(popupIframeDocument).queryAllByText('Newsletter 1'); + // expect(newsletter1).toBeInTheDocument(); + // }); + + // it('redirects to the sign in page when not signed in', async () => { + // const {popupFrame, queryByTitle, popupIframeDocument} = await setup({ + // site: FixtureSite.singleTier.onlyFreePlanWithoutStripe, + // member: FixtureMember.subbedToNewsletter, + // newsletters: Newsletters + // }, true); + + // // console.log(`popupFrame`, popupFrame); + // // console.log(`queryByTitle`, queryByTitle); + // // console.log(`popupIframeDocument`, popupIframeDocument); + + // }); + // }); }); diff --git a/apps/portal/src/utils/fixtures-generator.js b/apps/portal/src/utils/fixtures-generator.js index 5f850686f4..4fda8d6de3 100644 --- a/apps/portal/src/utils/fixtures-generator.js +++ b/apps/portal/src/utils/fixtures-generator.js @@ -66,7 +66,7 @@ export function getSiteData({ portal_button_signup_text, portal_button_style, members_support_address, - comments_enabled: !!commentsEnabled, + comments_enabled: commentsEnabled !== 'off', newsletters, recommendations, recommendations_enabled: !!recommendationsEnabled diff --git a/ghost/core/core/server/services/members/middleware.js b/ghost/core/core/server/services/members/middleware.js index e31d407a90..e73bfd65f4 100644 --- a/ghost/core/core/server/services/members/middleware.js +++ b/ghost/core/core/server/services/members/middleware.js @@ -110,7 +110,7 @@ const authMemberByUuid = async function authMemberByUuid(req, res, next) { } throw new errors.UnauthorizedError({ - messsage: tpl(messages.missingUuid) + message: tpl(messages.missingUuid) }); } diff --git a/ghost/core/core/shared/config/env/config.development.json b/ghost/core/core/shared/config/env/config.development.json index 5cd8e57c8b..d9551493f5 100644 --- a/ghost/core/core/shared/config/env/config.development.json +++ b/ghost/core/core/shared/config/env/config.development.json @@ -1,5 +1,17 @@ { "url": "http://localhost:2368", + "mail": { + "from": "test@example.com", + "transport": "SMTP", + "options": { + "host": "127.0.0.1", + "port": 1025, + "auth": { + "user": "user", + "pass": "unsecure" + } + } + }, "database": { "client": "sqlite3", "connection": { diff --git a/ghost/core/test/e2e-browser/portal/member-actions.spec.js b/ghost/core/test/e2e-browser/portal/member-actions.spec.js index 1f158e5cad..e0ac61631a 100644 --- a/ghost/core/test/e2e-browser/portal/member-actions.spec.js +++ b/ghost/core/test/e2e-browser/portal/member-actions.spec.js @@ -107,7 +107,7 @@ test.describe('Portal', () => { await portalFrame.locator('[data-test-button="manage-newsletters"]').click(); // check amount of newsletterss - const newsletters = await portalFrame.locator('[data-test-toggle-wrapper="true"]'); + const newsletters = await portalFrame.locator('[data-testid="toggle-wrapper"]'); const count = await newsletters.count(); await expect(count).toEqual(2); diff --git a/ghost/core/test/unit/server/services/members/middleware.test.js b/ghost/core/test/unit/server/services/members/middleware.test.js index cca19f1411..94d0242984 100644 --- a/ghost/core/test/unit/server/services/members/middleware.test.js +++ b/ghost/core/test/unit/server/services/members/middleware.test.js @@ -192,4 +192,95 @@ describe('Members Service Middleware', function () { res.redirect.firstCall.args[0].should.eql('/blah/?action=signin&success=true'); }); }); -}); + + describe('updateMemberNewsletters', function () { + // let oldMembersService; + let req; + let res; + + before(function () { + models.init(); + }); + + beforeEach(function () { + req = {body: {newsletters: [], enable_comment_notifications: null}}; + res = {writeHead: sinon.stub(), end: sinon.stub()}; + }); + + afterEach(function () { + sinon.restore(); + }); + + it('returns 400 if no member uuid is part of the request', async function () { + req.query = {}; + + // Call the middleware + await membersMiddleware.updateMemberNewsletters(req, res); + + // Check behavior + res.writeHead.calledOnce.should.be.true(); + res.writeHead.firstCall.args[0].should.eql(400); + res.end.calledOnce.should.be.true(); + res.end.firstCall.args[0].should.eql('Invalid member uuid'); + }); + + it('returns 404 if member uuid is not found', async function () { + req.query = {uuid: 'test'}; + sinon.stub(membersService, 'api').get(() => { + return { + members: { + get: sinon.stub().resolves() + } + }; + }); + + // Call the middleware + await membersMiddleware.updateMemberNewsletters(req, res); + + // Check behavior + res.writeHead.calledOnce.should.be.true(); + res.writeHead.firstCall.args[0].should.eql(404); + res.end.calledOnce.should.be.true(); + res.end.firstCall.args[0].should.eql('Email address not found.'); + }); + + it('attempts to update newsletters', async function () { + res.json = sinon.stub(); + req.query = {uuid: 'test'}; + const memberData = { + id: 'test', + email: 'test@email.com', + name: 'Test Name', + newsletters: [], + enable_comment_notifications: false, + status: 'free' + }; + sinon.stub(membersService, 'api').get(() => { + return { + members: { + get: sinon.stub().resolves({id: 'test', email: 'test@email.com', get: () => 'test'}), + update: sinon.stub().resolves({ + ...memberData, + toJSON: () => JSON.stringify(memberData) + }) + } + }; + }); + await membersMiddleware.updateMemberNewsletters(req, res); + // the stubbing of the api is difficult to test with the current design, so we just check that the response is sent + res.json.calledOnce.should.be.true(); + }); + + it('returns 400 on error', async function () { + // use a malformed request to trigger an error + req = {}; + await membersMiddleware.updateMemberNewsletters(req, res); + + // Check behavior + res.writeHead.calledOnce.should.be.true(); + res.writeHead.firstCall.args[0].should.eql(400); + res.end.calledOnce.should.be.true(); + res.end.firstCall.args[0].should.eql('Failed to update newsletters'); + }); + }); +}); \ No newline at end of file From ed4b57736a54a594ce53a844b3122d44fb591d57 Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Sat, 27 Jul 2024 07:59:45 -0500 Subject: [PATCH 075/164] Fixed e2e test for Portal (#20674) no ref - updated the locator to use the class --- ghost/core/test/e2e-browser/portal/member-actions.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/core/test/e2e-browser/portal/member-actions.spec.js b/ghost/core/test/e2e-browser/portal/member-actions.spec.js index e0ac61631a..c6a16aa3af 100644 --- a/ghost/core/test/e2e-browser/portal/member-actions.spec.js +++ b/ghost/core/test/e2e-browser/portal/member-actions.spec.js @@ -106,8 +106,8 @@ test.describe('Portal', () => { await portalTriggerButton.click(); await portalFrame.locator('[data-test-button="manage-newsletters"]').click(); - // check amount of newsletterss - const newsletters = await portalFrame.locator('[data-testid="toggle-wrapper"]'); + // check amount of newsletters + const newsletters = await portalFrame.locator('.gh-portal-list-toggle-wrapper'); const count = await newsletters.count(); await expect(count).toEqual(2); From 768ba8c5589d567485a10d40b487f71488f23a77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 01:38:16 +0000 Subject: [PATCH 076/164] Update dependency mysql2 to v3.11.0 --- ghost/core/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/core/package.json b/ghost/core/package.json index d9551825f9..6dd02b45bf 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -214,7 +214,7 @@ "moment": "2.24.0", "moment-timezone": "0.5.45", "multer": "1.4.4", - "mysql2": "3.10.3", + "mysql2": "3.11.0", "nconf": "0.12.1", "node-jose": "2.2.0", "path-match": "1.2.4", diff --git a/yarn.lock b/yarn.lock index 88f33df5c6..f3685fd55b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24013,10 +24013,10 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -mysql2@3.10.3: - version "3.10.3" - resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.10.3.tgz#82646a2b9018370769ae1bb590e015af190e070d" - integrity sha512-k43gmH9i79rZD4hGPdj7pDuT0UBiFjs4UzXEy1cJrV0QqcSABomoLwvejqdbcXN+Vd7gi999CVM6o9vCPKq29g== +mysql2@3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.11.0.tgz#2a7bd7c615ab43f8167ed9922063b968f3e48f33" + integrity sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA== dependencies: aws-ssl-profiles "^1.1.1" denque "^2.1.0" From 14cabf97dfa4a1d3768e04c19cb869f61a0227a4 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:25:09 +0200 Subject: [PATCH 077/164] Updated tips and donations copy in editor button card (#20662) REF MOM-313 --- .../src/components/settings/growth/TipsAndDonations.tsx | 4 ++-- ghost/admin/app/components/koenig-lexical-editor.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx index 10cbf0ed67..8c9f06237a 100644 --- a/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/TipsAndDonations.tsx @@ -88,7 +88,7 @@ const TipsAndDonations: React.FC<{ keywords: string[] }> = ({keywords}) => { const inputFields = ( -
    +
    = ({keywords}) => { return ( { if (this.feature.tipsAndDonations && this.settings.donationsEnabled) { return [{ - label: 'Tip or donation', + label: 'Tips and donations', value: '#/portal/support' }]; } From 0193ad90bb6e75405412e99dcf764d945808dc0f Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Mon, 29 Jul 2024 09:32:12 +0200 Subject: [PATCH 078/164] Protected against quick escapes when loading post fix https://linear.app/tryghost/issue/SLO-180/typeerror-cannot-read-properties-of-null-reading-displayname - in the event you click on a post and then press Back really quickly, `this.post` will be null because the post is not loaded - the code here fails because it tries to read a property from `null` - we can protect against that by using optional chaining on the property --- ghost/admin/app/controllers/lexical-editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index ee15feab8f..71c6331437 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -871,11 +871,11 @@ export default class LexicalEditorController extends Controller { this.ui.updateDocumentTitle(); } - /* + /* // sync the post slug with the post title, except when: // - the user has already typed a custom slug, which should not be overwritten // - the post has been published, so that published URLs are not broken - */ + */ @enqueueTask *generateSlugTask() { const currentTitle = this.get('post.title'); @@ -916,7 +916,7 @@ export default class LexicalEditorController extends Controller { *backgroundLoaderTask() { yield this.store.query('snippet', {limit: 'all'}); - if (this.post.displayName === 'page' && this.feature.get('collections') && this.feature.get('collectionsCard')) { + if (this.post?.displayName === 'page' && this.feature.get('collections') && this.feature.get('collectionsCard')) { yield this.store.query('collection', {limit: 'all'}); } From dc3539ebca3a3096bd0138b1178d3e54f11da84d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:31:06 +0000 Subject: [PATCH 079/164] Update dependency html-validate to v8.21.0 --- ghost/email-service/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/email-service/package.json b/ghost/email-service/package.json index 189f9be6ff..96842c6917 100644 --- a/ghost/email-service/package.json +++ b/ghost/email-service/package.json @@ -19,7 +19,7 @@ ], "devDependencies": { "c8": "8.0.1", - "html-validate": "8.20.1", + "html-validate": "8.21.0", "mocha": "10.2.0", "should": "13.2.3", "sinon": "15.2.0" diff --git a/yarn.lock b/yarn.lock index f3685fd55b..1f81ca7c03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19524,10 +19524,10 @@ html-to-text@8.2.1: minimist "^1.2.6" selderee "^0.6.0" -html-validate@8.20.1: - version "8.20.1" - resolved "https://registry.yarnpkg.com/html-validate/-/html-validate-8.20.1.tgz#8cdd1fc32f4578efa5a9dea596cdd9bf0e26f805" - integrity sha512-EawDiHzvZtnbBIfxE90lvKOWqNsmZGqRXTy+utxlGo525Vqjowg+RK42q1AeJ6zm1AyVTFIDSah1eBe9tc6YHg== +html-validate@8.21.0: + version "8.21.0" + resolved "https://registry.yarnpkg.com/html-validate/-/html-validate-8.21.0.tgz#fcb8aa4d05d95c9b806bebf3d1be6836a1d8a196" + integrity sha512-f6uyHdNeul4f/E6TDaUrH8agrVmnG5VbWwmIhbkg+Vrz+To/2xxbc+soBKXqani1QSaA+5I12Qr7dQt/HVFJtw== dependencies: "@babel/code-frame" "^7.10.0" "@html-validate/stylish" "^4.1.0" From 184ef6274ab961f4ec933cfd30acaf08d31dbb9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 09:30:47 +0000 Subject: [PATCH 080/164] Update dependency tailwindcss to v3.4.6 --- apps/admin-x-design-system/package.json | 2 +- apps/comments-ui/package.json | 2 +- apps/signup-form/package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 4d1841c4fc..1524ae194f 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -71,7 +71,7 @@ "react-colorful": "5.6.1", "react-hot-toast": "2.4.1", "react-select": "5.8.0", - "tailwindcss": "3.4.5" + "tailwindcss": "3.4.6" }, "peerDependencies": { "react": "^18.2.0", diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 3f89a3258c..78a9ec7e07 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -75,7 +75,7 @@ "eslint-plugin-tailwindcss": "3.13.0", "jsdom": "24.1.1", "postcss": "8.4.39", - "tailwindcss": "3.4.5", + "tailwindcss": "3.4.6", "vite": "4.5.3", "vite-plugin-css-injected-by-js": "3.3.0", "vite-plugin-svgr": "3.3.0", diff --git a/apps/signup-form/package.json b/apps/signup-form/package.json index e1e608d751..fc3841fdec 100644 --- a/apps/signup-form/package.json +++ b/apps/signup-form/package.json @@ -65,7 +65,7 @@ "rollup-plugin-node-builtins": "2.1.2", "storybook": "7.6.20", "stylelint": "15.10.3", - "tailwindcss": "3.4.5", + "tailwindcss": "3.4.6", "vite": "4.5.3", "vite-plugin-commonjs": "0.10.1", "vite-plugin-svgr": "3.3.0", diff --git a/yarn.lock b/yarn.lock index 1f81ca7c03..e4cccdb81f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30049,10 +30049,10 @@ table@^6.0.9, table@^6.8.1: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@3.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.5.tgz#0de2e92ed4d00fb015feb962fa0781605761724d" - integrity sha512-DlTxttYcogpDfx3tf/8jfnma1nfAYi2cBUYV2YNoPPecwmO3YGiFlOX9D8tGAu+EDF38ryBzvrDKU/BLMsUwbw== +tailwindcss@3.4.6: + version "3.4.6" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.6.tgz#41faae16607e0916da1eaa4a3b44053457ba70dd" + integrity sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" From 103672ef57ad5d9ae1f3a1185b70d9d1eed36ca4 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Mon, 29 Jul 2024 11:57:53 +0200 Subject: [PATCH 081/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20spurious=20error?= =?UTF-8?q?s=20when=20loading=20modal=20before=20provider?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix https://linear.app/tryghost/issue/SLO-190/error-no-dispatch-method-detected-did-you-embed-your-app-with - we've spuriously been seeing `No dispatch method detected, did you embed your app with NiceModal.Provider` when browsing to a URL that loads a modal in Safari - it looks like DesignSystemProvider (via DesignSystemApp) contains the NiceModal.Provider, but this is loaded within the RoutingProvider that could trigger a modal to load - I tried switching around RoutingProvider and DesignSystemApp but many other tests failed, so my fix here is to add a NiceModal.Provider to wrap the RoutingProvider - unfortunately, this bug is flaky to occur and I've only been able to reproduce it on Safari, so writing a test for this would be very tricky --- apps/admin-x-settings/src/App.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/admin-x-settings/src/App.tsx b/apps/admin-x-settings/src/App.tsx index c6590c9882..26292ceeb7 100644 --- a/apps/admin-x-settings/src/App.tsx +++ b/apps/admin-x-settings/src/App.tsx @@ -1,4 +1,5 @@ import MainContent from './MainContent'; +import NiceModal from '@ebay/nice-modal-react'; import SettingsAppProvider, {OfficialTheme, UpgradeStatusType} from './components/providers/SettingsAppProvider'; import SettingsRouter, {loadModals, modalPaths} from './components/providers/SettingsRouter'; import {DesignSystemApp, DesignSystemAppProps} from '@tryghost/admin-x-design-system'; @@ -18,12 +19,17 @@ function App({framework, designSystem, officialThemes, zapierTemplates, upgradeS return ( - - - - - - + {/* NOTE: we need to have an extra NiceModal.Provider here because the one inside DesignSystemApp + is loaded too late for possible modals in RoutingProvider, and it's quite hard to change it at + this point */} + + + + + + + + ); From 7411724a0389d911db3d2904aa8a165556bdd7df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:39:22 +0000 Subject: [PATCH 082/164] Update dependency terser to v5.31.3 --- ghost/minifier/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/minifier/package.json b/ghost/minifier/package.json index fd2783dd9c..6a6e1abfb9 100644 --- a/ghost/minifier/package.json +++ b/ghost/minifier/package.json @@ -28,7 +28,7 @@ "@tryghost/errors": "1.3.2", "@tryghost/tpl": "0.1.30", "csso": "5.0.5", - "terser": "5.31.1", + "terser": "5.31.3", "tiny-glob": "0.2.9" } } diff --git a/yarn.lock b/yarn.lock index e4cccdb81f..93278ba4f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30228,10 +30228,10 @@ terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@5.31.1, terser@^5.26.0, terser@^5.7.0: - version "5.31.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.1.tgz#735de3c987dd671e95190e6b98cfe2f07f3cf0d4" - integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== +terser@5.31.3, terser@^5.26.0, terser@^5.7.0: + version "5.31.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.3.tgz#b24b7beb46062f4653f049eea4f0cd165d0f0c38" + integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" From f64820b1be41dcceb144f1d996f30c24c4b98f5e Mon Sep 17 00:00:00 2001 From: Sag Date: Mon, 29 Jul 2024 17:33:23 +0200 Subject: [PATCH 083/164] =?UTF-8?q?=F0=9F=90=9B=20Disabled=20bulk=20deleti?= =?UTF-8?q?on=20when=20multiple=20member=20filters=20are=20applied=20(#206?= =?UTF-8?q?81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes https://linear.app/tryghost/issue/ONC-206 ref https://app.incident.io/ghost/incidents/90 - when multiple member filters are used in combination, NQL sometimes hit a limitation that results in the wrong members being returned - while we work on the NQL limitation, we are temporarily disabling bulk member deletion when more than one member filter has been applied --- ghost/admin/app/controllers/members.js | 4 ++ ghost/admin/app/templates/members.hbs | 14 +++--- ghost/admin/tests/acceptance/members-test.js | 49 ++++++++++++++++++- .../tests/acceptance/members/filter-test.js | 7 ++- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/ghost/admin/app/controllers/members.js b/ghost/admin/app/controllers/members.js index 016987fda2..1270b5a06a 100644 --- a/ghost/admin/app/controllers/members.js +++ b/ghost/admin/app/controllers/members.js @@ -209,6 +209,10 @@ export default class MembersController extends Controller { return uniqueColumns.splice(0, 2); // Maximum 2 columns } + get isMultiFiltered() { + return this.isFiltered && this.filters.length >= 2; + } + includeTierQuery() { const availableFilters = this.filters.length ? this.filters : this.softFilters; return availableFilters.some((f) => { diff --git a/ghost/admin/app/templates/members.hbs b/ghost/admin/app/templates/members.hbs index c2671358d6..d72c91a904 100644 --- a/ghost/admin/app/templates/members.hbs +++ b/ghost/admin/app/templates/members.hbs @@ -104,12 +104,14 @@ {{/if}} -
  • -
  • - -
  • + {{#unless this.isMultiFiltered}} +
  • +
  • + +
  • + {{/unless}} {{/if}} diff --git a/ghost/admin/tests/acceptance/members-test.js b/ghost/admin/tests/acceptance/members-test.js index 778d29a33b..b59f9514ca 100644 --- a/ghost/admin/tests/acceptance/members-test.js +++ b/ghost/admin/tests/acceptance/members-test.js @@ -143,6 +143,53 @@ describe('Acceptance: Members', function () { .to.equal('example@domain.com'); }); + /* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation. + * Delete this test once we have fixed the root NQL limitation. + * See https://linear.app/tryghost/issue/ONC-203 + */ + it('cannot bulk delete members if more than 1 filter is selected', async function () { + // Members with label + const labelOne = this.server.create('label'); + const labelTwo = this.server.create('label'); + this.server.createList('member', 2, {labels: [labelOne]}); + this.server.createList('member', 2, {labels: [labelOne, labelTwo]}); + + await visit('/members'); + expect(findAll('[data-test-member]').length).to.equal(4); + + // The delete button should not be visible by default + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // Apply a single filter + await click('[data-test-button="members-filter-actions"]'); + await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', 'label'); + await click('.gh-member-label-input input'); + await click(`[data-test-label-filter="${labelOne.name}"]`); + await click(`[data-test-button="members-apply-filter"]`); + + expect(findAll('[data-test-member]').length).to.equal(4); + expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D`); + + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.exist; + + // Apply a second filter + await click('[data-test-button="members-filter-actions"]'); + await click('[data-test-button="add-members-filter"]'); + + await fillIn('[data-test-members-filter="1"] [data-test-select="members-filter"]', 'label'); + await click('[data-test-members-filter="1"] .gh-member-label-input input'); + await click(`[data-test-members-filter="1"] [data-test-label-filter="${labelTwo.name}"]`); + await click(`[data-test-button="members-apply-filter"]`); + + expect(findAll('[data-test-member]').length).to.equal(2); + expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D%2Blabel%3A%5B${labelTwo.slug}%5D`); + + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + }); + it('can bulk delete members', async function () { // members to be kept this.server.createList('member', 6); @@ -167,7 +214,7 @@ describe('Acceptance: Members', function () { await click(`[data-test-button="members-apply-filter"]`); expect(findAll('[data-test-member]').length).to.equal(5); - expect(currentURL()).to.equal('/members?filter=label%3A%5Blabel-0%5D'); + expect(currentURL()).to.equal(`/members?filter=label%3A%5B${label.slug}%5D`); await click('[data-test-button="members-actions"]'); diff --git a/ghost/admin/tests/acceptance/members/filter-test.js b/ghost/admin/tests/acceptance/members/filter-test.js index cf9f3a1e49..3068b9a60e 100644 --- a/ghost/admin/tests/acceptance/members/filter-test.js +++ b/ghost/admin/tests/acceptance/members/filter-test.js @@ -1328,7 +1328,12 @@ describe('Acceptance: Members filtering', function () { expect(find('[data-test-button="add-label-selected"]'), 'add label to selected button').to.exist; expect(find('[data-test-button="remove-label-selected"]'), 'remove label from selected button').to.exist; expect(find('[data-test-button="unsubscribe-selected"]'), 'unsubscribe selected button').to.exist; - expect(find('[data-test-button="delete-selected"]'), 'delete selected button').to.exist; + + /* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation. + * Re-enable following line once we have fixed the root NQL limitation. + * See https://linear.app/tryghost/issue/ONC-203 + */ + // expect(find('[data-test-button="delete-selected"]'), 'delete selected button').to.exist; // filter is active and has # of filters expect(find('[data-test-button="members-filter-actions"] span'), 'filter button').to.have.class('gh-btn-label-green'); From a109b255f0311a09151aab06cde8db06813abab6 Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Mon, 29 Jul 2024 10:58:08 -0500 Subject: [PATCH 084/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20no=20redirect=20?= =?UTF-8?q?on=20Portal=20signin=20when=20trying=20to=20access=20newsletter?= =?UTF-8?q?s=20(#20683)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/ENG-1464 - added redirect to sign in page when trying to access newsletter management If a user tries to access newsletter management when not logged in, Portal requires sign in via magic link. This magic link didn't previous redirect the user back to newsletter management, requiring some extra clicks. --- .../src/components/pages/AccountEmailPage.js | 5 +++- .../components/pages/AccountEmailPage.test.js | 2 +- .../src/tests/EmailSubscriptionsFlow.test.js | 29 ------------------- 3 files changed, 5 insertions(+), 31 deletions(-) diff --git a/apps/portal/src/components/pages/AccountEmailPage.js b/apps/portal/src/components/pages/AccountEmailPage.js index b971c09440..ed31a676f4 100644 --- a/apps/portal/src/components/pages/AccountEmailPage.js +++ b/apps/portal/src/components/pages/AccountEmailPage.js @@ -9,7 +9,10 @@ export default function AccountEmailPage() { useEffect(() => { if (!member) { onAction('switchPage', { - page: 'signin' + page: 'signin', + pageData: { + redirect: window.location.href // This includes the search/fragment of the URL (#/portal/account) which is missing from the default referer header + } }); } }, [member, onAction]); diff --git a/apps/portal/src/components/pages/AccountEmailPage.test.js b/apps/portal/src/components/pages/AccountEmailPage.test.js index 87c5f8ea57..8bd6a2f082 100644 --- a/apps/portal/src/components/pages/AccountEmailPage.test.js +++ b/apps/portal/src/components/pages/AccountEmailPage.test.js @@ -112,6 +112,6 @@ describe('Account Email Page', () => { newsletters: newsletterData }); const {mockOnActionFn} = setup({site: siteData, member: null}); - expect(mockOnActionFn).toHaveBeenCalledWith('switchPage', {page: 'signin'}); + expect(mockOnActionFn).toHaveBeenCalledWith('switchPage', {page: 'signin', pageData: {redirect: window.location.href}}); }); }); diff --git a/apps/portal/src/tests/EmailSubscriptionsFlow.test.js b/apps/portal/src/tests/EmailSubscriptionsFlow.test.js index 960a240858..e77fb33c27 100644 --- a/apps/portal/src/tests/EmailSubscriptionsFlow.test.js +++ b/apps/portal/src/tests/EmailSubscriptionsFlow.test.js @@ -254,33 +254,4 @@ describe('Newsletter Subscriptions', () => { expect(newsletter2Toggle).toHaveClass('gh-portal-toggle-checked'); }); }); - - // describe('navigating straight to /portal/account/newsletters', () => { - // it('shows the newsletter management page when signed in', async () => { - // const {popupFrame, triggerButton, queryAllByText, popupIframeDocument} = await setup({ - // site: FixtureSite.singleTier.onlyFreePlanWithoutStripe, - // member: FixtureMember.subbedToNewsletter, - // newsletters: Newsletters - // }); - - // const manageSubscriptionsButton = within(popupIframeDocument).queryByRole('button', {name: 'Manage'}); - // await userEvent.click(manageSubscriptionsButton); - - // const newsletter1 = within(popupIframeDocument).queryAllByText('Newsletter 1'); - // expect(newsletter1).toBeInTheDocument(); - // }); - - // it('redirects to the sign in page when not signed in', async () => { - // const {popupFrame, queryByTitle, popupIframeDocument} = await setup({ - // site: FixtureSite.singleTier.onlyFreePlanWithoutStripe, - // member: FixtureMember.subbedToNewsletter, - // newsletters: Newsletters - // }, true); - - // // console.log(`popupFrame`, popupFrame); - // // console.log(`queryByTitle`, queryByTitle); - // // console.log(`popupIframeDocument`, popupIframeDocument); - - // }); - // }); }); From c61c42ce1dc64891854f5591a1796a2cd736452a Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Mon, 29 Jul 2024 11:19:28 -0500 Subject: [PATCH 085/164] =?UTF-8?q?=E2=9C=A8=20Improved=20performance=20lo?= =?UTF-8?q?ading=20posts=20&=20pages=20in=20admin=20(#20646)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref 8ea1dfb ref https://linear.app/tryghost/issue/ONC-111 * undid the reversion for the performance improvements * built upon new tests for the posts list functionality in admin, including right click actions * added tests for pages view in Admin This was reverted because it broke the Pages list view in Admin, which is a thin extension of the Posts functionality in admin (route & controller). That has been fixed and tests added. This was originally reverted because the changes to improve loading response times broke right click (bulk) actions in the posts list. This was not caught because it turned out we had near-zero test coverage of that part of the codebase. Test coverage has been expanded for the posts list, and while not comprehensive, is a much better place for us to be in. --- ghost/admin/app/components/gh-context-menu.js | 2 +- .../app/components/posts-list/context-menu.js | 39 +- .../admin/app/components/posts-list/list.hbs | 41 +- .../posts-list}/selection-list.js | 98 +- ghost/admin/app/controllers/pages.js | 2 +- ghost/admin/app/controllers/posts.js | 10 +- ghost/admin/app/routes/posts.js | 56 +- ghost/admin/app/templates/pages.hbs | 23 +- ghost/admin/app/templates/posts.hbs | 23 +- ghost/admin/mirage/config/pages.js | 1 - ghost/admin/mirage/config/posts.js | 25 +- ghost/admin/tests/acceptance/content-test.js | 1143 +++++++++-------- 12 files changed, 834 insertions(+), 629 deletions(-) rename ghost/admin/app/{utils => components/posts-list}/selection-list.js (67%) diff --git a/ghost/admin/app/components/gh-context-menu.js b/ghost/admin/app/components/gh-context-menu.js index 8b3de5a54b..205e488bb1 100644 --- a/ghost/admin/app/components/gh-context-menu.js +++ b/ghost/admin/app/components/gh-context-menu.js @@ -1,5 +1,5 @@ import Component from '@glimmer/component'; -import SelectionList from '../utils/selection-list'; +import SelectionList from './posts-list/selection-list'; import {action} from '@ember/object'; import {inject as service} from '@ember/service'; import {task} from 'ember-concurrency'; diff --git a/ghost/admin/app/components/posts-list/context-menu.js b/ghost/admin/app/components/posts-list/context-menu.js index f945cfda40..3eabecb4a1 100644 --- a/ghost/admin/app/components/posts-list/context-menu.js +++ b/ghost/admin/app/components/posts-list/context-menu.js @@ -216,11 +216,14 @@ export default class PostsContextMenu extends Component { yield this.performBulkDestroy(); this.notifications.showNotification(this.#getToastMessage('deleted'), {type: 'success'}); - const remainingModels = this.selectionList.infinityModel.content.filter((model) => { - return !deletedModels.includes(model); - }); - // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this - this.infinity.replace(this.selectionList.infinityModel, remainingModels); + for (const key in this.selectionList.infinityModel) { + const remainingModels = this.selectionList.infinityModel[key].content.filter((model) => { + return !deletedModels.includes(model); + }); + // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this + this.infinity.replace(this.selectionList.infinityModel[key], remainingModels); + } + this.selectionList.clearSelection({force: true}); return true; } @@ -247,9 +250,7 @@ export default class PostsContextMenu extends Component { } } - // Remove posts that no longer match the filter this.updateFilteredPosts(); - return true; } @@ -282,14 +283,16 @@ export default class PostsContextMenu extends Component { ] }); - const remainingModels = this.selectionList.infinityModel.content.filter((model) => { - if (!updatedModels.find(u => u.id === model.id)) { - return true; - } - return filterNql.queryJSON(model.serialize({includeId: true})); - }); - // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this - this.infinity.replace(this.selectionList.infinityModel, remainingModels); + for (const key in this.selectionList.infinityModel) { + const remainingModels = this.selectionList.infinityModel[key].content.filter((model) => { + if (!updatedModels.find(u => u.id === model.id)) { + return true; + } + return filterNql.queryJSON(model.serialize({includeId: true})); + }); + // Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this + this.infinity.replace(this.selectionList.infinityModel[key], remainingModels); + } this.selectionList.clearUnavailableItems(); } @@ -386,8 +389,10 @@ export default class PostsContextMenu extends Component { const data = result[this.type === 'post' ? 'posts' : 'pages'][0]; const model = this.store.peekRecord(this.type, data.id); - // Update infinity list - this.selectionList.infinityModel.content.unshiftObject(model); + // Update infinity draft posts content - copied posts are always drafts + if (this.selectionList.infinityModel.draftInfinityModel) { + this.selectionList.infinityModel.draftInfinityModel.content.unshiftObject(model); + } // Show notification this.notifications.showNotification(this.#getToastMessage('duplicated'), {type: 'success'}); diff --git a/ghost/admin/app/components/posts-list/list.hbs b/ghost/admin/app/components/posts-list/list.hbs index 4755c76d62..99e0dcf7d5 100644 --- a/ghost/admin/app/components/posts-list/list.hbs +++ b/ghost/admin/app/components/posts-list/list.hbs @@ -1,14 +1,39 @@ - {{#each @model as |post|}} - - - + {{!-- always order as scheduled, draft, remainder --}} + {{#if (or @model.scheduledInfinityModel (or @model.draftInfinityModel @model.publishedAndSentInfinityModel))}} + {{#if @model.scheduledInfinityModel}} + {{#each @model.scheduledInfinityModel as |post|}} + + + + {{/each}} + {{/if}} + {{#if (and @model.draftInfinityModel (or (not @model.scheduledInfinityModel) (and @model.scheduledInfinityModel @model.scheduledInfinityModel.reachedInfinity)))}} + {{#each @model.draftInfinityModel as |post|}} + + + + {{/each}} + {{/if}} + {{#if (and @model.publishedAndSentInfinityModel (and (or (not @model.scheduledInfinityModel) @model.scheduledInfinityModel.reachedInfinity) (or (not @model.draftInfinityModel) @model.draftInfinityModel.reachedInfinity)))}} + {{#each @model.publishedAndSentInfinityModel as |post|}} + + + + {{/each}} + {{/if}} {{else}} {{yield}} - {{/each}} + {{/if}} {{!-- The currently selected item or items are passed to the context menu --}} diff --git a/ghost/admin/app/utils/selection-list.js b/ghost/admin/app/components/posts-list/selection-list.js similarity index 67% rename from ghost/admin/app/utils/selection-list.js rename to ghost/admin/app/components/posts-list/selection-list.js index b409d6da4b..ec45475be0 100644 --- a/ghost/admin/app/utils/selection-list.js +++ b/ghost/admin/app/components/posts-list/selection-list.js @@ -18,7 +18,11 @@ export default class SelectionList { #clearOnNextUnfreeze = false; constructor(infinityModel) { - this.infinityModel = infinityModel ?? {content: []}; + this.infinityModel = infinityModel ?? { + draftInfinityModel: { + content: [] + } + }; } freeze() { @@ -41,7 +45,12 @@ export default class SelectionList { * Returns an NQL filter for all items, not the selection */ get allFilter() { - return this.infinityModel.extraParams?.filter ?? ''; + const models = this.infinityModel; + // grab filter from the first key in the infinityModel object (they should all be identical) + for (const key in models) { + return models[key].extraParams?.allFilter ?? ''; + } + return ''; } /** @@ -81,10 +90,13 @@ export default class SelectionList { * Keep in mind that when using CMD + A, we don't have all items in memory! */ get availableModels() { + const models = this.infinityModel; const arr = []; - for (const item of this.infinityModel.content) { - if (this.isSelected(item.id)) { - arr.push(item); + for (const key in models) { + for (const item of models[key].content) { + if (this.isSelected(item.id)) { + arr.push(item); + } } } return arr; @@ -102,7 +114,13 @@ export default class SelectionList { if (!this.inverted) { return this.selectedIds.size; } - return Math.max((this.infinityModel.meta?.pagination?.total ?? 0) - this.selectedIds.size, 1); + + const models = this.infinityModel; + let total; + for (const key in models) { + total += models[key].meta?.pagination?.total; + } + return Math.max((total ?? 0) - this.selectedIds.size, 1); } isSelected(id) { @@ -147,9 +165,12 @@ export default class SelectionList { clearUnavailableItems() { const newSelection = new Set(); - for (const item of this.infinityModel.content) { - if (this.selectedIds.has(item.id)) { - newSelection.add(item.id); + const models = this.infinityModel; + for (const key in models) { + for (const item of models[key].content) { + if (this.selectedIds.has(item.id)) { + newSelection.add(item.id); + } } } this.selectedIds = newSelection; @@ -181,37 +202,40 @@ export default class SelectionList { // todo let running = false; - for (const item of this.infinityModel.content) { - // Exlusing the last selected item - if (item.id === this.lastSelectedId || item.id === id) { - if (!running) { - running = true; + const models = this.infinityModel; + for (const key in models) { + for (const item of this.models[key].content) { + // Exlusing the last selected item + if (item.id === this.lastSelectedId || item.id === id) { + if (!running) { + running = true; - // Skip last selected on its own - if (item.id === this.lastSelectedId) { - continue; - } - } else { - // Still include id - if (item.id === id) { - this.lastShiftSelectionGroup.add(item.id); - - if (this.inverted) { - this.selectedIds.delete(item.id); - } else { - this.selectedIds.add(item.id); + // Skip last selected on its own + if (item.id === this.lastSelectedId) { + continue; } - } - break; - } - } + } else { + // Still include id + if (item.id === id) { + this.lastShiftSelectionGroup.add(item.id); - if (running) { - this.lastShiftSelectionGroup.add(item.id); - if (this.inverted) { - this.selectedIds.delete(item.id); - } else { - this.selectedIds.add(item.id); + if (this.inverted) { + this.selectedIds.delete(item.id); + } else { + this.selectedIds.add(item.id); + } + } + break; + } + } + + if (running) { + this.lastShiftSelectionGroup.add(item.id); + if (this.inverted) { + this.selectedIds.delete(item.id); + } else { + this.selectedIds.add(item.id); + } } } } diff --git a/ghost/admin/app/controllers/pages.js b/ghost/admin/app/controllers/pages.js index 6a011d1564..cd62cc80b5 100644 --- a/ghost/admin/app/controllers/pages.js +++ b/ghost/admin/app/controllers/pages.js @@ -40,4 +40,4 @@ export default class PagesController extends PostsController { openEditor(page) { this.router.transitionTo('lexical-editor.edit', 'page', page.get('id')); } -} +} \ No newline at end of file diff --git a/ghost/admin/app/controllers/posts.js b/ghost/admin/app/controllers/posts.js index 014cad0f47..8f48fca2db 100644 --- a/ghost/admin/app/controllers/posts.js +++ b/ghost/admin/app/controllers/posts.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -import SelectionList from 'ghost-admin/utils/selection-list'; +import SelectionList from 'ghost-admin/components/posts-list/selection-list'; import {DEFAULT_QUERY_PARAMS} from 'ghost-admin/helpers/reset-query-params'; import {action} from '@ember/object'; import {inject} from 'ghost-admin/decorators/inject'; @@ -85,14 +85,6 @@ export default class PostsController extends Controller { Object.assign(this, DEFAULT_QUERY_PARAMS.posts); } - get postsInfinityModel() { - return this.model; - } - - get totalPosts() { - return this.model.meta?.pagination?.total ?? 0; - } - get showingAll() { const {type, author, tag, visibility} = this; diff --git a/ghost/admin/app/routes/posts.js b/ghost/admin/app/routes/posts.js index 93e7d5d4ab..a1c11aac27 100644 --- a/ghost/admin/app/routes/posts.js +++ b/ghost/admin/app/routes/posts.js @@ -1,4 +1,5 @@ import AuthenticatedRoute from 'ghost-admin/routes/authenticated'; +import RSVP from 'rsvp'; import {action} from '@ember/object'; import {assign} from '@ember/polyfills'; import {isBlank} from '@ember/utils'; @@ -39,43 +40,54 @@ export default class PostsRoute extends AuthenticatedRoute { model(params) { const user = this.session.user; - let queryParams = {}; let filterParams = {tag: params.tag, visibility: params.visibility}; let paginationParams = { perPageParam: 'limit', totalPagesParam: 'meta.pagination.pages' }; - + + // type filters are actually mapping statuses assign(filterParams, this._getTypeFilters(params.type)); - + if (params.type === 'featured') { filterParams.featured = true; } - + + // authors and contributors can only view their own posts if (user.isAuthor) { - // authors can only view their own posts filterParams.authors = user.slug; } else if (user.isContributor) { - // Contributors can only view their own draft posts filterParams.authors = user.slug; - // filterParams.status = 'draft'; + // otherwise we need to filter by author if present } else if (params.author) { filterParams.authors = params.author; } - - let filter = this._filterString(filterParams); - if (!isBlank(filter)) { - queryParams.filter = filter; - } - - if (!isBlank(params.order)) { - queryParams.order = params.order; - } - + let perPage = this.perPage; - let paginationSettings = assign({perPage, startingPage: 1}, paginationParams, queryParams); + + const filterStatuses = filterParams.status; + let queryParams = {allFilter: this._filterString({...filterParams})}; // pass along the parent filter so it's easier to apply the params filter to each infinity model + let models = {}; - return this.infinity.model(this.modelName, paginationSettings); + if (filterStatuses.includes('scheduled')) { + let scheduledInfinityModelParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: 'scheduled'})}; + models.scheduledInfinityModel = this.infinity.model(this.modelName, assign({perPage, startingPage: 1}, paginationParams, scheduledInfinityModelParams)); + } + if (filterStatuses.includes('draft')) { + let draftInfinityModelParams = {...queryParams, order: params.order || 'updated_at desc', filter: this._filterString({...filterParams, status: 'draft'})}; + models.draftInfinityModel = this.infinity.model(this.modelName, assign({perPage, startingPage: 1}, paginationParams, draftInfinityModelParams)); + } + if (filterStatuses.includes('published') || filterStatuses.includes('sent')) { + let publishedAndSentInfinityModelParams; + if (filterStatuses.includes('published') && filterStatuses.includes('sent')) { + publishedAndSentInfinityModelParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: '[published,sent]'})}; + } else { + publishedAndSentInfinityModelParams = {...queryParams, order: params.order || 'published_at desc', filter: this._filterString({...filterParams, status: filterStatuses.includes('published') ? 'published' : 'sent'})}; + } + models.publishedAndSentInfinityModel = this.infinity.model(this.modelName, assign({perPage, startingPage: 1}, paginationParams, publishedAndSentInfinityModelParams)); + } + + return RSVP.hash(models); } // trigger a background load of all tags and authors for use in filter dropdowns @@ -120,6 +132,12 @@ export default class PostsRoute extends AuthenticatedRoute { }; } + /** + * Returns an object containing the status filter based on the given type. + * + * @param {string} type - The type of filter to generate (draft, published, scheduled, sent). + * @returns {Object} - An object containing the status filter. + */ _getTypeFilters(type) { let status = '[draft,scheduled,published,sent]'; diff --git a/ghost/admin/app/templates/pages.hbs b/ghost/admin/app/templates/pages.hbs index 723ebf8c17..7dba875970 100644 --- a/ghost/admin/app/templates/pages.hbs +++ b/ghost/admin/app/templates/pages.hbs @@ -28,7 +28,7 @@
  • @@ -41,7 +41,7 @@ {{else}}

    No pages match the current filter

    - + Show all pages {{/if}} @@ -49,11 +49,26 @@
  • + {{!-- only show one infinity loader wheel at a time - always order as scheduled, draft, remainder --}} + {{#if @model.scheduledInfinityModel}} -
    + {{/if}} + {{#if (and @model.draftInfinityModel (or (not @model.scheduledInfinityModel) (and @model.scheduledInfinityModel @model.scheduledInfinityModel.reachedInfinity)))}} + + {{/if}} + {{#if (and @model.publishedAndSentInfinityModel (and (or (not @model.scheduledInfinityModel) @model.scheduledInfinityModel.reachedInfinity) (or (not @model.draftInfinityModel) @model.draftInfinityModel.reachedInfinity)))}} + + {{/if}} +
    {{outlet}}
    diff --git a/ghost/admin/app/templates/posts.hbs b/ghost/admin/app/templates/posts.hbs index f0d0b6bbe8..3d99d9ee66 100644 --- a/ghost/admin/app/templates/posts.hbs +++ b/ghost/admin/app/templates/posts.hbs @@ -30,7 +30,7 @@
  • @@ -43,7 +43,7 @@ {{else}}

    No posts match the current filter

    - + Show all posts {{/if}} @@ -51,11 +51,26 @@
  • + {{!-- only show one infinity loader wheel at a time - always order as scheduled, draft, remainder --}} + {{#if @model.scheduledInfinityModel}} -
    + {{/if}} + {{#if (and @model.draftInfinityModel (or (not @model.scheduledInfinityModel) (and @model.scheduledInfinityModel @model.scheduledInfinityModel.reachedInfinity)))}} + + {{/if}} + {{#if (and @model.publishedAndSentInfinityModel (and (or (not @model.scheduledInfinityModel) @model.scheduledInfinityModel.reachedInfinity) (or (not @model.draftInfinityModel) @model.draftInfinityModel.reachedInfinity)))}} + + {{/if}} +
    {{outlet}}
    diff --git a/ghost/admin/mirage/config/pages.js b/ghost/admin/mirage/config/pages.js index 9ab0162c06..6faeb2b44a 100644 --- a/ghost/admin/mirage/config/pages.js +++ b/ghost/admin/mirage/config/pages.js @@ -37,7 +37,6 @@ export default function mockPages(server) { return pages.create(attrs); }); - // TODO: handle authors filter server.get('/pages/', function ({pages}, {queryParams}) { let {filter, page, limit} = queryParams; diff --git a/ghost/admin/mirage/config/posts.js b/ghost/admin/mirage/config/posts.js index a12863bfe7..2836e0613d 100644 --- a/ghost/admin/mirage/config/posts.js +++ b/ghost/admin/mirage/config/posts.js @@ -23,7 +23,6 @@ function extractTags(postAttrs, tags) { }); } -// TODO: handle authors filter export function getPosts({posts}, {queryParams}) { let {filter, page, limit} = queryParams; @@ -31,15 +30,27 @@ export function getPosts({posts}, {queryParams}) { limit = +limit || 15; let statusFilter = extractFilterParam('status', filter); + let authorsFilter = extractFilterParam('authors', filter); + let visibilityFilter = extractFilterParam('visibility', filter); let collection = posts.all().filter((post) => { let matchesStatus = true; + let matchesAuthors = true; + let matchesVisibility = true; if (!isEmpty(statusFilter)) { matchesStatus = statusFilter.includes(post.status); } - return matchesStatus; + if (!isEmpty(authorsFilter)) { + matchesAuthors = authorsFilter.includes(post.authors.models[0].slug); + } + + if (!isEmpty(visibilityFilter)) { + matchesVisibility = visibilityFilter.includes(post.visibility); + } + + return matchesStatus && matchesAuthors && matchesVisibility; }); return paginateModelCollection('posts', collection, page, limit); @@ -59,7 +70,6 @@ export default function mockPosts(server) { return posts.create(attrs); }); - // TODO: handle authors filter server.get('/posts/', getPosts); server.get('/posts/:id/', function ({posts}, {params}) { @@ -100,6 +110,13 @@ export default function mockPosts(server) { posts.find(ids).destroy(); }); + server.post('/posts/:id/copy/', function ({posts}, {params}) { + let post = posts.find(params.id); + let attrs = post.attrs; + + return posts.create(attrs); + }); + server.put('/posts/bulk/', function ({tags}, {requestBody}) { const bulk = JSON.parse(requestBody).bulk; const action = bulk.action; @@ -115,7 +132,7 @@ export default function mockPosts(server) { tags.create(tag); } }); - // TODO: update the actual posts in the mock db + // TODO: update the actual posts in the mock db if wanting to write tests where we navigate around (refresh model) // const postsToUpdate = posts.find(ids); // getting the posts is fine, but within this we CANNOT manipulate them (???) not even iterate with .forEach } diff --git a/ghost/admin/tests/acceptance/content-test.js b/ghost/admin/tests/acceptance/content-test.js index 5fbbf3f743..2690dfc655 100644 --- a/ghost/admin/tests/acceptance/content-test.js +++ b/ghost/admin/tests/acceptance/content-test.js @@ -17,7 +17,9 @@ const findButton = (text, buttons) => { return Array.from(buttons).find(button => button.innerText.trim() === text); }; -describe('Acceptance: Content', function () { +// NOTE: With accommodations for faster loading of posts in the UI, the requests to fetch the posts have been split into separate requests based +// on the status of the post. This means that the tests for filtering by status will have multiple requests to check against. +describe('Acceptance: Posts / Pages', function () { let hooks = setupApplicationTest(); setupMirage(hooks); @@ -25,550 +27,643 @@ describe('Acceptance: Content', function () { this.server.loadFixtures('configs'); }); - it('redirects to signin when not authenticated', async function () { - await invalidateSession(); - await visit('/posts'); - - expect(currentURL()).to.equal('/signin'); - }); - - describe('as admin', function () { - let admin, editor, publishedPost, scheduledPost, draftPost, authorPost; - - beforeEach(async function () { - let adminRole = this.server.create('role', {name: 'Administrator'}); - admin = this.server.create('user', {roles: [adminRole]}); - let editorRole = this.server.create('role', {name: 'Editor'}); - editor = this.server.create('user', {roles: [editorRole]}); - - publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post'}); - scheduledPost = this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'}); - // draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post', visibility: 'paid'}); - draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'}); - authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post', visibiity: 'paid'}); - - // pages shouldn't appear in the list - this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'}); - - return await authenticateSession(); + describe('posts', function () { + it('redirects to signin when not authenticated', async function () { + await invalidateSession(); + + await visit('/posts'); + expect(currentURL()).to.equal('/signin'); }); - describe('displays and filter posts', function () { - it('displays posts', async function () { - await visit('/posts'); + describe('as contributor', function () { + beforeEach(async function () { + let contributorRole = this.server.create('role', {name: 'Contributor'}); + this.server.create('user', {roles: [contributorRole]}); - const posts = findAll('[data-test-post-id]'); - // displays all posts by default (all statuses) [no pages] - expect(posts.length, 'all posts count').to.equal(4); - - // note: atm the mirage backend doesn't support ordering of the results set + return await authenticateSession(); }); - it('can filter by status', async function () { + // NOTE: This test seems to fail if run AFTER the 'can change access' test in the 'as admin' section; router seems to fail, did not look into it further + it('shows posts list and allows post creation', async function () { await visit('/posts'); - // show draft posts - await selectChoose('[data-test-type-select]', 'Draft posts'); + // has an empty state + expect(findAll('[data-test-post-id]')).to.have.length(0); + expect(find('[data-test-no-posts-box]')).to.exist; + expect(find('[data-test-link="write-a-new-post"]')).to.exist; + + await click('[data-test-link="write-a-new-post"]'); + + expect(currentURL()).to.equal('/editor/post'); + + await fillIn('[data-test-editor-title-input]', 'First contributor post'); + await blur('[data-test-editor-title-input]'); + + expect(currentURL()).to.equal('/editor/post/1'); + + await click('[data-test-link="posts"]'); + + expect(findAll('[data-test-post-id]')).to.have.length(1); + expect(find('[data-test-no-posts-box]')).to.not.exist; + }); + }); + + describe('as author', function () { + let author, authorPost; + + beforeEach(async function () { + let authorRole = this.server.create('role', {name: 'Author'}); + author = this.server.create('user', {roles: [authorRole]}); + let adminRole = this.server.create('role', {name: 'Administrator'}); + let admin = this.server.create('user', {roles: [adminRole]}); + + // create posts + authorPost = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'}); + this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'}); + + return await authenticateSession(); + }); + + it('only fetches the author\'s posts', async function () { + await visit('/posts'); + // trigger a filter request so we can grab the posts API request easily + await selectChoose('[data-test-type-select]', 'Published posts'); + + // API request includes author filter + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter).to.have.string(`authors:${author.slug}`); + + // only author's post is shown + expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1); + expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist; + }); + }); + + describe('as admin', function () { + let admin, editor, publishedPost, scheduledPost, draftPost, authorPost; + + beforeEach(async function () { + let adminRole = this.server.create('role', {name: 'Administrator'}); + admin = this.server.create('user', {roles: [adminRole]}); + let editorRole = this.server.create('role', {name: 'Editor'}); + editor = this.server.create('user', {roles: [editorRole]}); + + publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + scheduledPost = this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'}); + draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'}); + authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'}); + + // pages shouldn't appear in the list + this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'}); + + return await authenticateSession(); + }); + + describe('displays and filter posts', function () { + it('displays posts', async function () { + await visit('/posts'); + + const posts = findAll('[data-test-post-id]'); + // displays all posts by default (all statuses) [no pages] + expect(posts.length, 'all posts count').to.equal(4); + + // make sure display is scheduled > draft > published/sent + expect(posts[0].querySelector('.gh-content-entry-title').textContent, 'post 1 title').to.contain('Scheduled Post'); + expect(posts[1].querySelector('.gh-content-entry-title').textContent, 'post 2 title').to.contain('Draft Post'); + expect(posts[2].querySelector('.gh-content-entry-title').textContent, 'post 3 title').to.contain('Published Post'); + expect(posts[3].querySelector('.gh-content-entry-title').textContent, 'post 4 title').to.contain('Editor Published Post'); + + // check API requests + let lastRequests = this.server.pretender.handledRequests.filter(request => request.url.includes('/posts/')); + expect(lastRequests[0].queryParams.filter, 'scheduled request filter').to.have.string('status:scheduled'); + expect(lastRequests[1].queryParams.filter, 'drafts request filter').to.have.string('status:draft'); + expect(lastRequests[2].queryParams.filter, 'published request filter').to.have.string('status:[published,sent]'); + }); + + it('can filter by status', async function () { + await visit('/posts'); + + // show draft posts + await selectChoose('[data-test-type-select]', 'Draft posts'); + + // API request is correct + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, '"drafts" request status filter').to.have.string('status:draft'); + // Displays draft post + expect(findAll('[data-test-post-id]').length, 'drafts count').to.equal(1); + expect(find(`[data-test-post-id="${draftPost.id}"]`), 'draft post').to.exist; + + // show published posts + await selectChoose('[data-test-type-select]', 'Published posts'); + + // API request is correct + [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, '"published" request status filter').to.have.string('status:published'); + // Displays three published posts + pages + expect(findAll('[data-test-post-id]').length, 'published count').to.equal(2); + expect(find(`[data-test-post-id="${publishedPost.id}"]`), 'admin published post').to.exist; + expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author published post').to.exist; + + // show scheduled posts + await selectChoose('[data-test-type-select]', 'Scheduled posts'); + + // API request is correct + [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, '"scheduled" request status filter').to.have.string('status:scheduled'); + // Displays scheduled post + expect(findAll('[data-test-post-id]').length, 'scheduled count').to.equal(1); + expect(find(`[data-test-post-id="${scheduledPost.id}"]`), 'scheduled post').to.exist; + }); + + it('can filter by author', async function () { + await visit('/posts'); + + // show all posts by editor + await selectChoose('[data-test-author-select]', editor.name); + + // API request is correct + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.allFilter, '"editor" request status filter') + .to.have.string('status:[draft,scheduled,published,sent]'); + expect(lastRequest.queryParams.allFilter, '"editor" request filter param') + .to.have.string(`authors:${editor.slug}`); + + // Displays editor post + expect(findAll('[data-test-post-id]').length, 'editor count').to.equal(1); + }); + + it('can filter by visibility', async function () { + await visit('/posts'); + + await selectChoose('[data-test-visibility-select]', 'Paid members-only'); + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.allFilter, '"visibility" request filter param') + .to.have.string('visibility:[paid,tiers]'); + let posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(1); + + await selectChoose('[data-test-visibility-select]', 'Public'); + [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.allFilter, '"visibility" request filter param') + .to.have.string('visibility:public'); + posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(3); + }); + + it('can filter by tag', async function () { + this.server.create('tag', {name: 'B - Second', slug: 'second'}); + this.server.create('tag', {name: 'Z - Last', slug: 'last'}); + this.server.create('tag', {name: 'A - First', slug: 'first'}); + + await visit('/posts'); + await clickTrigger('[data-test-tag-select]'); + + let options = findAll('.ember-power-select-option'); + + // check that dropdown sorts alphabetically + expect(options[0].textContent.trim()).to.equal('All tags'); + expect(options[1].textContent.trim()).to.equal('A - First'); + expect(options[2].textContent.trim()).to.equal('B - Second'); + expect(options[3].textContent.trim()).to.equal('Z - Last'); + + // select one + await selectChoose('[data-test-tag-select]', 'B - Second'); + // affirm request + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.allFilter, '"tag" request filter param').to.have.string('tag:second'); + }); + }); + + describe('context menu actions', function () { + describe('single post', function () { + it('can duplicate a post', async function () { + await visit('/posts'); + + // get the post + const post = find(`[data-test-post-id="${publishedPost.id}"]`); + expect(post, 'post').to.exist; + + await triggerEvent(post, 'contextmenu'); + + let contextMenu = find('.gh-posts-context-menu'); // this is a
      element + + let buttons = contextMenu.querySelectorAll('button'); + + expect(contextMenu, 'context menu').to.exist; + expect(buttons.length, 'context menu buttons').to.equal(5); + expect(buttons[0].innerText.trim(), 'context menu button 1').to.contain('Unpublish'); + expect(buttons[1].innerText.trim(), 'context menu button 2').to.contain('Feature'); // or Unfeature + expect(buttons[2].innerText.trim(), 'context menu button 3').to.contain('Add a tag'); + expect(buttons[3].innerText.trim(), 'context menu button 4').to.contain('Duplicate'); + expect(buttons[4].innerText.trim(), 'context menu button 5').to.contain('Delete'); + + // duplicate the post + await click(buttons[3]); + + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(5); + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.url, 'request url').to.match(new RegExp(`/posts/${publishedPost.id}/copy/`)); + }); + }); + + describe('multiple posts', function () { + it('can feature and unfeature', async function () { + await visit('/posts'); + + // get all posts + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(4); + + const postThreeContainer = posts[2].parentElement; // draft post + const postFourContainer = posts[3].parentElement; // published post + + await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + + expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; + expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + + // NOTE: right clicks don't seem to work in these tests + // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event + await triggerEvent(postFourContainer, 'contextmenu'); + + let contextMenu = find('.gh-posts-context-menu'); // this is a
        element + expect(contextMenu, 'context menu').to.exist; + + // feature the post + let buttons = contextMenu.querySelectorAll('button'); + let featureButton = findButton('Feature', buttons); + expect(featureButton, 'feature button').to.exist; + await click(featureButton); + + // API request is correct - note, we don't mock the actual model updates + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, 'feature request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(JSON.parse(lastRequest.requestBody).bulk.action, 'feature request action').to.equal('feature'); + + // ensure ui shows these are now featured + expect(postThreeContainer.querySelector('.gh-featured-post'), 'postFour featured').to.exist; + expect(postFourContainer.querySelector('.gh-featured-post'), 'postFour featured').to.exist; + + // unfeature the posts + await triggerEvent(postFourContainer, 'contextmenu'); + + contextMenu = find('.gh-posts-context-menu'); // this is a
          element + expect(contextMenu, 'context menu').to.exist; + + // unfeature the posts + buttons = contextMenu.querySelectorAll('button'); + featureButton = findButton('Unfeature', buttons); + expect(featureButton, 'unfeature button').to.exist; + await click(featureButton); + + // API request is correct - note, we don't mock the actual model updates + [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, 'unfeature request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unfeature request action').to.equal('unfeature'); + + // ensure ui shows these are now unfeatured + expect(postThreeContainer.querySelector('.gh-featured-post'), 'postFour featured').to.not.exist; + expect(postFourContainer.querySelector('.gh-featured-post'), 'postFour featured').to.not.exist; + }); + + it('can add a tag', async function () { + await visit('/posts'); + + // get all posts + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(4); + + const postThreeContainer = posts[2].parentElement; // draft post + const postFourContainer = posts[3].parentElement; // published post + + await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + + expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; + expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + + // NOTE: right clicks don't seem to work in these tests + // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event + await triggerEvent(postFourContainer, 'contextmenu'); + + let contextMenu = find('.gh-posts-context-menu'); // this is a
            element + expect(contextMenu, 'context menu').to.exist; + + // add a tag to the posts + let buttons = contextMenu.querySelectorAll('button'); + let addTagButton = findButton('Add a tag', buttons); + expect(addTagButton, 'add tag button').to.exist; + await click(addTagButton); + + const addTagsModal = find('[data-test-modal="add-tags"]'); + expect(addTagsModal, 'tag settings modal').to.exist; + + const input = addTagsModal.querySelector('input'); + expect(input, 'tag input').to.exist; + await fillIn(input, 'test-tag'); + await triggerKeyEvent(input, 'keydown', 13); + await click('[data-test-button="confirm"]'); + + // API request is correct - note, we don't mock the actual model updates + let [lastRequest] = this.server.pretender.handledRequests.slice(-2); + expect(lastRequest.queryParams.filter, 'add tag request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(JSON.parse(lastRequest.requestBody).bulk.action, 'add tag request action').to.equal('addTag'); + }); + + // TODO: Skip for now. This causes the member creation test to fail ('New member' text doesn't show... ???). + it.skip('can change access', async function () { + await visit('/posts'); + + // get all posts + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(4); + + const postThreeContainer = posts[2].parentElement; // draft post + const postFourContainer = posts[3].parentElement; // published post + + await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + + expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; + expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + + await triggerEvent(postFourContainer, 'contextmenu'); + let contextMenu = find('.gh-posts-context-menu'); // this is a
              element + expect(contextMenu, 'context menu').to.exist; + let buttons = contextMenu.querySelectorAll('button'); + let changeAccessButton = findButton('Change access', buttons); + + expect(changeAccessButton, 'change access button').not.to.exist; + + const settingsService = this.owner.lookup('service:settings'); + await settingsService.set('membersEnabled', true); + + await triggerEvent(postFourContainer, 'contextmenu'); + contextMenu = find('.gh-posts-context-menu'); // this is a
                element + expect(contextMenu, 'context menu').to.exist; + buttons = contextMenu.querySelectorAll('button'); + changeAccessButton = findButton('Change access', buttons); + + expect(changeAccessButton, 'change access button').to.exist; + await click(changeAccessButton); + + const changeAccessModal = find('[data-test-modal="edit-posts-access"]'); + const selectElement = changeAccessModal.querySelector('select'); + await fillIn(selectElement, 'members'); + await click('[data-test-button="confirm"]'); + + // check API request + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, 'change access request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(JSON.parse(lastRequest.requestBody).bulk.action, 'change access request action').to.equal('access'); + }); + + it('can unpublish', async function () { + await visit('/posts'); + + // get all posts + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(4); + + const postThreeContainer = posts[2].parentElement; // draft post + const postFourContainer = posts[3].parentElement; // published post + + await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + + expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; + expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + + // NOTE: right clicks don't seem to work in these tests + // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event + await triggerEvent(postFourContainer, 'contextmenu'); + + let contextMenu = find('.gh-posts-context-menu'); // this is a
                  element + expect(contextMenu, 'context menu').to.exist; + + // unpublish the posts + let buttons = contextMenu.querySelectorAll('button'); + let unpublishButton = findButton('Unpublish', buttons); + expect(unpublishButton, 'unpublish button').to.exist; + await click(unpublishButton); + + // handle modal + const modal = find('[data-test-modal="unpublish-posts"]'); + expect(modal, 'unpublish modal').to.exist; + await click('[data-test-button="confirm"]'); + + // API request is correct - note, we don't mock the actual model updates + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, 'unpublish request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unpublish request action').to.equal('unpublish'); + + // ensure ui shows these are now unpublished + expect(postThreeContainer.querySelector('.gh-content-entry-status').textContent, 'postThree status').to.contain('Draft'); + expect(postFourContainer.querySelector('.gh-content-entry-status').textContent, 'postThree status').to.contain('Draft'); + }); + + it('can delete', async function () { + await visit('/posts'); + + // get all posts + const posts = findAll('[data-test-post-id]'); + expect(posts.length, 'all posts count').to.equal(4); + + const postThreeContainer = posts[2].parentElement; // draft post + const postFourContainer = posts[3].parentElement; // published post + + await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); + + expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; + expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; + + // NOTE: right clicks don't seem to work in these tests + // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event + await triggerEvent(postFourContainer, 'contextmenu'); + + let contextMenu = find('.gh-posts-context-menu'); // this is a
                    element + expect(contextMenu, 'context menu').to.exist; + + // delete the posts + let buttons = contextMenu.querySelectorAll('button'); + let deleteButton = findButton('Delete', buttons); + expect(deleteButton, 'delete button').to.exist; + await click(deleteButton); + + // handle modal + const modal = find('[data-test-modal="delete-posts"]'); + expect(modal, 'delete modal').to.exist; + await click('[data-test-button="confirm"]'); + + // API request is correct - note, we don't mock the actual model updates + let [lastRequest] = this.server.pretender.handledRequests.slice(-1); + expect(lastRequest.queryParams.filter, 'delete request id').to.equal(`id:['${publishedPost.id}','${authorPost.id}']`); + expect(lastRequest.method, 'delete request method').to.equal('DELETE'); + + // ensure ui shows these are now deleted + expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(2); + }); + }); + }); + + it('can add and edit custom views', async function () { + // actions are not visible when there's no filter + await visit('/posts'); + expect(find('[data-test-button="edit-view"]'), 'edit-view button (no filter)').to.not.exist; + expect(find('[data-test-button="add-view"]'), 'add-view button (no filter)').to.not.exist; + + // add action is visible after filtering to a non-default filter + await selectChoose('[data-test-author-select]', admin.name); + expect(find('[data-test-button="add-view"]'), 'add-view button (with filter)').to.exist; + + // adding view shows it in the sidebar + await click('[data-test-button="add-view"]'), 'add-view button'; + expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on add)').to.exist; + expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('New view'); + await fillIn('[data-test-input="custom-view-name"]', 'Test view'); + await click('[data-test-button="save-custom-view"]'); + // modal closes on save + expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after add save)').to.not.exist; + // UI updates + expect(find('[data-test-nav-custom="posts-Test view"]'), 'new view nav').to.exist; + expect(find('[data-test-nav-custom="posts-Test view"]').textContent.trim()).to.equal('Test view'); + expect(find('[data-test-button="add-view"]'), 'add-view button (on existing view)').to.not.exist; + expect(find('[data-test-button="edit-view"]'), 'edit-view button (on existing view)').to.exist; + + // editing view + await click('[data-test-button="edit-view"]'), 'edit-view button'; + expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on edit)').to.exist; + expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('Edit view'); + await fillIn('[data-test-input="custom-view-name"]', 'Updated view'); + await click('[data-test-button="save-custom-view"]'); + // modal closes on save + expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after edit save)').to.not.exist; + // UI updates + expect(find('[data-test-nav-custom="posts-Updated view"]')).to.exist; + expect(find('[data-test-nav-custom="posts-Updated view"]').textContent.trim()).to.equal('Updated view'); + expect(find('[data-test-button="add-view"]'), 'add-view button (after edit)').to.not.exist; + expect(find('[data-test-button="edit-view"]'), 'edit-view button (after edit)').to.exist; + }); + + it('can navigate to custom views', async function () { + this.server.create('setting', { + group: 'site', + key: 'shared_views', + value: JSON.stringify([{ + route: 'posts', + name: 'My posts', + filter: { + author: admin.slug + } + }]) + }); + + await visit('/posts'); + + // nav bar contains default + custom views + expect(find('[data-test-nav-custom="posts-Drafts"]')).to.exist; + expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.exist; + expect(find('[data-test-nav-custom="posts-Published"]')).to.exist; + expect(find('[data-test-nav-custom="posts-My posts"]')).to.exist; + + // screen has default title and sidebar is showing inactive custom view + expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts'); + expect(find('[data-test-nav="posts"]')).to.have.class('active'); + + // clicking sidebar custom view link works + await click('[data-test-nav-custom="posts-Scheduled"]'); + expect(currentURL()).to.equal('/posts?type=scheduled'); + expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/); + expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active'); + + // clicking the main posts link resets + await click('[data-test-nav="posts"]'); + expect(currentURL()).to.equal('/posts'); + expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts'); + expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.not.have.class('active'); + + // changing a filter to match a custom view shows custom view + await selectChoose('[data-test-type-select]', 'Scheduled posts'); + expect(currentURL()).to.equal('/posts?type=scheduled'); + expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active'); + expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/); + }); + }); + }); + + // NOTE: Because the pages list is (at this point in time) a thin extension of the posts list, we should not need to duplicate all of the tests. + // The main difference is that we fetch pages, not posts. + // IF we implement any kind of functionality that *is* specific to a post or page and differentiate these models further, we will need to add tests then. + describe('pages', function () { + describe('as admin', function () { + let admin, editor; + + beforeEach(async function () { + let adminRole = this.server.create('role', {name: 'Administrator'}); + admin = this.server.create('user', {roles: [adminRole]}); + let editorRole = this.server.create('role', {name: 'Editor'}); + editor = this.server.create('user', {roles: [editorRole]}); + + // posts shouldn't show in the pages list + // TODO: figure out why we need post counts to be >= page count for mirage to work right + this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post', visibility: 'paid'}); + + this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'}); + this.server.create('page', {authors: [editor], status: 'published', title: 'Editor Published Page'}); + this.server.create('page', {authors: [admin], status: 'draft', title: 'Draft Page'}); + this.server.create('page', {authors: [admin], status: 'scheduled', title: 'Scheduled Page'}); + + return await authenticateSession(); + }); + + it('can view pages', async function () { + await visit('/pages'); + + const pages = findAll('[data-test-post-id]'); + // displays all pages by default (all statuses) + expect(pages.length, 'all pages count').to.equal(4); + }); + + it('can filter pages', async function () { + await visit('/pages'); + + // show draft pages + await selectChoose('[data-test-type-select]', 'Draft pages'); // API request is correct let [lastRequest] = this.server.pretender.handledRequests.slice(-1); expect(lastRequest.queryParams.filter, '"drafts" request status filter').to.have.string('status:draft'); - // Displays draft post + // Displays draft page expect(findAll('[data-test-post-id]').length, 'drafts count').to.equal(1); - expect(find(`[data-test-post-id="${draftPost.id}"]`), 'draft post').to.exist; - - // show published posts - await selectChoose('[data-test-type-select]', 'Published posts'); - + expect(find('[data-test-post-id="3"]'), 'draft page').to.exist; + + // show published pages + await selectChoose('[data-test-type-select]', 'Published pages'); + // API request is correct [lastRequest] = this.server.pretender.handledRequests.slice(-1); expect(lastRequest.queryParams.filter, '"published" request status filter').to.have.string('status:published'); - // Displays three published posts + pages + // Displays two published pages expect(findAll('[data-test-post-id]').length, 'published count').to.equal(2); - expect(find(`[data-test-post-id="${publishedPost.id}"]`), 'admin published post').to.exist; - expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author published post').to.exist; - - // show scheduled posts - await selectChoose('[data-test-type-select]', 'Scheduled posts'); - + expect(find('[data-test-post-id="1"]'), 'admin published page').to.exist; + expect(find('[data-test-post-id="2"]'), 'editor published page').to.exist; + + // show scheduled pages + await selectChoose('[data-test-type-select]', 'Scheduled pages'); + // API request is correct [lastRequest] = this.server.pretender.handledRequests.slice(-1); expect(lastRequest.queryParams.filter, '"scheduled" request status filter').to.have.string('status:scheduled'); - // Displays scheduled post + // Displays scheduled page expect(findAll('[data-test-post-id]').length, 'scheduled count').to.equal(1); - expect(find(`[data-test-post-id="${scheduledPost.id}"]`), 'scheduled post').to.exist; - - // show all posts - await selectChoose('[data-test-type-select]', 'All posts'); - - // API request is correct - [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:[draft,scheduled,published,sent]'); + expect(find('[data-test-post-id="4"]'), 'scheduled page').to.exist; }); - - it('can filter by author', async function () { - await visit('/posts'); - - // show all posts by editor - await selectChoose('[data-test-author-select]', editor.name); - - // API request is correct - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, '"editor" request status filter') - .to.have.string('status:[draft,scheduled,published,sent]'); - expect(lastRequest.queryParams.filter, '"editor" request filter param') - .to.have.string(`authors:${editor.slug}`); - }); - - it('can filter by visibility', async function () { - await visit('/posts'); - - await selectChoose('[data-test-visibility-select]', 'Paid members-only'); - - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, '"visibility" request filter param') - .to.have.string('visibility:[paid,tiers]+status:[draft,scheduled,published,sent]'); - }); - - it('can filter by tag', async function () { - this.server.create('tag', {name: 'B - Second', slug: 'second'}); - this.server.create('tag', {name: 'Z - Last', slug: 'last'}); - this.server.create('tag', {name: 'A - First', slug: 'first'}); - - await visit('/posts'); - await clickTrigger('[data-test-tag-select]'); - - let options = findAll('.ember-power-select-option'); - - // check that dropdown sorts alphabetically - expect(options[0].textContent.trim()).to.equal('All tags'); - expect(options[1].textContent.trim()).to.equal('A - First'); - expect(options[2].textContent.trim()).to.equal('B - Second'); - expect(options[3].textContent.trim()).to.equal('Z - Last'); - - // select one - await selectChoose('[data-test-tag-select]', 'B - Second'); - // affirm request - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'request filter').to.have.string('tag:second'); - }); - }); - - describe('context menu actions', function () { - describe('single post', function () { - // has a duplicate option - it.skip('can duplicate a post', async function () { - await visit('/posts'); - - // get the post - const post = find(`[data-test-post-id="${publishedPost.id}"]`); - expect(post, 'post').to.exist; - - await triggerEvent(post, 'contextmenu'); - // await this.pauseTest(); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                      element - - let buttons = contextMenu.querySelectorAll('button'); - - // should have three options for a published post - expect(contextMenu, 'context menu').to.exist; - expect(buttons.length, 'context menu buttons').to.equal(5); - expect(buttons[0].innerText.trim(), 'context menu button 1').to.contain('Unpublish'); - expect(buttons[1].innerText.trim(), 'context menu button 2').to.contain('Feature'); // or Unfeature - expect(buttons[2].innerText.trim(), 'context menu button 3').to.contain('Add a tag'); - expect(buttons[3].innerText.trim(), 'context menu button 4').to.contain('Duplicate'); - expect(buttons[4].innerText.trim(), 'context menu button 5').to.contain('Delete'); - - // duplicate the post - await click(buttons[3]); - - // API request is correct - // POST /ghost/api/admin/posts/{id}/copy/?formats=mobiledoc,lexical - - // TODO: probably missing endpoint in mirage... - - // let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - // console.log(`lastRequest`, lastRequest); - // expect(lastRequest.url, 'request url').to.match(new RegExp(`/posts/${publishedPost.id}/copy/`)); - }); - }); - - describe('multiple posts', function () { - it('can feature and unfeature posts', async function () { - await visit('/posts'); - - // get all posts - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(4); - - const postThreeContainer = posts[2].parentElement; // draft post - const postFourContainer = posts[3].parentElement; // published post - - await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - - expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; - expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; - - // NOTE: right clicks don't seem to work in these tests - // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event - await triggerEvent(postFourContainer, 'contextmenu'); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                        element - expect(contextMenu, 'context menu').to.exist; - - // feature the post - let buttons = contextMenu.querySelectorAll('button'); - let featureButton = findButton('Feature', buttons); - expect(featureButton, 'feature button').to.exist; - await click(featureButton); - - // API request is correct - note, we don't mock the actual model updates - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'feature request id').to.equal(`id:['3','4']`); - expect(JSON.parse(lastRequest.requestBody).bulk.action, 'feature request action').to.equal('feature'); - - // ensure ui shows these are now featured - expect(postThreeContainer.querySelector('.gh-featured-post'), 'postFour featured').to.exist; - expect(postFourContainer.querySelector('.gh-featured-post'), 'postFour featured').to.exist; - - // unfeature the posts - await triggerEvent(postFourContainer, 'contextmenu'); - - contextMenu = find('.gh-posts-context-menu'); // this is a
                          element - expect(contextMenu, 'context menu').to.exist; - - // unfeature the posts - buttons = contextMenu.querySelectorAll('button'); - featureButton = findButton('Unfeature', buttons); - expect(featureButton, 'unfeature button').to.exist; - await click(featureButton); - - // API request is correct - note, we don't mock the actual model updates - [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'unfeature request id').to.equal(`id:['3','4']`); - expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unfeature request action').to.equal('unfeature'); - - // ensure ui shows these are now unfeatured - expect(postThreeContainer.querySelector('.gh-featured-post'), 'postFour featured').to.not.exist; - expect(postFourContainer.querySelector('.gh-featured-post'), 'postFour featured').to.not.exist; - }); - - it('can add a tag to multiple posts', async function () { - await visit('/posts'); - - // get all posts - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(4); - - const postThreeContainer = posts[2].parentElement; // draft post - const postFourContainer = posts[3].parentElement; // published post - - await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - - expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; - expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; - - // NOTE: right clicks don't seem to work in these tests - // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event - await triggerEvent(postFourContainer, 'contextmenu'); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                            element - expect(contextMenu, 'context menu').to.exist; - - // add a tag to the posts - let buttons = contextMenu.querySelectorAll('button'); - let addTagButton = findButton('Add a tag', buttons); - expect(addTagButton, 'add tag button').to.exist; - await click(addTagButton); - - const addTagsModal = find('[data-test-modal="add-tags"]'); - expect(addTagsModal, 'tag settings modal').to.exist; - - const input = addTagsModal.querySelector('input'); - expect(input, 'tag input').to.exist; - await fillIn(input, 'test-tag'); - await triggerKeyEvent(input, 'keydown', 13); - await click('[data-test-button="confirm"]'); - - // API request is correct - note, we don't mock the actual model updates - let [lastRequest] = this.server.pretender.handledRequests.slice(-2); - expect(lastRequest.queryParams.filter, 'add tag request id').to.equal(`id:['3','4']`); - expect(JSON.parse(lastRequest.requestBody).bulk.action, 'add tag request action').to.equal('addTag'); - }); - - // NOTE: we do not seem to be loading the settings properly into the membersutil service, such that the members - // service doesn't think members are enabled - it.skip('can change access to multiple posts', async function () { - await visit('/posts'); - - // get all posts - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(4); - - const postThreeContainer = posts[2].parentElement; // draft post - const postFourContainer = posts[3].parentElement; // published post - - await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - - expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; - expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; - - // NOTE: right clicks don't seem to work in these tests - // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event - await triggerEvent(postFourContainer, 'contextmenu'); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                              element - expect(contextMenu, 'context menu').to.exist; - - // TODO: the change access button is not showing; need to debug the UI to see what field it expects - // change access to the posts - let buttons = contextMenu.querySelectorAll('button'); - let changeAccessButton = findButton('Change access', buttons); - - expect(changeAccessButton, 'change access button').to.exist; - await click(changeAccessButton); - - const changeAccessModal = find('[data-test-modal="edit-posts-access"]'); - expect(changeAccessModal, 'change access modal').to.exist; - }); - - it('can unpublish posts', async function () { - await visit('/posts'); - - // get all posts - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(4); - - const postThreeContainer = posts[2].parentElement; // draft post - const postFourContainer = posts[3].parentElement; // published post - - await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - - expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; - expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; - - // NOTE: right clicks don't seem to work in these tests - // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event - await triggerEvent(postFourContainer, 'contextmenu'); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                                element - expect(contextMenu, 'context menu').to.exist; - - // unpublish the posts - let buttons = contextMenu.querySelectorAll('button'); - let unpublishButton = findButton('Unpublish', buttons); - expect(unpublishButton, 'unpublish button').to.exist; - await click(unpublishButton); - - // handle modal - const modal = find('[data-test-modal="unpublish-posts"]'); - expect(modal, 'unpublish modal').to.exist; - await click('[data-test-button="confirm"]'); - - // API request is correct - note, we don't mock the actual model updates - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'unpublish request id').to.equal(`id:['3','4']`); - expect(JSON.parse(lastRequest.requestBody).bulk.action, 'unpublish request action').to.equal('unpublish'); - - // ensure ui shows these are now unpublished - expect(postThreeContainer.querySelector('.gh-content-entry-status').textContent, 'postThree status').to.contain('Draft'); - expect(postFourContainer.querySelector('.gh-content-entry-status').textContent, 'postThree status').to.contain('Draft'); - }); - - it('can delete posts', async function () { - await visit('/posts'); - - // get all posts - const posts = findAll('[data-test-post-id]'); - expect(posts.length, 'all posts count').to.equal(4); - - const postThreeContainer = posts[2].parentElement; // draft post - const postFourContainer = posts[3].parentElement; // published post - - await click(postThreeContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - await click(postFourContainer, {metaKey: ctrlOrCmd === 'command', ctrlKey: ctrlOrCmd === 'ctrl'}); - - expect(postFourContainer.getAttribute('data-selected'), 'postFour selected').to.exist; - expect(postThreeContainer.getAttribute('data-selected'), 'postThree selected').to.exist; - - // NOTE: right clicks don't seem to work in these tests - // contextmenu is the event triggered - https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event - await triggerEvent(postFourContainer, 'contextmenu'); - - let contextMenu = find('.gh-posts-context-menu'); // this is a
                                  element - expect(contextMenu, 'context menu').to.exist; - - // delete the posts - let buttons = contextMenu.querySelectorAll('button'); - let deleteButton = findButton('Delete', buttons); - expect(deleteButton, 'delete button').to.exist; - await click(deleteButton); - - // handle modal - const modal = find('[data-test-modal="delete-posts"]'); - expect(modal, 'delete modal').to.exist; - await click('[data-test-button="confirm"]'); - - // API request is correct - note, we don't mock the actual model updates - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter, 'delete request id').to.equal(`id:['3','4']`); - expect(lastRequest.method, 'delete request method').to.equal('DELETE'); - - // ensure ui shows these are now deleted - expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(2); - }); - }); - }); - - it('can add and edit custom views', async function () { - // actions are not visible when there's no filter - await visit('/posts'); - expect(find('[data-test-button="edit-view"]'), 'edit-view button (no filter)').to.not.exist; - expect(find('[data-test-button="add-view"]'), 'add-view button (no filter)').to.not.exist; - - // add action is visible after filtering to a non-default filter - await selectChoose('[data-test-author-select]', admin.name); - expect(find('[data-test-button="add-view"]'), 'add-view button (with filter)').to.exist; - - // adding view shows it in the sidebar - await click('[data-test-button="add-view"]'), 'add-view button'; - expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on add)').to.exist; - expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('New view'); - await fillIn('[data-test-input="custom-view-name"]', 'Test view'); - await click('[data-test-button="save-custom-view"]'); - // modal closes on save - expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after add save)').to.not.exist; - // UI updates - expect(find('[data-test-nav-custom="posts-Test view"]'), 'new view nav').to.exist; - expect(find('[data-test-nav-custom="posts-Test view"]').textContent.trim()).to.equal('Test view'); - expect(find('[data-test-button="add-view"]'), 'add-view button (on existing view)').to.not.exist; - expect(find('[data-test-button="edit-view"]'), 'edit-view button (on existing view)').to.exist; - - // editing view - await click('[data-test-button="edit-view"]'), 'edit-view button'; - expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on edit)').to.exist; - expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('Edit view'); - await fillIn('[data-test-input="custom-view-name"]', 'Updated view'); - await click('[data-test-button="save-custom-view"]'); - // modal closes on save - expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after edit save)').to.not.exist; - // UI updates - expect(find('[data-test-nav-custom="posts-Updated view"]')).to.exist; - expect(find('[data-test-nav-custom="posts-Updated view"]').textContent.trim()).to.equal('Updated view'); - expect(find('[data-test-button="add-view"]'), 'add-view button (after edit)').to.not.exist; - expect(find('[data-test-button="edit-view"]'), 'edit-view button (after edit)').to.exist; - }); - - it('can navigate to custom views', async function () { - this.server.create('setting', { - group: 'site', - key: 'shared_views', - value: JSON.stringify([{ - route: 'posts', - name: 'My posts', - filter: { - author: admin.slug - } - }]) - }); - - await visit('/posts'); - - // nav bar contains default + custom views - expect(find('[data-test-nav-custom="posts-Drafts"]')).to.exist; - expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.exist; - expect(find('[data-test-nav-custom="posts-Published"]')).to.exist; - expect(find('[data-test-nav-custom="posts-My posts"]')).to.exist; - - // screen has default title and sidebar is showing inactive custom view - expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts'); - expect(find('[data-test-nav="posts"]')).to.have.class('active'); - - // clicking sidebar custom view link works - await click('[data-test-nav-custom="posts-Scheduled"]'); - expect(currentURL()).to.equal('/posts?type=scheduled'); - expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/); - expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active'); - - // clicking the main posts link resets - await click('[data-test-nav="posts"]'); - expect(currentURL()).to.equal('/posts'); - expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts'); - expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.not.have.class('active'); - - // changing a filter to match a custom view shows custom view - await selectChoose('[data-test-type-select]', 'Scheduled posts'); - expect(currentURL()).to.equal('/posts?type=scheduled'); - expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active'); - expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/); - }); - }); - - describe('as author', function () { - let author, authorPost; - - beforeEach(async function () { - let authorRole = this.server.create('role', {name: 'Author'}); - author = this.server.create('user', {roles: [authorRole]}); - let adminRole = this.server.create('role', {name: 'Administrator'}); - let admin = this.server.create('user', {roles: [adminRole]}); - - // create posts - authorPost = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'}); - this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'}); - - return await authenticateSession(); - }); - - it('only fetches the author\'s posts', async function () { - await visit('/posts'); - // trigger a filter request so we can grab the posts API request easily - await selectChoose('[data-test-type-select]', 'Published posts'); - - // API request includes author filter - let [lastRequest] = this.server.pretender.handledRequests.slice(-1); - expect(lastRequest.queryParams.filter).to.have.string(`authors:${author.slug}`); - - // only author's post is shown - expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1); - expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist; - }); - }); - - describe('as contributor', function () { - beforeEach(async function () { - let contributorRole = this.server.create('role', {name: 'Contributor'}); - this.server.create('user', {roles: [contributorRole]}); - - return await authenticateSession(); - }); - - it('shows posts list and allows post creation', async function () { - await visit('/posts'); - - // has an empty state - expect(findAll('[data-test-post-id]')).to.have.length(0); - expect(find('[data-test-no-posts-box]')).to.exist; - expect(find('[data-test-link="write-a-new-post"]')).to.exist; - - await click('[data-test-link="write-a-new-post"]'); - - expect(currentURL()).to.equal('/editor/post'); - - await fillIn('[data-test-editor-title-input]', 'First contributor post'); - await blur('[data-test-editor-title-input]'); - - expect(currentURL()).to.equal('/editor/post/1'); - - await click('[data-test-link="posts"]'); - - expect(findAll('[data-test-post-id]')).to.have.length(1); - expect(find('[data-test-no-posts-box]')).to.not.exist; }); }); }); \ No newline at end of file From 8afdc10ec9da671480a3a094101c5cf91ff12f1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:10:12 +0000 Subject: [PATCH 086/164] Update dependency tailwindcss to v3.4.7 --- apps/admin-x-design-system/package.json | 2 +- apps/comments-ui/package.json | 2 +- apps/signup-form/package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 1524ae194f..124ee2b4e6 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -71,7 +71,7 @@ "react-colorful": "5.6.1", "react-hot-toast": "2.4.1", "react-select": "5.8.0", - "tailwindcss": "3.4.6" + "tailwindcss": "3.4.7" }, "peerDependencies": { "react": "^18.2.0", diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 78a9ec7e07..3ad5cca6f3 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -75,7 +75,7 @@ "eslint-plugin-tailwindcss": "3.13.0", "jsdom": "24.1.1", "postcss": "8.4.39", - "tailwindcss": "3.4.6", + "tailwindcss": "3.4.7", "vite": "4.5.3", "vite-plugin-css-injected-by-js": "3.3.0", "vite-plugin-svgr": "3.3.0", diff --git a/apps/signup-form/package.json b/apps/signup-form/package.json index fc3841fdec..867922bdea 100644 --- a/apps/signup-form/package.json +++ b/apps/signup-form/package.json @@ -65,7 +65,7 @@ "rollup-plugin-node-builtins": "2.1.2", "storybook": "7.6.20", "stylelint": "15.10.3", - "tailwindcss": "3.4.6", + "tailwindcss": "3.4.7", "vite": "4.5.3", "vite-plugin-commonjs": "0.10.1", "vite-plugin-svgr": "3.3.0", diff --git a/yarn.lock b/yarn.lock index 93278ba4f9..8c9eb84e07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30049,10 +30049,10 @@ table@^6.0.9, table@^6.8.1: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@3.4.6: - version "3.4.6" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.6.tgz#41faae16607e0916da1eaa4a3b44053457ba70dd" - integrity sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA== +tailwindcss@3.4.7: + version "3.4.7" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.7.tgz#6092f18767f5933f59375b9afe558e592fc77201" + integrity sha512-rxWZbe87YJb4OcSopb7up2Ba4U82BoiSGUdoDr3Ydrg9ckxFS/YWsvhN323GMcddgU65QRy7JndC7ahhInhvlQ== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" From fd335d3f4cd4c0a2a41fd10a56171edc6e5f149f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 07:37:55 +0000 Subject: [PATCH 087/164] Update dependency i18next to v23.12.2 --- ghost/i18n/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/i18n/package.json b/ghost/i18n/package.json index f1ec201952..e55ed4b862 100644 --- a/ghost/i18n/package.json +++ b/ghost/i18n/package.json @@ -30,6 +30,6 @@ "mocha": "10.2.0" }, "dependencies": { - "i18next": "23.12.1" + "i18next": "23.12.2" } } diff --git a/yarn.lock b/yarn.lock index 8c9eb84e07..9023ab5a15 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19802,10 +19802,10 @@ i18next-parser@8.13.0: vinyl-fs "^4.0.0" vue-template-compiler "^2.6.11" -i18next@23.12.1, i18next@^23.5.1: - version "23.12.1" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.12.1.tgz#1cdb4d6dde62404e128ae1212af586d14c70d389" - integrity sha512-l4y291ZGRgUhKuqVSiqyuU2DDzxKStlIWSaoNBR4grYmh0X+pRYbFpTMs3CnJ5ECKbOI8sQcJ3PbTUfLgPRaMA== +i18next@23.12.2, i18next@^23.5.1: + version "23.12.2" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.12.2.tgz#c5b44bb95e4d4a5908a51577fa06c63dc2f650a4" + integrity sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg== dependencies: "@babel/runtime" "^7.23.2" From f7ec72488af496cb684ebe111706c9ae14436e7c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 07:38:15 +0000 Subject: [PATCH 088/164] Update tiptap monorepo to v2.5.8 --- apps/comments-ui/package.json | 20 +++---- yarn.lock | 100 +++++++++++++++++----------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 3ad5cca6f3..605af91ba1 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.7", - "@tiptap/extension-blockquote": "2.5.7", - "@tiptap/extension-document": "2.5.7", - "@tiptap/extension-hard-break": "2.5.7", - "@tiptap/extension-link": "2.5.7", - "@tiptap/extension-paragraph": "2.5.7", - "@tiptap/extension-placeholder": "2.5.7", - "@tiptap/extension-text": "2.5.7", - "@tiptap/pm": "2.5.7", - "@tiptap/react": "2.5.7", + "@tiptap/core": "2.5.8", + "@tiptap/extension-blockquote": "2.5.8", + "@tiptap/extension-document": "2.5.8", + "@tiptap/extension-hard-break": "2.5.8", + "@tiptap/extension-link": "2.5.8", + "@tiptap/extension-paragraph": "2.5.8", + "@tiptap/extension-placeholder": "2.5.8", + "@tiptap/extension-text": "2.5.8", + "@tiptap/pm": "2.5.8", + "@tiptap/react": "2.5.8", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index 9023ab5a15..1b373200a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7563,66 +7563,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== -"@tiptap/core@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.7.tgz#681eefd198f9b7b8ad543ca29c56d46aab4919cf" - integrity sha512-8fBW+yBRSc2rEDOs6P+53kF0EAmSv17M4ruQBABo18Nt5qIyr/Uo4p+/E4NkV30bKgKI1zyq1dPeznDplSseqQ== +"@tiptap/core@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.8.tgz#58de366b0d2acb0a6e67a4780de64d619ebd90fa" + integrity sha512-lkWCKyoAoMTxM137MoEsorG7tZ5MZU6O3wMRuZ0P9fcTRY5vd1NWncWuPzuGSJIpL20gwBQOsS6PaQSfR3xjlA== -"@tiptap/extension-blockquote@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.7.tgz#378921028a25f39d6a1dcebc86efb73fc4f19cce" - integrity sha512-cSnk5ViQgG6SgKnvJ5qaW47jl5qTN0oADXdcfyaY5XrbCPBGCVq1yRZlUtPU/J0YocZpjNLRRSMPVQ3wya5vtQ== +"@tiptap/extension-blockquote@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.8.tgz#95880f0b687790dbff85a1c9e83f2afd0011be67" + integrity sha512-P8vDiagtRrUfIewfCKrJe0ddDSjPgOTKzqoM1UXKS+MenT8C/wT4bjiwopAoWP6zMoV0TfHWXah9emllmCfXFA== -"@tiptap/extension-bubble-menu@^2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.7.tgz#24c3f41d93022ed4bebe3610124bb114333c834d" - integrity sha512-gkuBuVGm5YPDRUG5Bscj6IYjDbzM7iJ2aXBGCM1rzuIiwT04twY51dKMIeseXa49uk/AQs/mqt3kGQjgSdSFAw== +"@tiptap/extension-bubble-menu@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.8.tgz#e39b176c574b9fd2f59c6457724f3f22a22fb1b8" + integrity sha512-COmd1Azudu7i281emZFIESECe7FnvWiRoBoQBVjjWSyq5PVzwJaA3PAlnU7GyNZKtVXMZ4xbrckdyNQfDeVQDA== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.7.tgz#cfc608656ab9173334247baf8ddb93135b024260" - integrity sha512-tcK6aleya6pmC/ForF/y2PiwPhN5hK8JSm07pcWV9FmP2Qemx26GWS+1u1EzPDeTTbRBvk+9txHGcq9NYZem0Q== +"@tiptap/extension-document@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.8.tgz#644f042f1d4a8d3f74af057477cc627da7b54dc7" + integrity sha512-r3rP4ihCJAdp3VRIeqd80etHx7jttzZaKNFX8hkQShHK6eTHwrR92VL0jDE4K+NOE3bxjMsOlYizJYWV042BtA== -"@tiptap/extension-floating-menu@^2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.7.tgz#b97101d82629335f47663bb4ddbc9231985a2b80" - integrity sha512-tQjNNx0gPb7GxMiozcQ4R1Tl1znmlx/ZkbCF9rqxTzPTD4fnCliqBQAWjtHl98+D8+yEJBcB2DimtP7ztkv2mg== +"@tiptap/extension-floating-menu@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.8.tgz#6af3fa169bf293ab79a671a7b60b5199992a9154" + integrity sha512-qsM6tCyRlXnI/gADrkO/2p0Tldu5aY96CnsXpZMaflMgsO577qhcXD0ReGg17uLXBzJa5xmV8qOik0Ptq3WEWg== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.7.tgz#a832460a610f3ff6c3c4260562c8555e0d0734ec" - integrity sha512-Ki1JV2cz74wo4am8vIY6KWnfiFoE68RVQDIL0/29fNz1oZI46R4VV2Q5IvoVhetXcx7Qe9nTJVqy1vRS//Kcvg== +"@tiptap/extension-hard-break@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.8.tgz#95288faad3408b91284d925c3e4dbab66029dd98" + integrity sha512-samZEL0EXzHSmMQ7KyLnfSxdDv3qSjia0JzelfCnFZS6LLcbwjrIjV8ZPxEhJ7UlZqroQdFxPegllkLHZj/MdQ== -"@tiptap/extension-link@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.7.tgz#91326304a3f4f5fdeb23a8f630d5ae4a1ab64287" - integrity sha512-rxvcdV8H/TiRhR2SZfLHp7hUp5hwBAhkc6PsXEWj8lekG4/5lXGwPSPxLtHMBRtOyeJpXTv9DY6nsCGZuz9x6A== +"@tiptap/extension-link@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.8.tgz#f9264afed09bd25c37668303151ab80ba82ef044" + integrity sha512-qfeWR7sG2V7bn8z0f3HMyoR68pFlxYJmLs9cbW30diE9/zKClYEd3zTMPCgJ9yMSagCj4PWkqksIuktAhyRqOQ== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.7.tgz#7ce35b365e8222fb8e93f5e7bcdc18ef73c32ac5" - integrity sha512-7zmDE43jv+GMTLuWrztA6oAnYLdUki5fUjYFn0h5FPRHQTuDoxsCD+hX0N/sGQVlc8zl1yn7EYbPNn9rHi7ECw== +"@tiptap/extension-paragraph@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.8.tgz#5be7e7c4e5c19bd4f512c72d3dfc4e1e6d6dd876" + integrity sha512-AMfD3lfGSiomfkSE2tUourUjVahLtIfWUQew13NTPuWoxAXaSyoCGO0ULkiou/lO3JVUUUmF9+KJrAHWGIARdA== -"@tiptap/extension-placeholder@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.7.tgz#b01f695d370d72ce2efd81a2b9b61868290b1f36" - integrity sha512-Xjl0sCUlNyVq8HDrf+6n62gPEM3ymPr5d5t0zXjE+NPzfOeOARfiMXW2VB5QYFOsxnCd2MbZAeZ4+RY2sSVaZg== +"@tiptap/extension-placeholder@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.8.tgz#80fdf02133d94f41363f6fe28f5fc3ef09ac73c6" + integrity sha512-mvRl73OM5jBXVtDRLSTvp8/4+0mS2J2+ZcuiAHjABwEsZRCfJsiqty5NisOxSuy/AQtm8TK2kyt6ZCXQ2VRGig== -"@tiptap/extension-text@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.7.tgz#bc436206ba16214383064672a6ebe3dd0464f368" - integrity sha512-vukhh2K/MsaIIs/UzIAwp44IVxTHPJcAhSsDnmJd4iPlkpjLt1eph77dfxv5awq78bj6mGvnCM0/0F6fW1C6/w== +"@tiptap/extension-text@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.8.tgz#a9c4de33eec749c8c01d8bd81fb589f581c30dfc" + integrity sha512-CNkD51jRMdcYCqFVOkrnebqBQ6pCD3ZD5z9kO5bOC5UPZKZBkLsWdlrHGAVwosxcGxdJACbqJ0Nj+fMgIw4tNA== -"@tiptap/pm@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.7.tgz#9661d508fe34f7616b1078becc049baeff75d677" - integrity sha512-4Eb4vA4e4vesBAUmZgx+n3xjgJ58uRKKtnhFDJ3Gg+dfpXvtF8FcEwSIjHJsTlNJ8mSrzX/I7S157qPc5wZXVw== +"@tiptap/pm@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.8.tgz#b18afa77fdf69527b13614a05cfefc8b63e82224" + integrity sha512-CVhHaTG4QNHSkvuh6HHsUR4hE+nbUnk7z+VMUedaqPU8tNqkTwWGCMbiyTc+PCsz0T9Mni7vvBR+EXgEQ3+w4g== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7643,13 +7643,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.9" -"@tiptap/react@2.5.7": - version "2.5.7" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.7.tgz#0de723c468a37cf69bb1a9d7a38815137f156c71" - integrity sha512-QRMbo6eDtYHBwZ7ATFgKFWLlRZ/Q7NJrBS/Z6FW2lFhr1eM8UhOG6HMEMt/kibMJDJVi1FpXEavgaT75oe2BJg== +"@tiptap/react@2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.8.tgz#d6bc68710f084fe0f02855376cf869f8ca2cf6fd" + integrity sha512-twUMm8HV7scUgR/E1hYS9N6JDtKPl7cgDiPjxTynNHc5S5f5Ecv4ns/BZRq3TMZ/JDrp4rghLvgq+ImQsLvPOA== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.7" - "@tiptap/extension-floating-menu" "^2.5.7" + "@tiptap/extension-bubble-menu" "^2.5.8" + "@tiptap/extension-floating-menu" "^2.5.8" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From bbec5c0ba696c1c818cd71d3d2f11592750e3111 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Tue, 30 Jul 2024 10:40:10 +0100 Subject: [PATCH 089/164] Added support for displaying different types of Mastodon notes (#20684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added logic for displaying different attachments for Mastodon Notes (images, audio, video) - Centered the feed on the screen for better focus and made the Feed layout the default one - Moved Following and Followers counters to the new “Profile” tab --------- Co-authored-by: Fabien O'Carroll --- .../src/components/ListIndex.tsx | 174 +++++++++++------- apps/admin-x-framework/src/api/activitypub.ts | 2 +- 2 files changed, 105 insertions(+), 71 deletions(-) diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 476c81ff46..4bd3519a27 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -1,6 +1,7 @@ import ActivityPubWelcomeImage from '../assets/images/ap-welcome.png'; import React, {useEffect, useRef, useState} from 'react'; import articleBodyStyles from './articleBodyStyles'; +import getUsername from '../utils/get-username'; import {ActivityPubAPI} from '../api/activitypub'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; import {Avatar, Button, ButtonGroup, Heading, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; @@ -84,7 +85,7 @@ const ActivityPubComponent: React.FC = () => { setArticleContent(null); }; - const [selectedOption, setSelectedOption] = useState({label: 'Inbox', value: 'inbox'}); + const [selectedOption, setSelectedOption] = useState({label: 'Feed', value: 'feed'}); const [selectedTab, setSelectedTab] = useState('inbox'); @@ -92,10 +93,10 @@ const ActivityPubComponent: React.FC = () => { { id: 'inbox', title: 'Inbox', - contents:
                                  -
                                    - {activities && activities.some(activity => activity.type === 'Create' && activity.object.type === 'Article') ? (activities.slice().reverse().map(activity => ( - activity.type === 'Create' && activity.object.type === 'Article' && + contents:
                                    +
                                      + {activities && activities.some(activity => activity.type === 'Create' && (activity.object.type === 'Article' || activity.object.type === 'Note')) ? (activities.slice().reverse().map(activity => ( + activity.type === 'Create' && (activity.object.type === 'Article' || activity.object.type === 'Note') &&
                                    • handleViewContent(activity.object, activity.actor)}>
                                    • @@ -109,7 +110,6 @@ const ActivityPubComponent: React.FC = () => {
                                  }
                                -
    }, { @@ -120,7 +120,6 @@ const ActivityPubComponent: React.FC = () => { activity.type === 'Like' && } id='list-item' title={
    {activity.actor.name} liked your post {activity.object.name}
    }>
    ))} -
    }, { @@ -135,8 +134,24 @@ const ActivityPubComponent: React.FC = () => { ))} - + }, + { + id: 'profile', + title: 'Profile', + contents:
    +
    +
    +
    updateRoute('/view-following')}> + {followingCount} + Following +
    +
    updateRoute('/view-followers')}> + {followersCount} + Followers +
    +
    +
    } ]; @@ -148,18 +163,18 @@ const ActivityPubComponent: React.FC = () => { { icon: 'listview', size: 'sm', - iconColorClass: selectedOption.value === 'inbox' ? 'text-black' : 'text-grey-500', + iconColorClass: selectedOption.value === 'feed' ? 'text-black' : 'text-grey-500', onClick: () => { - setSelectedOption({label: 'Inbox', value: 'inbox'}); + setSelectedOption({label: 'Feed', value: 'feed'}); } }, { icon: 'cardview', size: 'sm', - iconColorClass: selectedOption.value === 'feed' ? 'text-black' : 'text-grey-500', + iconColorClass: selectedOption.value === 'inbox' ? 'text-black' : 'text-grey-500', onClick: () => { - setSelectedOption({label: 'Feed', value: 'feed'}); + setSelectedOption({label: 'Inbox', value: 'inbox'}); } } ]} clearBg={true} link outlineOnMobile />]} @@ -189,35 +204,6 @@ const ActivityPubComponent: React.FC = () => { ); }; -const Sidebar: React.FC<{followingCount: number, followersCount: number, updateRoute: (route: string) => void}> = ({followingCount, followersCount, updateRoute}) => ( -
    -
    -
    -
    -
    updateRoute('/view-following')}> - {followingCount} - Following -
    -
    updateRoute('/view-followers')}> - {followersCount} - Followers -
    -
    -
    -
    -
    - Explore -
    - - {}} />} avatar={} detail='829 followers' hideActions={true} title='404 Media' /> - {}} />} avatar={} detail='791 followers' hideActions={true} title='The Browser' /> - {}} />} avatar={} detail='854 followers' hideActions={true} title='Welcome to Hell World' /> - -
    -
    -); - const ArticleBody: React.FC<{heading: string, image: string|undefined, html: string}> = ({heading, image, html}) => { const site = useBrowseSite(); const siteData = site.data?.site; @@ -274,6 +260,60 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp const doc = parser.parseFromString(object.content || '', 'text/html'); const plainTextContent = doc.body.textContent; + let previewContent = ''; + if (object.preview) { + const previewDoc = parser.parseFromString(object.preview.content || '', 'text/html'); + previewContent = previewDoc.body.textContent || ''; + } else if (object.type === 'Note') { + previewContent = plainTextContent || ''; + } + + const renderAttachment = () => { + let attachment; + if (object.image) { + attachment = object.image; + } + + if (object.type === 'Note' && !attachment) { + attachment = object.attachment; + } + + // const attachment = object.attachment; + if (!attachment) { + return null; + } + + if (Array.isArray(attachment)) { + return ( +
    + {attachment.map((item, index) => ( + {`attachment-${index}`} + ))} +
    + ); + } + + switch (attachment.mediaType) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + return attachment; + case 'video/mp4': + case 'video/webm': + return
    +
    ; + + case 'audio/mpeg': + case 'audio/ogg': + return
    +
    ; + default: + return null; + } + }; + const timestamp = new Date(object?.published ?? new Date()).toLocaleDateString('default', {year: 'numeric', month: 'short', day: '2-digit'}) + ', ' + new Date(object?.published ?? new Date()).toLocaleTimeString('default', {hour: '2-digit', minute: '2-digit'}); @@ -291,34 +331,28 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp return ( <> {object && ( -
    - -
    - -
    -

    {actor.name}

    -
    - {/* {getUsername(actor)} */} - {timestamp} +
    + +
    +
    +

    {actor.name}

    + {getUsername(actor)} + {timestamp} +
    +
    +
    + {object.name && {object.name}} +

    {plainTextContent}

    + {/*

    {object.content}

    */} + {renderAttachment()} +
    +
    -
    -
    - - {object.image &&
    - -
    } - {object.name} -

    {plainTextContent}

    -
    -
    -
    -
    -
    - {/*
    */} +
    )} @@ -339,15 +373,15 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp
    {object.name}
    -

    {object.preview?.content}

    +

    {previewContent}

    - {object.image &&
    - -
    } + {/* {image &&
    + +
    } */}
    {/*
    */} diff --git a/apps/admin-x-framework/src/api/activitypub.ts b/apps/admin-x-framework/src/api/activitypub.ts index 246292d556..0d419e6568 100644 --- a/apps/admin-x-framework/src/api/activitypub.ts +++ b/apps/admin-x-framework/src/api/activitypub.ts @@ -9,7 +9,7 @@ export type FollowItem = { export type ObjectProperties = { '@context': string | (string | object)[]; - type: 'Article' | 'Link'; + type: 'Article' | 'Link' | 'Note'; name: string; content: string; url?: string | undefined; From 178c98c17fbc14af6b002d082fc29b46121eed3b Mon Sep 17 00:00:00 2001 From: Fabien 'egg' O'Carroll Date: Tue, 30 Jul 2024 17:33:25 +0700 Subject: [PATCH 090/164] Fixed handling of single item collections (#20688) Fedify will not use an array for the `items` key of collections when there is only a single item, which wasn't being handled in our activitypub api module. Now we always return an array so that the components recieve consistent data. --- .../src/api/activitypub.test.ts | 123 +++++++++++++++++- .../src/api/activitypub.ts | 6 +- 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/apps/admin-x-activitypub/src/api/activitypub.test.ts b/apps/admin-x-activitypub/src/api/activitypub.test.ts index 3e742b65f4..e194474e2c 100644 --- a/apps/admin-x-activitypub/src/api/activitypub.test.ts +++ b/apps/admin-x-activitypub/src/api/activitypub.test.ts @@ -95,7 +95,7 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); - test('Returns an the items array when the inbox is not empty', async function () { + test('Returns all the items array when the inbox is not empty', async function () { const fakeFetch = Fetch({ 'https://auth.api/': { response: JSONResponse({ @@ -137,6 +137,49 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); + + test('Returns an array when the items key is a single object', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/inbox/index': { + response: + JSONResponse({ + type: 'Collection', + items: { + type: 'Create', + object: { + type: 'Note' + } + } + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getInbox(); + const expected: Activity[] = [ + { + type: 'Create', + object: { + type: 'Note' + } + } + ]; + + expect(actual).toEqual(expected); + }); }); describe('getFollowing', function () { @@ -199,7 +242,7 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); - test('Returns an the items array when the following is not empty', async function () { + test('Returns all the items array when the following is not empty', async function () { const fakeFetch = Fetch({ 'https://auth.api/': { response: JSONResponse({ @@ -235,6 +278,43 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); + + test('Returns an array when the items key is a single object', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/following/index': { + response: + JSONResponse({ + type: 'Collection', + items: { + type: 'Person' + } + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowing(); + const expected: Activity[] = [ + { + type: 'Person' + } + ]; + + expect(actual).toEqual(expected); + }); }); describe('getFollowers', function () { @@ -297,7 +377,7 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); - test('Returns an the items array when the followers is not empty', async function () { + test('Returns all the items array when the followers is not empty', async function () { const fakeFetch = Fetch({ 'https://auth.api/': { response: JSONResponse({ @@ -333,6 +413,43 @@ describe('ActivityPubAPI', function () { expect(actual).toEqual(expected); }); + + test('Returns an array when the items key is a single object', async function () { + const fakeFetch = Fetch({ + 'https://auth.api/': { + response: JSONResponse({ + identities: [{ + token: 'fake-token' + }] + }) + }, + 'https://activitypub.api/.ghost/activitypub/followers/index': { + response: + JSONResponse({ + type: 'Collection', + items: { + type: 'Person' + } + }) + } + }); + + const api = new ActivityPubAPI( + new URL('https://activitypub.api'), + new URL('https://auth.api'), + 'index', + fakeFetch + ); + + const actual = await api.getFollowers(); + const expected: Activity[] = [ + { + type: 'Person' + } + ]; + + expect(actual).toEqual(expected); + }); }); describe('follow', function () { diff --git a/apps/admin-x-activitypub/src/api/activitypub.ts b/apps/admin-x-activitypub/src/api/activitypub.ts index fb7a185131..edb0da0ac1 100644 --- a/apps/admin-x-activitypub/src/api/activitypub.ts +++ b/apps/admin-x-activitypub/src/api/activitypub.ts @@ -45,7 +45,7 @@ export class ActivityPubAPI { return []; } if ('items' in json) { - return Array.isArray(json?.items) ? json.items : []; + return Array.isArray(json.items) ? json.items : [json.items]; } return []; } @@ -60,7 +60,7 @@ export class ActivityPubAPI { return []; } if ('items' in json) { - return Array.isArray(json?.items) ? json.items : []; + return Array.isArray(json.items) ? json.items : [json.items]; } return []; } @@ -86,7 +86,7 @@ export class ActivityPubAPI { return []; } if ('items' in json) { - return Array.isArray(json?.items) ? json.items : []; + return Array.isArray(json.items) ? json.items : [json.items]; } return []; } From f7a592e7612bcb61641f4c7d112b5842bd91aacf Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Tue, 30 Jul 2024 09:18:51 -0500 Subject: [PATCH 091/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20dark=20mode=20fo?= =?UTF-8?q?r=20standalone=20html=20editors=20in=20Admin=20X=20(#20689)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-591 - finished wiring up the darkMode prop through the context providers The main impact here was that the formatting toolbar was not respecting the dark mode settings. --- apps/admin-x-design-system/src/DesignSystemApp.tsx | 2 +- .../src/global/form/HtmlEditor.tsx | 10 ++++++---- .../src/providers/DesignSystemProvider.tsx | 9 ++++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/admin-x-design-system/src/DesignSystemApp.tsx b/apps/admin-x-design-system/src/DesignSystemApp.tsx index 6606867af1..9ea7f4a730 100644 --- a/apps/admin-x-design-system/src/DesignSystemApp.tsx +++ b/apps/admin-x-design-system/src/DesignSystemApp.tsx @@ -17,7 +17,7 @@ const DesignSystemApp: React.FC = ({darkMode, fetchKoenigL return (
    - + {children}
    diff --git a/apps/admin-x-design-system/src/global/form/HtmlEditor.tsx b/apps/admin-x-design-system/src/global/form/HtmlEditor.tsx index 68d9f13b22..632cdd0990 100644 --- a/apps/admin-x-design-system/src/global/form/HtmlEditor.tsx +++ b/apps/admin-x-design-system/src/global/form/HtmlEditor.tsx @@ -13,6 +13,7 @@ export interface HtmlEditorProps { placeholder?: string nodes?: 'DEFAULT_NODES' | 'BASIC_NODES' | 'MINIMAL_NODES' emojiPicker?: boolean; + darkMode?: boolean; } declare global { @@ -61,7 +62,8 @@ const KoenigWrapper: React.FC = ({ onBlur, placeholder, nodes, - emojiPicker = true + emojiPicker = true, + darkMode = false }) => { const onError = useCallback((error: unknown) => { try { @@ -128,12 +130,12 @@ const KoenigWrapper: React.FC = ({ return ( { - const {fetchKoenigLexical} = useDesignSystem(); + const {fetchKoenigLexical, darkMode} = useDesignSystem(); const editorResource = useMemo(() => loadKoenig(fetchKoenigLexical), [fetchKoenigLexical]); return
    Loading editor...

    }> - +
    diff --git a/apps/admin-x-design-system/src/providers/DesignSystemProvider.tsx b/apps/admin-x-design-system/src/providers/DesignSystemProvider.tsx index c655165d9c..a79413283c 100644 --- a/apps/admin-x-design-system/src/providers/DesignSystemProvider.tsx +++ b/apps/admin-x-design-system/src/providers/DesignSystemProvider.tsx @@ -9,12 +9,14 @@ interface DesignSystemContextType { isAnyTextFieldFocused: boolean; setFocusState: (value: boolean) => void; fetchKoenigLexical: FetchKoenigLexical; + darkMode: boolean; } const DesignSystemContext = createContext({ isAnyTextFieldFocused: false, setFocusState: () => {}, - fetchKoenigLexical: async () => {} + fetchKoenigLexical: async () => {}, + darkMode: false }); export const useDesignSystem = () => useContext(DesignSystemContext); @@ -29,10 +31,11 @@ export const useFocusContext = () => { interface DesignSystemProviderProps { fetchKoenigLexical: FetchKoenigLexical; + darkMode: boolean; children: React.ReactNode; } -const DesignSystemProvider: React.FC = ({fetchKoenigLexical, children}) => { +const DesignSystemProvider: React.FC = ({fetchKoenigLexical, darkMode, children}) => { const [isAnyTextFieldFocused, setIsAnyTextFieldFocused] = useState(false); const setFocusState = (value: boolean) => { @@ -40,7 +43,7 @@ const DesignSystemProvider: React.FC = ({fetchKoenigL }; return ( - + From 61641bc7c608110d801a025e527625359b6a0480 Mon Sep 17 00:00:00 2001 From: Michael Barrett Date: Tue, 30 Jul 2024 15:23:34 +0100 Subject: [PATCH 092/164] Fixed ActivityPub app tabs content (#20690) no refs The `Activity` and `Likes` tabs of the AcitivityPub app were not showing the correct content. This commit fixes that. --- .../src/components/ListIndex.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 4bd3519a27..da6cf25995 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -115,16 +115,6 @@ const ActivityPubComponent: React.FC = () => { { id: 'activity', title: 'Activity', - contents:
    - {activities && activities.slice().reverse().map(activity => ( - activity.type === 'Like' && } id='list-item' title={
    {activity.actor.name} liked your post {activity.object.name}
    }>
    - ))} -
    -
    - }, - { - id: 'likes', - title: 'Likes', contents:
      {activities && activities.slice().reverse().map(activity => ( @@ -136,6 +126,16 @@ const ActivityPubComponent: React.FC = () => {
    }, + { + id: 'likes', + title: 'Likes', + contents:
    + {activities && activities.slice().reverse().map(activity => ( + activity.type === 'Like' && } id='list-item' title={
    {activity.actor.name} liked your post {activity.object.name}
    }>
    + ))} +
    +
    + }, { id: 'profile', title: 'Profile', @@ -167,7 +167,7 @@ const ActivityPubComponent: React.FC = () => { onClick: () => { setSelectedOption({label: 'Feed', value: 'feed'}); } - + }, { icon: 'cardview', @@ -191,9 +191,9 @@ const ActivityPubComponent: React.FC = () => { tabs={tabs} title='ActivityPub' toolbarBorder={true} - type='page' + type='page' onTabChange={setSelectedTab} - > + > ) : ( @@ -303,7 +303,7 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp return
    ; - + case 'audio/mpeg': case 'audio/ogg': return
    @@ -319,7 +319,7 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp const [isClicked, setIsClicked] = useState(false); const [isLiked, setIsLiked] = useState(false); - + const handleLikeClick = (event: React.MouseEvent | undefined) => { event?.stopPropagation(); setIsClicked(true); @@ -397,7 +397,7 @@ const ViewArticle: React.FC = ({object, onBackToList}) => { const [isClicked, setIsClicked] = useState(false); const [isLiked, setIsLiked] = useState(false); - + const handleLikeClick = (event: React.MouseEvent | undefined) => { event?.stopPropagation(); setIsClicked(true); @@ -415,7 +415,7 @@ const ViewArticle: React.FC = ({object, onBackToList}) => {
    -
    +
    From bb33a84058a2e2123e5b846d7d17642ef09478fc Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Wed, 31 Jul 2024 09:37:34 +0530 Subject: [PATCH 093/164] Optimising count query : Added option param to skip distinct from count query for members events API Ref https://linear.app/tryghost/issue/SLO-193/optimise-count-query-skip-distinct-from-count-query-for-members-events The member events endpoint have many queries like:- select count(distinct members_subscribe_events.id) as aggregate from `members_subscribe_events` where `members_subscribe_events`.`created_at` < '2024-07-30 11:30:39' In these queries, distinct is not required as id is a primary key. Skipping distinct would improve the performance. This PR will changed the query to:- select count(*) as aggregate from `members_subscribe_events` where `members_subscribe_events`.`created_at` < '2024-07-30 11:30:39' --- .../lib/repositories/EventRepository.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ghost/members-api/lib/repositories/EventRepository.js b/ghost/members-api/lib/repositories/EventRepository.js index eb3574e391..a44541b50c 100644 --- a/ghost/members-api/lib/repositories/EventRepository.js +++ b/ghost/members-api/lib/repositories/EventRepository.js @@ -151,6 +151,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'newsletter'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -193,6 +194,7 @@ module.exports = class EventRepository { 'stripeSubscription.stripePrice.stripeProduct.product' ], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -245,6 +247,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -277,6 +280,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -314,6 +318,7 @@ module.exports = class EventRepository { 'tagAttribution' ], filter: 'subscriptionCreatedEvent.id:null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -368,6 +373,7 @@ module.exports = class EventRepository { 'tagAttribution' ], filter: 'member_id:-null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -416,6 +422,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'post', 'parent'], filter: 'member_id:-null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -449,6 +456,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'link', 'link.post'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -537,6 +545,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'post'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -571,6 +580,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'email'], filter: filterStr, + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -613,6 +623,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'email'], filter: 'delivered_at:-null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -655,6 +666,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'email'], filter: 'opened_at:-null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -697,6 +709,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'email'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -730,6 +743,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member', 'email'], filter: 'failed_at:-null+custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), @@ -772,6 +786,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), From ffbcb5a69ea105963f3a9e2c0a0cbf94e15437bd Mon Sep 17 00:00:00 2001 From: Michael Barrett Date: Wed, 31 Jul 2024 09:57:10 +0100 Subject: [PATCH 094/164] Refactored ActivityPub tabs to make tabs more maintainable (#20692) no refs Refactorings include: - Formatting JSX to be more readable - Filtering activities before rendering - Fixing invalid inbox empty state HTML (nesting div inside ul) - Adding initial support for announce activities in the inbox --- .../src/components/ListIndex.tsx | 153 +++++++++++++----- 1 file changed, 109 insertions(+), 44 deletions(-) diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index da6cf25995..4c785eead9 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -14,6 +14,13 @@ interface ViewArticleProps { onBackToList: () => void; } +type Activity = { + type: string, + object: { + type: string + } +} + function useBrowseInboxForUser(handle: string) { const site = useBrowseSite(); const siteData = site.data?.site; @@ -89,69 +96,127 @@ const ActivityPubComponent: React.FC = () => { const [selectedTab, setSelectedTab] = useState('inbox'); + const inboxTabActivities = activities.filter((activity: Activity) => { + const isCreate = activity.type === 'Create' && ['Article', 'Note'].includes(activity.object.type); + const isAnnounce = activity.type === 'Announce' && activity.object.type === 'Note'; + + return isCreate || isAnnounce; + }); + const activityTabActivities = activities.filter((activity: Activity) => activity.type === 'Create' && activity.object.type === 'Article'); + const likeTabActivies = activities.filter((activity: Activity) => activity.type === 'Like'); + const tabs: ViewTab[] = [ { id: 'inbox', title: 'Inbox', - contents:
    -
      - {activities && activities.some(activity => activity.type === 'Create' && (activity.object.type === 'Article' || activity.object.type === 'Note')) ? (activities.slice().reverse().map(activity => ( - activity.type === 'Create' && (activity.object.type === 'Article' || activity.object.type === 'Note') && -
    • handleViewContent(activity.object, activity.actor)}> - -
    • - ))) :
      -
      - Ghost site logos - Welcome to ActivityPub -

      We’re so glad to have you on board! At the moment, you can follow other Ghost sites and enjoy their content right here inside Ghost.

      -

      You can see all of the users on the right—find your favorite ones and give them a follow.

      -
      -
    } - -
    + )} +
    + ) }, { id: 'activity', title: 'Activity', - contents:
    -
      - {activities && activities.slice().reverse().map(activity => ( - activity.type === 'Create' && activity.object.type === 'Article' && -
    • handleViewContent(activity.object, activity.actor)}> - -
    • - ))} -
    -
    + contents: ( +
    +
      + {activityTabActivities.reverse().map(activity => ( +
    • handleViewContent(activity.object, activity.actor)} + > + +
    • + ))} +
    +
    + ) }, { id: 'likes', title: 'Likes', - contents:
    - {activities && activities.slice().reverse().map(activity => ( - activity.type === 'Like' && } id='list-item' title={
    {activity.actor.name} liked your post {activity.object.name}
    }>
    - ))} -
    -
    + contents: ( +
    + + {likeTabActivies.reverse().map(activity => ( + } + id='list-item' + title={ +
    + {activity.actor.name} + liked your post + {activity.object.name} +
    + } + /> + ))} +
    +
    + ) }, { id: 'profile', title: 'Profile', - contents:
    -
    -
    -
    updateRoute('/view-following')}> - {followingCount} - Following -
    -
    updateRoute('/view-followers')}> - {followersCount} - Followers + contents: ( +
    +
    +
    +
    +
    updateRoute('/view-following')}> + {followingCount} + Following +
    +
    updateRoute('/view-followers')}> + {followersCount} + Followers +
    +
    -
    + ) } ]; From ae1ac83fc543ae955266289c5b340909d4f0039f Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Wed, 31 Jul 2024 11:16:25 +0100 Subject: [PATCH 095/164] Fixed members import-with-tier alpha creating unexpected invoices (#20695) ref https://linear.app/tryghost/issue/ONC-199 The `updateSubscriptionItemPrice()` method in our Stripe library used by the importer when moving a subscription over to a Ghost product/price was setting `proration_behavior: 'always_invoice'`. This resulted in invoices being created when changing the subscription (even though no prices were changing as far as the customer is concerned) and in some cases where a customer previously had a one-off discount the customer was incorrectly charged the proration difference because the discount was no longer applied to the new invoice. - updated `updateSubscriptionItemPrice()` to accept an `options` param allowing the `proration_behavior` property passed to the Stripe API to be overridden on a per-call basis - updated the `forceStripeSubscriptionToProduct()` method used by the importer to pass an options object with `prorationBehavior: 'none'` when updating the subscription item price so that no invoice and no unexpected charges occur when importing --- .../lib/MembersCSVImporterStripeUtils.js | 6 ++++-- .../test/MembersCSVImporterStripeUtils.test.js | 6 ++++-- ghost/stripe/lib/StripeAPI.js | 9 ++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js b/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js index 82f19adb40..113d97295a 100644 --- a/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js +++ b/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js @@ -153,7 +153,8 @@ module.exports = class MembersCSVImporterStripeUtils { await this._stripeAPIService.updateSubscriptionItemPrice( stripeSubscription.id, stripeSubscriptionItem.id, - newStripePrice.id + newStripePrice.id, + {prorationBehavior: 'none'} ); stripePriceId = newStripePrice.id; @@ -167,7 +168,8 @@ module.exports = class MembersCSVImporterStripeUtils { await this._stripeAPIService.updateSubscriptionItemPrice( stripeSubscription.id, stripeSubscriptionItem.id, - stripePriceId + stripePriceId, + {prorationBehavior: 'none'} ); } } diff --git a/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js b/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js index 9b1176613d..f76159c72e 100644 --- a/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js +++ b/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js @@ -304,7 +304,8 @@ describe('MembersCSVImporterStripeUtils', function () { stripeAPIServiceStub.updateSubscriptionItemPrice.calledWithExactly( stripeCustomer.subscriptions.data[0].id, stripeCustomerSubscriptionItem.id, - GHOST_PRODUCT_STRIPE_PRICE_ID + GHOST_PRODUCT_STRIPE_PRICE_ID, + {prorationBehavior: 'none'} ).should.be.true(); }); @@ -346,7 +347,8 @@ describe('MembersCSVImporterStripeUtils', function () { stripeAPIServiceStub.updateSubscriptionItemPrice.calledWithExactly( stripeCustomer.subscriptions.data[0].id, stripeCustomerSubscriptionItem.id, - NEW_STRIPE_PRICE_ID + NEW_STRIPE_PRICE_ID, + {prorationBehavior: 'none'} ).should.be.true(); }); diff --git a/ghost/stripe/lib/StripeAPI.js b/ghost/stripe/lib/StripeAPI.js index 504b8b0c1f..999e5a0155 100644 --- a/ghost/stripe/lib/StripeAPI.js +++ b/ghost/stripe/lib/StripeAPI.js @@ -698,20 +698,23 @@ module.exports = class StripeAPI { * @param {string} subscriptionId - The ID of the Subscription to modify * @param {string} id - The ID of the SubscriptionItem * @param {string} price - The ID of the new Price + * @param {object} [options={}] - Additional data to set on the subscription object + * @param {('always_invoice'|'create_prorations'|'none')} [options.prorationBehavior='always_invoice'] - The proration behavior to use. See [Stripe docs](https://docs.stripe.com/api/subscriptions/update#update_subscription-proration_behavior) for more info + * @param {string} [options.cancellationReason=null] - The user defined cancellation reason * * @returns {Promise} */ - async updateSubscriptionItemPrice(subscriptionId, id, price) { + async updateSubscriptionItemPrice(subscriptionId, id, price, options = {}) { await this._rateLimitBucket.throttle(); const subscription = await this._stripe.subscriptions.update(subscriptionId, { - proration_behavior: 'always_invoice', + proration_behavior: options.prorationBehavior || 'always_invoice', items: [{ id, price }], cancel_at_period_end: false, metadata: { - cancellation_reason: null + cancellation_reason: options.cancellationReason ?? null } }); return subscription; From dde7e70f7ccef50dcb1de4107d92045c7eb80bd7 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Wed, 31 Jul 2024 11:16:25 +0100 Subject: [PATCH 096/164] Fixed members import-with-tier alpha creating unexpected invoices (#20695) ref https://linear.app/tryghost/issue/ONC-199 The `updateSubscriptionItemPrice()` method in our Stripe library used by the importer when moving a subscription over to a Ghost product/price was setting `proration_behavior: 'always_invoice'`. This resulted in invoices being created when changing the subscription (even though no prices were changing as far as the customer is concerned) and in some cases where a customer previously had a one-off discount the customer was incorrectly charged the proration difference because the discount was no longer applied to the new invoice. - updated `updateSubscriptionItemPrice()` to accept an `options` param allowing the `proration_behavior` property passed to the Stripe API to be overridden on a per-call basis - updated the `forceStripeSubscriptionToProduct()` method used by the importer to pass an options object with `prorationBehavior: 'none'` when updating the subscription item price so that no invoice and no unexpected charges occur when importing --- .../lib/MembersCSVImporterStripeUtils.js | 6 ++++-- .../test/MembersCSVImporterStripeUtils.test.js | 6 ++++-- ghost/stripe/lib/StripeAPI.js | 9 ++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js b/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js index 82f19adb40..113d97295a 100644 --- a/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js +++ b/ghost/members-importer/lib/MembersCSVImporterStripeUtils.js @@ -153,7 +153,8 @@ module.exports = class MembersCSVImporterStripeUtils { await this._stripeAPIService.updateSubscriptionItemPrice( stripeSubscription.id, stripeSubscriptionItem.id, - newStripePrice.id + newStripePrice.id, + {prorationBehavior: 'none'} ); stripePriceId = newStripePrice.id; @@ -167,7 +168,8 @@ module.exports = class MembersCSVImporterStripeUtils { await this._stripeAPIService.updateSubscriptionItemPrice( stripeSubscription.id, stripeSubscriptionItem.id, - stripePriceId + stripePriceId, + {prorationBehavior: 'none'} ); } } diff --git a/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js b/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js index 9b1176613d..f76159c72e 100644 --- a/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js +++ b/ghost/members-importer/test/MembersCSVImporterStripeUtils.test.js @@ -304,7 +304,8 @@ describe('MembersCSVImporterStripeUtils', function () { stripeAPIServiceStub.updateSubscriptionItemPrice.calledWithExactly( stripeCustomer.subscriptions.data[0].id, stripeCustomerSubscriptionItem.id, - GHOST_PRODUCT_STRIPE_PRICE_ID + GHOST_PRODUCT_STRIPE_PRICE_ID, + {prorationBehavior: 'none'} ).should.be.true(); }); @@ -346,7 +347,8 @@ describe('MembersCSVImporterStripeUtils', function () { stripeAPIServiceStub.updateSubscriptionItemPrice.calledWithExactly( stripeCustomer.subscriptions.data[0].id, stripeCustomerSubscriptionItem.id, - NEW_STRIPE_PRICE_ID + NEW_STRIPE_PRICE_ID, + {prorationBehavior: 'none'} ).should.be.true(); }); diff --git a/ghost/stripe/lib/StripeAPI.js b/ghost/stripe/lib/StripeAPI.js index 504b8b0c1f..999e5a0155 100644 --- a/ghost/stripe/lib/StripeAPI.js +++ b/ghost/stripe/lib/StripeAPI.js @@ -698,20 +698,23 @@ module.exports = class StripeAPI { * @param {string} subscriptionId - The ID of the Subscription to modify * @param {string} id - The ID of the SubscriptionItem * @param {string} price - The ID of the new Price + * @param {object} [options={}] - Additional data to set on the subscription object + * @param {('always_invoice'|'create_prorations'|'none')} [options.prorationBehavior='always_invoice'] - The proration behavior to use. See [Stripe docs](https://docs.stripe.com/api/subscriptions/update#update_subscription-proration_behavior) for more info + * @param {string} [options.cancellationReason=null] - The user defined cancellation reason * * @returns {Promise} */ - async updateSubscriptionItemPrice(subscriptionId, id, price) { + async updateSubscriptionItemPrice(subscriptionId, id, price, options = {}) { await this._rateLimitBucket.throttle(); const subscription = await this._stripe.subscriptions.update(subscriptionId, { - proration_behavior: 'always_invoice', + proration_behavior: options.prorationBehavior || 'always_invoice', items: [{ id, price }], cancel_at_period_end: false, metadata: { - cancellation_reason: null + cancellation_reason: options.cancellationReason ?? null } }); return subscription; From f4f7b91c12dcc8fb1fb606a597b0a151db7c633f Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:55:36 +0000 Subject: [PATCH 097/164] v5.88.3 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 21dc8328ed..61ca1e00f4 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.88.2", + "version": "5.88.3", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index d9551825f9..eb88247ff9 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.88.2", + "version": "5.88.3", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From 086ed9e7a95fc1c636a8ab11ee2a707314062816 Mon Sep 17 00:00:00 2001 From: Princi Vershwal Date: Thu, 1 Aug 2024 13:14:40 +0530 Subject: [PATCH 098/164] Optimising count query : Added option param to skip distinct from count query for members events API (#20700) Ref: https://linear.app/tryghost/issue/ENG-1470/improve-the-performance-of-the-membersevents-aggregated-click-event --- ghost/members-api/lib/repositories/EventRepository.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ghost/members-api/lib/repositories/EventRepository.js b/ghost/members-api/lib/repositories/EventRepository.js index a44541b50c..7bf1f8fc10 100644 --- a/ghost/members-api/lib/repositories/EventRepository.js +++ b/ghost/members-api/lib/repositories/EventRepository.js @@ -507,6 +507,7 @@ module.exports = class EventRepository { ...options, withRelated: ['member'], filter: 'custom:true', + useBasicCount: true, mongoTransformer: chainTransformers( // First set the filter manually replaceCustomFilterTransformer(filter), From 42299abf82634df1ce5077605d0b974d0c0a6166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Winden?= Date: Thu, 1 Aug 2024 11:46:11 +0200 Subject: [PATCH 099/164] Added styling for accent colours on Tiers and Offers (#20696) fixes https://linear.app/tryghost/issue/DES-81/misleading-hardcoded-tier-preview-title-colors Tiers and Offers in Admin were shown with hardcoded pink titles. This changes that. They are now shown with black titles, and only in the preview will they render with the site's accent colour. --------- Co-authored-by: Princi Vershwal --- apps/admin-x-design-system/styles.css | 3 ++- apps/admin-x-design-system/tailwind.config.cjs | 1 + .../src/components/settings/growth/Offers.tsx | 2 +- .../settings/membership/tiers/TierDetailPreview.tsx | 6 +++--- .../src/components/settings/membership/tiers/TiersList.tsx | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/admin-x-design-system/styles.css b/apps/admin-x-design-system/styles.css index 1842f304c2..5ba0b87bf7 100644 --- a/apps/admin-x-design-system/styles.css +++ b/apps/admin-x-design-system/styles.css @@ -96,4 +96,5 @@ /* Prose classes are for formatting arbitrary HTML that comes from the API */ .gh-prose-links a { color: #30CF43; -} \ No newline at end of file +} + diff --git a/apps/admin-x-design-system/tailwind.config.cjs b/apps/admin-x-design-system/tailwind.config.cjs index 7a614ce4f6..59a302365e 100644 --- a/apps/admin-x-design-system/tailwind.config.cjs +++ b/apps/admin-x-design-system/tailwind.config.cjs @@ -18,6 +18,7 @@ module.exports = { colors: { transparent: 'transparent', current: 'currentColor', + accent: 'var(--accent-color, #ff0095)', white: '#FFF', black: '#15171A', grey: { diff --git a/apps/admin-x-settings/src/components/settings/growth/Offers.tsx b/apps/admin-x-settings/src/components/settings/growth/Offers.tsx index dedcc9daaa..75b07ffff6 100644 --- a/apps/admin-x-settings/src/components/settings/growth/Offers.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/Offers.tsx @@ -13,7 +13,7 @@ const OfferContainer: React.FC<{offerTitle: string, tier: Tier, cadence: string, {offerTitle, tier, cadence, redemptions, type, amount, currency, offerId, offerCode, goToOfferEdit}) => { const {discountOffer} = getOfferDiscount(type, amount, cadence, currency || 'USD', tier); return
    goToOfferEdit(offerId)}> - {offerTitle} + {offerTitle}
    {discountOffer}
    diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailPreview.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailPreview.tsx index ae5c115087..ced9678a6d 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailPreview.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailPreview.tsx @@ -22,8 +22,8 @@ export const TrialDaysLabel: React.FC<{size?: 'sm' | 'md'; trialDays: number;}> return ( - - {trialDays} days free + + {trialDays} days free ); }; @@ -96,7 +96,7 @@ const TierDetailPreview: React.FC = ({tier, isFreeTier})
    -

    {name || (isFreeTier ? 'Free' : 'Bronze')}

    +

    {name || (isFreeTier ? 'Free' : 'Bronze')}

    {currencySymbol} diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx index 28c5c6dca1..3aa1b36a08 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx @@ -30,7 +30,7 @@ const TierCard: React.FC = ({tier}) => {
    { updateRoute({route: `tiers/${tier.id}`}); }}> -
    {tier.name}
    +
    {tier.name}
    {currencySymbol} {numberWithCommas(currencyToDecimal(tier.monthly_price || 0))} From aca4626c725bfcbcd6ffa33c532683be9f9a1814 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Thu, 1 Aug 2024 15:35:46 +0200 Subject: [PATCH 100/164] Updated design of announced Notes and Notes containing links (#20703) - Announced (reposted) Notes show information about both the Actor that created the Note, and the Actor that Announced it - The content of notes now keeps the formatting and links are clickable and shown in different color --- .../src/components/ListIndex.tsx | 47 +++++++++++-------- apps/admin-x-activitypub/src/styles/index.css | 15 +++++- .../src/assets/icons/reload.svg | 1 + 3 files changed, 43 insertions(+), 20 deletions(-) create mode 100644 apps/admin-x-design-system/src/assets/icons/reload.svg diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 4c785eead9..75e219edba 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -4,7 +4,7 @@ import articleBodyStyles from './articleBodyStyles'; import getUsername from '../utils/get-username'; import {ActivityPubAPI} from '../api/activitypub'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; -import {Avatar, Button, ButtonGroup, Heading, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; +import {Avatar, Button, ButtonGroup, Heading, Icon, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; import {useQuery} from '@tanstack/react-query'; import {useRouting} from '@tryghost/admin-x-framework/routing'; @@ -123,6 +123,7 @@ const ActivityPubComponent: React.FC = () => { actor={activity.actor} layout={selectedOption.value} object={activity.object} + type={activity.type} /> ))} @@ -320,7 +321,7 @@ ${image && ); }; -const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string }> = ({actor, object, layout}) => { +const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string, type: string }> = ({actor, object, layout, type}) => { const parser = new DOMParser(); const doc = parser.parseFromString(object.content || '', 'text/html'); @@ -396,23 +397,31 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp return ( <> {object && ( -
    - -
    -
    -

    {actor.name}

    - {getUsername(actor)} - {timestamp} -
    -
    -
    - {object.name && {object.name}} -

    {plainTextContent}

    - {/*

    {object.content}

    */} - {renderAttachment()} -
    -
    diff --git a/apps/admin-x-activitypub/src/styles/index.css b/apps/admin-x-activitypub/src/styles/index.css index c3b58ac682..a59dd422e7 100644 --- a/apps/admin-x-activitypub/src/styles/index.css +++ b/apps/admin-x-activitypub/src/styles/index.css @@ -22,4 +22,17 @@ animation: bump 0.3s ease-in-out; .ap-red-heart path { fill: #F50B23; -} \ No newline at end of file +} + +.ap-note-content a { + color: rgb(236 72 153) !important; +} + +.ap-note-content a:hover { + color: rgb(219 39 119) !important; +} + +.ap-note-content p + p { + margin-top: 1.5rem !important; +} + diff --git a/apps/admin-x-design-system/src/assets/icons/reload.svg b/apps/admin-x-design-system/src/assets/icons/reload.svg new file mode 100644 index 0000000000..5afa3c7227 --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/reload.svg @@ -0,0 +1 @@ +Button Refresh Arrows Streamline Icon: https://streamlinehq.com \ No newline at end of file From 68d8f19d5a1339a9d71f77753b0b4b8db977c3af Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Thu, 1 Aug 2024 17:24:11 +0200 Subject: [PATCH 101/164] Revert "Updated design of announced Notes and Notes containing links (#20703)" This reverts commit aca4626c725bfcbcd6ffa33c532683be9f9a1814. --- .../src/components/ListIndex.tsx | 47 ++++++++----------- apps/admin-x-activitypub/src/styles/index.css | 15 +----- .../src/assets/icons/reload.svg | 1 - 3 files changed, 20 insertions(+), 43 deletions(-) delete mode 100644 apps/admin-x-design-system/src/assets/icons/reload.svg diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 75e219edba..4c785eead9 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -4,7 +4,7 @@ import articleBodyStyles from './articleBodyStyles'; import getUsername from '../utils/get-username'; import {ActivityPubAPI} from '../api/activitypub'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; -import {Avatar, Button, ButtonGroup, Heading, Icon, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; +import {Avatar, Button, ButtonGroup, Heading, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; import {useQuery} from '@tanstack/react-query'; import {useRouting} from '@tryghost/admin-x-framework/routing'; @@ -123,7 +123,6 @@ const ActivityPubComponent: React.FC = () => { actor={activity.actor} layout={selectedOption.value} object={activity.object} - type={activity.type} /> ))} @@ -321,7 +320,7 @@ ${image && ); }; -const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string, type: string }> = ({actor, object, layout, type}) => { +const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string }> = ({actor, object, layout}) => { const parser = new DOMParser(); const doc = parser.parseFromString(object.content || '', 'text/html'); @@ -397,31 +396,23 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp return ( <> {object && ( -
    - {(type === 'Announce' && object.type === 'Note') &&
    -
    - {actor.name} reposted -
    } -
    - -
    -
    - {(type === 'Announce' && object.type === 'Note') ? object.attributedTo?.name : actor.name} -
    - {(type === 'Announce' && object.type === 'Note') ? getUsername(object.attributedTo) : getUsername(actor)} - {timestamp} -
    -
    -
    -
    - {object.name && {object.name}} -
    - {/*

    {object.content}

    */} - {renderAttachment()} -
    -
    +
    + +
    +
    +

    {actor.name}

    + {getUsername(actor)} + {timestamp} +
    +
    +
    + {object.name && {object.name}} +

    {plainTextContent}

    + {/*

    {object.content}

    */} + {renderAttachment()} +
    +
    diff --git a/apps/admin-x-activitypub/src/styles/index.css b/apps/admin-x-activitypub/src/styles/index.css index a59dd422e7..c3b58ac682 100644 --- a/apps/admin-x-activitypub/src/styles/index.css +++ b/apps/admin-x-activitypub/src/styles/index.css @@ -22,17 +22,4 @@ animation: bump 0.3s ease-in-out; .ap-red-heart path { fill: #F50B23; -} - -.ap-note-content a { - color: rgb(236 72 153) !important; -} - -.ap-note-content a:hover { - color: rgb(219 39 119) !important; -} - -.ap-note-content p + p { - margin-top: 1.5rem !important; -} - +} \ No newline at end of file diff --git a/apps/admin-x-design-system/src/assets/icons/reload.svg b/apps/admin-x-design-system/src/assets/icons/reload.svg deleted file mode 100644 index 5afa3c7227..0000000000 --- a/apps/admin-x-design-system/src/assets/icons/reload.svg +++ /dev/null @@ -1 +0,0 @@ -Button Refresh Arrows Streamline Icon: https://streamlinehq.com \ No newline at end of file From 1eab73c76d2d48cfa3ce15d2ec6505b37e9335e7 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 1 Aug 2024 16:50:31 +0100 Subject: [PATCH 102/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20YouTube=20live?= =?UTF-8?q?=20embeds=20failing=20in=20some=20situations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/ONC-197 - YouTube has started responding to video page requests with localised content when requested from certain IPs, with that localised content not containing the required `` tag pointing to the oembed endpoint - we were fetching video pages rather than the oembed endpoint for YouTube Live URLs because they are not recognised by the oembed extraction library we use - by modifying the URL from a live URL to a watch URL before we perform oembed lookup/extraction we are able to bypass the (localised) page fetch and instead grab the oembed content directly --- ghost/oembed-service/lib/OEmbedService.js | 12 +++++++++ .../test/oembed-service.test.js | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/ghost/oembed-service/lib/OEmbedService.js b/ghost/oembed-service/lib/OEmbedService.js index c9876027bb..dc7b7cce48 100644 --- a/ghost/oembed-service/lib/OEmbedService.js +++ b/ghost/oembed-service/lib/OEmbedService.js @@ -373,6 +373,18 @@ class OEmbedService { try { const urlObject = new URL(url); + // YouTube has started not returning oembed tags for some live URLs + // when fetched from an IP address that's in a non-EN region. + // We convert live URLs to watch URLs so we can go straight to the + // oembed request via a known provider rather than going through the page fetch routine. + const ytLiveRegex = /^\/live\/([a-zA-Z0-9_-]+)$/; + if (urlObject.hostname === 'www.youtube.com' && ytLiveRegex.test(urlObject.pathname)) { + const videoId = ytLiveRegex.exec(urlObject.pathname)[1]; + urlObject.pathname = '/watch'; + urlObject.searchParams.set('v', videoId); + url = urlObject.toString(); + } + // Trimming solves the difference of url validation between `new URL(url)` // and metascraper. url = url.trim(); diff --git a/ghost/oembed-service/test/oembed-service.test.js b/ghost/oembed-service/test/oembed-service.test.js index 18adf678e2..2e4d45984f 100644 --- a/ghost/oembed-service/test/oembed-service.test.js +++ b/ghost/oembed-service/test/oembed-service.test.js @@ -172,5 +172,31 @@ describe('oembed-service', function () { assert.equal(response.url, 'https://www.example.com'); assert.equal(response.metadata.title, 'Example'); }); + + it('converts YT live URLs to watch URLs', async function () { + nock('https://www.youtube.com') + .get('/oembed') + .query((query) => { + // Ensure the URL is converted to a watch URL and retains existing query params. + const actual = query.url; + const expected = 'https://youtube.com/watch?param=existing&v=1234'; + + assert.equal(actual, expected, 'URL passed to oembed endpoint is incorrect'); + + return actual === expected; + }) + .reply(200, { + type: 'rich', + version: '1.0', + title: 'Test Title', + author_name: 'Test Author', + author_url: 'https://example.com/user/testauthor', + html: '', + width: 640, + height: null + }); + + await oembedService.fetchOembedDataFromUrl('https://www.youtube.com/live/1234?param=existing'); + }); }); }); From ad1a00f60dde9628c404b6a06a95b3a2800672c6 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Thu, 1 Aug 2024 17:13:31 +0100 Subject: [PATCH 103/164] Fixed YT live embeds for non-www URLs ref https://github.com/TryGhost/Ghost/pull/20706 ref https://linear.app/tryghost/issue/ONC-197 - previous check for YT live match was a little too specific and required the www which should have been optional --- ghost/oembed-service/lib/OEmbedService.js | 2 +- .../test/oembed-service.test.js | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ghost/oembed-service/lib/OEmbedService.js b/ghost/oembed-service/lib/OEmbedService.js index dc7b7cce48..85e1330b71 100644 --- a/ghost/oembed-service/lib/OEmbedService.js +++ b/ghost/oembed-service/lib/OEmbedService.js @@ -378,7 +378,7 @@ class OEmbedService { // We convert live URLs to watch URLs so we can go straight to the // oembed request via a known provider rather than going through the page fetch routine. const ytLiveRegex = /^\/live\/([a-zA-Z0-9_-]+)$/; - if (urlObject.hostname === 'www.youtube.com' && ytLiveRegex.test(urlObject.pathname)) { + if (urlObject.hostname.match(/(?:www\.)?youtube\.com/) && ytLiveRegex.test(urlObject.pathname)) { const videoId = ytLiveRegex.exec(urlObject.pathname)[1]; urlObject.pathname = '/watch'; urlObject.searchParams.set('v', videoId); diff --git a/ghost/oembed-service/test/oembed-service.test.js b/ghost/oembed-service/test/oembed-service.test.js index 2e4d45984f..7266c2a53d 100644 --- a/ghost/oembed-service/test/oembed-service.test.js +++ b/ghost/oembed-service/test/oembed-service.test.js @@ -198,5 +198,31 @@ describe('oembed-service', function () { await oembedService.fetchOembedDataFromUrl('https://www.youtube.com/live/1234?param=existing'); }); + + it('converts YT live URLs to watch URLs (non-www)', async function () { + nock('https://www.youtube.com') + .get('/oembed') + .query((query) => { + // Ensure the URL is converted to a watch URL and retains existing query params. + const actual = query.url; + const expected = 'https://youtube.com/watch?param=existing&v=1234'; + + assert.equal(actual, expected, 'URL passed to oembed endpoint is incorrect'); + + return actual === expected; + }) + .reply(200, { + type: 'rich', + version: '1.0', + title: 'Test Title', + author_name: 'Test Author', + author_url: 'https://example.com/user/testauthor', + html: '', + width: 640, + height: null + }); + + await oembedService.fetchOembedDataFromUrl('https://youtube.com/live/1234?param=existing'); + }); }); }); From f147167a291a635f041c208da81cbc3aff5bb1b2 Mon Sep 17 00:00:00 2001 From: Chris Raible Date: Thu, 1 Aug 2024 13:38:59 -0700 Subject: [PATCH 104/164] Added SQLite and MySQL check to migration review checklist (#20708) no issue - knex can behave differently with SQLite and MySQL, which can cause migrations to behave differently in each database. This PR adds a check to the migration review checklist to remind us to test the migration in both databases before merging. --- .github/workflows/migration-review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/migration-review.yml b/.github/workflows/migration-review.yml index 18d0adbcf1..885fa98f7e 100644 --- a/.github/workflows/migration-review.yml +++ b/.github/workflows/migration-review.yml @@ -38,6 +38,7 @@ jobs: - [ ] Uses the correct utils - [ ] Contains a minimal changeset - [ ] Does not mix DDL/DML operations + - [ ] Tested in MySQL and SQLite ### Schema changes From 7522b74e1f9fae259dfb4172ce9a96769bb30218 Mon Sep 17 00:00:00 2001 From: Chris Raible Date: Thu, 1 Aug 2024 16:30:50 -0700 Subject: [PATCH 105/164] Backfilled missing offer redemptions (#20647) ref https://linear.app/tryghost/issue/ENG-1440/backfill-offer-redemption-data-with-a-migration There was a bug that caused offer redemptions to not be recorded in the database for some subscriptions that were created with an offer. However, we still have the `offer_id` attached to the subscriptions, so we are able to backfill the missing redemptions. The bug was fixed in https://github.com/TryGhost/Ghost/commit/bf895e6e99e6fe8e90f0d582fa6e38d5c4e37f5d This commit only contains a migration, which queries for subscriptions that have an `offer_id` but do not have any offer redemptions recorded, and adds any missing redemptions to the `offer_redemptions` table. --- ...-30-19-51-06-backfill-offer-redemptions.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 ghost/core/core/server/data/migrations/versions/5.89/2024-07-30-19-51-06-backfill-offer-redemptions.js diff --git a/ghost/core/core/server/data/migrations/versions/5.89/2024-07-30-19-51-06-backfill-offer-redemptions.js b/ghost/core/core/server/data/migrations/versions/5.89/2024-07-30-19-51-06-backfill-offer-redemptions.js new file mode 100644 index 0000000000..b37b1e3ab7 --- /dev/null +++ b/ghost/core/core/server/data/migrations/versions/5.89/2024-07-30-19-51-06-backfill-offer-redemptions.js @@ -0,0 +1,64 @@ +// For information on writing migrations, see https://www.notion.so/ghost/Database-migrations-eb5b78c435d741d2b34a582d57c24253 + +const logging = require('@tryghost/logging'); +const DatabaseInfo = require('@tryghost/database-info'); +const {default: ObjectID} = require('bson-objectid'); + +// For DML - data changes +const {createTransactionalMigration} = require('../../utils'); + +module.exports = createTransactionalMigration( + async function up(knex) { + // Backfill missing offer redemptions + try { + // Select all subscriptions that have an `offer_id` but don't have a matching row in the `offer_redemptions` table + logging.info('Selecting subscriptions with missing offer redemptions'); + const result = await knex.raw(` + SELECT + mscs.id AS subscription_id, + mscs.offer_id, + mscs.start_date AS created_at, + m.id AS member_id + FROM + members_stripe_customers_subscriptions mscs + LEFT JOIN + offer_redemptions r ON r.subscription_id = mscs.id + INNER JOIN + members_stripe_customers msc ON mscs.customer_id = msc.customer_id + INNER JOIN + members m ON msc.member_id = m.id + WHERE + mscs.offer_id IS NOT NULL and r.id IS NULL; + `); + + // knex.raw() returns a different result depending on the database. We need to handle either case + let rows = []; + if (DatabaseInfo.isSQLite(knex)) { + rows = result; + } else { + rows = result[0]; + } + + // Do the backfil + if (rows && rows.length > 0) { + logging.info(`Backfilling ${rows.length} offer redemptions`); + // Generate IDs for each row + const offerRedemptions = rows.map((row) => { + return { + id: (new ObjectID()).toHexString(), + ...row + }; + }); + // Batch insert rows into the offer_redemptions table + await knex.batchInsert('offer_redemptions', offerRedemptions, 1000); + } else { + logging.info('No offer redemptions to backfill'); + } + } catch (error) { + logging.error(`Error backfilling offer redemptions: ${error.message}`); + } + }, + async function down() { + // We don't want to un-backfill data, so do nothing here. + } +); \ No newline at end of file From 9d44d62b620fa556796588b8456f3ee7bb228b44 Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:05:06 +0000 Subject: [PATCH 106/164] v5.89.0 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 61ca1e00f4..155b57a412 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.88.3", + "version": "5.89.0", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index 571cbd3a11..70c2781e17 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.88.3", + "version": "5.89.0", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From 0a1920ff2ccbca503876ad108e22f7aea60896b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 01:53:45 +0000 Subject: [PATCH 107/164] Update dependency lint-staged to v15.2.8 --- package.json | 2 +- yarn.lock | 119 +++++++++++++++++++++++++++++---------------------- 2 files changed, 70 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 45db669c69..78f8ba82e2 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "eslint-plugin-ghost": "3.4.0", "eslint-plugin-react": "7.33.0", "husky": "8.0.3", - "lint-staged": "15.2.7", + "lint-staged": "15.2.8", "nx": "16.8.1", "rimraf": "5.0.9", "ts-node": "10.9.2", diff --git a/yarn.lock b/yarn.lock index 1b373200a5..e7912b56e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9862,10 +9862,12 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-escapes@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.1.tgz#76c54ce9b081dad39acec4b5d53377913825fb0f" - integrity sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig== +ansi-escapes@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" + integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== + dependencies: + environment "^1.0.0" ansi-html@^0.0.7: version "0.0.7" @@ -13120,12 +13122,12 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" - integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: - restore-cursor "^4.0.0" + restore-cursor "^5.0.0" cli-progress@3.12.0: version "3.12.0" @@ -14379,10 +14381,10 @@ debug@3.2.7, debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -16654,6 +16656,11 @@ envinfo@^7.7.3: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + eol@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" @@ -22184,7 +22191,7 @@ lilconfig@^2.0.5, lilconfig@^2.1.0: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== -lilconfig@^3.0.0, lilconfig@^3.1.1, lilconfig@^3.1.2, lilconfig@~3.1.1: +lilconfig@^3.0.0, lilconfig@^3.1.1, lilconfig@^3.1.2, lilconfig@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== @@ -22238,21 +22245,21 @@ linkifyjs@^4.1.0: resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.1.tgz#73d427e3bbaaf4ca8e71c589ad4ffda11a9a5fde" integrity sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA== -lint-staged@15.2.7: - version "15.2.7" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.7.tgz#97867e29ed632820c0fb90be06cd9ed384025649" - integrity sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw== +lint-staged@15.2.8: + version "15.2.8" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.8.tgz#5e19eb7b4dbb922f56fafb4635b44ee3c92f7322" + integrity sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ== dependencies: chalk "~5.3.0" commander "~12.1.0" - debug "~4.3.4" + debug "~4.3.6" execa "~8.0.1" - lilconfig "~3.1.1" - listr2 "~8.2.1" + lilconfig "~3.1.2" + listr2 "~8.2.4" micromatch "~4.0.7" pidtree "~0.6.0" string-argv "~0.3.2" - yaml "~2.4.2" + yaml "~2.5.0" liquid-fire@0.34.0: version "0.34.0" @@ -22285,16 +22292,16 @@ liquid-wormhole@3.0.1: ember-decorators "^6.1.1" perf-primitives RobbieTheWagner/perf-primitives#a6a26f11497ca27be3763a88a5f20744e424756b -listr2@~8.2.1: - version "8.2.1" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.1.tgz#06a1a6efe85f23c5324180d7c1ddbd96b5eefd6d" - integrity sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g== +listr2@~8.2.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.4.tgz#486b51cbdb41889108cb7e2c90eeb44519f5a77f" + integrity sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g== dependencies: cli-truncate "^4.0.0" colorette "^2.0.20" eventemitter3 "^5.0.1" - log-update "^6.0.0" - rfdc "^1.3.1" + log-update "^6.1.0" + rfdc "^1.4.1" wrap-ansi "^9.0.0" livereload-js@^3.3.1: @@ -22737,14 +22744,14 @@ log-symbols@^2.2.0: dependencies: chalk "^2.0.1" -log-update@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59" - integrity sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw== +log-update@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: - ansi-escapes "^6.2.0" - cli-cursor "^4.0.0" - slice-ansi "^7.0.0" + ansi-escapes "^7.0.0" + cli-cursor "^5.0.0" + slice-ansi "^7.1.0" strip-ansi "^7.1.0" wrap-ansi "^9.0.0" @@ -23507,6 +23514,11 @@ mimic-fn@~3.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -24881,6 +24893,13 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + open@^8.0.4, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -28134,13 +28153,13 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -restore-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" - integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" + onetime "^7.0.0" + signal-exit "^4.1.0" ret@~0.1.10: version "0.1.15" @@ -28164,10 +28183,10 @@ rewire@6.0.0: dependencies: eslint "^7.32.0" -rfdc@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" - integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rgb-regex@^1.0.1: version "1.0.1" @@ -28951,7 +28970,7 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" -slice-ansi@^7.0.0: +slice-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== @@ -32238,10 +32257,10 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.1.1, yaml@^2.4.2, yaml@~2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" - integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== +yaml@^2.1.1, yaml@^2.4.2, yaml@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== yargs-parser@20.2.4: version "20.2.4" From d82f97efcc1ffb45982a472a4e4558237c86fc26 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 01:54:14 +0000 Subject: [PATCH 108/164] Update dependency luxon to v3.5.0 --- ghost/core/package.json | 2 +- ghost/stats-service/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ghost/core/package.json b/ghost/core/package.json index 70c2781e17..6594fe88cc 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -210,7 +210,7 @@ "knex-migrator": "5.2.1", "lib0": "0.2.94", "lodash": "4.17.21", - "luxon": "3.4.4", + "luxon": "3.5.0", "moment": "2.24.0", "moment-timezone": "0.5.45", "multer": "1.4.4", diff --git a/ghost/stats-service/package.json b/ghost/stats-service/package.json index 3d1bd5715b..0585c9025a 100644 --- a/ghost/stats-service/package.json +++ b/ghost/stats-service/package.json @@ -25,7 +25,7 @@ "@types/sinon": "10.0.16", "c8": "8.0.1", "knex": "2.4.2", - "luxon": "3.4.4", + "luxon": "3.5.0", "mocha": "10.2.0", "should": "13.2.3", "sinon": "15.2.0", diff --git a/yarn.lock b/yarn.lock index e7912b56e2..af84ea4338 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22884,10 +22884,10 @@ ltgt@^2.1.2: resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== -luxon@3.4.4, luxon@^3.0.0, luxon@^3.3.0: - version "3.4.4" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" - integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== +luxon@3.5.0, luxon@^3.0.0, luxon@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== luxon@^1.26.0: version "1.28.0" From 4b28812861f1e6a1e8c7346d84c4b9951ff1ba7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:14:06 +0000 Subject: [PATCH 109/164] Update TryGhost packages --- ghost/adapter-manager/package.json | 2 +- ghost/api-framework/package.json | 10 +- ghost/audience-feedback/package.json | 4 +- ghost/bootstrap-socket/package.json | 2 +- ghost/collections/package.json | 8 +- ghost/core/package.json | 38 +- .../package.json | 6 +- ghost/data-generator/package.json | 4 +- ghost/domain-events/package.json | 2 +- ghost/email-analytics-service/package.json | 2 +- ghost/email-service/package.json | 8 +- ghost/ghost/package.json | 2 +- ghost/importer-revue/package.json | 2 +- ghost/job-manager/package.json | 4 +- ghost/link-tracking/package.json | 4 +- ghost/magic-link/package.json | 6 +- ghost/mail-events/package.json | 4 +- ghost/mailgun-client/package.json | 4 +- ghost/members-api/package.json | 8 +- ghost/members-events-service/package.json | 4 +- ghost/members-importer/package.json | 6 +- ghost/members-ssr/package.json | 4 +- ghost/milestones/package.json | 2 +- ghost/minifier/package.json | 6 +- ghost/mw-api-version-mismatch/package.json | 2 +- ghost/mw-error-handler/package.json | 8 +- ghost/mw-version-match/package.json | 4 +- ghost/oembed-service/package.json | 6 +- ghost/offers/package.json | 2 +- ghost/package-json/package.json | 4 +- ghost/payments/package.json | 2 +- ghost/posts-service/package.json | 4 +- ghost/recommendations/package.json | 6 +- ghost/session-service/package.json | 2 +- ghost/settings-path-manager/package.json | 4 +- ghost/slack-notifications/package.json | 6 +- ghost/stripe/package.json | 6 +- ghost/tiers/package.json | 4 +- ghost/update-check-service/package.json | 8 +- ghost/verification-trigger/package.json | 2 +- ghost/webmentions/package.json | 4 +- package.json | 4 +- yarn.lock | 1754 ++++++++--------- 43 files changed, 931 insertions(+), 1043 deletions(-) diff --git a/ghost/adapter-manager/package.json b/ghost/adapter-manager/package.json index f262c6e8a8..68d1f516a3 100644 --- a/ghost/adapter-manager/package.json +++ b/ghost/adapter-manager/package.json @@ -24,6 +24,6 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2" + "@tryghost/errors": "1.3.5" } } diff --git a/ghost/api-framework/package.json b/ghost/api-framework/package.json index 82afad8e19..cd4310d1a6 100644 --- a/ghost/api-framework/package.json +++ b/ghost/api-framework/package.json @@ -24,11 +24,11 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", - "@tryghost/promise": "0.3.10", - "@tryghost/tpl": "0.1.30", - "@tryghost/validator": "0.2.11", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", + "@tryghost/promise": "0.3.12", + "@tryghost/tpl": "0.1.32", + "@tryghost/validator": "0.2.14", "jsonpath": "1.1.1", "lodash": "4.17.21" } diff --git a/ghost/audience-feedback/package.json b/ghost/audience-feedback/package.json index 894ca81fec..d3e8b8ebe0 100644 --- a/ghost/audience-feedback/package.json +++ b/ghost/audience-feedback/package.json @@ -24,8 +24,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", "bson-objectid": "2.0.4" } } diff --git a/ghost/bootstrap-socket/package.json b/ghost/bootstrap-socket/package.json index 102a925e8b..d59ce01e6a 100644 --- a/ghost/bootstrap-socket/package.json +++ b/ghost/bootstrap-socket/package.json @@ -24,6 +24,6 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/logging": "2.4.15" + "@tryghost/logging": "2.4.18" } } diff --git a/ghost/collections/package.json b/ghost/collections/package.json index 39f9e00454..5de7caac5a 100644 --- a/ghost/collections/package.json +++ b/ghost/collections/package.json @@ -26,14 +26,14 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", "@tryghost/in-memory-repository": "0.0.0", - "@tryghost/logging": "2.4.15", + "@tryghost/logging": "2.4.18", "@tryghost/nql": "0.12.3", "@tryghost/nql-filter-expansions": "0.0.0", "@tryghost/post-events": "0.0.0", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "bson-objectid": "2.0.4", "lodash": "4.17.21" }, diff --git a/ghost/core/package.json b/ghost/core/package.json index 6594fe88cc..3bcac936c0 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -76,7 +76,7 @@ "@tryghost/api-framework": "0.0.0", "@tryghost/api-version-compatibility-service": "0.0.0", "@tryghost/audience-feedback": "0.0.0", - "@tryghost/bookshelf-plugins": "0.6.19", + "@tryghost/bookshelf-plugins": "0.6.21", "@tryghost/bootstrap-socket": "0.0.0", "@tryghost/collections": "0.0.0", "@tryghost/color-utils": "0.2.2", @@ -84,24 +84,24 @@ "@tryghost/constants": "0.0.0", "@tryghost/custom-theme-settings-service": "0.0.0", "@tryghost/data-generator": "0.0.0", - "@tryghost/database-info": "0.3.24", - "@tryghost/debug": "0.1.30", + "@tryghost/database-info": "0.3.27", + "@tryghost/debug": "0.1.32", "@tryghost/domain-events": "0.0.0", "@tryghost/donations": "0.0.0", "@tryghost/dynamic-routing-events": "0.0.0", "@tryghost/email-analytics-provider-mailgun": "0.0.0", "@tryghost/email-analytics-service": "0.0.0", "@tryghost/email-content-generator": "0.0.0", - "@tryghost/email-mock-receiver": "0.3.6", + "@tryghost/email-mock-receiver": "0.3.8", "@tryghost/email-service": "0.0.0", "@tryghost/email-suppression-list": "0.0.0", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/express-dynamic-redirects": "0.0.0", "@tryghost/external-media-inliner": "0.0.0", "@tryghost/ghost": "0.0.0", "@tryghost/helpers": "1.1.90", "@tryghost/html-to-plaintext": "0.0.0", - "@tryghost/http-cache-utils": "0.1.15", + "@tryghost/http-cache-utils": "0.1.17", "@tryghost/i18n": "0.0.0", "@tryghost/image-transform": "1.3.0", "@tryghost/importer-handler-content-files": "0.0.0", @@ -119,7 +119,7 @@ "@tryghost/link-redirects": "0.0.0", "@tryghost/link-replacer": "0.0.0", "@tryghost/link-tracking": "0.0.0", - "@tryghost/logging": "2.4.15", + "@tryghost/logging": "2.4.18", "@tryghost/magic-link": "0.0.0", "@tryghost/mail-events": "0.0.0", "@tryghost/mailgun-client": "0.0.0", @@ -143,16 +143,16 @@ "@tryghost/mw-session-from-token": "0.0.0", "@tryghost/mw-version-match": "0.0.0", "@tryghost/mw-vhost": "0.0.0", - "@tryghost/nodemailer": "0.3.42", + "@tryghost/nodemailer": "0.3.45", "@tryghost/nql": "0.12.3", "@tryghost/oembed-service": "0.0.0", "@tryghost/package-json": "0.0.0", "@tryghost/post-revisions": "0.0.0", "@tryghost/posts-service": "0.0.0", - "@tryghost/pretty-cli": "1.2.42", - "@tryghost/promise": "0.3.10", + "@tryghost/pretty-cli": "1.2.44", + "@tryghost/promise": "0.3.12", "@tryghost/recommendations": "0.0.0", - "@tryghost/request": "1.0.5", + "@tryghost/request": "1.0.8", "@tryghost/security": "0.0.0", "@tryghost/session-service": "0.0.0", "@tryghost/settings-path-manager": "0.0.0", @@ -162,14 +162,14 @@ "@tryghost/stats-service": "0.0.0", "@tryghost/string": "0.2.12", "@tryghost/tiers": "0.0.0", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "@tryghost/update-check-service": "0.0.0", "@tryghost/url-utils": "4.4.8", - "@tryghost/validator": "0.2.11", + "@tryghost/validator": "0.2.14", "@tryghost/verification-trigger": "0.0.0", - "@tryghost/version": "0.1.28", + "@tryghost/version": "0.1.30", "@tryghost/webmentions": "0.0.0", - "@tryghost/zip": "1.1.43", + "@tryghost/zip": "1.1.46", "amperize": "0.6.1", "body-parser": "1.20.2", "bookshelf": "1.2.0", @@ -237,8 +237,8 @@ "devDependencies": { "@actions/core": "1.10.1", "@playwright/test": "1.38.1", - "@tryghost/express-test": "0.13.12", - "@tryghost/webhook-mock-receiver": "0.2.12", + "@tryghost/express-test": "0.13.15", + "@tryghost/webhook-mock-receiver": "0.2.14", "@types/common-tags": "1.8.4", "c8": "8.0.1", "cli-progress": "3.12.0", @@ -265,8 +265,8 @@ "toml": "3.0.0" }, "resolutions": { - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "jackspeak": "2.1.1", "moment": "2.24.0", "moment-timezone": "0.5.45" diff --git a/ghost/custom-theme-settings-service/package.json b/ghost/custom-theme-settings-service/package.json index 35db5859f3..f2ed6b8694 100644 --- a/ghost/custom-theme-settings-service/package.json +++ b/ghost/custom-theme-settings-service/package.json @@ -25,10 +25,10 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", "@tryghost/nql": "0.12.3", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "lodash": "4.17.21" } } diff --git a/ghost/data-generator/package.json b/ghost/data-generator/package.json index 07686799ae..97deb478a2 100644 --- a/ghost/data-generator/package.json +++ b/ghost/data-generator/package.json @@ -18,7 +18,7 @@ "lib" ], "devDependencies": { - "@tryghost/debug": "0.1.30", + "@tryghost/debug": "0.1.32", "c8": "8.0.1", "knex": "2.4.2", "mocha": "10.2.0", @@ -27,7 +27,7 @@ }, "dependencies": { "@faker-js/faker": "7.6.0", - "@tryghost/root-utils": "0.3.28", + "@tryghost/root-utils": "0.3.30", "@tryghost/string": "0.2.12", "csv-writer": "1.6.0", "probability-distributions": "0.9.1" diff --git a/ghost/domain-events/package.json b/ghost/domain-events/package.json index 103251b27f..70be7359d4 100644 --- a/ghost/domain-events/package.json +++ b/ghost/domain-events/package.json @@ -19,7 +19,7 @@ "lib" ], "devDependencies": { - "@tryghost/logging": "2.4.15", + "@tryghost/logging": "2.4.18", "c8": "8.0.1", "mocha": "10.2.0", "should": "13.2.3" diff --git a/ghost/email-analytics-service/package.json b/ghost/email-analytics-service/package.json index 43be69c86e..473c67fad3 100644 --- a/ghost/email-analytics-service/package.json +++ b/ghost/email-analytics-service/package.json @@ -23,7 +23,7 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", + "@tryghost/debug": "0.1.32", "lodash": "4.17.21" } } diff --git a/ghost/email-service/package.json b/ghost/email-service/package.json index 96842c6917..b8e2d30751 100644 --- a/ghost/email-service/package.json +++ b/ghost/email-service/package.json @@ -27,12 +27,12 @@ "dependencies": { "@tryghost/color-utils": "0.2.2", "@tryghost/email-events": "0.0.0", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/html-to-plaintext": "0.0.0", "@tryghost/kg-default-cards": "10.0.6", - "@tryghost/logging": "2.4.15", - "@tryghost/tpl": "0.1.30", - "@tryghost/validator": "0.2.11", + "@tryghost/logging": "2.4.18", + "@tryghost/tpl": "0.1.32", + "@tryghost/validator": "0.2.14", "bson-objectid": "2.0.4", "cheerio": "0.22.0", "handlebars": "4.7.8", diff --git a/ghost/ghost/package.json b/ghost/ghost/package.json index cbfae89329..cc07181be9 100644 --- a/ghost/ghost/package.json +++ b/ghost/ghost/package.json @@ -36,7 +36,7 @@ "@nestjs/common": "10.3.10", "@nestjs/core": "10.3.10", "@nestjs/platform-express": "10.3.10", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "bson-objectid": "2.0.4", "express": "4.19.2", "reflect-metadata": "0.1.14", diff --git a/ghost/importer-revue/package.json b/ghost/importer-revue/package.json index d6046580ba..c2e5e14a2a 100644 --- a/ghost/importer-revue/package.json +++ b/ghost/importer-revue/package.json @@ -21,7 +21,7 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", + "@tryghost/debug": "0.1.32", "@tryghost/kg-default-cards": "10.0.6", "@tryghost/string": "0.2.12", "lodash": "4.17.21", diff --git a/ghost/job-manager/package.json b/ghost/job-manager/package.json index 77a6e0af54..b89f594892 100644 --- a/ghost/job-manager/package.json +++ b/ghost/job-manager/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "@breejs/later": "4.2.0", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "bree": "6.5.0", "cron-validate": "1.4.5", "fastq": "1.17.1", diff --git a/ghost/link-tracking/package.json b/ghost/link-tracking/package.json index 455f963288..a82652c3e4 100644 --- a/ghost/link-tracking/package.json +++ b/ghost/link-tracking/package.json @@ -24,10 +24,10 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/link-redirects": "0.0.0", "@tryghost/nql": "0.12.3", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "bson-objectid": "2.0.4", "lodash": "4.17.21", "moment": "2.29.4" diff --git a/ghost/magic-link/package.json b/ghost/magic-link/package.json index e9cab6d08a..dad7f48e82 100644 --- a/ghost/magic-link/package.json +++ b/ghost/magic-link/package.json @@ -26,9 +26,9 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", - "@tryghost/validator": "0.2.11", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", + "@tryghost/validator": "0.2.14", "jsonwebtoken": "8.5.1" } } diff --git a/ghost/mail-events/package.json b/ghost/mail-events/package.json index db55eee5a9..7a9978a298 100644 --- a/ghost/mail-events/package.json +++ b/ghost/mail-events/package.json @@ -25,9 +25,9 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/in-memory-repository": "0.0.0", - "@tryghost/tpl": "0.1.30" + "@tryghost/tpl": "0.1.32" }, "c8": { "exclude": [ diff --git a/ghost/mailgun-client/package.json b/ghost/mailgun-client/package.json index f8c09d69be..eb8a288b18 100644 --- a/ghost/mailgun-client/package.json +++ b/ghost/mailgun-client/package.json @@ -24,8 +24,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/logging": "2.4.15", + "@tryghost/debug": "0.1.32", + "@tryghost/logging": "2.4.18", "@tryghost/metrics": "1.0.34", "form-data": "4.0.0", "lodash": "4.17.21", diff --git a/ghost/members-api/package.json b/ghost/members-api/package.json index d3e004e6db..e6cb3a542e 100644 --- a/ghost/members-api/package.json +++ b/ghost/members-api/package.json @@ -31,14 +31,14 @@ }, "dependencies": { "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "@tryghost/magic-link": "0.0.0", "@tryghost/member-events": "0.0.0", "@tryghost/members-payments": "0.0.0", "@tryghost/nql": "0.12.3", - "@tryghost/tpl": "0.1.30", - "@tryghost/validator": "0.2.11", + "@tryghost/tpl": "0.1.32", + "@tryghost/validator": "0.2.14", "@types/jsonwebtoken": "9.0.6", "body-parser": "1.20.2", "bson-objectid": "2.0.4", diff --git a/ghost/members-events-service/package.json b/ghost/members-events-service/package.json index 566df42419..4d431e5c92 100644 --- a/ghost/members-events-service/package.json +++ b/ghost/members-events-service/package.json @@ -26,8 +26,8 @@ }, "dependencies": { "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "@tryghost/member-events": "0.0.0", "moment-timezone": "0.5.34" } diff --git a/ghost/members-importer/package.json b/ghost/members-importer/package.json index d681e9d851..7727b98d4a 100644 --- a/ghost/members-importer/package.json +++ b/ghost/members-importer/package.json @@ -25,11 +25,11 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "@tryghost/members-csv": "0.0.0", "@tryghost/metrics": "1.0.34", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "moment-timezone": "0.5.45" } } diff --git a/ghost/members-ssr/package.json b/ghost/members-ssr/package.json index fcc1b099fe..72a485a8de 100644 --- a/ghost/members-ssr/package.json +++ b/ghost/members-ssr/package.json @@ -26,8 +26,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", "cookies": "0.9.1", "jsonwebtoken": "8.5.1" } diff --git a/ghost/milestones/package.json b/ghost/milestones/package.json index 80d92a0e80..0723edd9cb 100644 --- a/ghost/milestones/package.json +++ b/ghost/milestones/package.json @@ -23,7 +23,7 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "bson-objectid": "2.0.4" } } diff --git a/ghost/minifier/package.json b/ghost/minifier/package.json index 6a6e1abfb9..e9db19f5b7 100644 --- a/ghost/minifier/package.json +++ b/ghost/minifier/package.json @@ -24,9 +24,9 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", "csso": "5.0.5", "terser": "5.31.3", "tiny-glob": "0.2.9" diff --git a/ghost/mw-api-version-mismatch/package.json b/ghost/mw-api-version-mismatch/package.json index 9bcc10c009..727eedf455 100644 --- a/ghost/mw-api-version-mismatch/package.json +++ b/ghost/mw-api-version-mismatch/package.json @@ -18,7 +18,7 @@ "lib" ], "devDependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "c8": "8.0.1", "mocha": "10.2.0", "sinon": "15.2.0" diff --git a/ghost/mw-error-handler/package.json b/ghost/mw-error-handler/package.json index 208718369f..15130d7b9e 100644 --- a/ghost/mw-error-handler/package.json +++ b/ghost/mw-error-handler/package.json @@ -23,10 +23,10 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", - "@tryghost/http-cache-utils": "0.1.15", - "@tryghost/tpl": "0.1.30", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", + "@tryghost/http-cache-utils": "0.1.17", + "@tryghost/tpl": "0.1.32", "lodash": "4.17.21", "semver": "7.6.3" } diff --git a/ghost/mw-version-match/package.json b/ghost/mw-version-match/package.json index 890676ee29..7f3dc6705a 100644 --- a/ghost/mw-version-match/package.json +++ b/ghost/mw-version-match/package.json @@ -23,8 +23,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", "semver": "7.6.3" } } diff --git a/ghost/oembed-service/package.json b/ghost/oembed-service/package.json index 8708ee9d69..187381fe4a 100644 --- a/ghost/oembed-service/package.json +++ b/ghost/oembed-service/package.json @@ -23,9 +23,9 @@ }, "dependencies": { "@extractus/oembed-extractor": "3.2.1", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", - "@tryghost/tpl": "0.1.30", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", + "@tryghost/tpl": "0.1.32", "charset": "1.0.1", "cheerio": "0.22.0", "iconv-lite": "0.6.3", diff --git a/ghost/offers/package.json b/ghost/offers/package.json index aa104048a9..25d02f8180 100644 --- a/ghost/offers/package.json +++ b/ghost/offers/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/mongo-utils": "0.6.2", "@tryghost/string": "0.2.12", "lodash": "4.17.21" diff --git a/ghost/package-json/package.json b/ghost/package-json/package.json index 4b3d8ea228..60d3edb3fc 100644 --- a/ghost/package-json/package.json +++ b/ghost/package-json/package.json @@ -25,8 +25,8 @@ "tmp": "0.2.1" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", "fs-extra": "11.2.0", "lodash": "4.17.21" } diff --git a/ghost/payments/package.json b/ghost/payments/package.json index aa72723503..8bd5acfe64 100644 --- a/ghost/payments/package.json +++ b/ghost/payments/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/members-offers": "0.0.0", "@tryghost/tiers": "0.0.0" } diff --git a/ghost/posts-service/package.json b/ghost/posts-service/package.json index b9b913a72a..8119717be7 100644 --- a/ghost/posts-service/package.json +++ b/ghost/posts-service/package.json @@ -23,10 +23,10 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/nql": "0.12.3", "@tryghost/post-events": "0.0.0", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "bson-objectid": "2.0.4" } } diff --git a/ghost/recommendations/package.json b/ghost/recommendations/package.json index 530e773076..b380c88926 100644 --- a/ghost/recommendations/package.json +++ b/ghost/recommendations/package.json @@ -30,10 +30,10 @@ "typescript": "5.4.5" }, "dependencies": { - "@tryghost/tpl": "0.1.30", - "@tryghost/errors": "1.3.2", + "@tryghost/tpl": "0.1.32", + "@tryghost/errors": "1.3.5", "@tryghost/in-memory-repository": "0.0.0", "@tryghost/bookshelf-repository": "0.0.0", - "@tryghost/logging": "2.4.15" + "@tryghost/logging": "2.4.18" } } diff --git a/ghost/session-service/package.json b/ghost/session-service/package.json index b93b13c641..082e5a48a3 100644 --- a/ghost/session-service/package.json +++ b/ghost/session-service/package.json @@ -25,6 +25,6 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2" + "@tryghost/errors": "1.3.5" } } diff --git a/ghost/settings-path-manager/package.json b/ghost/settings-path-manager/package.json index 8bcf869280..d9d75d97bd 100644 --- a/ghost/settings-path-manager/package.json +++ b/ghost/settings-path-manager/package.json @@ -24,8 +24,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/tpl": "0.1.30", + "@tryghost/errors": "1.3.5", + "@tryghost/tpl": "0.1.32", "date-fns": "2.30.0" } } diff --git a/ghost/slack-notifications/package.json b/ghost/slack-notifications/package.json index 0c8307766e..b728f7b816 100644 --- a/ghost/slack-notifications/package.json +++ b/ghost/slack-notifications/package.json @@ -23,9 +23,9 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/validator": "0.2.11", - "@tryghost/version": "0.1.28", + "@tryghost/errors": "1.3.5", + "@tryghost/validator": "0.2.14", + "@tryghost/version": "0.1.30", "got": "9.6.0" } } diff --git a/ghost/stripe/package.json b/ghost/stripe/package.json index a175465f7d..ee08ccf5f0 100644 --- a/ghost/stripe/package.json +++ b/ghost/stripe/package.json @@ -25,10 +25,10 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/debug": "0.1.30", + "@tryghost/debug": "0.1.32", "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "@tryghost/member-events": "0.0.0", "leaky-bucket": "2.2.0", "lodash": "4.17.21", diff --git a/ghost/tiers/package.json b/ghost/tiers/package.json index 15decb771a..f3d3bb56db 100644 --- a/ghost/tiers/package.json +++ b/ghost/tiers/package.json @@ -22,9 +22,9 @@ "mocha": "10.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/string": "0.2.12", - "@tryghost/tpl": "0.1.30", + "@tryghost/tpl": "0.1.32", "bson-objectid": "2.0.4" } } diff --git a/ghost/update-check-service/package.json b/ghost/update-check-service/package.json index c0a9b77ee7..976f4d27d0 100644 --- a/ghost/update-check-service/package.json +++ b/ghost/update-check-service/package.json @@ -25,10 +25,10 @@ "uuid": "9.0.1" }, "dependencies": { - "@tryghost/debug": "0.1.30", - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", - "@tryghost/tpl": "0.1.30", + "@tryghost/debug": "0.1.32", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", + "@tryghost/tpl": "0.1.32", "lodash": "4.17.21", "moment": "2.24.0" } diff --git a/ghost/verification-trigger/package.json b/ghost/verification-trigger/package.json index 4bf23b5ff4..7260b5484b 100644 --- a/ghost/verification-trigger/package.json +++ b/ghost/verification-trigger/package.json @@ -25,7 +25,7 @@ }, "dependencies": { "@tryghost/domain-events": "0.0.0", - "@tryghost/errors": "1.3.2", + "@tryghost/errors": "1.3.5", "@tryghost/member-events": "0.0.0" } } diff --git a/ghost/webmentions/package.json b/ghost/webmentions/package.json index 8c9bb05a99..b36acd059e 100644 --- a/ghost/webmentions/package.json +++ b/ghost/webmentions/package.json @@ -25,8 +25,8 @@ "sinon": "15.2.0" }, "dependencies": { - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "cheerio": "0.22.0" } } diff --git a/package.json b/package.json index 78f8ba82e2..fdf79efde4 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "prepare": "husky install .github/hooks" }, "resolutions": { - "@tryghost/errors": "1.3.2", - "@tryghost/logging": "2.4.15", + "@tryghost/errors": "1.3.5", + "@tryghost/logging": "2.4.18", "jackspeak": "2.1.1", "moment": "2.24.0", "moment-timezone": "0.5.45" diff --git a/yarn.lock b/yarn.lock index af84ea4338..f3ed36ecfb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5532,927 +5532,838 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== -"@stdlib/array-float32@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-float32/-/array-float32-0.0.6.tgz#7a1c89db3c911183ec249fa32455abd9328cfa27" - integrity sha512-QgKT5UaE92Rv7cxfn7wBKZAlwFFHPla8eXsMFsTGt5BiL4yUy36lwinPUh4hzybZ11rw1vifS3VAPuk6JP413Q== +"@stdlib/array-float32@^0.2.1", "@stdlib/array-float32@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-float32/-/array-float32-0.2.2.tgz#88dcbb6cb138da3f3b4bc565423a0afc4dec4e1b" + integrity sha512-pTcy1FNQrrJLL1LMxJjuVpcKJaibbGCFFTe41iCSXpSOC8SuTBuNohrO6K9+xR301Ruxxn4yrzjJJ6Fa3nQJ2g== dependencies: - "@stdlib/assert-has-float32array-support" "^0.0.x" + "@stdlib/assert-has-float32array-support" "^0.2.2" -"@stdlib/array-float64@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-float64/-/array-float64-0.0.6.tgz#02d1c80dd4c38a0f1ec150ddfefe706e148bfc10" - integrity sha512-oE8y4a84LyBF1goX5//sU1mOjet8gLI0/6wucZcjg+j/yMmNV1xFu84Az9GOGmFSE6Ze6lirGOhfBeEWNNNaJg== +"@stdlib/array-float64@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-float64/-/array-float64-0.2.2.tgz#66b3a6fd0e030da1b3d9ba195b865791486ec3a7" + integrity sha512-ZmV5wcacGrhT0maw9dfLXNv4N3ZwFUV3D7ItFfZFGFnKIJbubrWzwtaYnxzIXigrDc8g3F6FVHRpsQLMxq0/lA== dependencies: - "@stdlib/assert-has-float64array-support" "^0.0.x" + "@stdlib/assert-has-float64array-support" "^0.2.2" -"@stdlib/array-int16@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-int16/-/array-int16-0.0.6.tgz#01ce2a8f5b1d3e4dfeaec257a48d8d201bdc9bff" - integrity sha512-WLx0PivdjosNAp+4ZWPlsBh/nUn50j+7H+SLxASPIILv217muLUGvttMyFCEmJE7Fs2cP51SHDR1EPAfypvY+g== +"@stdlib/array-int16@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-int16/-/array-int16-0.2.2.tgz#00855f829f68aad659049de86b9180c662b1f6a7" + integrity sha512-kHxyQ1INGtga38Grr/5MnDVAuJgnerh+MsJQcpT5jxxnc9QAnVc7O6DRv8i/hfOOxUOH15C/MeoBs+zim4CnLQ== dependencies: - "@stdlib/assert-has-int16array-support" "^0.0.x" + "@stdlib/assert-has-int16array-support" "^0.2.2" -"@stdlib/array-int32@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-int32/-/array-int32-0.0.6.tgz#2ab3dc8fb018a36151728324bb6b686bde52bada" - integrity sha512-BKYOoqNsFwEOiPjZp9jKLY4UE5Rp+Liwuwd91QpZW6/cTUeOpTnwZheFWjMFuY06JYRIMaEBwcnr0RfaMetH6Q== +"@stdlib/array-int32@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-int32/-/array-int32-0.2.2.tgz#50b6b6cf8e5f4a11a8c3bcec22dd41e26d2b95a8" + integrity sha512-+jFqht43oPJ8YnlyCZ7cSf9Z8xenIIsJDgLZ9zW+gh8o13SSfF+ukm0AGAdnKnKGR3zPBLnSso7JXyDe2r134g== dependencies: - "@stdlib/assert-has-int32array-support" "^0.0.x" + "@stdlib/assert-has-int32array-support" "^0.2.2" -"@stdlib/array-int8@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-int8/-/array-int8-0.0.6.tgz#1720035f12afe571b144395f7f678888b208dc0c" - integrity sha512-ZZsAQixtzk7v80DAFUZDn58AhDXpUtDjVFdOKnEw5td9nGBv3vXCM2y7zz48n/NUZOOeoGc5GTVR72anJ/Vi4g== +"@stdlib/array-int8@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-int8/-/array-int8-0.2.2.tgz#ca1adf97fe04ab1f4c87a608d04a0273d7c42d91" + integrity sha512-UW3KlKt7Lww1XML5Gzj+YYHRXD8+RIUrnlPcTwYH9O8j+/m5vyvGYlBIJD2MDO1fgUl2skgmpNkK9ULfsBlIRA== dependencies: - "@stdlib/assert-has-int8array-support" "^0.0.x" + "@stdlib/assert-has-int8array-support" "^0.2.2" -"@stdlib/array-uint16@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-uint16/-/array-uint16-0.0.6.tgz#2545110f0b611a1d55b01e52bd9160aaa67d6973" - integrity sha512-/A8Tr0CqJ4XScIDRYQawosko8ha1Uy+50wsTgJhjUtXDpPRp7aUjmxvYkbe7Rm+ImYYbDQVix/uCiPAFQ8ed4Q== +"@stdlib/array-uint16@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-uint16/-/array-uint16-0.2.2.tgz#d9647ec67f86dcb032b4e72659df818874498959" + integrity sha512-z5c/Izw43HkKfb1pTgEUMAS8GFvhtHkkHZSjX3XJN+17P0VjknxjlSvPiCBGqaDX9jXtlWH3mn1LSyDKtJQoeA== dependencies: - "@stdlib/assert-has-uint16array-support" "^0.0.x" + "@stdlib/assert-has-uint16array-support" "^0.2.2" -"@stdlib/array-uint32@^0.0.x": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/array-uint32/-/array-uint32-0.0.6.tgz#5a923576475f539bfb2fda4721ea7bac6e993949" - integrity sha512-2hFPK1Fg7obYPZWlGDjW9keiIB6lXaM9dKmJubg/ergLQCsJQJZpYsG6mMAfTJi4NT1UF4jTmgvyKD+yf0D9cA== +"@stdlib/array-uint32@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-uint32/-/array-uint32-0.2.2.tgz#0e772f971706e7060fa1878f81b0fe05b86c8f07" + integrity sha512-3T894I9C2MqZJJmRCYFTuJp4Qw9RAt+GzYnVPyIXoK1h3TepUXe9VIVx50cUFIibdXycgu0IFGASeAb3YMyupw== dependencies: - "@stdlib/assert-has-uint32array-support" "^0.0.x" + "@stdlib/assert-has-uint32array-support" "^0.2.2" -"@stdlib/array-uint8@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/array-uint8/-/array-uint8-0.0.7.tgz#56f82b361da6bd9caad0e1d05e7f6ef20af9c895" - integrity sha512-qYJQQfGKIcky6TzHFIGczZYTuVlut7oO+V8qUBs7BJC9TwikVnnOmb3hY3jToY4xaoi5p9OvgdJKPInhyIhzFg== +"@stdlib/array-uint8@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-uint8/-/array-uint8-0.2.2.tgz#4add6fc8fd574c6330a6162aac1ebb421f8a0a82" + integrity sha512-Ip9MUC8+10U9x0crMKWkpvfoUBBhWzc6k5SI4lxx38neFVmiJ3f+5MBADEagjpoKSBs71vlY2drnEZe+Gs2Ytg== dependencies: - "@stdlib/assert-has-uint8array-support" "^0.0.x" + "@stdlib/assert-has-uint8array-support" "^0.2.2" -"@stdlib/array-uint8c@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/array-uint8c/-/array-uint8c-0.0.8.tgz#ce9298512dfa25dca559b72b080d3e906b2289b3" - integrity sha512-gKc6m6QUpcUrMJsWe9na7Mb20Cswdu1ul31kxq+MKRtkV5eCTVksh69Q9FKjaNdEy0A19sR413sGV7YY8ZvdSQ== +"@stdlib/array-uint8c@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/array-uint8c/-/array-uint8c-0.2.2.tgz#91c79bdf4d755c08b8fc6c9ff150216ee0fb9d86" + integrity sha512-uBEJ1yKLZjwgmCV7iSNLkr/SGCxL7qVwnb+f4avVSBxlIv/k29oIO/sibgkHbZMBlBSw39dWQzIKD0UQJWDVDQ== dependencies: - "@stdlib/assert-has-uint8clampedarray-support" "^0.0.x" + "@stdlib/assert-has-uint8clampedarray-support" "^0.2.2" -"@stdlib/assert-has-float32array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-float32array-support/-/assert-has-float32array-support-0.0.8.tgz#77371183726e26ca9e6f9db41d34543607074067" - integrity sha512-Yrg7K6rBqwCzDWZ5bN0VWLS5dNUWcoSfUeU49vTERdUmZID06J069CDc07UUl8vfQWhFgBWGocH3rrpKm1hi9w== +"@stdlib/assert-has-float32array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-float32array-support/-/assert-has-float32array-support-0.2.2.tgz#dacf3439d9a91be30c5637144a2f9afc342ef258" + integrity sha512-pi2akQl8mVki43fF1GNQVLYW0bHIPp2HuRNThX9GjB3OFQTpvrV8/3zPSh4lOxQa5gRiabgf0+Rgeu3AOhEw9A== dependencies: - "@stdlib/assert-is-float32array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-float64-pinf" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-float32array" "^0.2.2" + "@stdlib/constants-float64-pinf" "^0.2.2" -"@stdlib/assert-has-float64array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-float64array-support/-/assert-has-float64array-support-0.0.8.tgz#4d154994d348f5d894f63b3fbb9d7a6e2e4e5311" - integrity sha512-UVQcoeWqgMw9b8PnAmm/sgzFnuWkZcNhJoi7xyMjbiDV/SP1qLCrvi06mq86cqS3QOCma1fEayJdwgteoXyyuw== +"@stdlib/assert-has-float64array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-float64array-support/-/assert-has-float64array-support-0.2.2.tgz#228ed3c8a174c4a467b6daccb24b6c9c307cbab5" + integrity sha512-8L3GuKY1o0dJARCOsW9MXcugXapaMTpSG6dGxyNuUVEvFfY5UOzcj9/JIDal5FjqSgqVOGL5qZl2qtRwub34VA== dependencies: - "@stdlib/assert-is-float64array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-float64array" "^0.2.2" -"@stdlib/assert-has-int16array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int16array-support/-/assert-has-int16array-support-0.0.8.tgz#1adf8a4341788a56b50a3ab2000feb065bede794" - integrity sha512-w/5gByEPRWpbEWfzvcBbDHAkzK0tp8ExzF00N+LY6cJR1BxcBIXXtLfhY3G6jchs3Od3Pn89rhnsAxygumuw4w== +"@stdlib/assert-has-int16array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int16array-support/-/assert-has-int16array-support-0.2.2.tgz#8cec13b0406e9e361861e44cbb9246a6bfdf5af8" + integrity sha512-rIrJ2371vd4kg5sHb/bz0xX1dlwyElT6kivnFPtZUXMaMS1breSUIpwPkUPXdr1AbmQdeJYv0c/YBtKB0PCo2g== dependencies: - "@stdlib/assert-is-int16array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-int16-max" "^0.0.x" - "@stdlib/constants-int16-min" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-int16array" "^0.2.2" + "@stdlib/constants-int16-max" "^0.2.2" + "@stdlib/constants-int16-min" "^0.2.2" -"@stdlib/assert-has-int32array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int32array-support/-/assert-has-int32array-support-0.0.8.tgz#efd01955b4c11feb5d1703fdd994c17413fede97" - integrity sha512-xFbbDTp8pNMucuL45mhr0p10geTXE2A46/uor1l6riAP61c3qPRTKbe+0YapEjR9E6JyL134IX8AYQlqjYdBnQ== +"@stdlib/assert-has-int32array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int32array-support/-/assert-has-int32array-support-0.2.2.tgz#bab04f3378db0ad45b85898a7fd3c240b7dbdab9" + integrity sha512-3o/6PuZ+z6+cimb9L1f0d4LGP2GJO4EUVlD6oz8I6vKz35438coM5CduXOMVWvdzm4Rku4ty5mi1Gz0U53pC4Q== dependencies: - "@stdlib/assert-is-int32array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-int32-max" "^0.0.x" - "@stdlib/constants-int32-min" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-int32array" "^0.2.2" + "@stdlib/constants-int32-max" "^0.3.0" + "@stdlib/constants-int32-min" "^0.2.2" -"@stdlib/assert-has-int8array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int8array-support/-/assert-has-int8array-support-0.0.8.tgz#4e65306197e75e136920241a98b8934022564ddd" - integrity sha512-c+6eq8OtUBtJrn1HaBfT+zk+FjkNA2JG9GqI2/eq8c/l6fUI1TCKmKAML63rp95aJhosCCAMMLJmnG4jFkGG1g== +"@stdlib/assert-has-int8array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-int8array-support/-/assert-has-int8array-support-0.2.2.tgz#3995dbcd3beb62f579c474adc9c6e8dbb0bf3695" + integrity sha512-QUasYxAqQdgqDglQTwV9dZSwMXvLzLpVC4FcN+kia/+nX1HGjYUBq95fBj0vigW+SC2OiuepJkNiB/PlhA08+g== dependencies: - "@stdlib/assert-is-int8array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-int8-max" "^0.0.x" - "@stdlib/constants-int8-min" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-int8array" "^0.2.2" + "@stdlib/constants-int8-max" "^0.2.2" + "@stdlib/constants-int8-min" "^0.2.2" -"@stdlib/assert-has-node-buffer-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-node-buffer-support/-/assert-has-node-buffer-support-0.0.8.tgz#5564d8e797c850f6ffc522b720eab1f6cba9c814" - integrity sha512-fgI+hW4Yg4ciiv4xVKH+1rzdV7e5+6UKgMnFbc1XDXHcxLub3vOr8+H6eDECdAIfgYNA7X0Dxa/DgvX9dwDTAQ== +"@stdlib/assert-has-node-buffer-support@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-node-buffer-support/-/assert-has-node-buffer-support-0.2.2.tgz#cb6b1a2a2927ef40dc4c8368a6c0d36854ccb70f" + integrity sha512-9ReyJGYe9BEZXbUxeRBima0nYy6GwFLVBj7eQ+UUzfG8w7LYYUCpWk954yDpd0v/u+XZQvp1M8EQC2gJLd/i0g== dependencies: - "@stdlib/assert-is-buffer" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-buffer" "^0.2.2" -"@stdlib/assert-has-own-property@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-own-property/-/assert-has-own-property-0.0.7.tgz#8b55b38e25db8366b028cb871905ac09c9c253fb" - integrity sha512-3YHwSWiUqGlTLSwxAWxrqaD1PkgcJniGyotJeIt5X0tSNmSW0/c9RWroCImTUUB3zBkyBJ79MyU9Nf4Qgm59fQ== +"@stdlib/assert-has-own-property@^0.2.1", "@stdlib/assert-has-own-property@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-own-property/-/assert-has-own-property-0.2.2.tgz#072661539bb79c353dc5e62ae9252ce428adb5f1" + integrity sha512-m5rV4Z2/iNkwx2vRsNheM6sQZMzc8rQQOo90LieICXovXZy8wA5jNld4kRKjMNcRt/TjrNP7i2Rhh8hruRDlHg== -"@stdlib/assert-has-symbol-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-symbol-support/-/assert-has-symbol-support-0.0.8.tgz#8606b247f0d023f2a7a6aa8a6fe5e346aa802a8f" - integrity sha512-PoQ9rk8DgDCuBEkOIzGGQmSnjtcdagnUIviaP5YskB45/TJHXseh4NASWME8FV77WFW9v/Wt1MzKFKMzpDFu4Q== +"@stdlib/assert-has-symbol-support@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-symbol-support/-/assert-has-symbol-support-0.2.2.tgz#ed7abecb6ae513c5f52dbd14d4601f3d707ab19f" + integrity sha512-vCsGGmDZz5dikGgdF26rIL0y0nHvH7qaVf89HLLTybceuZijAqFSJEqcB3Gpl5uaeueLNAWExHi2EkoUVqKHGg== + +"@stdlib/assert-has-tostringtag-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-tostringtag-support/-/assert-has-tostringtag-support-0.2.2.tgz#4e5053b69571aca325b9ccf26f8e6acbf8190acb" + integrity sha512-bSHGqku11VH0swPEzO4Y2Dr+lTYEtjSWjamwqCTC8udOiOIOHKoxuU4uaMGKJjVfXG1L+XefLHqzuO5azxdRaA== dependencies: - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-has-symbol-support" "^0.2.1" -"@stdlib/assert-has-tostringtag-support@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-tostringtag-support/-/assert-has-tostringtag-support-0.0.9.tgz#1080ef0a4be576a72d19a819498719265456f170" - integrity sha512-UTsqdkrnQ7eufuH5BeyWOJL3ska3u5nvDWKqw3onNNZ2mvdgkfoFD7wHutVGzAA2rkTsSJAMBHVwWLsm5SbKgw== +"@stdlib/assert-has-uint16array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint16array-support/-/assert-has-uint16array-support-0.2.2.tgz#b94f9adf53292151129e46a4f2aae2629c679a86" + integrity sha512-aL188V7rOkkEH4wYjfpB+1waDO4ULxo5ppGEK6X0kG4YiXYBL2Zyum53bjEQvo0Nkn6ixe18dNzqqWWytBmDeg== dependencies: - "@stdlib/assert-has-symbol-support" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-uint16array" "^0.2.1" + "@stdlib/constants-uint16-max" "^0.2.2" -"@stdlib/assert-has-uint16array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint16array-support/-/assert-has-uint16array-support-0.0.8.tgz#083828067d55e3cc896796bc63cbf5726f67eecf" - integrity sha512-vqFDn30YrtzD+BWnVqFhB130g3cUl2w5AdOxhIkRkXCDYAM5v7YwdNMJEON+D4jI8YB4D5pEYjqKweYaCq4nyg== +"@stdlib/assert-has-uint32array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint32array-support/-/assert-has-uint32array-support-0.2.2.tgz#d5b70c4c068cff8dec176fcd71868690e47abee9" + integrity sha512-+UHKP3mZOACkJ9CQjeKNfbXHm5HGQB862V5nV5q3UQlHPzhslnXKyG1SwAxTx+0g88C/2vlDLeqG8H4TH2UTFA== dependencies: - "@stdlib/assert-is-uint16array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-uint16-max" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-uint32array" "^0.2.1" + "@stdlib/constants-uint32-max" "^0.2.2" -"@stdlib/assert-has-uint32array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint32array-support/-/assert-has-uint32array-support-0.0.8.tgz#a98c431fee45743088adb9602ef753c7552f9155" - integrity sha512-tJtKuiFKwFSQQUfRXEReOVGXtfdo6+xlshSfwwNWXL1WPP2LrceoiUoQk7zMCMT6VdbXgGH92LDjVcPmSbH4Xw== +"@stdlib/assert-has-uint8array-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint8array-support/-/assert-has-uint8array-support-0.2.2.tgz#33af366594a8540a643360763aada11a1d837075" + integrity sha512-VfzrB0BMik9MvPyKcMDJL3waq4nM30RZUrr2EuuQ/RbUpromRWSDbzGTlRq5SfjtJrHDxILPV3rytDCc03dgWA== dependencies: - "@stdlib/assert-is-uint32array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-uint32-max" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-uint8array" "^0.2.1" + "@stdlib/constants-uint8-max" "^0.2.2" -"@stdlib/assert-has-uint8array-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint8array-support/-/assert-has-uint8array-support-0.0.8.tgz#9bed19de9834c3ced633551ed630982f0f424724" - integrity sha512-ie4vGTbAS/5Py+LLjoSQi0nwtYBp+WKk20cMYCzilT0rCsBI/oez0RqHrkYYpmt4WaJL4eJqC+/vfQ5NsI7F5w== +"@stdlib/assert-has-uint8clampedarray-support@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint8clampedarray-support/-/assert-has-uint8clampedarray-support-0.2.2.tgz#8b1ee4ab857b19747290f4448ac9a69e2ec5695f" + integrity sha512-/zT8Piv1UUFUpelBo0LuTE4V9BOEW7DTwfGlPvez93lk72XtaIYhTHkj+Z9YBGfAMV2PbL6eteqFffMVzUgnXg== dependencies: - "@stdlib/assert-is-uint8array" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/constants-uint8-max" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-uint8clampedarray" "^0.2.1" -"@stdlib/assert-has-uint8clampedarray-support@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-has-uint8clampedarray-support/-/assert-has-uint8clampedarray-support-0.0.8.tgz#07aa0274a5ce78c12fb30b00dde5e2dfcf568120" - integrity sha512-Z6ZeUZqsfZ48rTE7o58k4DXP8kP6rrlmPCpDaMlBqP/yZcmt8qSLtdT68PiAJ/gzURbRbHYD1hwLWPJDzhRS9g== +"@stdlib/assert-is-arguments@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-arguments/-/assert-is-arguments-0.2.2.tgz#32e13d3723987d13afb580840a56835e24142aab" + integrity sha512-SejXrHpneNcn0nDot2K49XoTzPQQUvCFQj+2vc4UtJdtFFzB5r1fYgvtL7RqkhVglD7zNJG1acJYa7PiZCpZog== dependencies: - "@stdlib/assert-is-uint8clampedarray" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.2" + "@stdlib/assert-is-array" "^0.2.2" + "@stdlib/assert-is-enumerable-property" "^0.2.2" + "@stdlib/constants-uint32-max" "^0.2.2" + "@stdlib/math-base-assert-is-integer" "^0.2.5" + "@stdlib/utils-native-class" "^0.2.2" -"@stdlib/assert-is-arguments@^0.0.x": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-arguments/-/assert-is-arguments-0.0.14.tgz#5a7266634df0e30be1c06fed1aa62c1e28ea67b3" - integrity sha512-jhMkdQsCHcAUQmk0t8Dof/I1sThotcJ3vcFigqwTEzVS7DQb2BVQ5egHtwdHFRyNf46u0Yfm8b2r6es+uYdWOQ== +"@stdlib/assert-is-array@^0.2.1", "@stdlib/assert-is-array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-array/-/assert-is-array-0.2.2.tgz#ba820d24dd914fe8c29bd61033417ab5a2c2c34f" + integrity sha512-aJyTX2U3JqAGCATgaAX9ygvDHc97GCIKkIhiZm/AZaLoFHPtMA1atQ4bKcefEC8Um9eefryxTHfFPfSr9CoNQQ== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-array" "^0.0.x" - "@stdlib/assert-is-enumerable-property" "^0.0.x" - "@stdlib/constants-uint32-max" "^0.0.x" - "@stdlib/math-base-assert-is-integer" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-array@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-array/-/assert-is-array-0.0.7.tgz#7f30904f88a195d918c588540a6807d1ae639d79" - integrity sha512-/o6KclsGkNcZ5hiROarsD9XUs6xQMb4lTwF6O71UHbKWTtomEF/jD0rxLvlvj0BiCxfKrReddEYd2CnhUyskMA== +"@stdlib/assert-is-boolean@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-boolean/-/assert-is-boolean-0.2.2.tgz#1d6361f66a25cd81ae12085da6ce1457311758ee" + integrity sha512-3KFLRTYZpX6u95baZ6PubBvjehJs2xBU6+zrenR0jx8KToUYCnJPxqqj7JXRhSD+cOURmcjj9rocVaG9Nz18Pg== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-has-tostringtag-support" "^0.2.2" + "@stdlib/boolean-ctor" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-boolean@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-boolean/-/assert-is-boolean-0.0.8.tgz#6b38c2e799e4475d7647fb0e44519510e67080ce" - integrity sha512-PRCpslMXSYqFMz1Yh4dG2K/WzqxTCtlKbgJQD2cIkAtXux4JbYiXCtepuoV7l4Wv1rm0a1eU8EqNPgnOmWajGw== +"@stdlib/assert-is-buffer@^0.2.1", "@stdlib/assert-is-buffer@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-buffer/-/assert-is-buffer-0.2.2.tgz#f32894cc86103c151e144cf3dbac63ef9e3f8f15" + integrity sha512-4/WMFTEcDYlVbRhxY8Wlqag4S70QCnn6WmQ4wmfiLW92kqQHsLvTNvdt/qqh/SDyDV31R/cpd3QPsVN534dNEA== dependencies: - "@stdlib/assert-has-tostringtag-support" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-is-object-like" "^0.2.1" -"@stdlib/assert-is-buffer@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-buffer/-/assert-is-buffer-0.0.8.tgz#633b98bc342979e9ed8ed71c3a0f1366782d1412" - integrity sha512-SYmGwOXkzZVidqUyY1IIx6V6QnSL36v3Lcwj8Rvne/fuW0bU2OomsEBzYCFMvcNgtY71vOvgZ9VfH3OppvV6eA== +"@stdlib/assert-is-collection@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-collection/-/assert-is-collection-0.2.2.tgz#398c8138202c2334a32254edb00ea74a614c6768" + integrity sha512-tbh6ySMqzTkHjFzcNkwtfnJgt65qWeu+0Wv3N0D9QEd3gnJfWq4mJJS3DyJ5n91VoB7RXppB/giDxDUCw/Y+KQ== dependencies: - "@stdlib/assert-is-object-like" "^0.0.x" + "@stdlib/constants-array-max-typed-array-length" "^0.2.2" + "@stdlib/math-base-assert-is-integer" "^0.2.5" -"@stdlib/assert-is-collection@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-collection/-/assert-is-collection-0.0.8.tgz#5710cd14010a83007922b0c66c8b605b9db0b8af" - integrity sha512-OyKXC8OgvxqLUuJPzVX58j26puOVqnIG2OsxxwtZQ5rwFIcwirYy0LrBfSaF0JX+njau6zb5de+QEURA+mQIgA== +"@stdlib/assert-is-enumerable-property@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-enumerable-property/-/assert-is-enumerable-property-0.2.2.tgz#c6f6460ea0a01712bc8bca26049523294e13d486" + integrity sha512-An5QM9Zb3bjKZp5XVdsHTv6/8pJMJvnweHDdLPQhEtLve3YIM4Xo0CQ3TlvKgTr6Lz2UO/+yx8rrDdN5i9Dp5Q== dependencies: - "@stdlib/constants-array-max-typed-array-length" "^0.0.x" - "@stdlib/math-base-assert-is-integer" "^0.0.x" + "@stdlib/assert-is-integer" "^0.2.2" + "@stdlib/assert-is-nan" "^0.2.2" + "@stdlib/assert-is-string" "^0.2.2" -"@stdlib/assert-is-enumerable-property@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-enumerable-property/-/assert-is-enumerable-property-0.0.7.tgz#0eb71ff950278d22de5ad337ee4a8d79228a81cd" - integrity sha512-jkhuJgpaiJlTxxkAvacbFl23PI5oO41ecmz1UcngVYI6bMeWZLNdkvFQri0W3ZaDem4zyXi6Kw3G/ohkIHq92g== +"@stdlib/assert-is-error@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-error/-/assert-is-error-0.2.2.tgz#07e56ad03cb55ac8630dd8ecac842e00568e8182" + integrity sha512-HKw/vTJvXG8OvDSWSRA5nCKAgKxvzG7xack0xjCUTw1Xh3q8mMmQtCTjwkRtFyvSFDd0DeacMc/Ur5sU5bzOgg== dependencies: - "@stdlib/assert-is-integer" "^0.0.x" - "@stdlib/assert-is-nan" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" + "@stdlib/utils-get-prototype-of" "^0.2.1" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-error@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-error/-/assert-is-error-0.0.8.tgz#9161fb469292314231d0c56565efa94ee65ce7c3" - integrity sha512-844/g+vprVw2QP4VzgJZdlZ2hVDvC72vTKMEZFLJL7Rlx0bC+CXxi0rN2BE9txnkn3ILkBYbi9VYH1UREsP/hQ== +"@stdlib/assert-is-float32array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-float32array/-/assert-is-float32array-0.2.2.tgz#8b6187136f95e3ef8ba8acad33197736e4844bfb" + integrity sha512-hxEKz/Y4m1NYuOaiQKoqQA1HeAYwNXFqSk3FJ4hC71DuGNit2tuxucVyck3mcWLpLmqo0+Qlojgwo5P9/C/9MQ== dependencies: - "@stdlib/utils-get-prototype-of" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-float32array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-float32array/-/assert-is-float32array-0.0.8.tgz#a43f6106a2ef8797496ab85aaf6570715394654a" - integrity sha512-Phk0Ze7Vj2/WLv5Wy8Oo7poZIDMSTiTrEnc1t4lBn3Svz2vfBXlvCufi/i5d93vc4IgpkdrOEwfry6nldABjNQ== +"@stdlib/assert-is-float64array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-float64array/-/assert-is-float64array-0.2.2.tgz#c69a894d85a0a9c71f8b68b3aea1ea35bd3ebe85" + integrity sha512-3R1wLi6u/IHXsXMtaLnvN9BSpqAJ8tWhwjOOr6kadDqCWsU7Odc7xKLeAXAInAxwnV8VDpO4ifym4A3wehazPQ== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-float64array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-float64array/-/assert-is-float64array-0.0.8.tgz#8c27204ae6cf309e16f0bbad1937f8aa06c2a812" - integrity sha512-UC0Av36EEYIgqBbCIz1lj9g7qXxL5MqU1UrWun+n91lmxgdJ+Z77fHy75efJbJlXBf6HXhcYXECIsc0u3SzyDQ== +"@stdlib/assert-is-function@^0.2.1", "@stdlib/assert-is-function@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-function/-/assert-is-function-0.2.2.tgz#97b54f449e54fd15913054cc69c7385ea9baab81" + integrity sha512-whY69DUYWljCJ79Cvygp7VzWGOtGTsh3SQhzNuGt+ut6EsOW+8nwiRkyBXYKf/MOF+NRn15pxg8cJEoeRgsPcA== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-type-of" "^0.2.1" -"@stdlib/assert-is-function@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-function/-/assert-is-function-0.0.8.tgz#e4925022b7dd8c4a67e86769691d1d29ab159db9" - integrity sha512-M55Dt2njp5tnY8oePdbkKBRIypny+LpCMFZhEjJIxjLE4rA6zSlHs1yRMqD4PmW+Wl9WTeEM1GYO4AQHl1HAjA== +"@stdlib/assert-is-int16array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int16array/-/assert-is-int16array-0.2.2.tgz#2358c371ff651231a3d0ccf4a0cd1edf13cfef77" + integrity sha512-HQ9yyX1di07pWreBk6njw9x1sipJKSP4SCSkidLfUHKxaooxeUprAFONfMiEMdNBbn7f3awfs23h1bpN/Z6Vmg== dependencies: - "@stdlib/utils-type-of" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-int16array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int16array/-/assert-is-int16array-0.0.8.tgz#af4aaabb74a81b5eb52e534f4508b587664ee70e" - integrity sha512-liepMcQ58WWLQdBv9bz6Ium2llUlFzr3ximhCSaswpAAUQw3Zpd+vY3mEzG+b6hDhQoj3bBllUkaN2kkCUCwMw== +"@stdlib/assert-is-int32array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int32array/-/assert-is-int32array-0.2.2.tgz#64a948b9b23b0943c39930d4e59f55e2917715c4" + integrity sha512-YomgTwoJD/ci8K9mWNCyqSDtkYfHNplMYw+B9rmcxrjX//1LVZkrzgwWEc6dC3RlY0Ou+uDHJpKeKL9G2fj4GQ== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-int32array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int32array/-/assert-is-int32array-0.0.8.tgz#226a6dd57807dafe298a14f8feedd834b33b1c9b" - integrity sha512-bsrGwVNiaasGnQgeup1RLFRSEk8GE/cm0iKvvPZLlzTBC+NJ1wUZgjLSiEh+ccy4JdgfMddJf4j7zSqOxoFWxw== +"@stdlib/assert-is-int8array@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int8array/-/assert-is-int8array-0.2.2.tgz#9fc5063c8a3ed70feee357fe3b8fa01bde376e89" + integrity sha512-Y1QP3uIZ+CG+rFrD6nOO/N/8O1rRbXgG+iVo5aj8xNRUtfg1iYekUspfNKqxeZUJ95Ocv705m7/vsGlvI1MugQ== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-int8array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-int8array/-/assert-is-int8array-0.0.8.tgz#43e29e8b1f57b80543e5e46a37100e05dc40e8de" - integrity sha512-hzJAFSsG702hHO0nkMkog8nelK6elJdBNsuHWDciMd7iTIIjernGL1GbB8712Yg9xPGYgm8n6tXonDEEQ5loIw== +"@stdlib/assert-is-integer@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-integer/-/assert-is-integer-0.2.2.tgz#2b0b76e11926b7530b510c80e2f3e3fdf271a368" + integrity sha512-2d4CioQmnPcNDvNfC3Q6+xAJLwYYcSUamnxP0bSBJ1oAazWaVArdXNUAUxufek2Uaq6TVIM2gNSMyivIKIJd2w== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-is-number" "^0.2.2" + "@stdlib/constants-float64-ninf" "^0.2.2" + "@stdlib/constants-float64-pinf" "^0.2.2" + "@stdlib/math-base-assert-is-integer" "^0.2.4" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/assert-is-integer@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-integer/-/assert-is-integer-0.0.8.tgz#7a2b5778a9ec530a12031b6a6ff7c58c6892e50f" - integrity sha512-gCjuKGglSt0IftXJXIycLFNNRw0C+8235oN0Qnw3VAdMuEWauwkNhoiw0Zsu6Arzvud8MQJY0oBGZtvLUC6QzQ== +"@stdlib/assert-is-nan@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-nan/-/assert-is-nan-0.2.2.tgz#8d1a65a4ea0c5db87dadb0778bb1eef97b007826" + integrity sha512-Wh7KPIVfi6UVBRuPgkjVnoJP6mVtDNg+Y4m3Hko86TSf78KqFXfyZy/m6hnlYBWZRkNJDKo1J/7A/zpPwcEUVg== dependencies: - "@stdlib/assert-is-number" "^0.0.x" - "@stdlib/constants-float64-ninf" "^0.0.x" - "@stdlib/constants-float64-pinf" "^0.0.x" - "@stdlib/math-base-assert-is-integer" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/assert-is-number" "^0.2.2" + "@stdlib/math-base-assert-is-nan" "^0.2.1" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/assert-is-nan@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-nan/-/assert-is-nan-0.0.8.tgz#91d5289c088a03063f9d603de2bd99d3dec6d40d" - integrity sha512-K57sjcRzBybdRpCoiuqyrn/d+R0X98OVlmXT4xEk3VPYqwux8e0NModVFHDehe+zuhmZLvYM50mNwp1TQC2AxA== +"@stdlib/assert-is-nonnegative-integer@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-nonnegative-integer/-/assert-is-nonnegative-integer-0.2.2.tgz#c47a7afabede723bfc05ed02b28a590163ec03f9" + integrity sha512-4t2FoZQeZ5nMYHYSeTVlgAp/HLEMYqe9qMcJgbvj63KTrGCDsuIpTE0S+UTxAc6Oc3Ftgb0ygjBFJQ0mxwN0Ow== dependencies: - "@stdlib/assert-is-number" "^0.0.x" - "@stdlib/math-base-assert-is-nan" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/assert-is-integer" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/assert-is-nonnegative-integer@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-nonnegative-integer/-/assert-is-nonnegative-integer-0.0.7.tgz#e6aa304dbca14020e87ea05687eccd696ef27035" - integrity sha512-+5SrGM3C1QRpzmi+JnyZF9QsH29DCkSONm2558yOTdfCLClYOXDs++ktQo/8baCBFSi9JnFaLXVt1w1sayQeEQ== +"@stdlib/assert-is-number@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-number/-/assert-is-number-0.2.2.tgz#269ab5bf779a26a2cec7575c9a47e163f5bb74b2" + integrity sha512-sWpJ59GqGbmlcdYSUV/OYkmQW8k47w10+E0K0zPu1x1VKzhjgA5ZB2sJcpgI8Vt3ckRLjdhuc62ZHJkrJujG7A== dependencies: - "@stdlib/assert-is-integer" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/assert-has-tostringtag-support" "^0.2.2" + "@stdlib/number-ctor" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-number@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-number/-/assert-is-number-0.0.7.tgz#82b07cda4045bd0ecc846d3bc26d39dca7041c61" - integrity sha512-mNV4boY1cUOmoWWfA2CkdEJfXA6YvhcTvwKC0Fzq+HoFFOuTK/scpTd9HanUyN6AGBlWA8IW+cQ1ZwOT3XMqag== +"@stdlib/assert-is-object-like@^0.2.1", "@stdlib/assert-is-object-like@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-object-like/-/assert-is-object-like-0.2.2.tgz#3bd47386addeb7ccb4ac82b9d924ddaa5fddde57" + integrity sha512-MjQBpHdEebbJwLlxh/BKNH8IEHqY0YlcCMRKOQU0UOlILSJg0vG+GL4fDDqtx9FSXxcTqC+w3keHx8kAKvQhzg== dependencies: - "@stdlib/assert-has-tostringtag-support" "^0.0.x" - "@stdlib/number-ctor" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-tools-array-function" "^0.2.1" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/assert-is-object-like@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-object-like/-/assert-is-object-like-0.0.8.tgz#f6fc36eb7b612d650c6201d177214733426f0c56" - integrity sha512-pe9selDPYAu/lYTFV5Rj4BStepgbzQCr36b/eC8EGSJh6gMgRXgHVv0R+EbdJ69KNkHvKKRjnWj0A/EmCwW+OA== +"@stdlib/assert-is-object@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-object/-/assert-is-object-0.2.2.tgz#671297efc43788aa5368ce59ede28a8089387a7f" + integrity sha512-sNnphJuHyMDHHHaonlx6vaCKMe4sHOn0ag5Ck4iW3kJtM2OZB2J4h8qFcwKzlMk7fgFu7vYNGCZtpm1dYbbUfQ== dependencies: - "@stdlib/assert-tools-array-function" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/assert-is-array" "^0.2.1" -"@stdlib/assert-is-object@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-object/-/assert-is-object-0.0.8.tgz#0220dca73bc3df044fc43e73b02963d5ef7ae489" - integrity sha512-ooPfXDp9c7w+GSqD2NBaZ/Du1JRJlctv+Abj2vRJDcDPyrnRTb1jmw+AuPgcW7Ca7op39JTbArI+RVHm/FPK+Q== +"@stdlib/assert-is-plain-object@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-plain-object/-/assert-is-plain-object-0.2.2.tgz#90b67c33ec6430ee5ca5a4c053ef5843550a3435" + integrity sha512-o4AFWgBsSNzZAOOfIrxoDFYTqnLuGiaHDFwIeZGUHdpQeav2Fll+sGeaqOcekF7yKawoswnwWdJqTsjapb4Yzw== dependencies: - "@stdlib/assert-is-array" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.1" + "@stdlib/assert-is-function" "^0.2.1" + "@stdlib/assert-is-object" "^0.2.1" + "@stdlib/utils-get-prototype-of" "^0.2.1" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-plain-object@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-plain-object/-/assert-is-plain-object-0.0.7.tgz#0c3679faf61b03023363f1ce30f8d00f8ed1c37b" - integrity sha512-t/CEq2a083ajAgXgSa5tsH8l3kSoEqKRu1qUwniVLFYL4RGv3615CrpJUDQKVtEX5S/OKww5q0Byu3JidJ4C5w== +"@stdlib/assert-is-regexp@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-regexp/-/assert-is-regexp-0.2.2.tgz#4d0f24c5ab189da3839ceca7e6955d263d7b798d" + integrity sha512-2JtiUtRJxPaVXL7dkWoV3n5jouI65DwYDXsDXg3xo23TXlTNGgU/HhKO4FWC1Yqju7YMZi0hcZSW6E9v8ISqeQ== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/assert-is-object" "^0.0.x" - "@stdlib/utils-get-prototype-of" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-has-tostringtag-support" "^0.2.2" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-regexp-string@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-regexp-string/-/assert-is-regexp-string-0.0.9.tgz#424f77b4aaa46a19f4b60ba4b671893a2e5df066" - integrity sha512-FYRJJtH7XwXEf//X6UByUC0Eqd0ZYK5AC8or5g5m5efQrgr2lOaONHyDQ3Scj1A2D6QLIJKZc9XBM4uq5nOPXA== +"@stdlib/assert-is-string@^0.2.1", "@stdlib/assert-is-string@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-string/-/assert-is-string-0.2.2.tgz#2f3099045f5c9bdb85bf7620c021c17e5be19f2f" + integrity sha512-SOkFg4Hq443hkadM4tzcwTHWvTyKP9ULOZ8MSnnqmU0nBX1zLVFLFGY8jnF6Cary0dL0V7QQBCfuxqKFM6u2PQ== dependencies: - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-read-stdin" "^0.0.x" - "@stdlib/regexp-eol" "^0.0.x" - "@stdlib/regexp-regexp" "^0.0.x" - "@stdlib/streams-node-stdin" "^0.0.x" + "@stdlib/assert-has-tostringtag-support" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-regexp@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-regexp/-/assert-is-regexp-0.0.7.tgz#430fe42417114e7ea01d21399a70ed9c4cbae867" - integrity sha512-ty5qvLiqkDq6AibHlNJe0ZxDJ9Mg896qolmcHb69mzp64vrsORnPPOTzVapAq0bEUZbXoypeijypLPs9sCGBSQ== +"@stdlib/assert-is-uint16array@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint16array/-/assert-is-uint16array-0.2.2.tgz#85346d95d8fd08c879a0b33a210d9224f54a2d4b" + integrity sha512-w3+HeTiXGLJGw5nCqr0WbvgArNMEj7ulED1Yd19xXbmmk2W1ZUB+g9hJDOQTiKsTU4AVyH4/As+aA8eDVmWtmg== dependencies: - "@stdlib/assert-has-tostringtag-support" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-string@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-string/-/assert-is-string-0.0.8.tgz#b07e4a4cbd93b13d38fa5ebfaa281ccd6ae9e43f" - integrity sha512-Uk+bR4cglGBbY0q7O7HimEJiW/DWnO1tSzr4iAGMxYgf+VM2PMYgI5e0TLy9jOSOzWon3YS39lc63eR3a9KqeQ== +"@stdlib/assert-is-uint32array@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint32array/-/assert-is-uint32array-0.2.2.tgz#37f35526101e5847c54cb8c9952976d1888a0bb8" + integrity sha512-3F4nIHg1Qp0mMIsImWUC8DwQ3qBK5vdIJTjS2LufLbFBhHNmv5kK1yJiIXQDTLkENU0STZe05TByo01ZNLOmDQ== dependencies: - "@stdlib/assert-has-tostringtag-support" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-uint16array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint16array/-/assert-is-uint16array-0.0.8.tgz#770cc5d86906393d30d387a291e81df0a984fdfb" - integrity sha512-M+qw7au+qglRXcXHjvoUZVLlGt1mPjuKudrVRto6KL4+tDsP2j+A89NDP3Fz8/XIUD+5jhj+65EOKHSMvDYnng== +"@stdlib/assert-is-uint8array@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint8array/-/assert-is-uint8array-0.2.2.tgz#2d46b13d58b8d1b6aa4e4841fbb6903c6cd07a08" + integrity sha512-51WnDip6H2RrN0CbqWmfqySAjam8IZ0VjlfUDc3PtcgrZGrKKjVgyHAsT/L3ZDydwF+aB94uvYJu5QyrCPNaZw== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-uint32array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint32array/-/assert-is-uint32array-0.0.8.tgz#2a7f1265db25d728e3fc084f0f59be5f796efac5" - integrity sha512-cnZi2DicYcplMnkJ3dBxBVKsRNFjzoGpmG9A6jXq4KH5rFl52SezGAXSVY9o5ZV7bQGaF5JLyCLp6n9Y74hFGg== +"@stdlib/assert-is-uint8clampedarray@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint8clampedarray/-/assert-is-uint8clampedarray-0.2.2.tgz#3b4cbbe0c74326967fe868ab1d1288ce02cbbc83" + integrity sha512-MjHhOxjOXesqUNgoDGOiO9vib1HV3uCNoYQfiEDWAv30pVAty70wEcwDQ7cdQs1ZGfGC/355ob8AR2Z8lY4ryw== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/assert-is-uint8array@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint8array/-/assert-is-uint8array-0.0.8.tgz#4521054b5d3a2206b406cad7368e0a50eaee4dec" - integrity sha512-8cqpDQtjnJAuVtRkNAktn45ixq0JHaGJxVsSiK79k7GRggvMI6QsbzO6OvcLnZ/LimD42FmgbLd13Yc2esDmZw== +"@stdlib/assert-tools-array-function@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/assert-tools-array-function/-/assert-tools-array-function-0.2.2.tgz#aba9b71b5164e97872cd2d6b16b221e01bd8c5e0" + integrity sha512-FYeT7X9x0C8Nh+MN6IJUDz+7i7yB6mio2/SDlrvyepjyPSU/cfHfwW0GEOnQhxZ+keLZC/YqDD930WjRODwMdA== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-is-array" "^0.2.1" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/assert-is-uint8clampedarray@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/assert-is-uint8clampedarray/-/assert-is-uint8clampedarray-0.0.8.tgz#e0206354dd3055e170a8c998ca1d0663d3799ab9" - integrity sha512-CkXVpivLTkfrPBJf/60tJLHCzMEjVdwzKxNSybdSJ5w8lXVXIp7jgs44mXqIHJm09XgPEc3ljEyXUf5FcJTIvw== +"@stdlib/boolean-ctor@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/boolean-ctor/-/boolean-ctor-0.2.2.tgz#d0add4760adeca22631625dd95bb9ca32abb931a" + integrity sha512-qIkHzmfxDvGzQ3XI9R7sZG97QSaWG5TvWVlrvcysOGT1cs6HtQgnf4D//SRzZ52VLm8oICP+6OKtd8Hpm6G7Ww== + +"@stdlib/buffer-ctor@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/buffer-ctor/-/buffer-ctor-0.2.2.tgz#8469a6d301b4b11e08763b3238b949b2aa132841" + integrity sha512-Q/FkXxyZUzCA1fwOl7sa8ZYg6e60fTksCYr01nJv8fvmr9l9Ju6MKmm20n833yE7KA5jDDtZW9lB1V7552fLMA== dependencies: - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-has-node-buffer-support" "^0.2.1" -"@stdlib/assert-tools-array-function@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/assert-tools-array-function/-/assert-tools-array-function-0.0.7.tgz#34e9e5a3fca62ea75da99fc9995ba845ba514988" - integrity sha512-3lqkaCIBMSJ/IBHHk4NcCnk2NYU52tmwTYbbqhAmv7vim8rZPNmGfj3oWkzrCsyCsyTF7ooD+In2x+qTmUbCtQ== +"@stdlib/buffer-from-buffer@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/buffer-from-buffer/-/buffer-from-buffer-0.2.2.tgz#10e277a9856f457017f78ada38177b7dd2178e98" + integrity sha512-EU/Mju7j3Hw/z6xxlZ14h6ZEyhiarR4nXhDQwycJ4NcFb7Oi+KaeNvX5rRQgf/k04cpcq8VkQiM1xOMrKPCvqA== dependencies: - "@stdlib/assert-is-array" "^0.0.x" + "@stdlib/assert-is-buffer" "^0.2.2" + "@stdlib/assert-is-function" "^0.2.2" + "@stdlib/buffer-ctor" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/buffer-ctor@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/buffer-ctor/-/buffer-ctor-0.0.7.tgz#d05b7f4a6ef26defe6cdd41ca244a927b96c55ec" - integrity sha512-4IyTSGijKUQ8+DYRaKnepf9spvKLZ+nrmZ+JrRcB3FrdTX/l9JDpggcUcC/Fe+A4KIZOnClfxLn6zfIlkCZHNA== +"@stdlib/complex-float32-ctor@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@stdlib/complex-float32-ctor/-/complex-float32-ctor-0.0.2.tgz#57f6d3f0217c1ae1f83ea12b044a80e951a215d3" + integrity sha512-QsTLynhTRmDT0mSkfdHj0FSqQSxh2nKx+vvrH3Y0/Cd/r0WoHFZwyibndDxshfkf9B7nist8QKyvV82I3IZciA== dependencies: - "@stdlib/assert-has-node-buffer-support" "^0.0.x" + "@stdlib/assert-is-number" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/number-float64-base-to-float32" "^0.2.1" + "@stdlib/string-format" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" + "@stdlib/utils-define-property" "^0.2.4" -"@stdlib/buffer-from-buffer@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/buffer-from-buffer/-/buffer-from-buffer-0.0.7.tgz#871d2eb4307776b5c14d57175d1f57ed8a058d54" - integrity sha512-ytFnWFXdkrpiFNb/ZlyJrqRyiGMGuv9zDa/IbbotcbEwfmjvvLa+nvKS5B57HfFrcBxq6L0oWYmZ2uYctKckyg== +"@stdlib/complex-float32-reim@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@stdlib/complex-float32-reim/-/complex-float32-reim-0.1.2.tgz#095fcd9e4c2657d58c294eeeaeb5d46591d844bc" + integrity sha512-24H+t1xwQF6vhOoMZdDA3TFB4M+jb5Swm/FwNaepovlzVIG2NlthUZs6mZg1T3oegqesIRQRwhpn4jIPjuGiTw== dependencies: - "@stdlib/assert-is-buffer" "^0.0.x" - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/buffer-ctor" "^0.0.x" + "@stdlib/array-float32" "^0.2.2" + "@stdlib/complex-float32-ctor" "^0.0.2" -"@stdlib/buffer-from-string@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/buffer-from-string/-/buffer-from-string-0.0.8.tgz#0901a6e66c278db84836e483a7278502e2a33994" - integrity sha512-Dws5ZbK2M9l4Bkn/ODHFm3lNZ8tWko+NYXqGS/UH/RIQv3PGp+1tXFUSvjwjDneM6ppjQVExzVedUH1ftABs9A== - dependencies: - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/buffer-ctor" "^0.0.x" - "@stdlib/string-format" "^0.0.x" - -"@stdlib/cli-ctor@^0.0.x": +"@stdlib/complex-float64-ctor@^0.0.3": version "0.0.3" - resolved "https://registry.yarnpkg.com/@stdlib/cli-ctor/-/cli-ctor-0.0.3.tgz#5b0a6d253217556c778015eee6c14be903f82c2b" - integrity sha512-0zCuZnzFyxj66GoF8AyIOhTX5/mgGczFvr6T9h4mXwegMZp8jBC/ZkOGMwmp+ODLBTvlcnnDNpNFkDDyR6/c2g== + resolved "https://registry.yarnpkg.com/@stdlib/complex-float64-ctor/-/complex-float64-ctor-0.0.3.tgz#740fdb24f5d1d5db82fa7800b91037e552a47bb6" + integrity sha512-oixCtBif+Uab2rKtgedwQTbQTEC+wVSu4JQH935eJ8Jo0eL6vXUHHlVrkLgYKlCDLvq5px1QQn42Czg/ixh6Gw== dependencies: - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-noop" "^0.0.x" - minimist "^1.2.0" + "@stdlib/assert-is-number" "^0.2.2" + "@stdlib/complex-float32-ctor" "^0.0.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" + "@stdlib/utils-define-property" "^0.2.4" -"@stdlib/complex-float32@^0.0.7", "@stdlib/complex-float32@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/complex-float32/-/complex-float32-0.0.7.tgz#fb9a0c34254eaf3ed91c39983e19ef131fc18bc1" - integrity sha512-POCtQcBZnPm4IrFmTujSaprR1fcOFr/MRw2Mt7INF4oed6b1nzeG647K+2tk1m4mMrMPiuXCdvwJod4kJ0SXxQ== +"@stdlib/complex-float64-reim@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@stdlib/complex-float64-reim/-/complex-float64-reim-0.1.2.tgz#22276ea7feef9d61c5bc0d4b0d0137386c57960e" + integrity sha512-q6RnfgbUunApAYuGmkft1oOM3x3xVMVJwNRlRgfIXwKDb8pYt+S/CeIwi3Su5SF6ay3AqA1s+ze7m21osXAJyw== dependencies: - "@stdlib/assert-is-number" "^0.0.x" - "@stdlib/number-float64-base-to-float32" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-define-property" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/array-float64" "^0.2.2" + "@stdlib/complex-float64-ctor" "^0.0.3" -"@stdlib/complex-float64@^0.0.8", "@stdlib/complex-float64@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/complex-float64/-/complex-float64-0.0.8.tgz#00ee3a0629d218a01b830a20406aea7d7aff6fb3" - integrity sha512-lUJwsXtGEziOWAqCcnKnZT4fcVoRsl6t6ECaCJX45Z7lAc70yJLiwUieLWS5UXmyoADHuZyUXkxtI4oClfpnaw== +"@stdlib/constants-array-max-typed-array-length@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-array-max-typed-array-length/-/constants-array-max-typed-array-length-0.2.2.tgz#1cf750d8f0732a88159f2bc6a9c881fcb816add0" + integrity sha512-uAoBItVIfuzR4zKK1F57Znrn2frKL0U9gqJkg30BXuno3YlUvbhIfVP3VsUmGJCmi9ztgYLqX10yqb0KvlM2Ig== + +"@stdlib/constants-float64-ninf@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-float64-ninf/-/constants-float64-ninf-0.2.2.tgz#d7f5c5d445701dca25d39c14cac7a17acd7c5ee0" + integrity sha512-Iu+wZs/vgudAKVg9FEcRY3FadkmvsWuq/wJ3jIHjhaP5xcnoF3XJUO4IneEndybHwehfJL65NShnDsJcg1gicw== dependencies: - "@stdlib/assert-is-number" "^0.0.x" - "@stdlib/complex-float32" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" - "@stdlib/utils-define-property" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/number-ctor" "^0.2.2" -"@stdlib/complex-reim@^0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@stdlib/complex-reim/-/complex-reim-0.0.6.tgz#9657971e36f2a1f1930a21249c1934c8c5087efd" - integrity sha512-28WXfPSIFMtHb0YgdatkGS4yxX5sPYea5MiNgqPv3E78+tFcg8JJG52NQ/MviWP2wsN9aBQAoCPeu8kXxSPdzA== +"@stdlib/constants-float64-pinf@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-float64-pinf/-/constants-float64-pinf-0.2.2.tgz#e568ccfc63f8788f48acb55821bc9f0a7403ec5d" + integrity sha512-UcwnWaSkUMD8QyKADwkXPlY7yOosCPZpE2EDXf/+WOzuWi5vpsec+JaasD5ggAN8Rv8OTVmexTFs1uZfrHgqVQ== + +"@stdlib/constants-int16-max@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int16-max/-/constants-int16-max-0.2.2.tgz#151a4ba8cd09176f201c308e0d5bc15100b94043" + integrity sha512-w7XnWFxYXRyAnbKOxur3981FeaSlhKvHlhETwH5ZhtOQerk3Jn/iJFdtbN8CD0he1Kml4DWhnoKB7P9PcOaTIw== + +"@stdlib/constants-int16-min@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int16-min/-/constants-int16-min-0.2.2.tgz#4e2162619b551f8f552a9625149340e73ac65092" + integrity sha512-zn15vCgNoyD97z7mNQMChEneyc6xQudVGj1BOv5vZl827vHAs+KV6xeCI7VGY8Lpd6V22piDoGG3Mvj/43u9vQ== + +"@stdlib/constants-int32-max@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int32-max/-/constants-int32-max-0.3.0.tgz#e575c365738d81b5fa1273877893312d3597af2c" + integrity sha512-jYN84QfG/yP2RYw98OR6UYehFFs0PsGAihV6pYU0ey+WF9IOXgSjRP56KMoZ7ctHwl4wsnj9I+qB2tGuEXr+pQ== + +"@stdlib/constants-int32-min@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int32-min/-/constants-int32-min-0.2.2.tgz#5ba8b290dad74a1f5cb4adb49ea59082df537ac9" + integrity sha512-4QMOTpo5QykiWp52Wtugu1WK1wV/Bi2Hjj9L97dfZ3BPB1Oa9ykiUZvTsq3GBNCMu2YHPv1ugbV91C3p3bw+Aw== + +"@stdlib/constants-int8-max@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int8-max/-/constants-int8-max-0.2.2.tgz#b92848bf8281e02af0eb4df2e20ef9187952c02a" + integrity sha512-zp1L61S/ycOmkILmvuXEKvtXrEJ0QUAwP65sNAWMJOtdT0mhGMfGpXKvCK84TC3+jP5Wk4LU13cgO2bf/pmGTw== + +"@stdlib/constants-int8-min@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-int8-min/-/constants-int8-min-0.2.2.tgz#7355f162229b2a774e817f88e4255e753bb5c093" + integrity sha512-nxPloZUqbGuyuOPC0U3xQOn9YdyRq2g9uc1dzcw6k0XBhql9mlz9kCbdC74HeMm4K9Dyyb7IlAZLCezdv60s6g== + +"@stdlib/constants-uint16-max@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-uint16-max/-/constants-uint16-max-0.2.2.tgz#8bba489909ea11a468a01afe57be912cbce57f56" + integrity sha512-qaFXbxgFnAkt73P5Ch7ODb0TsOTg0LEBM52hw6qt7+gTMZUdS0zBAiy5J2eEkTxA9rD9X3nIyUtLf2C7jafNdw== + +"@stdlib/constants-uint32-max@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-uint32-max/-/constants-uint32-max-0.2.2.tgz#354b3c0f78ad54ff565087f01d9d8c337af63831" + integrity sha512-2G44HQgIKDrh3tJUkmvtz+eM+uwDvOMF+2I3sONcTHacANb+zP7la4LDYiTp+HFkPJyfh/kPapXBiHpissAb1A== + +"@stdlib/constants-uint8-max@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/constants-uint8-max/-/constants-uint8-max-0.2.2.tgz#1187e326b5f03d94a72051cace560ef156ac609d" + integrity sha512-ZTBQq3fqS/Y4ll6cPY5SKaS266EfmKP9PW3YLJaTELmYIzVo9w2RFtfCqN05G3olTQ6Le9MUEE/C6VFgZNElDQ== + +"@stdlib/error-tools-fmtprodmsg@^0.2.1", "@stdlib/error-tools-fmtprodmsg@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/error-tools-fmtprodmsg/-/error-tools-fmtprodmsg-0.2.2.tgz#0b42240fc5131b460f1120b77da8345dd22ee2dd" + integrity sha512-2IliQfTes4WV5odPidZFGD5eYDswZrPXob7oOu95Q69ERqImo8WzSwnG2EDbHPyOyYCewuMfM5Ha6Ggf+u944Q== + +"@stdlib/fs-exists@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/fs-exists/-/fs-exists-0.2.2.tgz#ccb289c0784f765796c27593abe6e398fb1bbdd2" + integrity sha512-uGLqc7izCIam2aTyv0miyktl4l8awgRkCS39eIEvvvnKIaTBF6pxfac7FtFHeEQKE3XhtKsOmdQ/yJjUMChLuA== dependencies: - "@stdlib/array-float64" "^0.0.x" - "@stdlib/complex-float64" "^0.0.x" - "@stdlib/types" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/complex-reimf@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@stdlib/complex-reimf/-/complex-reimf-0.0.1.tgz#6797bc1bfb668a30511611f2544d0cff4d297775" - integrity sha512-P9zu05ZW2i68Oppp3oHelP7Tk0D7tGBL0hGl1skJppr2vY9LltuNbeYI3C96tQe/7Enw/5GyAWgxoQI4cWccQA== +"@stdlib/fs-resolve-parent-path@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/fs-resolve-parent-path/-/fs-resolve-parent-path-0.2.2.tgz#434fa93c067894fea7632aa4b93fba41d7a58cf5" + integrity sha512-ZG78ouZc+pdPLtU+sSpYTvbKTiLUgn6NTtlVFYmcmkYRFn+fGOOakwVuhYMcYG6ti10cLD6WzB/YujxIt8f+nA== dependencies: - "@stdlib/array-float32" "^0.0.x" - "@stdlib/complex-float32" "^0.0.x" - "@stdlib/types" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.2" + "@stdlib/assert-is-function" "^0.2.2" + "@stdlib/assert-is-plain-object" "^0.2.2" + "@stdlib/assert-is-string" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/fs-exists" "^0.2.2" + "@stdlib/process-cwd" "^0.2.2" + "@stdlib/string-format" "^0.2.2" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/constants-array-max-typed-array-length@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-array-max-typed-array-length/-/constants-array-max-typed-array-length-0.0.7.tgz#b6e4cd8e46f4a1ae2b655646d46393ba3d8d5c2b" - integrity sha512-KoQtZUGxP+ljOjUfc/dpH9dEZmqxXaLs7HV1D0W+Gnwa8GnuPJijTwmYZwglmjtbeWIzlaLksqPAvlQE7rj2jg== - -"@stdlib/constants-float64-ninf@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/constants-float64-ninf/-/constants-float64-ninf-0.0.8.tgz#4a83691d4d46503e2339fa3ec21d0440877b5bb7" - integrity sha512-bn/uuzCne35OSLsQZJlNrkvU1/40spGTm22g1+ZI1LL19J8XJi/o4iupIHRXuLSTLFDBqMoJlUNphZlWQ4l8zw== +"@stdlib/math-base-assert-is-integer@^0.2.4", "@stdlib/math-base-assert-is-integer@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@stdlib/math-base-assert-is-integer/-/math-base-assert-is-integer-0.2.5.tgz#fa30a62ee27a90bf5cf598f78d7c0de50b582413" + integrity sha512-Zi8N66GbWtSCR3OUsRdBknjNlX+aBN8w6CaVEP5+Jy/a7MgMYzevS52TNS5sm8jqzKBlFhZlPLex+Zl2GlPvSA== dependencies: - "@stdlib/number-ctor" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/math-base-special-floor" "^0.2.3" + "@stdlib/utils-library-manifest" "^0.2.2" -"@stdlib/constants-float64-pinf@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/constants-float64-pinf/-/constants-float64-pinf-0.0.8.tgz#ad3d5b267b142b0927363f6eda74c94b8c4be8bf" - integrity sha512-I3R4rm2cemoMuiDph07eo5oWZ4ucUtpuK73qBJiJPDQKz8fSjSe4wJBAigq2AmWYdd7yJHsl5NJd8AgC6mP5Qw== +"@stdlib/math-base-assert-is-nan@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/math-base-assert-is-nan/-/math-base-assert-is-nan-0.2.2.tgz#84289029340e0002a3795e640b7c46be3c3e1696" + integrity sha512-QVS8rpWdkR9YmHqiYLDVLsCiM+dASt/2feuTl4T/GSdou3Y/PS/4j/tuDvCDoHDNfDkULUW+FCVjKYpbyoeqBQ== dependencies: - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/utils-library-manifest" "^0.2.1" -"@stdlib/constants-int16-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int16-max/-/constants-int16-max-0.0.7.tgz#7f62b6dc93aa468f51a5907d4da894c2b696deef" - integrity sha512-VCJVtehM+b27PB1+KcK97MCNfp9xhVaJQ+EJAi6sDIVtuMkx4HGW4GDmJB8vzBqqWaWo3M9bjNvuXHN/TQHZsA== - -"@stdlib/constants-int16-min@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int16-min/-/constants-int16-min-0.0.7.tgz#bef88532974e57aa60e060474d6314ba9bb457e6" - integrity sha512-HzuhrBMmkpR9vMsmYKFC3MSsx+cWOXDtKrg/L7OUK32dr1hFrlMJrFbjq83FgfGEdGO1hw519vZvKpZd4wJx6A== - -"@stdlib/constants-int32-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int32-max/-/constants-int32-max-0.0.7.tgz#83e55486670c1dad5c568640efe9742dc0ee0b2b" - integrity sha512-um/tgiIotQy7jkN6b7GzaOMQT4PN/o7Z6FR0CJn0cHIZfWCNKyVObfaR68uDX1nDwYGfNrO7BkCbU4ccrtflDA== - -"@stdlib/constants-int32-min@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int32-min/-/constants-int32-min-0.0.7.tgz#97d50ecca6f2a3e8b2f1cc7cf50926ae9e287009" - integrity sha512-/I7rK7sIhFOqz20stP9H6wVE+hfAcVKRKGBvNRsxbTiEcXnM3RjD6LxPGa/4dl6q/bq2ypJti8kfR8bKvepeDQ== - -"@stdlib/constants-int8-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int8-max/-/constants-int8-max-0.0.7.tgz#71e1eb536f1c4e5594a18d7ad2fc68760825f6c4" - integrity sha512-4qkN6H9PqBCkt/PEW/r6/RoLr3144mJuiyhxoUJ5kLmKPjjKJKKdTxORQFGOon/NykLS9EqjZdK16/n1FXJPqA== - -"@stdlib/constants-int8-min@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-int8-min/-/constants-int8-min-0.0.7.tgz#736942d0321fcfde901660d6842da32d8c6ccb28" - integrity sha512-Ux1P8v+KijoG3MgEeIWFggK8MsT1QhSkWBoT0evVyO1ftK+51WXqC+0uAwPoP06nhW4UTW3i4eJS9BVyyz7Beg== - -"@stdlib/constants-uint16-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-uint16-max/-/constants-uint16-max-0.0.7.tgz#c20dbe90cf3825f03f5f44b9ee7e8cbada26f4f1" - integrity sha512-7TPoku7SlskA67mAm7mykIAjeEnkQJemw1cnKZur0mT5W4ryvDR6iFfL9xBiByVnWYq/+ei7DHbOv6/2b2jizw== - -"@stdlib/constants-uint32-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-uint32-max/-/constants-uint32-max-0.0.7.tgz#60bda569b226120a5d2e01f3066da8e2d3b8e21a" - integrity sha512-8+NK0ewqc1vnEZNqzwFJgFSy3S543Eft7i8WyW/ygkofiqEiLAsujvYMHzPAB8/3D+PYvjTSe37StSwRwvQ6uw== - -"@stdlib/constants-uint8-max@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/constants-uint8-max/-/constants-uint8-max-0.0.7.tgz#d50affeaeb6e67a0f39059a8f5122f3fd5ff4447" - integrity sha512-fqV+xds4jgwFxwWu08b8xDuIoW6/D4/1dtEjZ1sXVeWR7nf0pjj1cHERq4kdkYxsvOGu+rjoR3MbjzpFc4fvSw== - -"@stdlib/fs-exists@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/fs-exists/-/fs-exists-0.0.8.tgz#391b2cee3e014a3b20266e5d047847f68ef82331" - integrity sha512-mZktcCxiLmycCJefm1+jbMTYkmhK6Jk1ShFmUVqJvs+Ps9/2EEQXfPbdEniLoVz4HeHLlcX90JWobUEghOOnAQ== +"@stdlib/math-base-napi-unary@^0.2.1": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@stdlib/math-base-napi-unary/-/math-base-napi-unary-0.2.3.tgz#57862685d6ce037aa927020d272e8d74cc243320" + integrity sha512-BCyJmpq2S8EFo2yMt1z+v1EL7nn8RHcM6jn7fa8n3BTP679K0MSlawIh3A0CFogfrTdjPM4G44VO1ddsdLExcg== dependencies: - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-cwd" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/complex-float32-ctor" "^0.0.2" + "@stdlib/complex-float32-reim" "^0.1.1" + "@stdlib/complex-float64-ctor" "^0.0.3" + "@stdlib/complex-float64-reim" "^0.1.1" + "@stdlib/utils-library-manifest" "^0.2.2" -"@stdlib/fs-read-file@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/fs-read-file/-/fs-read-file-0.0.8.tgz#2f12669fa6dd2d330fb5006a94dc8896f0aaa0e0" - integrity sha512-pIZID/G91+q7ep4x9ECNC45+JT2j0+jdz/ZQVjCHiEwXCwshZPEvxcPQWb9bXo6coOY+zJyX5TwBIpXBxomWFg== +"@stdlib/math-base-special-floor@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@stdlib/math-base-special-floor/-/math-base-special-floor-0.2.3.tgz#978f69d99f298e571cadf00d8d4b92111db4644d" + integrity sha512-zTkxVRawtWwJ4NmAT/1e+ZsIoBj1JqUquGOpiNVGNIKtyLOeCONZlZSbN7zuxPkshvmcSjpQ/VLKR8Tw/37E9A== dependencies: - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/math-base-napi-unary" "^0.2.1" + "@stdlib/utils-library-manifest" "^0.2.2" -"@stdlib/fs-resolve-parent-path@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/fs-resolve-parent-path/-/fs-resolve-parent-path-0.0.8.tgz#628119952dfaae78afe3916dca856408a4f5c1eb" - integrity sha512-ok1bTWsAziChibQE3u7EoXwbCQUDkFjjRAHSxh7WWE5JEYVJQg1F0o3bbjRr4D/wfYYPWLAt8AFIKBUDmWghpg== +"@stdlib/number-ctor@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/number-ctor/-/number-ctor-0.2.2.tgz#64f76c5b5e2adcde7f089e9fd6625881e35a6fb0" + integrity sha512-98pL4f1uiXVIw9uRV6t4xecMFUYRRTUoctsqDDV8MSRtKEYDzqkWCNz/auupJFJ135L1ejzkejh73fASsgcwKQ== + +"@stdlib/number-float64-base-to-float32@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/number-float64-base-to-float32/-/number-float64-base-to-float32-0.2.2.tgz#5cb3bd9bf59fddd5747d50b5d54913178c562c3a" + integrity sha512-T5snDkVNZY6pomrSW/qLWQfZ9JHgqCFLi8jaaarfNj2o+5NMUuvvRifLUIacTm8/uC96xB0j3+wKTh1zbIV5ig== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/assert-is-plain-object" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-exists" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-cwd" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/array-float32" "^0.2.1" -"@stdlib/math-base-assert-is-integer@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/math-base-assert-is-integer/-/math-base-assert-is-integer-0.0.7.tgz#d70faf41bed1bd737333877eb21660bf0ee779df" - integrity sha512-swIEKQJZOwzacYDiX5SSt5/nHd6PYJkLlVKZiVx/GCpflstQnseWA0TmudG7XU5HJnxDGV/w6UL02dEyBH7VEw== +"@stdlib/object-ctor@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@stdlib/object-ctor/-/object-ctor-0.2.1.tgz#a3e261cd65eecffcb03e2cc7472aa5058efba48f" + integrity sha512-HEIBBpfdQS9Nh5mmIqMk9fzedx6E0tayJrVa2FD7No86rVuq/Ikxq1QP7qNXm+i6z9iNUUS/lZq7BmJESWO/Zg== + +"@stdlib/process-cwd@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/process-cwd/-/process-cwd-0.2.2.tgz#228df717417c335da7eeda37b6cc2b90fc3205f1" + integrity sha512-8Q/nA/ud5d5PEzzG6ZtKzcOw+RMLm5CWR8Wd+zVO5vcPj+JD7IV7M2lBhbzfUzr63Torrf/vEhT3cob8vUHV/A== + +"@stdlib/regexp-extended-length-path@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/regexp-extended-length-path/-/regexp-extended-length-path-0.2.2.tgz#5ea1664bc07de520236f8ab8201b160c9d9bffcd" + integrity sha512-z3jqauEsaxpsQU3rj1A1QnOgu17pyW5kt+Az8QkoTk7wqNE8HhPikI6k4o7XBHV689rSFWZCl4c4W+7JAiNObQ== dependencies: - "@stdlib/math-base-special-floor" "^0.0.x" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/math-base-assert-is-nan@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/math-base-assert-is-nan/-/math-base-assert-is-nan-0.0.8.tgz#0cd6a546ca1e758251f04898fc906f6fce9e0f80" - integrity sha512-m+gCVBxLFW8ZdAfdkATetYMvM7sPFoMKboacHjb1pe21jHQqVb+/4bhRSDg6S7HGX7/8/bSzEUm9zuF7vqK5rQ== +"@stdlib/regexp-function-name@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/regexp-function-name/-/regexp-function-name-0.2.2.tgz#e85e4e94eb382c9c8416b18ffe712c934c2b2b1f" + integrity sha512-0z/KRsgHJJ3UQkmBeLH+Nin0hXIeA+Fw1T+mnG2V5CHnTA6FKlpxJxWrvwLEsRX7mR/DNtDp06zGyzMFE/4kig== dependencies: - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/math-base-napi-unary@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/math-base-napi-unary/-/math-base-napi-unary-0.0.9.tgz#3a70fa64128aca7011c5a477110d2682d06c8ea8" - integrity sha512-2WNKhjCygkGMp0RgjaD7wAHJTqPZmuVW7yPOc62Tnz2U+Ad8q/tcOcN+uvq2dtKsAGr1HDMIQxZ/XrrThMePyA== +"@stdlib/regexp-regexp@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/regexp-regexp/-/regexp-regexp-0.2.2.tgz#624d7c64529016986ef1493b7db621766b1f74cd" + integrity sha512-LlWqVH7wou4rJ2vovmn8ZZf4Z5/sMYxGQkmUcURvdCDMSL4pt91uPMi9I2hLECcIYXLiKUD87VSR56Y5luaafg== dependencies: - "@stdlib/complex-float32" "^0.0.7" - "@stdlib/complex-float64" "^0.0.8" - "@stdlib/complex-reim" "^0.0.6" - "@stdlib/complex-reimf" "^0.0.1" - "@stdlib/utils-library-manifest" "^0.0.8" + "@stdlib/utils-define-nonenumerable-read-only-property" "^0.2.2" -"@stdlib/math-base-special-floor@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/math-base-special-floor/-/math-base-special-floor-0.0.8.tgz#c0bbde6f984aa132917a47c8bcc71b31ed0cbf26" - integrity sha512-VwpaiU0QhQKB8p+r9p9mNzhrjU5ZVBnUcLjKNCDADiGNvO5ACI/I+W++8kxBz5XSp5PAQhaFCH4MpRM1tSkd/w== +"@stdlib/string-base-format-interpolate@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/string-base-format-interpolate/-/string-base-format-interpolate-0.2.2.tgz#67c22f0ca93ccffd0eb7e1c7276e487b26e786c6" + integrity sha512-i9nU9rAB2+o/RR66TS9iQ8x+YzeUDL1SGiAo6GY3hP6Umz5Dx9Qp/v8T69gWVsb4a1YSclz5+YeCWaFgwvPjKA== + +"@stdlib/string-base-format-tokenize@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/string-base-format-tokenize/-/string-base-format-tokenize-0.2.2.tgz#3ef9e49f6619ce39d9ba8399c9f4f63b3199289a" + integrity sha512-kXq2015i+LJjqth5dN+hYnvJXBSzRm8w0ABWB5tYAsIuQTpQK+mSo2muM8JBEFEnqUHAwpUsu2qNTK/9o8lsJg== + +"@stdlib/string-base-lowercase@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@stdlib/string-base-lowercase/-/string-base-lowercase-0.4.0.tgz#079b55c30be8f2ea5b63c7a24707852e63a8d1b8" + integrity sha512-IH35Z5e4T+S3b3SfYY39mUhrD2qvJVp4VS7Rn3+jgj4+C3syocuAPsJ8C4OQXWGfblX/N9ymizbpFBCiVvMW8w== + +"@stdlib/string-base-replace@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/string-base-replace/-/string-base-replace-0.2.2.tgz#d5f8967600d530b2b2938ba1a8c1b333b6be8c1f" + integrity sha512-Y4jZwRV4Uertw7AlA/lwaYl1HjTefSriN5+ztRcQQyDYmoVN3gzoVKLJ123HPiggZ89vROfC+sk/6AKvly+0CA== + +"@stdlib/string-format@^0.2.1", "@stdlib/string-format@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/string-format/-/string-format-0.2.2.tgz#5f2ac8cfb06e1b11be9ac8fc546075d0c77ec938" + integrity sha512-GUa50uxgMAtoItsxTbMmwkyhIwrCxCrsjzk3nAbLnt/1Kt1EWOWMwsALqZdD6K4V/xSJ4ns6PZur3W6w+vKk9g== dependencies: - "@stdlib/math-base-napi-unary" "^0.0.x" - "@stdlib/utils-library-manifest" "^0.0.x" + "@stdlib/string-base-format-interpolate" "^0.2.1" + "@stdlib/string-base-format-tokenize" "^0.2.2" -"@stdlib/number-ctor@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/number-ctor/-/number-ctor-0.0.7.tgz#e97a66664639c9853b6c80bc7a15f7d67a2fc991" - integrity sha512-kXNwKIfnb10Ro3RTclhAYqbE3DtIXax+qpu0z1/tZpI2vkmTfYDQLno2QJrzJsZZgdeFtXIws+edONN9kM34ow== - -"@stdlib/number-float64-base-to-float32@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/number-float64-base-to-float32/-/number-float64-base-to-float32-0.0.7.tgz#c7b82bb26cb7404017ede32cebe5864fd84c0e35" - integrity sha512-PNUSi6+cqfFiu4vgFljUKMFY2O9PxI6+T+vqtIoh8cflf+PjSGj3v4QIlstK9+6qU40eGR5SHZyLTWdzmNqLTQ== +"@stdlib/string-replace@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/string-replace/-/string-replace-0.2.2.tgz#c4a526abdec7ec031beeb87f98c4c9356fdce969" + integrity sha512-czNS5IU7sBuHjac45Y3VWUTsUoi82yc8JsMZrOMcjgSrEuDrVmA6sNJg7HC1DuSpdPjm/v9uUk102s1gIfk3Nw== dependencies: - "@stdlib/array-float32" "^0.0.x" + "@stdlib/assert-is-function" "^0.2.2" + "@stdlib/assert-is-regexp" "^0.2.2" + "@stdlib/assert-is-string" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-base-replace" "^0.2.2" + "@stdlib/string-format" "^0.2.2" + "@stdlib/utils-escape-regexp-string" "^0.2.2" -"@stdlib/process-cwd@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/process-cwd/-/process-cwd-0.0.8.tgz#5eef63fb75ffb5fc819659d2f450fa3ee2aa10bf" - integrity sha512-GHINpJgSlKEo9ODDWTHp0/Zc/9C/qL92h5Mc0QlIFBXAoUjy6xT4FB2U16wCNZMG3eVOzt5+SjmCwvGH0Wbg3Q== +"@stdlib/symbol-ctor@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/symbol-ctor/-/symbol-ctor-0.2.2.tgz#07a1477df50d9c54f4b79f810a0f0667e52c24d6" + integrity sha512-XsmiTfHnTb9jSPf2SoK3O0wrNOXMxqzukvDvtzVur1XBKfim9+seaAS4akmV1H3+AroAXQWVtde885e1B6jz1w== + +"@stdlib/utils-constructor-name@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-constructor-name/-/utils-constructor-name-0.2.2.tgz#3462fb107196d00698604aac32089353273c82a2" + integrity sha512-TBtO3MKDAf05ij5ajmyBCbpKKt0Lfahn5tu18gqds4PkFltgcw5tVZfSHY5DZ2HySJQ2GMMYjPW2Kbg6yPCSVg== dependencies: - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" + "@stdlib/assert-is-buffer" "^0.2.1" + "@stdlib/regexp-function-name" "^0.2.2" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/process-read-stdin@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/process-read-stdin/-/process-read-stdin-0.0.7.tgz#684ad531759c6635715a67bdd8721fc249baa200" - integrity sha512-nep9QZ5iDGrRtrZM2+pYAvyCiYG4HfO0/9+19BiLJepjgYq4GKeumPAQo22+1xawYDL7Zu62uWzYszaVZcXuyw== +"@stdlib/utils-convert-path@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-convert-path/-/utils-convert-path-0.2.2.tgz#7ffcd09a4f2384e0421a4154e31fe520ee0a62b7" + integrity sha512-8nNuAgt23Np9NssjShUrPK42c6gRTweGuoQw+yTpTfBR9VQv8WFyt048n8gRGUlAHizrdMNpEY9VAb7IBzpVYw== dependencies: - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/buffer-ctor" "^0.0.x" - "@stdlib/buffer-from-string" "^0.0.x" - "@stdlib/streams-node-stdin" "^0.0.x" - "@stdlib/utils-next-tick" "^0.0.x" + "@stdlib/assert-is-string" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/regexp-extended-length-path" "^0.2.2" + "@stdlib/string-base-lowercase" "^0.4.0" + "@stdlib/string-format" "^0.2.2" + "@stdlib/string-replace" "^0.2.1" -"@stdlib/regexp-eol@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/regexp-eol/-/regexp-eol-0.0.7.tgz#cf1667fdb5da1049c2c2f8d5c47dcbaede8650a4" - integrity sha512-BTMpRWrmlnf1XCdTxOrb8o6caO2lmu/c80XSyhYCi1DoizVIZnqxOaN5yUJNCr50g28vQ47PpsT3Yo7J3SdlRA== +"@stdlib/utils-copy@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-copy/-/utils-copy-0.2.2.tgz#d7359e59de632a0dd1a315feb0ccff7e0c96e42a" + integrity sha512-DM8O5tgOHHyhaERDJKa/ThDhIewDyo5SxoFJnmxSriAlJsV9uAmzF8rm7vY969TPRACb+Uxj2GXUoYlmUcHTmA== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-boolean" "^0.0.x" - "@stdlib/assert-is-plain-object" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/array-float32" "^0.2.2" + "@stdlib/array-float64" "^0.2.2" + "@stdlib/array-int16" "^0.2.2" + "@stdlib/array-int32" "^0.2.2" + "@stdlib/array-int8" "^0.2.2" + "@stdlib/array-uint16" "^0.2.2" + "@stdlib/array-uint32" "^0.2.2" + "@stdlib/array-uint8" "^0.2.2" + "@stdlib/array-uint8c" "^0.2.2" + "@stdlib/assert-has-own-property" "^0.2.2" + "@stdlib/assert-is-array" "^0.2.2" + "@stdlib/assert-is-buffer" "^0.2.2" + "@stdlib/assert-is-error" "^0.2.2" + "@stdlib/assert-is-nonnegative-integer" "^0.2.2" + "@stdlib/buffer-from-buffer" "^0.2.2" + "@stdlib/constants-float64-pinf" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" + "@stdlib/utils-define-property" "^0.2.4" + "@stdlib/utils-get-prototype-of" "^0.2.2" + "@stdlib/utils-index-of" "^0.2.2" + "@stdlib/utils-keys" "^0.2.2" + "@stdlib/utils-property-descriptor" "^0.2.2" + "@stdlib/utils-property-names" "^0.2.2" + "@stdlib/utils-regexp-from-string" "^0.2.2" + "@stdlib/utils-type-of" "^0.2.2" -"@stdlib/regexp-extended-length-path@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/regexp-extended-length-path/-/regexp-extended-length-path-0.0.7.tgz#7f76641c29895771e6249930e1863e7e137a62e0" - integrity sha512-z6uqzMWq3WPDKbl4MIZJoNA5ZsYLQI9G3j2TIvhU8X2hnhlku8p4mvK9F+QmoVvgPxKliwNnx/DAl7ltutSDKw== +"@stdlib/utils-define-nonenumerable-read-only-property@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-define-nonenumerable-read-only-property/-/utils-define-nonenumerable-read-only-property-0.2.2.tgz#80be97888609d1e471d20812cc5ba83a01f92e88" + integrity sha512-V3mpAesJemLYDKG376CsmoczWPE/4LKsp8xBvUxCt5CLNAx3J/1W39iZQyA5q6nY1RStGinGn1/dYZwa8ig0Uw== dependencies: - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/utils-define-property" "^0.2.3" -"@stdlib/regexp-function-name@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/regexp-function-name/-/regexp-function-name-0.0.7.tgz#e8dc6c7fe9276f0a8b4bc7f630a9e32ba9f37250" - integrity sha512-MaiyFUUqkAUpUoz/9F6AMBuMQQfA9ssQfK16PugehLQh4ZtOXV1LhdY8e5Md7SuYl9IrvFVg1gSAVDysrv5ZMg== +"@stdlib/utils-define-property@^0.2.3", "@stdlib/utils-define-property@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@stdlib/utils-define-property/-/utils-define-property-0.2.4.tgz#a8b6e120c829ee99ed81cf0111bb4c76ef85da9e" + integrity sha512-XlMdz7xwuw/sqXc9LbsV8XunCzZXjbZPC+OAdf4t4PBw4ZRwGzlTI6WED+f4PYR5Tp9F1cHgLPyMYCIBfA2zRg== dependencies: - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/error-tools-fmtprodmsg" "^0.2.1" + "@stdlib/string-format" "^0.2.1" -"@stdlib/regexp-regexp@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/regexp-regexp/-/regexp-regexp-0.0.8.tgz#50221b52088cd427ef19fae6593977c1c3f77e87" - integrity sha512-S5PZICPd/XRcn1dncVojxIDzJsHtEleuJHHD7ji3o981uPHR7zI2Iy9a1eV2u7+ABeUswbI1Yuix6fXJfcwV1w== +"@stdlib/utils-escape-regexp-string@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-escape-regexp-string/-/utils-escape-regexp-string-0.2.2.tgz#dd407c9324c1da4fa7b25e5c862502e8dc6d61ab" + integrity sha512-areCibzgpmvm6pGKBg+mXkSDJW4NxtS5jcAT7RtunGMdAYhA/I5whISMPaeJkIT2XhjjFkjKBaIs5pF6aPr4fQ== dependencies: - "@stdlib/utils-define-nonenumerable-read-only-property" "^0.0.x" + "@stdlib/assert-is-string" "^0.2.1" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/streams-node-stdin@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/streams-node-stdin/-/streams-node-stdin-0.0.7.tgz#65ff09a2140999702a1ad885e6505334d947428f" - integrity sha512-gg4lgrjuoG3V/L29wNs32uADMCqepIcmoOFHJCTAhVe0GtHDLybUVnLljaPfdvmpPZmTvmusPQtIcscbyWvAyg== - -"@stdlib/string-base-format-interpolate@^0.0.x": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@stdlib/string-base-format-interpolate/-/string-base-format-interpolate-0.0.4.tgz#297eeb23c76f745dcbb3d9dbd24e316773944538" - integrity sha512-8FC8+/ey+P5hf1B50oXpXzRzoAgKI1rikpyKZ98Xmjd5rcbSq3NWYi8TqOF8mUHm9hVZ2CXWoNCtEe2wvMQPMg== - -"@stdlib/string-base-format-tokenize@^0.0.x": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@stdlib/string-base-format-tokenize/-/string-base-format-tokenize-0.0.4.tgz#c1fc612ee0c0de5516dbf083e88c11d14748c30e" - integrity sha512-+vMIkheqAhDeT/iF5hIQo95IMkt5IzC68eR3CxW1fhc48NMkKFE2UfN73ET8fmLuOanLo/5pO2E90c2G7PExow== - -"@stdlib/string-format@^0.0.x": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@stdlib/string-format/-/string-format-0.0.3.tgz#e916a7be14d83c83716f5d30b1b1af94c4e105b9" - integrity sha512-1jiElUQXlI/tTkgRuzJi9jUz/EjrO9kzS8VWHD3g7gdc3ZpxlA5G9JrIiPXGw/qmZTi0H1pXl6KmX+xWQEQJAg== +"@stdlib/utils-get-prototype-of@^0.2.1", "@stdlib/utils-get-prototype-of@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-get-prototype-of/-/utils-get-prototype-of-0.2.2.tgz#a65def101deece8d81f3bbf892ababe4d61114bb" + integrity sha512-eDb1BAvt7GW/jduBkfuQrUsA9p09mV8RW20g0DWPaxci6ORYg/UB0tdbAA23aZz2QUoxdYY5s/UJxlq/GHwoKQ== dependencies: - "@stdlib/string-base-format-interpolate" "^0.0.x" - "@stdlib/string-base-format-tokenize" "^0.0.x" + "@stdlib/assert-is-function" "^0.2.1" + "@stdlib/object-ctor" "^0.2.1" + "@stdlib/utils-native-class" "^0.2.1" -"@stdlib/string-lowercase@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/string-lowercase/-/string-lowercase-0.0.9.tgz#487361a10364bd0d9b5ee44f5cc654c7da79b66d" - integrity sha512-tXFFjbhIlDak4jbQyV1DhYiSTO8b1ozS2g/LELnsKUjIXECDKxGFyWYcz10KuyAWmFotHnCJdIm8/blm2CfDIA== +"@stdlib/utils-global@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-global/-/utils-global-0.2.2.tgz#61f875ef4ed74a091ed841127262961edef2d973" + integrity sha512-A4E8VFHn+1bpfJ4PA8H2b62CMQpjv2A+H3QDEBrouLFWne0wrx0TNq8vH6VYHxx9ZRxhgWQjfHiSAxtUJobrbQ== dependencies: - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-read-stdin" "^0.0.x" - "@stdlib/streams-node-stdin" "^0.0.x" - "@stdlib/string-format" "^0.0.x" + "@stdlib/assert-is-boolean" "^0.2.1" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/string-replace@^0.0.x": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@stdlib/string-replace/-/string-replace-0.0.11.tgz#5e8790cdf4d9805ab78cc5798ab3d364dfbf5016" - integrity sha512-F0MY4f9mRE5MSKpAUfL4HLbJMCbG6iUTtHAWnNeAXIvUX1XYIw/eItkA58R9kNvnr1l5B08bavnjrgTJGIKFFQ== +"@stdlib/utils-index-of@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-index-of/-/utils-index-of-0.2.2.tgz#9c60f95bb480dbe5a5107daaf6b12d61da69dc89" + integrity sha512-yVnjPk3Arzf3no+Ju5ys4ZVG4aKHA8xV4g53Ni+FO8c0AStKq9F8O+E/aFxluCKqLz6jGLaLea7996e0Hcw9mQ== dependencies: - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/assert-is-regexp" "^0.0.x" - "@stdlib/assert-is-regexp-string" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-read-stdin" "^0.0.x" - "@stdlib/regexp-eol" "^0.0.x" - "@stdlib/streams-node-stdin" "^0.0.x" - "@stdlib/string-format" "^0.0.x" - "@stdlib/utils-escape-regexp-string" "^0.0.x" - "@stdlib/utils-regexp-from-string" "^0.0.x" + "@stdlib/assert-is-collection" "^0.2.1" + "@stdlib/assert-is-integer" "^0.2.2" + "@stdlib/assert-is-nan" "^0.2.2" + "@stdlib/assert-is-string" "^0.2.2" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/types@^0.0.x": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@stdlib/types/-/types-0.0.14.tgz#02d3aab7a9bfaeb86e34ab749772ea22f7b2f7e0" - integrity sha512-AP3EI9/il/xkwUazcoY+SbjtxHRrheXgSbWZdEGD+rWpEgj6n2i63hp6hTOpAB5NipE0tJwinQlDGOuQ1lCaCw== - -"@stdlib/utils-constructor-name@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-constructor-name/-/utils-constructor-name-0.0.8.tgz#ef63d17466c555b58b348a0c1175cee6044b8848" - integrity sha512-GXpyNZwjN8u3tyYjL2GgGfrsxwvfogUC3gg7L7NRZ1i86B6xmgfnJUYHYOUnSfB+R531ET7NUZlK52GxL7P82Q== +"@stdlib/utils-keys@^0.2.1", "@stdlib/utils-keys@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-keys/-/utils-keys-0.2.2.tgz#efdd5b14370468d146bb4fdd3819caa0f76699e0" + integrity sha512-mvmvhpewElNalx5YotZ/jI57CiYHc9y6N8SGxJiOUs04NkWMkW8rnXRvJEi0rPj2BOIJReEGZ1WaZpdL68DUcQ== dependencies: - "@stdlib/assert-is-buffer" "^0.0.x" - "@stdlib/regexp-function-name" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.2" + "@stdlib/assert-is-arguments" "^0.2.1" + "@stdlib/assert-is-enumerable-property" "^0.2.2" + "@stdlib/assert-is-object-like" "^0.2.2" + "@stdlib/utils-index-of" "^0.2.2" + "@stdlib/utils-noop" "^0.2.2" + "@stdlib/utils-type-of" "^0.2.2" -"@stdlib/utils-convert-path@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-convert-path/-/utils-convert-path-0.0.8.tgz#a959d02103eee462777d222584e72eceef8c223b" - integrity sha512-GNd8uIswrcJCctljMbmjtE4P4oOjhoUIfMvdkqfSrRLRY+ZqPB2xM+yI0MQFfUq/0Rnk/xtESlGSVLz9ZDtXfA== +"@stdlib/utils-library-manifest@^0.2.1", "@stdlib/utils-library-manifest@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-library-manifest/-/utils-library-manifest-0.2.2.tgz#1908504dbdbb665a8b72ff40c4f426afefbd7fd2" + integrity sha512-YqzVLuBsB4wTqzdUtRArAjBJoT3x61iop2jFChXexhl6ejV3vDpHcukEEkqIOcJKut+1cG5TLJdexgHNt1C0NA== dependencies: - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-read-file" "^0.0.x" - "@stdlib/process-read-stdin" "^0.0.x" - "@stdlib/regexp-eol" "^0.0.x" - "@stdlib/regexp-extended-length-path" "^0.0.x" - "@stdlib/streams-node-stdin" "^0.0.x" - "@stdlib/string-lowercase" "^0.0.x" - "@stdlib/string-replace" "^0.0.x" - -"@stdlib/utils-copy@^0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-copy/-/utils-copy-0.0.7.tgz#e2c59993a0833e20ccedd8efaf9081043bd61d76" - integrity sha512-nGwWpKOwKw5JnY4caefhZtOglopK6vLlJiqKAjSLK0jz7g5ziyOZAc3ps1E6U5z+xtVhWaXa3VxiG42v9U/TSA== - dependencies: - "@stdlib/array-float32" "^0.0.x" - "@stdlib/array-float64" "^0.0.x" - "@stdlib/array-int16" "^0.0.x" - "@stdlib/array-int32" "^0.0.x" - "@stdlib/array-int8" "^0.0.x" - "@stdlib/array-uint16" "^0.0.x" - "@stdlib/array-uint32" "^0.0.x" - "@stdlib/array-uint8" "^0.0.x" - "@stdlib/array-uint8c" "^0.0.x" - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-array" "^0.0.x" - "@stdlib/assert-is-buffer" "^0.0.x" - "@stdlib/assert-is-error" "^0.0.x" - "@stdlib/assert-is-nonnegative-integer" "^0.0.x" - "@stdlib/buffer-from-buffer" "^0.0.x" - "@stdlib/constants-float64-pinf" "^0.0.x" - "@stdlib/utils-define-property" "^0.0.x" - "@stdlib/utils-get-prototype-of" "^0.0.x" - "@stdlib/utils-index-of" "^0.0.x" - "@stdlib/utils-keys" "^0.0.x" - "@stdlib/utils-property-descriptor" "^0.0.x" - "@stdlib/utils-property-names" "^0.0.x" - "@stdlib/utils-regexp-from-string" "^0.0.x" - "@stdlib/utils-type-of" "^0.0.x" - -"@stdlib/utils-define-nonenumerable-read-only-property@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-define-nonenumerable-read-only-property/-/utils-define-nonenumerable-read-only-property-0.0.7.tgz#ee74540c07bfc3d997ef6f8a1b2df267ea0c07ca" - integrity sha512-c7dnHDYuS4Xn3XBRWIQBPcROTtP/4lkcFyq0FrQzjXUjimfMgHF7cuFIIob6qUTnU8SOzY9p0ydRR2QJreWE6g== - dependencies: - "@stdlib/types" "^0.0.x" - "@stdlib/utils-define-property" "^0.0.x" - -"@stdlib/utils-define-property@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/utils-define-property/-/utils-define-property-0.0.9.tgz#2f40ad66e28099714e3774f3585db80b13816e76" - integrity sha512-pIzVvHJvVfU/Lt45WwUAcodlvSPDDSD4pIPc9WmIYi4vnEBA9U7yHtiNz2aTvfGmBMTaLYTVVFIXwkFp+QotMA== - dependencies: - "@stdlib/types" "^0.0.x" - -"@stdlib/utils-escape-regexp-string@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/utils-escape-regexp-string/-/utils-escape-regexp-string-0.0.9.tgz#36f25d78b2899384ca6c97f4064a8b48edfedb6e" - integrity sha512-E+9+UDzf2mlMLgb+zYrrPy2FpzbXh189dzBJY6OG+XZqEJAXcjWs7DURO5oGffkG39EG5KXeaQwDXUavcMDCIw== - dependencies: - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/string-format" "^0.0.x" - -"@stdlib/utils-get-prototype-of@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-get-prototype-of/-/utils-get-prototype-of-0.0.7.tgz#f677132bcbc0ec89373376637148d364435918df" - integrity sha512-fCUk9lrBO2ELrq+/OPJws1/hquI4FtwG0SzVRH6UJmJfwb1zoEFnjcwyDAy+HWNVmo3xeRLsrz6XjHrJwer9pg== - dependencies: - "@stdlib/assert-is-function" "^0.0.x" - "@stdlib/utils-native-class" "^0.0.x" - -"@stdlib/utils-global@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-global/-/utils-global-0.0.7.tgz#0d99dcd11b72ad10b97dfb43536ff50436db6fb4" - integrity sha512-BBNYBdDUz1X8Lhfw9nnnXczMv9GztzGpQ88J/6hnY7PHJ71av5d41YlijWeM9dhvWjnH9I7HNE3LL7R07yw0kA== - dependencies: - "@stdlib/assert-is-boolean" "^0.0.x" - -"@stdlib/utils-index-of@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-index-of/-/utils-index-of-0.0.8.tgz#e0cebb11e76b017b9c8bd38e4482e5336392306c" - integrity sha512-tz8pL9CgEYp73xWp0hQIR5vLjvM0jnoX5cCpRjn7SHzgDb4/fkpfJX4c0HznK+cCA35jvVVNhM2J0M4Y0IPg2A== - dependencies: - "@stdlib/assert-is-collection" "^0.0.x" - "@stdlib/assert-is-integer" "^0.0.x" - "@stdlib/assert-is-nan" "^0.0.x" - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/types" "^0.0.x" - -"@stdlib/utils-keys@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-keys/-/utils-keys-0.0.7.tgz#7e1545ed728b0f4de31f7b8475307ab36ff7ced1" - integrity sha512-9qzmetloJ0A6iO71n3f9F4cAs/Hq0E7bYHlYNnXwS03wmwI97x3QSzWVoL5o0qpluQVidSQIxeNXPHNEvEa04A== - dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-is-arguments" "^0.0.x" - "@stdlib/assert-is-enumerable-property" "^0.0.x" - "@stdlib/assert-is-object-like" "^0.0.x" - "@stdlib/utils-index-of" "^0.0.x" - "@stdlib/utils-noop" "^0.0.x" - "@stdlib/utils-type-of" "^0.0.x" - -"@stdlib/utils-library-manifest@^0.0.8", "@stdlib/utils-library-manifest@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-library-manifest/-/utils-library-manifest-0.0.8.tgz#61d3ed283e82c8f14b7f952d82cfb8e47d036825" - integrity sha512-IOQSp8skSRQn9wOyMRUX9Hi0j/P5v5TvD8DJWTqtE8Lhr8kVVluMBjHfvheoeKHxfWAbNHSVpkpFY/Bdh/SHgQ== - dependencies: - "@stdlib/cli-ctor" "^0.0.x" - "@stdlib/fs-resolve-parent-path" "^0.0.x" - "@stdlib/utils-convert-path" "^0.0.x" + "@stdlib/fs-resolve-parent-path" "^0.2.1" + "@stdlib/utils-convert-path" "^0.2.1" debug "^2.6.9" resolve "^1.1.7" -"@stdlib/utils-native-class@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-native-class/-/utils-native-class-0.0.8.tgz#2e79de97f85d88a2bb5baa7a4528add71448d2be" - integrity sha512-0Zl9me2V9rSrBw/N8o8/9XjmPUy8zEeoMM0sJmH3N6C9StDsYTjXIAMPGzYhMEWaWHvGeYyNteFK2yDOVGtC3w== +"@stdlib/utils-native-class@^0.2.1", "@stdlib/utils-native-class@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-native-class/-/utils-native-class-0.2.2.tgz#dbb00a84e8c583cdd1bc40b163f1786dc44c4f09" + integrity sha512-cSn/FozbEpfR/FlJAoceQtZ8yUJFhZ8RFkbEsxW/7+H4o09yln3lBS0HSfUJISYNtpTNN/2/Fup88vmvwspvwA== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" - "@stdlib/assert-has-tostringtag-support" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.1" + "@stdlib/assert-has-tostringtag-support" "^0.2.2" + "@stdlib/symbol-ctor" "^0.2.2" -"@stdlib/utils-next-tick@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-next-tick/-/utils-next-tick-0.0.8.tgz#72345745ec3b3aa2cedda056338ed95daae9388c" - integrity sha512-l+hPl7+CgLPxk/gcWOXRxX/lNyfqcFCqhzzV/ZMvFCYLY/wI9lcWO4xTQNMALY2rp+kiV+qiAiO9zcO+hewwUg== +"@stdlib/utils-noop@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-noop/-/utils-noop-0.2.2.tgz#527404ec9875f5e45ec295810bc462a32e3ce4d2" + integrity sha512-QlHCBCExrFlNFFqDBOvxPeHkvAuMBHsbQYWRjSG2FD6QumEDn9EqBAcJZNr+xYdkw/6SVRJ0ySTyroReg5vcAA== -"@stdlib/utils-noop@^0.0.x": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@stdlib/utils-noop/-/utils-noop-0.0.14.tgz#8a2077fae0877c4c9e4c5f72f3c9284ca109d4c3" - integrity sha512-A5faFEUfszMgd93RCyB+aWb62hQxgP+dZ/l9rIOwNWbIrCYNwSuL4z50lNJuatnwwU4BQ4EjQr+AmBsnvuLcyQ== - -"@stdlib/utils-property-descriptor@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-property-descriptor/-/utils-property-descriptor-0.0.7.tgz#f9ea361ad29f5d398c5b6716bb1788c18d0b55be" - integrity sha512-pi72eRantil7+5iyIwvB7gg3feI1ox8T6kbHoZCgHKwFdr9Bjp6lBHPzfiHBHgQQ0n54sU8EDmrVlLFmmG/qSg== +"@stdlib/utils-property-descriptor@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-property-descriptor/-/utils-property-descriptor-0.2.2.tgz#88d9b55c0a17d22ccd50ad9fbf47544a7b4a1c9a" + integrity sha512-iq0jFOOX5OmaOC1uyO8POjWPPA6GxbFFcDzH4QsyhKWN2P5ZDvnKMRfH6TuMmaLEA5vSzeNwGfcBPQMRBRphUg== dependencies: - "@stdlib/assert-has-own-property" "^0.0.x" + "@stdlib/assert-has-own-property" "^0.2.1" -"@stdlib/utils-property-names@^0.0.x": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@stdlib/utils-property-names/-/utils-property-names-0.0.7.tgz#1f67de736278d53a2dce7f5e8425c7f4a5435a27" - integrity sha512-Yr3z9eO6olGiEEcaR3lHAhB7FCT0RUB+u3FyExwOhyT3PXoH0CJwWHuzpNpVVxpp57JDhvKIhNqHHyqZI8n42w== +"@stdlib/utils-property-names@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-property-names/-/utils-property-names-0.2.2.tgz#a24852878f8c7b1d8bfa9288b14f720fac67bf1b" + integrity sha512-LlHK467gOxvD6T5yzSy/DnDPzeG00Qj1lrowapAOP7HbGMPvs3RI8Re32IqUke5OE698jXW3tL0VDu6yLi2e1A== dependencies: - "@stdlib/utils-keys" "^0.0.x" + "@stdlib/object-ctor" "^0.2.1" + "@stdlib/utils-keys" "^0.2.1" -"@stdlib/utils-regexp-from-string@^0.0.x": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@stdlib/utils-regexp-from-string/-/utils-regexp-from-string-0.0.9.tgz#fe4745a9a000157b365971c513fd7d4b2cb9ad6e" - integrity sha512-3rN0Mcyiarl7V6dXRjFAUMacRwe0/sYX7ThKYurf0mZkMW9tjTP+ygak9xmL9AL0QQZtbrFFwWBrDO+38Vnavw== +"@stdlib/utils-regexp-from-string@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-regexp-from-string/-/utils-regexp-from-string-0.2.2.tgz#0eeac962b9e43f8587b5eaf25a235f4380976fdb" + integrity sha512-xvyX44wYAn7DUSTwfIdzohFFo2J+kkzZf1I3cQZr09k8364/1x2Ro7bhPVXX/fEUJ1LbbPF3PAcGa44cvN8Nzg== dependencies: - "@stdlib/assert-is-string" "^0.0.x" - "@stdlib/regexp-regexp" "^0.0.x" - "@stdlib/string-format" "^0.0.x" + "@stdlib/assert-is-string" "^0.2.1" + "@stdlib/error-tools-fmtprodmsg" "^0.2.2" + "@stdlib/regexp-regexp" "^0.2.2" + "@stdlib/string-format" "^0.2.2" -"@stdlib/utils-type-of@^0.0.x": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@stdlib/utils-type-of/-/utils-type-of-0.0.8.tgz#c62ed3fcf629471fe80d83f44c4e325860109cbe" - integrity sha512-b4xqdy3AnnB7NdmBBpoiI67X4vIRxvirjg3a8BfhM5jPr2k0njby1jAbG9dUxJvgAV6o32S4kjUgfIdjEYpTNQ== +"@stdlib/utils-type-of@^0.2.1", "@stdlib/utils-type-of@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@stdlib/utils-type-of/-/utils-type-of-0.2.2.tgz#517de513c043d7c0a48743593be41f67f0d51a9f" + integrity sha512-RLGFxPNgY9AtVVrFGdKO6Y3pOd/Ov2WA4O2/czZN/AbgYzbPdoF0KkfvHRIney6k+TtvoyYB8YqZXJ4G88f9BQ== dependencies: - "@stdlib/utils-constructor-name" "^0.0.x" - "@stdlib/utils-global" "^0.0.x" + "@stdlib/utils-constructor-name" "^0.2.1" + "@stdlib/utils-global" "^0.2.2" "@storybook/addon-actions@7.6.20": version "7.6.20" @@ -7677,95 +7588,95 @@ ajv "^6.12.6" lodash "^4.17.11" -"@tryghost/bookshelf-collision@^0.1.43": - version "0.1.43" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-collision/-/bookshelf-collision-0.1.43.tgz#6491d47c642565735b0a0cbec65925862a7b88ee" - integrity sha512-RPe5ZpkkETlAmQxuVUKZ6LstYokfb1+mJCt6NMYFIcrXAPW1WSS7l11rCBs7WcuhvVKc6yPD+fwD4MmHuVtJ0w== +"@tryghost/bookshelf-collision@^0.1.45": + version "0.1.45" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-collision/-/bookshelf-collision-0.1.45.tgz#0581265964fa0c799f19ce0e0288e5ecb832c108" + integrity sha512-NJcl6WdRUSTutqFhmkZQwqx3nRjfGZImLasCHLWSY8oZvhG0IQ5yabkDUCzv2lCoUfQODyn32N9a3+5Rrmok0w== dependencies: - "@tryghost/errors" "^1.3.3" + "@tryghost/errors" "^1.3.5" lodash "^4.17.21" moment-timezone "^0.5.33" -"@tryghost/bookshelf-custom-query@^0.1.26": - version "0.1.26" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-custom-query/-/bookshelf-custom-query-0.1.26.tgz#57a0f1772710d2f2866847b333e03b2d2d7f1e3c" - integrity sha512-p4ocob5jchlsPZLw5bycCQwDL026t3HVB1kxZnZHmkdGluTEBpL5/rYCRJj/kGG2zWX/1wzhP1cFFiLCOQjfsQ== +"@tryghost/bookshelf-custom-query@^0.1.27": + version "0.1.27" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-custom-query/-/bookshelf-custom-query-0.1.27.tgz#b17385c5cbadf76f2dd287419ed04ae5becde98b" + integrity sha512-a4srouaBad+KqEihIlHqofplLMzUxiQyCNuMjFJeNUEjqxkskBGADvmFqUhwOJ0fVp2pABD/cZspAemF6flN/w== -"@tryghost/bookshelf-eager-load@^0.1.30": - version "0.1.30" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-eager-load/-/bookshelf-eager-load-0.1.30.tgz#3227ee336a073422f60c588daee7a338457f4a5e" - integrity sha512-bMnBc4LelXsp+cfC9ovebvWr9lV0QTkThWLKSbVi/FRUV5JiMJTGRLMNNwv0bZfH9MBalynDBpvaHv1t+G1qcw== - dependencies: - "@tryghost/debug" "^0.1.31" - lodash "^4.17.21" - -"@tryghost/bookshelf-filter@^0.5.15": - version "0.5.15" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-filter/-/bookshelf-filter-0.5.15.tgz#4979da196a8010a6262299d1927d905e3e500083" - integrity sha512-++k8dUOhccPikPDaRLFNdtiNgHLnlx5e64j/6IGTY4F8L/NeqEj/CxziR9Wxw5sV9USUMsg42fDulZXZ0dJgcg== - dependencies: - "@tryghost/debug" "^0.1.31" - "@tryghost/errors" "^1.3.3" - "@tryghost/nql" "^0.12.3" - "@tryghost/tpl" "^0.1.31" - -"@tryghost/bookshelf-has-posts@^0.1.31": +"@tryghost/bookshelf-eager-load@^0.1.31": version "0.1.31" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-has-posts/-/bookshelf-has-posts-0.1.31.tgz#e37622cdf2cdf537afb75a907428942af1587833" - integrity sha512-QJxytGKcBdo3aa+FZeKRglm0+Zb7rwLT9D8CEvCPJ8OxbbsKlCgjTm9Z2feeF7ui+2WQk9Mi8eV7q/wozkTfrw== + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-eager-load/-/bookshelf-eager-load-0.1.31.tgz#b26f1a114794d96ea0592e6a3dca02642e34f864" + integrity sha512-3UB5zrOlCsP2on0a8B8w5SQgHwFCQedkMB1qFuoy2G4PguYTRk4gC+pMs4QSiW3L50MFEtr5k9TrWBpGdSXpQQ== dependencies: - "@tryghost/debug" "^0.1.31" + "@tryghost/debug" "^0.1.32" lodash "^4.17.21" -"@tryghost/bookshelf-include-count@^0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-include-count/-/bookshelf-include-count-0.3.14.tgz#1fb400a8e4bb2cf1f73e06ff8749d986072b2b12" - integrity sha512-m01Phu29/HcDb1Y84dKRkjGaxDTPrSqebunJm7PBOpF4nR5Qy+w//2df/QB9XvD+rdUCwzLT+yi9E6zgfPdnng== +"@tryghost/bookshelf-filter@^0.5.17": + version "0.5.17" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-filter/-/bookshelf-filter-0.5.17.tgz#405364028babd7107922c3fa70bf8b7037296b26" + integrity sha512-nOkW2vnuAhPc91QoCDktQ6Y67eNRRoS7F0VNf1fh+DZj1wLfWZIhXYWlEip3jAd4PONg/xXKv1fDDKCA/JRDUw== dependencies: - "@tryghost/debug" "^0.1.31" + "@tryghost/debug" "^0.1.32" + "@tryghost/errors" "^1.3.5" + "@tryghost/nql" "^0.12.3" + "@tryghost/tpl" "^0.1.32" + +"@tryghost/bookshelf-has-posts@^0.1.32": + version "0.1.32" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-has-posts/-/bookshelf-has-posts-0.1.32.tgz#c64fced29281349df4bb06cf84bc99860c6aecf6" + integrity sha512-1vIQMJyeTQ//vCldu2x5kAjCUYtB8M90iiKrXmL06N1Tlz5SChntnXJi8xGesVBndQlqcuaJ5vsM8p5k4SdK1w== + dependencies: + "@tryghost/debug" "^0.1.32" lodash "^4.17.21" -"@tryghost/bookshelf-order@^0.1.26": - version "0.1.26" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-order/-/bookshelf-order-0.1.26.tgz#f45212f708e28fc9b613bdaf93891a8fe625bc9c" - integrity sha512-9R8sFqjgeD02lPDxbeweP8QY290rEgMCs7Fnqu4MX77eU4Rf8q8Tfg92Wsmfljie3JLPZYhItIR1OSqQpRd3xg== +"@tryghost/bookshelf-include-count@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-include-count/-/bookshelf-include-count-0.3.15.tgz#433ec7f3ec749b23c81fd60e03e9aadbf5513bfe" + integrity sha512-GamGZSt688D1jw8nupo1NSkiYDSTwj1FUGA0iwVMzFhwIuK3Uaq+Xg5RdGXfoayJrXEhqmbYJ3aoZkYTFloEzA== + dependencies: + "@tryghost/debug" "^0.1.32" + lodash "^4.17.21" + +"@tryghost/bookshelf-order@^0.1.27": + version "0.1.27" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-order/-/bookshelf-order-0.1.27.tgz#bd456ffa4413a5cc2c66480b79eec610d0edcc67" + integrity sha512-XJLuShGg+2Gv+U1EqsOF9nzSV/+b06/q8KRWqztGYgoKNYk+5lV9AuHjKIrJx1bcKtc0HdmjcDdHHgX0KIYOHA== dependencies: lodash "^4.17.21" -"@tryghost/bookshelf-pagination@^0.1.46": - version "0.1.46" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-pagination/-/bookshelf-pagination-0.1.46.tgz#6622dbf9afbfe8be7fa6ae13315c06a8c2866e3b" - integrity sha512-dsikUD00DyW+C6GMZiuTIHkipJbzNlx47Rb160ZAPhahuma8LoAr4GAUMxgxEjFVwjveg45v4ZGZjn7tpkMtgQ== +"@tryghost/bookshelf-pagination@^0.1.48": + version "0.1.48" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-pagination/-/bookshelf-pagination-0.1.48.tgz#095e7f0544f1fd4e94fb9aba0c395673a80164e5" + integrity sha512-oii7wOhXD+2/6tk6f/LvXMimL6+DYnN5VaCSKdSHqUPhX6FKPhTQ0mCL+jfMaI8cKASI2KHO+jeRBV8AJZoK3Q== dependencies: - "@tryghost/errors" "^1.3.3" - "@tryghost/tpl" "^0.1.31" + "@tryghost/errors" "^1.3.5" + "@tryghost/tpl" "^0.1.32" lodash "^4.17.21" -"@tryghost/bookshelf-plugins@0.6.19": - version "0.6.19" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-plugins/-/bookshelf-plugins-0.6.19.tgz#e5a51d6973a00ba7801aad6f13b3103e59d6acc4" - integrity sha512-/9UTHm1kMy8fJpAroEPpB8eU4MFGBmBr+GAr1ejm2jfSZBgeJFDEoWNDGJNvplnadtsbogprn6L2AAKD//kMBg== +"@tryghost/bookshelf-plugins@0.6.21": + version "0.6.21" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-plugins/-/bookshelf-plugins-0.6.21.tgz#e4b55542cec60d097c3fedd44e8fddf447ac9b21" + integrity sha512-qYdQrp/bIYnmPYwl/mROwO+uclAZJCPW95vUAAhX7Bg7O26cN+46X7RbzPbphTEKQ2M1JolpMFcWmctczLahxw== dependencies: - "@tryghost/bookshelf-collision" "^0.1.43" - "@tryghost/bookshelf-custom-query" "^0.1.26" - "@tryghost/bookshelf-eager-load" "^0.1.30" - "@tryghost/bookshelf-filter" "^0.5.15" - "@tryghost/bookshelf-has-posts" "^0.1.31" - "@tryghost/bookshelf-include-count" "^0.3.14" - "@tryghost/bookshelf-order" "^0.1.26" - "@tryghost/bookshelf-pagination" "^0.1.46" - "@tryghost/bookshelf-search" "^0.1.26" - "@tryghost/bookshelf-transaction-events" "^0.2.14" + "@tryghost/bookshelf-collision" "^0.1.45" + "@tryghost/bookshelf-custom-query" "^0.1.27" + "@tryghost/bookshelf-eager-load" "^0.1.31" + "@tryghost/bookshelf-filter" "^0.5.17" + "@tryghost/bookshelf-has-posts" "^0.1.32" + "@tryghost/bookshelf-include-count" "^0.3.15" + "@tryghost/bookshelf-order" "^0.1.27" + "@tryghost/bookshelf-pagination" "^0.1.48" + "@tryghost/bookshelf-search" "^0.1.27" + "@tryghost/bookshelf-transaction-events" "^0.2.16" -"@tryghost/bookshelf-search@^0.1.26": - version "0.1.26" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-search/-/bookshelf-search-0.1.26.tgz#7051d52a6fe3bebd5c4a280a976ebbede0cc55df" - integrity sha512-ALO7B+Y7jb8GWl8Z5oC57lb85INGj7JBXxCIc40g1+otori6qUuWmooCY/3AymeSr4TdKg5PMmgavSB7KnVUkw== +"@tryghost/bookshelf-search@^0.1.27": + version "0.1.27" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-search/-/bookshelf-search-0.1.27.tgz#514b4ac787a9df60dd1c26d0749a36f4159f0158" + integrity sha512-j9Y7uNoFscNDG39FVkK2TH/EKChr4n4vzpBgqSOp9R/k91SWTzmALOxKYvKqOfxuJs8uHDE1sxAdBbxK6bbjjA== -"@tryghost/bookshelf-transaction-events@^0.2.14": - version "0.2.14" - resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-transaction-events/-/bookshelf-transaction-events-0.2.14.tgz#194d375bd7400096169e709b4f071b1296171e05" - integrity sha512-tv1DLxHszuEbERUNzJBRLKZHh3ZlIu+8alGQPLN4F4okC1biZQIiiRp9qHaTFvTpzgY4uDD5ysH4HtdRoVV7yw== +"@tryghost/bookshelf-transaction-events@^0.2.16": + version "0.2.16" + resolved "https://registry.yarnpkg.com/@tryghost/bookshelf-transaction-events/-/bookshelf-transaction-events-0.2.16.tgz#d1978e37d919931c2361f415e27d3e2c93b63d8d" + integrity sha512-rNcbLxhHEL47czQ3TnlWxQ46IbpHrVYyutqaJDCoNFyvzb0ZIcULilMyoXt44JjpaSNig32Sy6IOTHbB3vlVcg== "@tryghost/bunyan-rotating-filestream@^0.0.7": version "0.0.7" @@ -7807,20 +7718,12 @@ resolved "https://registry.yarnpkg.com/@tryghost/database-info/-/database-info-0.3.22.tgz#8db1433f35fcee8fbca182ba8b7ea7e682a45fcd" integrity sha512-AwljcoXFUjSKFIoB/V9uohTLvWY8g+fIaHHjgPLH4Cq1tBZgcAlBIZR2ridhXiY1iDX7m3cws36uAxrN0KnoUw== -"@tryghost/database-info@0.3.24": - version "0.3.24" - resolved "https://registry.yarnpkg.com/@tryghost/database-info/-/database-info-0.3.24.tgz#a7bc3519dd6c035aadd0e0c0dac03b55637390f5" - integrity sha512-V5IHVJSyC8wrby/5YtFa1ajQ4qB3wfqc+AwlemDAEH2pZ5n5ygdb2qayeo4kwWPK0xn2Vul5KiRQK0bX+OePdw== +"@tryghost/database-info@0.3.27": + version "0.3.27" + resolved "https://registry.yarnpkg.com/@tryghost/database-info/-/database-info-0.3.27.tgz#b0f7e4b15eb9923094cf4f37084f8ada932c0883" + integrity sha512-0blN+k/NA6c/gZtihx4iyZi1yyPG40GxdYILjtp/8XwGZarmgTTiZGz3HVOVir4+TfL/Z1O5xPsBxO9+KeiH+g== -"@tryghost/debug@0.1.30": - version "0.1.30" - resolved "https://registry.yarnpkg.com/@tryghost/debug/-/debug-0.1.30.tgz#1daaac3a6bf63d82f28ba62f515c95dae29b12b4" - integrity sha512-bW/RBkF5QU9iwASVSzdwFM2ba41OWpqe8ffsmblJ+e6z0NPZYuul/n72E/SWyBa8jTVpMLIoxiKG/wO2ajzHWg== - dependencies: - "@tryghost/root-utils" "^0.3.28" - debug "^4.3.1" - -"@tryghost/debug@^0.1.13", "@tryghost/debug@^0.1.26", "@tryghost/debug@^0.1.31", "@tryghost/debug@^0.1.32": +"@tryghost/debug@0.1.32", "@tryghost/debug@^0.1.13", "@tryghost/debug@^0.1.26", "@tryghost/debug@^0.1.32": version "0.1.32" resolved "https://registry.npmjs.org/@tryghost/debug/-/debug-0.1.32.tgz#a6c1321132708c2e86bd8c47bf28292180fee722" integrity sha512-oU4hy27I3WDA5Zty74QQ+Ki842Ib0aWdXKwfK01YwQ/gqr127mtNOBimkRg39mY27hO4PvE/zvRhtlJ05Jk2cQ== @@ -7828,7 +7731,7 @@ "@tryghost/root-utils" "^0.3.30" debug "^4.3.1" -"@tryghost/elasticsearch@^3.0.19", "@tryghost/elasticsearch@^3.0.21": +"@tryghost/elasticsearch@^3.0.21": version "3.0.21" resolved "https://registry.npmjs.org/@tryghost/elasticsearch/-/elasticsearch-3.0.21.tgz#a4acbfccf1577d1f7c9750018cbd30afefa87b3a" integrity sha512-blSWDB1HCFwhmo5tcDWpOWeMJdFTxTrZpfxU1asKVhARVYuLx5Su5+a8C4x43wewyAhMn9dc9e7qSUHsnUq8XQ== @@ -7837,10 +7740,10 @@ "@tryghost/debug" "^0.1.32" split2 "4.2.0" -"@tryghost/email-mock-receiver@0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@tryghost/email-mock-receiver/-/email-mock-receiver-0.3.6.tgz#6c829923ff5c2dab1ca5c792fea5688030e9284f" - integrity sha512-ykO4YTjgLDnmkkJgAkr+9YmWG0agL+GxpoaI9JPTCsuEMESYo3YZ+a2LweXK7QgxKLQTw7mBtxCWqqSwsAYzUA== +"@tryghost/email-mock-receiver@0.3.8": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@tryghost/email-mock-receiver/-/email-mock-receiver-0.3.8.tgz#17480deaa75e8c0e66560969c0f13e39b8ce6196" + integrity sha512-olzxRq/xV1mS5flOHmKaFZQ5JFLPMq/9XJqR457fsNvaum/PPUoj/jddjJg/5tG8h++Ea9KqBxszo9vPvdbcCw== "@tryghost/ember-promise-modals@2.0.1": version "2.0.1" @@ -7858,20 +7761,20 @@ focus-trap "^6.7.2" postcss-preset-env "^7.3.1" -"@tryghost/errors@1.3.1", "@tryghost/errors@1.3.2", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.3", "@tryghost/errors@^1.3.2", "@tryghost/errors@^1.3.3": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.3.2.tgz#3612f6f59ca07e37d1095f9eb31ff6a069a3b7f2" - integrity sha512-Aqi2vz7HHwN6p2juIYUu8vpMtaKavjULBKNnL0l1req9qXjPs90i/HV8zhvK0ceeWuPdEXaCkfHSRr/yxG3/uw== +"@tryghost/errors@1.3.1", "@tryghost/errors@1.3.5", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.3", "@tryghost/errors@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.3.5.tgz#f4ef8e5c41a8a37456f2285271124180685827ae" + integrity sha512-iOkiHGnYFqSdFM9AVlgiL56Qcx6V9iQ3kbDKxyOAxrhMKq1OnOmOm7tr1CgGK1YDte9XYEZmR9hUZEg+ujn/jQ== dependencies: - "@stdlib/utils-copy" "^0.0.7" + "@stdlib/utils-copy" "^0.2.0" uuid "^9.0.0" -"@tryghost/express-test@0.13.12": - version "0.13.12" - resolved "https://registry.yarnpkg.com/@tryghost/express-test/-/express-test-0.13.12.tgz#2dcc0e573fa14af75f28a7ff595a31c2489d6d5d" - integrity sha512-w7r0qWqpwTjyLDYmWD3WwT9AupzTr8YYUM+Q4BcxzQUkvSUYjMHq2MrpJKcy0qRcO8j3uyo9pGcZOLCxYj/D0w== +"@tryghost/express-test@0.13.15": + version "0.13.15" + resolved "https://registry.yarnpkg.com/@tryghost/express-test/-/express-test-0.13.15.tgz#a906f6935e9cbf60fd5034a9549de50b76b544ae" + integrity sha512-FyugwFZkch76JSr60xkZWqXcyCaAK4VA01g/dZOA05iUvBdWN6qDNOqLXEQ4aRV3VWqt4X4BfqeVsrZQf4VoTw== dependencies: - "@tryghost/jest-snapshot" "^0.5.12" + "@tryghost/jest-snapshot" "^0.5.15" cookiejar "^2.1.3" reqresnext "^1.7.0" @@ -7891,18 +7794,18 @@ "@tryghost/mobiledoc-kit" "^0.12.4-ghost.1" jsdom "^24.0.0" -"@tryghost/http-cache-utils@0.1.15": - version "0.1.15" - resolved "https://registry.yarnpkg.com/@tryghost/http-cache-utils/-/http-cache-utils-0.1.15.tgz#721ffd1f3b7173da679f6c8c25cda6f0c728264b" - integrity sha512-G0x9ZUkBbTWIFg2Dnng6qKLststZKuUYx98pcowNwpYuEGUPOka4eAJj3frz/60F1CzMb1qYy9WEjD/xRGd5oQ== +"@tryghost/http-cache-utils@0.1.17": + version "0.1.17" + resolved "https://registry.yarnpkg.com/@tryghost/http-cache-utils/-/http-cache-utils-0.1.17.tgz#9dd01464cfa52947fa0b63ea57ef084106ff42ba" + integrity sha512-sO/C2nCX3C4sPz1ysN8/9em8dbhnSUGP0d84CjZsSrs/DYzZmw1nWJGKzDF80mOpYIs34GGL+JhybRRTlOrviA== -"@tryghost/http-stream@^0.1.30": - version "0.1.30" - resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-0.1.30.tgz#ef2a9a55a2ec7fab7e889a11ce23e7ebfaab8a0b" - integrity sha512-ZlcwtWQICN2OeHcVAL+BXxkzO5tICdLYZeQ2EuVk2E+mC+1ARFrpVUbpTnIbCXDL+uV3cOLqPh2KNGGJAdDChw== +"@tryghost/http-stream@^0.1.33": + version "0.1.33" + resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-0.1.33.tgz#8c74d63c0ad764e0a889709751ed6379d387daab" + integrity sha512-6DC/YmpEF/u8aez8e3+Xc4MkihSILP5EfErQsrUCT3QcdaEIxR+3u1JlsH9Qe7U5ld1wN7tzDS45mFDQy96G6Q== dependencies: - "@tryghost/errors" "^1.3.2" - "@tryghost/request" "^1.0.5" + "@tryghost/errors" "^1.3.5" + "@tryghost/request" "^1.0.8" "@tryghost/image-transform@1.3.0": version "1.3.0" @@ -7914,14 +7817,14 @@ optionalDependencies: sharp "^0.32.0" -"@tryghost/jest-snapshot@^0.5.12": - version "0.5.12" - resolved "https://registry.yarnpkg.com/@tryghost/jest-snapshot/-/jest-snapshot-0.5.12.tgz#79134e0a7b273552f9744d753a71b63712f81f55" - integrity sha512-R7q29RWFsguTSjCpRZoXprPqyaUPRQQ+4r9V/LaWjB6rc2yu4aLpOdtocl1yVOUlSDXApAy6NknHOCejqhA5MA== +"@tryghost/jest-snapshot@^0.5.15": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@tryghost/jest-snapshot/-/jest-snapshot-0.5.15.tgz#51b4e0e27450e1804cea8b86e9380a4d7384d890" + integrity sha512-HRz/dYX6t96Iv8ZrlIepQa286B2n/y/VnnLvZf3UoPXXKWe4sGcrgELgywP/26gSxfWfSkvK4hlWzCq2AQiiyg== dependencies: "@jest/expect" "^28.0.1" "@jest/expect-utils" "^28.0.1" - "@tryghost/errors" "^1.3.2" + "@tryghost/errors" "^1.3.5" jest-snapshot "^29.0.0" "@tryghost/kg-card-factory@5.0.4": @@ -8076,16 +7979,16 @@ lodash "^4.17.21" luxon "^1.26.0" -"@tryghost/logging@2.4.10", "@tryghost/logging@2.4.15", "@tryghost/logging@^2.4.7": - version "2.4.15" - resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.15.tgz#a94e37d760a62d6f2fc2868e4cd8bf6f219b2a2e" - integrity sha512-mSVdSR/9bd1D/DCFpfeFn2AnPE/0lK78ePHBrtteOipA7ogL0Kd+QvabHK5iKLe+/20flBZs4BvnU/DBuS8Pvw== +"@tryghost/logging@2.4.10", "@tryghost/logging@2.4.18", "@tryghost/logging@^2.4.7": + version "2.4.18" + resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.18.tgz#5d7ddb2d0a66dc6834a6048ebbf48418420445d5" + integrity sha512-mMJkdCFDXa0ohS0FlDTvOrJQd7VamBIqjljGYvNECdVli7BMjdUYgZyWr8bEJ/d7scsq8OE2bVVBJWLxvPxLAg== dependencies: "@tryghost/bunyan-rotating-filestream" "^0.0.7" - "@tryghost/elasticsearch" "^3.0.19" - "@tryghost/http-stream" "^0.1.30" - "@tryghost/pretty-stream" "^0.1.24" - "@tryghost/root-utils" "^0.3.28" + "@tryghost/elasticsearch" "^3.0.21" + "@tryghost/http-stream" "^0.1.33" + "@tryghost/pretty-stream" "^0.1.26" + "@tryghost/root-utils" "^0.3.30" bunyan "^1.8.15" bunyan-loggly "^1.4.2" fs-extra "^11.0.0" @@ -8126,13 +8029,13 @@ dependencies: lodash "^4.17.11" -"@tryghost/nodemailer@0.3.42": - version "0.3.42" - resolved "https://registry.yarnpkg.com/@tryghost/nodemailer/-/nodemailer-0.3.42.tgz#478ae3b002220e9495930bec01d6f31d7bdc8ab2" - integrity sha512-Bn0vjJxhTi72eaIFlEv92dhqDM0Rw0GBofC0XR2mhCv9Mdr4p/+sbRFSVvAQewXs4rsSszo3Xf1XkONKnx7c7Q== +"@tryghost/nodemailer@0.3.45": + version "0.3.45" + resolved "https://registry.yarnpkg.com/@tryghost/nodemailer/-/nodemailer-0.3.45.tgz#173a9bd378d8f161acee09f0e28abc6cf028bdc0" + integrity sha512-vvAjXN/oOpP9fo7T/J+IBSqdkZhdbqPL9OWUP+qulq1/6fIqwGmye+mu9E2aPckgIoVlOGCpHKqMz9G22ZeuIw== dependencies: "@aws-sdk/client-ses" "^3.31.0" - "@tryghost/errors" "^1.3.2" + "@tryghost/errors" "^1.3.5" nodemailer "^6.6.3" nodemailer-direct-transport "^3.3.2" nodemailer-mailgun-transport "^2.1.5" @@ -8155,15 +8058,15 @@ "@tryghost/nql-lang" "^0.6.1" mingo "^2.2.2" -"@tryghost/pretty-cli@1.2.42", "@tryghost/pretty-cli@^1.2.38": - version "1.2.42" - resolved "https://registry.yarnpkg.com/@tryghost/pretty-cli/-/pretty-cli-1.2.42.tgz#ea7435f7feeba9fe8950c7da0b8565b7371766f5" - integrity sha512-pXOhAIi9RliiLXc3wuVtiaHcFqd/0WvB3tI8ugSQMN+NieU7GDjPEEPx9fEFx/3/48ZPYl7PRjfm6tbDxdiEGQ== +"@tryghost/pretty-cli@1.2.44", "@tryghost/pretty-cli@^1.2.38": + version "1.2.44" + resolved "https://registry.yarnpkg.com/@tryghost/pretty-cli/-/pretty-cli-1.2.44.tgz#9ae1923076c3390457ed40df0217e216413b0f1e" + integrity sha512-1OVpdWdlkrUNyKwRSdEsfQQ87hR5dRj4GxG7UvJoTkbvxlmZjziUUF3nuDYF5w/4JIvO7w6Z5oxe7KqXv846KQ== dependencies: chalk "^4.1.0" sywac "^1.3.0" -"@tryghost/pretty-stream@^0.1.24", "@tryghost/pretty-stream@^0.1.26": +"@tryghost/pretty-stream@^0.1.26": version "0.1.26" resolved "https://registry.npmjs.org/@tryghost/pretty-stream/-/pretty-stream-0.1.26.tgz#1765f5080c37fa338ddd96003462a1da54e57061" integrity sha512-Z56lak7dBiK5rgwFd1vYbSpsHi65gLLAZaCq81UMxo8fulvyTiWpdvoY+7Q5XApSi6kf2eivYmeN0zmZa+zdNg== @@ -8172,37 +8075,29 @@ moment "^2.29.1" prettyjson "^1.2.5" -"@tryghost/promise@0.3.10": - version "0.3.10" - resolved "https://registry.yarnpkg.com/@tryghost/promise/-/promise-0.3.10.tgz#a1025c37773e8294fd4cecee33af1ed0bc07bcce" - integrity sha512-0Cs3cdOViYPHlDcgFLOPX2xyWMbnHRHN773hvJ8KfFAeF0QmNF07dvjYvTmHkXd5Dy551UgRjtZR5eZqpqAO3A== +"@tryghost/promise@0.3.12": + version "0.3.12" + resolved "https://registry.yarnpkg.com/@tryghost/promise/-/promise-0.3.12.tgz#90aff21f256767f1d1c06f358f5cf4012552cb34" + integrity sha512-0nktnEMM0sSKHYUb+QeVhQau1ziKWmgaqdffhomIItw5UQEnArEvwVCPH0qh+L7xGsrphf6SHSiMibNCtJIvvA== "@tryghost/promise@0.3.8": version "0.3.8" resolved "https://registry.yarnpkg.com/@tryghost/promise/-/promise-0.3.8.tgz#3134a044e187f6d61393267c680c6cc75235aa96" integrity sha512-ppcnLBWczpbo4sQcGWtjEA82kdZMv4NFF2MvZRi1MBP4lSOSgh9A636eUxlB1/FpIG+D5ixq84xlY4QJMqW2kA== -"@tryghost/request@1.0.5", "@tryghost/request@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@tryghost/request/-/request-1.0.5.tgz#76ef4139cfe59f4daf8f72ae9291f418c45e0419" - integrity sha512-LUXpbNZ4BvSXvH8e+ribMuq2jN9QIzGCB36YXkWP7vd0lcwACWxa1HOi+fC89dkMYzYYJ7YvQgIpxJilKfYbgg== +"@tryghost/request@1.0.8", "@tryghost/request@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tryghost/request/-/request-1.0.8.tgz#d2b87fe30bfaa1d9011a8e58152b941161465113" + integrity sha512-wto/TG5xQ6xq8sKEI/N66C+dxSFORusk4yjtsqZNV3E4LEFfg/2HzrIYH6YW9Iip15uR4u0uKhFRMs0J1RRCuA== dependencies: - "@tryghost/errors" "^1.3.2" - "@tryghost/validator" "^0.2.11" - "@tryghost/version" "^0.1.28" + "@tryghost/errors" "^1.3.5" + "@tryghost/validator" "^0.2.14" + "@tryghost/version" "^0.1.30" cacheable-lookup "7.0.0" got "13.0.0" lodash "^4.17.21" -"@tryghost/root-utils@0.3.28": - version "0.3.28" - resolved "https://registry.yarnpkg.com/@tryghost/root-utils/-/root-utils-0.3.28.tgz#43ae0047927a7753c9b526ea12ce6e382ec7fb1f" - integrity sha512-/izwMw9tCJIQ3DVHumzEWgKhKAw5FwTgrrYcCNHl89yijJKaVRBOJUhlB/u2ST6UWfhahodjaYauq7ymTItaeg== - dependencies: - caller "^1.0.1" - find-root "^1.1.0" - -"@tryghost/root-utils@^0.3.24", "@tryghost/root-utils@^0.3.28", "@tryghost/root-utils@^0.3.30": +"@tryghost/root-utils@0.3.30", "@tryghost/root-utils@^0.3.24", "@tryghost/root-utils@^0.3.30": version "0.3.30" resolved "https://registry.npmjs.org/@tryghost/root-utils/-/root-utils-0.3.30.tgz#766818cd4394b683338f4d9fccc52c435f77b0b5" integrity sha512-Pg9d0Igl3OGGKZ5RrCe6Wq8qAW9ABIjbn31WKlsO6OLLwRlH2ruNZXU8GBjNL3qrPEZwyI2FyNNDjSR+5gKXag== @@ -8235,17 +8130,10 @@ resolved "https://registry.yarnpkg.com/@tryghost/timezone-data/-/timezone-data-0.4.3.tgz#dad18d1c119ebf705b9720417684efe00081f149" integrity sha512-YrPm2m51/Ddqp+/9CclSv+tKqHkbv7iUu3BgiHJofGhs6ek0QQSpahgiBmJmi/o3R6V6DFbG7LT5qVEtkvMzKg== -"@tryghost/tpl@0.1.30": - version "0.1.30" - resolved "https://registry.yarnpkg.com/@tryghost/tpl/-/tpl-0.1.30.tgz#a012fd800e58d46a7d4146e2bde5f55892d8ae15" - integrity sha512-EhbwSYCTFdK9cXyesCNPFwd3pF6an72vwTeQ9A9IjTVDd4or3wBc6lUwOifxWdYarxhaq2sX67gOn2Js6NbJWg== - dependencies: - lodash.template "^4.5.0" - -"@tryghost/tpl@^0.1.30", "@tryghost/tpl@^0.1.31": - version "0.1.31" - resolved "https://registry.yarnpkg.com/@tryghost/tpl/-/tpl-0.1.31.tgz#3aad44ef36b6a3af4f4c9c33c320bc7ac565cfab" - integrity sha512-AJVKeGq8/5ZHCJyKJQAB26/KXXsoRmRZUY+viJ6fgTV4eMwgsWh24Dv2L3gzOCiUi1iPYNf3MZaPnZqSquyxHg== +"@tryghost/tpl@0.1.32", "@tryghost/tpl@^0.1.32": + version "0.1.32" + resolved "https://registry.yarnpkg.com/@tryghost/tpl/-/tpl-0.1.32.tgz#be0ef05abf8f959f459ab9a8fc5e39efaea542a7" + integrity sha512-OvUMLn5NoAYXqqwh4i9FInkCdJiVPGgCeaq4kdiZWT5ApP4NMOd84GFO8uitLsYtrLsHZ01UFuzZJt6rpKtjmw== dependencies: lodash.template "^4.5.0" @@ -8262,38 +8150,38 @@ remark-footnotes "^1.0.0" unist-util-visit "^2.0.0" -"@tryghost/validator@0.2.11", "@tryghost/validator@^0.2.11": - version "0.2.11" - resolved "https://registry.yarnpkg.com/@tryghost/validator/-/validator-0.2.11.tgz#c7c0be6b70c82aebefa29e91ca6ebe8939306443" - integrity sha512-VJ+u/UcqP7SRKHxdQn2S21EcJYjWBLfDD31rhfupkcEV7MwPQ5f3nRp+7xKnkUksB3VIsYRtVTAzufIPBBsnzw== +"@tryghost/validator@0.2.14", "@tryghost/validator@^0.2.14": + version "0.2.14" + resolved "https://registry.yarnpkg.com/@tryghost/validator/-/validator-0.2.14.tgz#7aea2725e23125b23fd3d9203bede8f1559e7b54" + integrity sha512-sSTzN8ngVyAQyqbMtNPSX+eGrK9XoGntVCk5m3V6/iRA3VB3NaI1tE4RB/8Tg5zNr0q899jEyrqWSKkx2sBCZw== dependencies: - "@tryghost/errors" "^1.3.2" - "@tryghost/tpl" "^0.1.30" + "@tryghost/errors" "^1.3.5" + "@tryghost/tpl" "^0.1.32" lodash "^4.17.21" moment-timezone "^0.5.23" validator "7.2.0" -"@tryghost/version@0.1.28", "@tryghost/version@^0.1.28": - version "0.1.28" - resolved "https://registry.yarnpkg.com/@tryghost/version/-/version-0.1.28.tgz#f95ad56df5e052e200fdd9b4cea49768ef9a730b" - integrity sha512-iiOYEHIcBGCjVkTOsHDLuaN+oLiHK8P1rgAy/DGmlPgRr35A4b8swk5xbKxGtCengSRzGYxvxd/z2GsryqlkbQ== +"@tryghost/version@0.1.30", "@tryghost/version@^0.1.30": + version "0.1.30" + resolved "https://registry.yarnpkg.com/@tryghost/version/-/version-0.1.30.tgz#0f6b0eb5e89edcaf829c9199727b6199977b609b" + integrity sha512-WDCVAllBMScplxnyATDgQWHZIrIy/gurK12Tr4pDUtWMujWf/24U/nWZE9dWMrQe1meam5VC4QdqLQWA7eE8UQ== dependencies: - "@tryghost/root-utils" "^0.3.28" + "@tryghost/root-utils" "^0.3.30" semver "^7.3.5" -"@tryghost/webhook-mock-receiver@0.2.12": - version "0.2.12" - resolved "https://registry.yarnpkg.com/@tryghost/webhook-mock-receiver/-/webhook-mock-receiver-0.2.12.tgz#b210cc682de134d273855d0056c2e836f9c82f34" - integrity sha512-TOg5+XGO+TiBzMuel41bFQwTmuN8qXIXFOCYbXbBBzn9UehYuEvOMXyQp3pRS7WNli7t8y5BV8gvkn7uZzSmYg== +"@tryghost/webhook-mock-receiver@0.2.14": + version "0.2.14" + resolved "https://registry.yarnpkg.com/@tryghost/webhook-mock-receiver/-/webhook-mock-receiver-0.2.14.tgz#6a62bff89a330e69c0112bd688ab25900df91149" + integrity sha512-jPUvercXPR7J5X7QVQJeXeF6tLVBz5bluWRQaDxasIT2I9y8+REqEvqFyaOUtL7tjQp83/8gUPxzR5jAyqaTsg== dependencies: p-wait-for "3.2.0" -"@tryghost/zip@1.1.43", "@tryghost/zip@^1.1.42": - version "1.1.43" - resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.43.tgz#e5ac6a13b19d0b1dad2aec04e9a1327de39c72d5" - integrity sha512-cB3gGiO29zQRjAz+Q3ly8z4DO3TA+YBhDNuNdN02unhkeTs6Hsf7eAcBNMYJVBroQVQ4N8femYKr90AIJe1OIQ== +"@tryghost/zip@1.1.46", "@tryghost/zip@^1.1.42": + version "1.1.46" + resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-1.1.46.tgz#e7c0097b6b6229193a7d6f9b4881eb5640a464a9" + integrity sha512-T25d3q1DXrDMWBX8zJ2UoxvKpy3PMahKrikO/1zFCMY9YEUXSKa8FV2YmJzmmmjnFITkVMlH4JXFkWbB8igZ4A== dependencies: - "@tryghost/errors" "^1.3.2" + "@tryghost/errors" "^1.3.5" archiver "^5.0.0" extract-zip "^2.0.1" From 779f9d19231e29ed0f3339305291d1a20d4d5c1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:03:58 +0000 Subject: [PATCH 110/164] Update dependency rimraf to v5.0.10 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fdf79efde4..4c3dcd389a 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "husky": "8.0.3", "lint-staged": "15.2.8", "nx": "16.8.1", - "rimraf": "5.0.9", + "rimraf": "5.0.10", "ts-node": "10.9.2", "typescript": "5.4.5", "inquirer": "8.2.6" diff --git a/yarn.lock b/yarn.lock index f3ed36ecfb..c3ccd86115 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28086,10 +28086,10 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== -rimraf@5.0.9: - version "5.0.9" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.9.tgz#c3baa1b886eadc2ec7981a06a593c3d01134ffe9" - integrity sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA== +rimraf@5.0.10: + version "5.0.10" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" + integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== dependencies: glob "^10.3.7" From 5376bd52c7afb91b3fc0c23bd81749b152d48fdd Mon Sep 17 00:00:00 2001 From: Fabien 'egg' O'Carroll Date: Mon, 5 Aug 2024 18:07:03 +0700 Subject: [PATCH 111/164] Updated design of announced Notes and Notes containing links - Announced (reposted) Notes show information about both the Actor that created the Note, and the Actor that Announced it - The content of notes now keeps the formatting and links are clickable and shown in different color Co-authored-by: Djordje Vlaisavljevic --- .../src/components/ListIndex.tsx | 53 ++++++++++++------- apps/admin-x-activitypub/src/styles/index.css | 15 +++++- .../src/assets/icons/reload.svg | 1 + 3 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 apps/admin-x-design-system/src/assets/icons/reload.svg diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 4c785eead9..9f9d1393ec 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -4,7 +4,7 @@ import articleBodyStyles from './articleBodyStyles'; import getUsername from '../utils/get-username'; import {ActivityPubAPI} from '../api/activitypub'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; -import {Avatar, Button, ButtonGroup, Heading, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; +import {Avatar, Button, ButtonGroup, Heading, Icon, List, ListItem, Page, SelectOption, SettingValue, ViewContainer, ViewTab} from '@tryghost/admin-x-design-system'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; import {useQuery} from '@tanstack/react-query'; import {useRouting} from '@tryghost/admin-x-framework/routing'; @@ -123,6 +123,7 @@ const ActivityPubComponent: React.FC = () => { actor={activity.actor} layout={selectedOption.value} object={activity.object} + type={activity.type} /> ))} @@ -167,6 +168,7 @@ const ActivityPubComponent: React.FC = () => { actor={activity.actor} layout={selectedOption.value} object={activity.object} + type={activity.object.type} /> ))} @@ -320,7 +322,7 @@ ${image && ); }; -const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string }> = ({actor, object, layout}) => { +const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string, type: string }> = ({actor, object, layout, type}) => { const parser = new DOMParser(); const doc = parser.parseFromString(object.content || '', 'text/html'); @@ -392,27 +394,40 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp setTimeout(() => setIsClicked(false), 300); // Reset the animation class after 300ms }; + let author = actor; + if (type === 'Announce' && object.type === 'Note') { + author = typeof object.attributedTo === 'object' ? object.attributedTo as ActorProperties : actor; + } + if (layout === 'feed') { return ( <> {object && ( -
    - -
    -
    -

    {actor.name}

    - {getUsername(actor)} - {timestamp} -
    -
    -
    - {object.name && {object.name}} -

    {plainTextContent}

    - {/*

    {object.content}

    */} - {renderAttachment()} -
    -
    diff --git a/apps/admin-x-activitypub/src/styles/index.css b/apps/admin-x-activitypub/src/styles/index.css index c3b58ac682..a59dd422e7 100644 --- a/apps/admin-x-activitypub/src/styles/index.css +++ b/apps/admin-x-activitypub/src/styles/index.css @@ -22,4 +22,17 @@ animation: bump 0.3s ease-in-out; .ap-red-heart path { fill: #F50B23; -} \ No newline at end of file +} + +.ap-note-content a { + color: rgb(236 72 153) !important; +} + +.ap-note-content a:hover { + color: rgb(219 39 119) !important; +} + +.ap-note-content p + p { + margin-top: 1.5rem !important; +} + diff --git a/apps/admin-x-design-system/src/assets/icons/reload.svg b/apps/admin-x-design-system/src/assets/icons/reload.svg new file mode 100644 index 0000000000..5afa3c7227 --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/reload.svg @@ -0,0 +1 @@ +Button Refresh Arrows Streamline Icon: https://streamlinehq.com \ No newline at end of file From 978ccc848353e621d75ca4713dcc846f6abc4c89 Mon Sep 17 00:00:00 2001 From: Fabien 'egg' O'Carroll Date: Mon, 5 Aug 2024 18:20:34 +0700 Subject: [PATCH 112/164] Updated types for `attributedTo` property of Objects (#20715) ref https://github.com/TryGhost/Ghost/commit/5376bd52c7afb91b This should have been included in the referenced commit --- apps/admin-x-framework/src/api/activitypub.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin-x-framework/src/api/activitypub.ts b/apps/admin-x-framework/src/api/activitypub.ts index 0d419e6568..97201808e0 100644 --- a/apps/admin-x-framework/src/api/activitypub.ts +++ b/apps/admin-x-framework/src/api/activitypub.ts @@ -13,7 +13,7 @@ export type ObjectProperties = { name: string; content: string; url?: string | undefined; - attributedTo?: string | object[] | undefined; + attributedTo?: object | string | object[] | undefined; image?: string; published?: string; preview?: {type: string, content: string}; From 5ed27933694566f323352cdc975a7e0a7e0c3735 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:43:24 +0200 Subject: [PATCH 113/164] Added publication icon to donation notification email (#20704) REF MOM-317 - Added site icon to add some flair and personal branding to the donation notification email. --- .../core/core/server/services/staff/index.js | 2 + ghost/staff-service/lib/StaffService.js | 3 +- ghost/staff-service/lib/StaffServiceEmails.js | 8 +++- .../lib/email-templates/donation.hbs | 44 ++++++++++++------- .../staff-service/test/staff-service.test.js | 9 ++++ 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/ghost/core/core/server/services/staff/index.js b/ghost/core/core/server/services/staff/index.js index 2b57d20de6..56c08cab2d 100644 --- a/ghost/core/core/server/services/staff/index.js +++ b/ghost/core/core/server/services/staff/index.js @@ -17,6 +17,7 @@ class StaffServiceWrapper { const mailer = new GhostMailer(); const settingsCache = require('../../../shared/settings-cache'); const urlUtils = require('../../../shared/url-utils'); + const {blogIcon} = require('../../../server/lib/image'); const settingsHelpers = require('../settings-helpers'); this.api = new StaffService({ @@ -26,6 +27,7 @@ class StaffServiceWrapper { settingsHelpers, settingsCache, urlUtils, + blogIcon, DomainEvents, memberAttributionService: memberAttribution.service, labs diff --git a/ghost/staff-service/lib/StaffService.js b/ghost/staff-service/lib/StaffService.js index 02d4e1b4dd..ed48a4a860 100644 --- a/ghost/staff-service/lib/StaffService.js +++ b/ghost/staff-service/lib/StaffService.js @@ -4,7 +4,7 @@ const {MilestoneCreatedEvent} = require('@tryghost/milestones'); // @NOTE: 'StaffService' is a vague name that does not describe what it's actually doing. // Possibly, "StaffNotificationService" or "StaffEventNotificationService" would be a more accurate name class StaffService { - constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, DomainEvents, labs, memberAttributionService}) { + constructor({logging, models, mailer, settingsCache, settingsHelpers, urlUtils, blogIcon, DomainEvents, labs, memberAttributionService}) { this.logging = logging; this.labs = labs; /** @private */ @@ -22,6 +22,7 @@ class StaffService { settingsHelpers, settingsCache, urlUtils, + blogIcon, labs }); } diff --git a/ghost/staff-service/lib/StaffServiceEmails.js b/ghost/staff-service/lib/StaffServiceEmails.js index b107af3ee1..fa15117087 100644 --- a/ghost/staff-service/lib/StaffServiceEmails.js +++ b/ghost/staff-service/lib/StaffServiceEmails.js @@ -5,12 +5,13 @@ const glob = require('glob'); const {EmailAddressParser} = require('@tryghost/email-addresses'); class StaffServiceEmails { - constructor({logging, models, mailer, settingsHelpers, settingsCache, urlUtils, labs}) { + constructor({logging, models, mailer, settingsHelpers, settingsCache, blogIcon, urlUtils, labs}) { this.logging = logging; this.models = models; this.mailer = mailer; this.settingsHelpers = settingsHelpers; this.settingsCache = settingsCache; + this.blogIcon = blogIcon; this.urlUtils = urlUtils; this.labs = labs; @@ -44,6 +45,7 @@ class StaffServiceEmails { attributionUrl: attribution?.url || '', referrerSource: attribution?.referrerSource, siteTitle: this.settingsCache.get('title'), + siteIconUrl: this.blogIcon.getIconUrl(true), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -103,6 +105,7 @@ class StaffServiceEmails { offerData, subscriptionData, siteTitle: this.settingsCache.get('title'), + siteIconUrl: this.blogIcon.getIconUrl(true), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -153,6 +156,7 @@ class StaffServiceEmails { tierData, subscriptionData, siteTitle: this.settingsCache.get('title'), + siteIconUrl: this.blogIcon.getIconUrl(true), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -182,6 +186,7 @@ class StaffServiceEmails { return { siteTitle: this.settingsCache.get('title'), + siteIconUrl: this.blogIcon.getIconUrl(true), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -282,6 +287,7 @@ class StaffServiceEmails { const templateData = { siteTitle: this.settingsCache.get('title'), siteUrl: this.urlUtils.getSiteUrl(), + siteIconUrl: this.blogIcon.getIconUrl(true), siteDomain: this.siteDomain, fromEmail: this.fromEmailAddress, toEmail: to, diff --git a/ghost/staff-service/lib/email-templates/donation.hbs b/ghost/staff-service/lib/email-templates/donation.hbs index 88b1e07a8f..35a33fc398 100644 --- a/ghost/staff-service/lib/email-templates/donation.hbs +++ b/ghost/staff-service/lib/email-templates/donation.hbs @@ -21,21 +21,33 @@
    -

    Cha-ching!

    -

    You received a one-time payment from {{#if memberData}} - {{donation.name}} ({{donation.email}}) - {{else}} - {{donation.name}} ({{donation.email}}) - {{/if}} - :

    +

    Cha-ching! 💰 You received {{donation.amount}} from {{donation.name}}.

    - +
    -

    {{donation.amount}}

    -
    +

    Type:

    +

    One-time payment

    +

    From:

    +

    {{#if memberData}}{{donation.name}} ({{donation.email}}){{else}}{{donation.name}} ({{donation.email}}){{/if}}

    +

    Amount received:

    +

    {{donation.amount}}

    +
    @@ -45,12 +44,12 @@
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +

    Don’t want to receive these emails? Manage your preferences here.

    + {{#if siteIconUrl}} + + + + {{/if}} @@ -43,13 +55,13 @@ - - diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index bf8b03d0b5..920ee9d37a 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -149,6 +149,12 @@ describe('StaffService', function () { } }; + const blogIcon = { + getIconUrl: () => { + return 'https://ghost.example/siteicon.png'; + } + }; + const settingsHelpers = { getDefaultEmailDomain: () => { return 'ghost.example'; @@ -184,6 +190,7 @@ describe('StaffService', function () { }, settingsCache, urlUtils, + blogIcon, settingsHelpers, labs }); @@ -220,6 +227,7 @@ describe('StaffService', function () { DomainEvents, settingsCache, urlUtils, + blogIcon, settingsHelpers }); service.subscribeEvents(); @@ -339,6 +347,7 @@ describe('StaffService', function () { }, settingsCache, urlUtils, + blogIcon, settingsHelpers, labs: { isSet: () => { From e378252d36d9f3c42ac84a7cb18162de537fd91a Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:44:24 +0200 Subject: [PATCH 114/164] Improved new free signup email design (#20716) REF MOM-339 --- .../lib/email-templates/new-free-signup.hbs | 108 ++++++++---------- .../staff-service/test/staff-service.test.js | 13 +-- 2 files changed, 50 insertions(+), 71 deletions(-) diff --git a/ghost/staff-service/lib/email-templates/new-free-signup.hbs b/ghost/staff-service/lib/email-templates/new-free-signup.hbs index c2a1007267..1d9e1fb69e 100644 --- a/ghost/staff-service/lib/email-templates/new-free-signup.hbs +++ b/ghost/staff-service/lib/email-templates/new-free-signup.hbs @@ -26,64 +26,46 @@
    {{siteTitle}}
    -

    Cha-ching! 💰 You received {{donation.amount}} from {{donation.name}}.

    - +

    Cha-ching! You received a {{donation.amount}} tip from {{donation.name}}.

    +
    - - - + + + + + +
    -

    Type:

    -

    One-time payment

    -

    From:

    -

    {{#if memberData}}{{donation.name}} ({{donation.email}}){{else}}{{donation.name}} ({{donation.email}}){{/if}}

    -

    Amount received:

    -

    {{donation.amount}}

    -
    +

    From:

    + +

    Amount received:

    +

    {{donation.amount}}

    +
    + {{#if memberData}} + View member + {{else}} + View dashboard + {{/if}} +
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +
    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    + {{#if siteIconUrl}} + + + + {{/if}}
    {{siteTitle}}
    -

    Congratulations!

    -

    You have a new free member.

    - - - - - - - -
    - - - - - -
    -
    - {{memberData.initials}} -
    -
    -

    {{memberData.name}}

    - {{#if memberData.showEmail}} - - {{/if}} -

    Created on {{memberData.createdAt}}{{#if memberData.location}} • {{memberData.location}} {{/if}} -

    -
    -
    - - - - - -
    - {{#if referrerSource}} -

    Signup info

    -
    -

    Source - - {{referrerSource}} -

    - {{#if attributionTitle}} -

    Page - - {{attributionTitle}} -

    - {{/if}} - {{/if}} -
    - - +

    New free subscriber to {{siteTitle}}

    +
    - - - diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index 920ee9d37a..1de3d71693 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -439,9 +439,6 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('🥳 Free member signup: Ghost')) ).should.be.true(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) - ).should.be.true(); }); it('sends free member signup alert without member name', async function () { @@ -464,18 +461,13 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('🥳 Free member signup: member@example.com')) ).should.be.true(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) - ).should.be.true(); }); it('sends free member signup alert with attribution', async function () { const member = { name: 'Ghost', email: 'member@example.com', - id: 'abc', - geolocation: '{"country": "France"}', - created_at: '2022-08-01T07:30:39.882Z' + id: 'abc' }; const attribution = { @@ -496,9 +488,6 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('🥳 Free member signup: Ghost')) ).should.be.true(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) - ).should.be.true(); mailStub.calledWith( sinon.match.has('html', sinon.match('Source')) From c8ba9e8027bd8269f8fb0ff3303da6d1e34b9696 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Mon, 5 Aug 2024 19:58:58 +0700 Subject: [PATCH 115/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20unsaved=20change?= =?UTF-8?q?s=20confirmation=20on=20Lexical=20schema=20change=20(#20687)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs ENG-661 Fixes a long-standing issue where an outdated Lexical schema in the database triggered the unsaved changes confirmation dialog incorrectly. Implemented a secondary hidden Lexical instance that loads the state from the database, renders it, and uses this updated state to compare with the live editor's scratch. This ensures the unsaved changes prompt appears only when there are real changes from the user. --- .../components/gh-koenig-editor-lexical.hbs | 2 + .../components/gh-koenig-editor-lexical.js | 7 ++ .../app/components/gh-post-settings-menu.hbs | 1 + .../app/components/koenig-lexical-editor.js | 57 +++++++++------- .../app/components/modal-post-history.hbs | 1 + .../app/components/modal-post-history.js | 7 ++ ghost/admin/app/controllers/lexical-editor.js | 66 +++++++++++-------- ghost/admin/app/models/post.js | 3 + ghost/admin/app/templates/lexical-editor.hbs | 3 + .../tests/unit/controllers/editor-test.js | 45 ++++++++++++- 10 files changed, 141 insertions(+), 51 deletions(-) diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs index af3524b998..3ad4aae7a0 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs @@ -95,7 +95,9 @@ @placeholder={{@bodyPlaceholder}} @cardConfig={{@cardOptions}} @onChange={{@onBodyChange}} + @updateSecondaryInstanceModel={{@updateSecondaryInstanceModel}} @registerAPI={{this.registerEditorAPI}} + @registerSecondaryAPI={{this.registerSecondaryEditorAPI}} @cursorDidExitAtTop={{if this.feature.editorExcerpt this.focusExcerpt this.focusTitle}} @updateWordCount={{@updateWordCount}} @updatePostTkCount={{@updatePostTkCount}} diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.js b/ghost/admin/app/components/gh-koenig-editor-lexical.js index 2319ae9c8a..be28f73a5f 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.js +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.js @@ -15,6 +15,7 @@ export default class GhKoenigEditorLexical extends Component { uploadUrl = `${ghostPaths().apiRoot}/images/upload/`; editorAPI = null; + secondaryEditorAPI = null; skipFocusEditor = false; @tracked titleIsHovered = false; @@ -232,6 +233,12 @@ export default class GhKoenigEditorLexical extends Component { this.args.registerAPI(API); } + @action + registerSecondaryEditorAPI(API) { + this.secondaryEditorAPI = API; + this.args.registerSecondaryAPI(API); + } + // focus the editor when the editor canvas is clicked below the editor content, // otherwise the browser will defocus the editor and the cursor will disappear @action diff --git a/ghost/admin/app/components/gh-post-settings-menu.hbs b/ghost/admin/app/components/gh-post-settings-menu.hbs index 1d01dcc5bc..ee91b2af80 100644 --- a/ghost/admin/app/components/gh-post-settings-menu.hbs +++ b/ghost/admin/app/components/gh-post-settings-menu.hbs @@ -853,6 +853,7 @@ post=this.post editorAPI=this.editorAPI toggleSettingsMenu=this.toggleSettingsMenu + secondaryEditorAPI=this.secondaryEditorAPI }} @close={{this.closePostHistory}} @modifier="total-overlay post-history" /> diff --git a/ghost/admin/app/components/koenig-lexical-editor.js b/ghost/admin/app/components/koenig-lexical-editor.js index 4365e4e26c..8f91477f74 100644 --- a/ghost/admin/app/components/koenig-lexical-editor.js +++ b/ghost/admin/app/components/koenig-lexical-editor.js @@ -669,34 +669,43 @@ export default class KoenigLexicalEditor extends Component { const multiplayerDocId = cardConfig.post.id; const multiplayerUsername = this.session.user.name; + const KGEditorComponent = ({isInitInstance}) => { + return ( +
    + + + {} : this.args.updateWordCount} /> + {} : this.args.updatePostTkCount} /> + +
    + ); + }; + return (
    Loading editor...

    }> - - - - - + +
    diff --git a/ghost/admin/app/components/modal-post-history.hbs b/ghost/admin/app/components/modal-post-history.hbs index 3989c29db5..f7f8ab1261 100644 --- a/ghost/admin/app/components/modal-post-history.hbs +++ b/ghost/admin/app/components/modal-post-history.hbs @@ -33,6 +33,7 @@ @lexical={{this.selectedRevision.lexical}} @cardConfig={{this.cardConfig}} @registerAPI={{this.registerSelectedEditorApi}} + @registerSecondaryAPI={{this.registerSecondarySelectedEditorApi}} /> diff --git a/ghost/admin/app/components/modal-post-history.js b/ghost/admin/app/components/modal-post-history.js index 05aba6646e..4dfa987e2e 100644 --- a/ghost/admin/app/components/modal-post-history.js +++ b/ghost/admin/app/components/modal-post-history.js @@ -31,6 +31,7 @@ export default class ModalPostHistory extends Component { super(...arguments); this.post = this.args.model.post; this.editorAPI = this.args.model.editorAPI; + this.secondaryEditorAPI = this.args.model.secondaryEditorAPI; this.toggleSettingsMenu = this.args.model.toggleSettingsMenu; } @@ -101,6 +102,11 @@ export default class ModalPostHistory extends Component { this.selectedEditor = api; } + @action + registerSecondarySelectedEditorApi(api) { + this.secondarySelectedEditor = api; + } + @action registerComparisonEditorApi(api) { this.comparisonEditor = api; @@ -130,6 +136,7 @@ export default class ModalPostHistory extends Component { updateEditor: () => { const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical); this.editorAPI.editorInstance.setEditorState(state); + this.secondaryEditorAPI.editorInstance.setEditorState(state); }, closePostHistoryModal: () => { this.closeModal(); diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index 71c6331437..2b8342afd9 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -297,6 +297,11 @@ export default class LexicalEditorController extends Controller { this._timedSaveTask.perform(); } + @action + updateSecondaryInstanceModel(lexical) { + this.set('post.secondaryLexicalState', JSON.stringify(lexical)); + } + @action updateTitleScratch(title) { this.set('post.titleScratch', title); @@ -423,6 +428,11 @@ export default class LexicalEditorController extends Controller { this.editorAPI = API; } + @action + registerSecondaryEditorAPI(API) { + this.secondaryEditorAPI = API; + } + @action clearFeatureImage() { this.post.set('featureImage', null); @@ -1221,7 +1231,6 @@ export default class LexicalEditorController extends Controller { _timedSaveTask; /* Private methods -------------------------------------------------------*/ - _hasDirtyAttributes() { let post = this.post; @@ -1229,8 +1238,7 @@ export default class LexicalEditorController extends Controller { return false; } - // if the Adapter failed to save the post isError will be true - // and we should consider the post still dirty. + // If the Adapter failed to save the post, isError will be true, and we should consider the post still dirty. if (post.get('isError')) { this._leaveModalReason = {reason: 'isError', context: post.errors.messages}; return true; @@ -1245,37 +1253,32 @@ export default class LexicalEditorController extends Controller { return true; } - // titleScratch isn't an attr so needs a manual dirty check + // Title scratch comparison if (post.titleScratch !== post.title) { this._leaveModalReason = {reason: 'title is different', context: {current: post.title, scratch: post.titleScratch}}; return true; } - // scratch isn't an attr so needs a manual dirty check + // Lexical and scratch comparison let lexical = post.get('lexical'); let scratch = post.get('lexicalScratch'); - // additional guard in case we are trying to compare null with undefined - if (scratch || lexical) { - if (scratch !== lexical) { - // lexical can dynamically set direction on loading editor state (e.g. "rtl"/"ltr") per the DOM context - // and we need to ignore this as a change from the user; see https://github.com/facebook/lexical/issues/4998 - const scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; - const lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; + let secondaryLexical = post.get('secondaryLexicalState'); - // // nullling is typically faster than delete - scratchChildNodes.forEach(child => child.direction = null); - lexicalChildNodes.forEach(child => child.direction = null); + let lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; + let scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; + let secondaryLexicalChildNodes = secondaryLexical ? JSON.parse(secondaryLexical).root?.children : []; - if (JSON.stringify(scratchChildNodes) === JSON.stringify(lexicalChildNodes)) { - return false; - } + lexicalChildNodes.forEach(child => child.direction = null); + scratchChildNodes.forEach(child => child.direction = null); + secondaryLexicalChildNodes.forEach(child => child.direction = null); - this._leaveModalReason = {reason: 'lexical is different', context: {current: lexical, scratch}}; - return true; - } - } + // Compare initLexical with scratch + let isSecondaryDirty = secondaryLexical && scratch && JSON.stringify(secondaryLexicalChildNodes) !== JSON.stringify(scratchChildNodes); - // new+unsaved posts always return `hasDirtyAttributes: true` + // Compare lexical with scratch + let isLexicalDirty = lexical && scratch && JSON.stringify(lexicalChildNodes) !== JSON.stringify(scratchChildNodes); + + // New+unsaved posts always return `hasDirtyAttributes: true` // so we need a manual check to see if any if (post.get('isNew')) { let changedAttributes = Object.keys(post.changedAttributes()); @@ -1286,15 +1289,26 @@ export default class LexicalEditorController extends Controller { return changedAttributes.length ? true : false; } - // we've covered all the non-tracked cases we care about so fall + // We've covered all the non-tracked cases we care about so fall // back on Ember Data's default dirty attribute checks let {hasDirtyAttributes} = post; - if (hasDirtyAttributes) { this._leaveModalReason = {reason: 'post.hasDirtyAttributes === true', context: post.changedAttributes()}; + return true; } - return hasDirtyAttributes; + // If either comparison is not dirty, return false, because scratch is always up to date. + if (!isSecondaryDirty || !isLexicalDirty) { + return false; + } + + // If both comparisons are dirty, consider the post dirty + if (isSecondaryDirty && isLexicalDirty) { + this._leaveModalReason = {reason: 'initLexical and lexical are different from scratch', context: {secondaryLexical, lexical, scratch}}; + return true; + } + + return false; } _showSaveNotification(prevStatus, status, delayed) { diff --git a/ghost/admin/app/models/post.js b/ghost/admin/app/models/post.js index 1ffb06d8d0..835d24d0a2 100644 --- a/ghost/admin/app/models/post.js +++ b/ghost/admin/app/models/post.js @@ -136,6 +136,9 @@ export default Model.extend(Comparable, ValidationEngine, { scratch: null, lexicalScratch: null, titleScratch: null, + //This is used to store the initial lexical state from the + // secondary editor to get the schema up to date in case its outdated + secondaryLexicalState: null, // For use by date/time pickers - will be validated then converted to UTC // on save. Updated by an observer whenever publishedAtUTC changes. diff --git a/ghost/admin/app/templates/lexical-editor.hbs b/ghost/admin/app/templates/lexical-editor.hbs index bd9d5d51e7..b7ee9a4a74 100644 --- a/ghost/admin/app/templates/lexical-editor.hbs +++ b/ghost/admin/app/templates/lexical-editor.hbs @@ -73,6 +73,7 @@ @body={{readonly this.post.lexicalScratch}} @bodyPlaceholder={{concat "Begin writing your " this.post.displayName "..."}} @onBodyChange={{this.updateScratch}} + @updateSecondaryInstanceModel={{this.updateSecondaryInstanceModel}} @headerOffset={{editor.headerHeight}} @scrollContainerSelector=".gh-koenig-editor" @scrollOffsetBottomSelector=".gh-mobile-nav-bar" @@ -97,6 +98,7 @@ }} @postType={{this.post.displayName}} @registerAPI={{this.registerEditorAPI}} + @registerSecondaryAPI={{this.registerSecondaryEditorAPI}} @savePostTask={{this.savePostTask}} /> @@ -136,6 +138,7 @@ @updateSlugTask={{this.updateSlugTask}} @savePostTask={{this.savePostTask}} @editorAPI={{this.editorAPI}} + @secondaryEditorAPI={{this.secondaryEditorAPI}} @toggleSettingsMenu={{this.toggleSettingsMenu}} /> {{/if}} diff --git a/ghost/admin/tests/unit/controllers/editor-test.js b/ghost/admin/tests/unit/controllers/editor-test.js index 088f22391d..acf7a5e95b 100644 --- a/ghost/admin/tests/unit/controllers/editor-test.js +++ b/ghost/admin/tests/unit/controllers/editor-test.js @@ -208,7 +208,8 @@ describe('Unit: Controller: lexical-editor', function () { titleScratch: 'this is a title', status: 'published', lexical: initialLexicalString, - lexicalScratch: initialLexicalString + lexicalScratch: initialLexicalString, + secondaryLexicalState: initialLexicalString })); // synthetically update the lexicalScratch as if the editor itself made the modifications on loading the initial editorState @@ -225,5 +226,47 @@ describe('Unit: Controller: lexical-editor', function () { isDirty = controller.get('hasDirtyAttributes'); expect(isDirty).to.be.true; }); + + it('dirty is false if secondaryLexical and scratch matches, but lexical is outdated', async function () { + const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + + let controller = this.owner.lookup('controller:lexical-editor'); + controller.set('post', EmberObject.create({ + title: 'this is a title', + titleScratch: 'this is a title', + status: 'published', + lexical: initialLexicalString, + lexicalScratch: lexicalScratch, + secondaryLexicalState: secondLexicalInstance + })); + + let isDirty = controller.get('hasDirtyAttributes'); + + expect(isDirty).to.be.false; + }); + + it('dirty is true if secondaryLexical and lexical does not match scratch', async function () { + const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content1234","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; + + let controller = this.owner.lookup('controller:lexical-editor'); + controller.set('post', EmberObject.create({ + title: 'this is a title', + titleScratch: 'this is a title', + status: 'published', + lexical: initialLexicalString, + lexicalScratch: lexicalScratch, + secondaryLexicalState: secondLexicalInstance + })); + + controller.send('updateScratch',JSON.parse(lexicalScratch)); + + let isDirty = controller.get('hasDirtyAttributes'); + + expect(isDirty).to.be.true; + }); }); }); From 61bf3ea57eee5b19f0aa00564af559dd9e41e56a Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Mon, 5 Aug 2024 21:40:18 +0700 Subject: [PATCH 116/164] Fixed browser tests for unsaved changes (#20717) no issue - takes into account that there is an additional editor in the DOM - which there is now. --- ghost/core/test/e2e-browser/admin/publishing.spec.js | 4 ++-- ghost/core/test/e2e-browser/utils/e2e-browser-utils.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ghost/core/test/e2e-browser/admin/publishing.spec.js b/ghost/core/test/e2e-browser/admin/publishing.spec.js index cb027af941..fc32202c21 100644 --- a/ghost/core/test/e2e-browser/admin/publishing.spec.js +++ b/ghost/core/test/e2e-browser/admin/publishing.spec.js @@ -78,7 +78,7 @@ const createPage = async (page, {title = 'Hello world', body = 'This is my post await page.locator('[data-test-editor-title-input]').fill(title); // wait for editor to be ready - await expect(page.locator('[data-lexical-editor="true"]')).toBeVisible(); + await expect(page.locator('[data-lexical-editor="true"]').first()).toBeVisible(); // Continue to the body by pressing enter await page.keyboard.press('Enter'); @@ -304,7 +304,7 @@ test.describe('Publishing', () => { await expect(publishedHeader).toContainText(date.toFormat('LLL d, yyyy')); // add some extra text to the post - await adminPage.locator('[data-kg="editor"]').click(); + await adminPage.locator('[data-kg="editor"]').first().click(); await adminPage.waitForTimeout(200); // await adminPage.keyboard.type(' This is some updated text.'); diff --git a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js index a2a13f0c23..1f6311992a 100644 --- a/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js +++ b/ghost/core/test/e2e-browser/utils/e2e-browser-utils.js @@ -423,7 +423,7 @@ const createPostDraft = async (page, {title = 'Hello world', body = 'This is my await page.locator('[data-test-editor-title-input]').fill(title); // wait for editor to be ready - await expect(page.locator('[data-lexical-editor="true"]')).toBeVisible(); + await expect(page.locator('[data-lexical-editor="true"]').first()).toBeVisible(); // Continue to the body by pressing enter await page.keyboard.press('Enter'); From c50af303d36fe73976feb2af2bba2c52623efbf0 Mon Sep 17 00:00:00 2001 From: Fabien 'egg' O'Carroll Date: Tue, 6 Aug 2024 20:53:12 +0700 Subject: [PATCH 117/164] Revert "Added initial setup for direnv" (#20721) This reverts commit ac8eb373672675d02ade9eb6f2561a62298dc7ec. We want to keep the top level directory clean --- .envrc | 2 -- .gitignore | 3 --- flake.lock | 43 ------------------------------------------- flake.nix | 49 ------------------------------------------------- 4 files changed, 97 deletions(-) delete mode 100644 .envrc delete mode 100644 flake.lock delete mode 100644 flake.nix diff --git a/.envrc b/.envrc deleted file mode 100644 index 7f0f48d8c3..0000000000 --- a/.envrc +++ /dev/null @@ -1,2 +0,0 @@ -layout node -use flake . diff --git a/.gitignore b/.gitignore index ac45d84b1d..3162730395 100644 --- a/.gitignore +++ b/.gitignore @@ -66,9 +66,6 @@ typings/ # dotenv environment variables file .env -# direnv -.direnv - # IDE .idea/* *.iml diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 2ab3e2a09a..0000000000 --- a/flake.lock +++ /dev/null @@ -1,43 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1708501555, - "narHash": "sha256-zJaF0RkdIPbh8LTmnpW/E7tZYpqIE+MePzlWwUNob4c=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "b50a77c03d640716296021ad58950b1bb0345799", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "systems": "systems" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index c7822686b5..0000000000 --- a/flake.nix +++ /dev/null @@ -1,49 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - systems.url = "github:nix-systems/default"; - }; - - outputs = { - systems, - nixpkgs, - ... - } @ inputs: let - yarn_overlay = final: prev: { - yarn = prev.yarn.overrideAttrs(finalAttrs: prevAttrs: { - # This is to make sure that yarn runs the correct node version - # https://github.com/NixOS/nixpkgs/issues/145634#issuecomment-1627476963 - installPhase = prevAttrs.installPhase + '' - ln -fs $out/libexec/yarn/bin/yarn $out/bin/yarn - ln -fs $out/libexec/yarn/bin/yarn.js $out/bin/yarn.js - ln -fs $out/libexec/yarn/bin/yarn $out/bin/yarnpkg - ''; - }); - }; - - # This gives us a central place to set the node version - node_overlay = final: prev: { - nodejs = prev.nodejs-18_x; - }; - - eachSystem = f: - nixpkgs.lib.genAttrs (import systems) ( - system: - f ((nixpkgs.legacyPackages.${system}.extend yarn_overlay).extend node_overlay) - ); - in { - - devShells = eachSystem (pkgs: { - default = pkgs.mkShell { - buildInputs = with pkgs; [ - nodejs - yarn - ]; - - shellHook = '' - echo "node `${pkgs.nodejs}/bin/node --version`" - ''; - }; - }); - }; -} From 9db1fe9a22e85e457350157de770925f5e715c09 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Tue, 6 Aug 2024 18:29:13 +0200 Subject: [PATCH 118/164] Improved new paid subscription email design (#20718) REF MOM-340 --- .../lib/email-templates/donation.hbs | 6 +- .../lib/email-templates/new-free-signup.hbs | 10 +- .../lib/email-templates/new-paid-started.hbs | 127 ++++++++---------- .../staff-service/test/staff-service.test.js | 12 +- 4 files changed, 65 insertions(+), 90 deletions(-) diff --git a/ghost/staff-service/lib/email-templates/donation.hbs b/ghost/staff-service/lib/email-templates/donation.hbs index 35a33fc398..420ac9c8d7 100644 --- a/ghost/staff-service/lib/email-templates/donation.hbs +++ b/ghost/staff-service/lib/email-templates/donation.hbs @@ -34,9 +34,9 @@
    @@ -61,7 +61,7 @@ diff --git a/ghost/staff-service/lib/email-templates/new-free-signup.hbs b/ghost/staff-service/lib/email-templates/new-free-signup.hbs index 1d9e1fb69e..712314fa42 100644 --- a/ghost/staff-service/lib/email-templates/new-free-signup.hbs +++ b/ghost/staff-service/lib/email-templates/new-free-signup.hbs @@ -33,7 +33,7 @@ {{/if}}
    - +
    + + + + +
    +

    Name:

    + + {{#if referrerSource}} +

    Source:

    +

    {{referrerSource}}

    + {{#if attributionTitle}} +

    Page:

    + + {{/if}} + {{/if}} +
    + - +
    View member + + + + + + +
    View member
    +
    @@ -91,21 +73,29 @@
    -
    -

    You can also copy & paste this URL into your browser:

    - + + + + + + + +
    +

    Or copy and paste this URL into your browser:

    + +
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +
    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    From:

    - +

    Amount received:

    -

    {{donation.amount}}

    +

    {{donation.amount}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +

    Don’t want to receive these emails? Manage your preferences here.

    -

    New free subscriber to {{siteTitle}}

    +

    You have a new free member

    @@ -42,13 +42,13 @@ @@ -95,7 +95,7 @@ diff --git a/ghost/staff-service/lib/email-templates/new-paid-started.hbs b/ghost/staff-service/lib/email-templates/new-paid-started.hbs index d3b7674207..4c09b96c8c 100644 --- a/ghost/staff-service/lib/email-templates/new-paid-started.hbs +++ b/ghost/staff-service/lib/email-templates/new-paid-started.hbs @@ -26,77 +26,52 @@

    Name:

    - + {{#if referrerSource}}

    Source:

    -

    {{referrerSource}}

    +

    {{referrerSource}}

    {{#if attributionTitle}}

    Page:

    - + {{/if}} {{/if}}
    -

    Don’t want to receive these emails? Manage your preferences here.

    +

    Don’t want to receive these emails? Manage your preferences here.

    + {{#if siteIconUrl}} + + + + {{/if}}
    {{siteTitle}}
    -

    Congratulations!

    -

    You have a new paid member.

    - - - - - - - -
    - - - - - -
    -
    - {{memberData.initials}} -
    -
    -

    {{memberData.name}}

    - {{#if memberData.showEmail}} - - {{/if}} -

    Subscription started on {{subscriptionData.startedOn}}

    -
    -
    - - - - - -
    - -

    Tier

    -
    -

    {{tierData.name}} - {{#if tierData.details}} - {{tierData.details}}{{/if}} -

    - - {{#if offerData}} -

    Offer

    -
    -

    {{offerData.name}} - {{offerData.details}}

    - {{/if}} - - {{#if referrerSource}} -

    Signup info

    -
    -

    Source - - {{referrerSource}} -

    - {{#if attributionTitle}} -

    Page - - {{attributionTitle}} -

    - {{/if}} - {{/if}} - -
    - - +

    You have a new paid subscriber

    +
    - - - diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index 1de3d71693..9540897a69 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -78,10 +78,6 @@ function testCommonPaidSubMailData({member, mailStub, getEmailAlertUsersStub}) { mailStub.calledWith( sinon.match.has('html', sinon.match('$50.00/month')) ).should.be.true(); - - mailStub.calledWith( - sinon.match.has('html', sinon.match('Subscription started on 1 Aug 2022')) - ).should.be.true(); } function testCommonPaidSubCancelMailData({mailStub, getEmailAlertUsersStub}) { @@ -518,9 +514,7 @@ describe('StaffService', function () { member = { name: 'Ghost', email: 'member@example.com', - id: 'abc', - geolocation: '{"country": "France"}', - created_at: '2022-08-01T07:30:39.882Z' + id: 'abc' }; offer = { name: 'Half price', @@ -586,9 +580,7 @@ describe('StaffService', function () { it('sends paid subscription start alert without member name', async function () { let memberData = { email: 'member@example.com', - id: 'abc', - geolocation: '{"country": "France"}', - created_at: '2022-08-01T07:30:39.882Z' + id: 'abc' }; await service.emails.notifyPaidSubscriptionStarted({member: memberData, offer: null, tier, subscription}, options); From 445a2b7008a772a03e1ae76f0593ce5e639e4d2f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:31:23 +0000 Subject: [PATCH 119/164] Update tiptap monorepo to v2.5.9 --- apps/comments-ui/package.json | 20 +++---- yarn.lock | 100 +++++++++++++++++----------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 605af91ba1..71725a17a6 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.8", - "@tiptap/extension-blockquote": "2.5.8", - "@tiptap/extension-document": "2.5.8", - "@tiptap/extension-hard-break": "2.5.8", - "@tiptap/extension-link": "2.5.8", - "@tiptap/extension-paragraph": "2.5.8", - "@tiptap/extension-placeholder": "2.5.8", - "@tiptap/extension-text": "2.5.8", - "@tiptap/pm": "2.5.8", - "@tiptap/react": "2.5.8", + "@tiptap/core": "2.5.9", + "@tiptap/extension-blockquote": "2.5.9", + "@tiptap/extension-document": "2.5.9", + "@tiptap/extension-hard-break": "2.5.9", + "@tiptap/extension-link": "2.5.9", + "@tiptap/extension-paragraph": "2.5.9", + "@tiptap/extension-placeholder": "2.5.9", + "@tiptap/extension-text": "2.5.9", + "@tiptap/pm": "2.5.9", + "@tiptap/react": "2.5.9", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index c3ccd86115..b02945160e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7474,66 +7474,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== -"@tiptap/core@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.8.tgz#58de366b0d2acb0a6e67a4780de64d619ebd90fa" - integrity sha512-lkWCKyoAoMTxM137MoEsorG7tZ5MZU6O3wMRuZ0P9fcTRY5vd1NWncWuPzuGSJIpL20gwBQOsS6PaQSfR3xjlA== +"@tiptap/core@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.9.tgz#1deb0b7c748e24ec32613263e0af8d55a3b3c2ca" + integrity sha512-PPUR+0tbr+wX2G8RG4FEps4qhbnAPEeXK1FUtirLXSRh8vm+TDgafu3sms7wBc4fAyw9zTO/KNNZ90GBe04guA== -"@tiptap/extension-blockquote@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.8.tgz#95880f0b687790dbff85a1c9e83f2afd0011be67" - integrity sha512-P8vDiagtRrUfIewfCKrJe0ddDSjPgOTKzqoM1UXKS+MenT8C/wT4bjiwopAoWP6zMoV0TfHWXah9emllmCfXFA== +"@tiptap/extension-blockquote@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.9.tgz#d873a8496fcf572c69aaac2a7a341e035fdbae22" + integrity sha512-LhGyigmd/v1OjYPeoVK8UvFHbH6ffh175ZuNvseZY4PsBd7kZhrSUiuMG8xYdNX8FxamsxAzr2YpsYnOzu3W7A== -"@tiptap/extension-bubble-menu@^2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.8.tgz#e39b176c574b9fd2f59c6457724f3f22a22fb1b8" - integrity sha512-COmd1Azudu7i281emZFIESECe7FnvWiRoBoQBVjjWSyq5PVzwJaA3PAlnU7GyNZKtVXMZ4xbrckdyNQfDeVQDA== +"@tiptap/extension-bubble-menu@^2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.9.tgz#d600bbcaa1d98a99f32b3b8b8c3d35752161200c" + integrity sha512-NddZ8Qn5dgPPa1W4yk0jdhF4tDBh0FwzBpbnDu2Xz/0TUHrA36ugB2CvR5xS1we4zUKckgpVqOqgdelrmqqFVg== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.8.tgz#644f042f1d4a8d3f74af057477cc627da7b54dc7" - integrity sha512-r3rP4ihCJAdp3VRIeqd80etHx7jttzZaKNFX8hkQShHK6eTHwrR92VL0jDE4K+NOE3bxjMsOlYizJYWV042BtA== +"@tiptap/extension-document@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.9.tgz#13a22b2d3bdc1463844872b1f1c926633df431a8" + integrity sha512-VdNZYDyCzC3W430UdeRXR9IZzPeODSbi5Xz/JEdV93THVp8AC9CrZR7/qjqdBTgbTB54VP8Yr6bKfCoIAF0BeQ== -"@tiptap/extension-floating-menu@^2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.8.tgz#6af3fa169bf293ab79a671a7b60b5199992a9154" - integrity sha512-qsM6tCyRlXnI/gADrkO/2p0Tldu5aY96CnsXpZMaflMgsO577qhcXD0ReGg17uLXBzJa5xmV8qOik0Ptq3WEWg== +"@tiptap/extension-floating-menu@^2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.9.tgz#b970905f3c1af49a916dcbd477a4302086187974" + integrity sha512-MWJIQQT6e5MgqHny8neeH2Dx926nVPF7sv4p84nX4E0dnkRbEYUP8mCsWYhSUvxxIif6e+yY+4654f2Q9qTx1w== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.8.tgz#95288faad3408b91284d925c3e4dbab66029dd98" - integrity sha512-samZEL0EXzHSmMQ7KyLnfSxdDv3qSjia0JzelfCnFZS6LLcbwjrIjV8ZPxEhJ7UlZqroQdFxPegllkLHZj/MdQ== +"@tiptap/extension-hard-break@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.9.tgz#4f38f06dbeb5fb3e58ff7fc0c48b9db9c4ee4ecd" + integrity sha512-8hQ63SgZRG4BqHOeSfeaowG2eMr2beced018pOGbpHbE3XSYoISkMVuFz4Z8UEVR3W9dTbKo4wxNufSTducocQ== -"@tiptap/extension-link@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.8.tgz#f9264afed09bd25c37668303151ab80ba82ef044" - integrity sha512-qfeWR7sG2V7bn8z0f3HMyoR68pFlxYJmLs9cbW30diE9/zKClYEd3zTMPCgJ9yMSagCj4PWkqksIuktAhyRqOQ== +"@tiptap/extension-link@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.9.tgz#6cb323d36b82700963ad2b9d189a7d07c81c7d6e" + integrity sha512-7v9yRsX7NuiY8DPslIsPIlFqcD8aGBMLqfEGXltJDvuG6kykdr+khEZeWcJ8ihHIL4yWR3/MAgeT2W72Z/nxiQ== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.8.tgz#5be7e7c4e5c19bd4f512c72d3dfc4e1e6d6dd876" - integrity sha512-AMfD3lfGSiomfkSE2tUourUjVahLtIfWUQew13NTPuWoxAXaSyoCGO0ULkiou/lO3JVUUUmF9+KJrAHWGIARdA== +"@tiptap/extension-paragraph@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.9.tgz#05210b6e7a9940b1acc09fdd4ec769fc6406da2b" + integrity sha512-HDXGiHTJ/V85dbDMjcFj4XfqyTQZqry6V21ucMzgBZYX60X3gIn7VpQTQnnRjvULSgtfOASSJP6BELc5TyiK0w== -"@tiptap/extension-placeholder@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.8.tgz#80fdf02133d94f41363f6fe28f5fc3ef09ac73c6" - integrity sha512-mvRl73OM5jBXVtDRLSTvp8/4+0mS2J2+ZcuiAHjABwEsZRCfJsiqty5NisOxSuy/AQtm8TK2kyt6ZCXQ2VRGig== +"@tiptap/extension-placeholder@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.9.tgz#c9bebc7e2bba2b0321e360d8a7a358152ffc9137" + integrity sha512-ytKmlSiebtCBXoMPE2cup48DR0rQiekXhLKLkNyt7m8tSXkaRO4eDaFqCqPEXLeQXWdhwWEoPM6Cejaaa3ztkA== -"@tiptap/extension-text@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.8.tgz#a9c4de33eec749c8c01d8bd81fb589f581c30dfc" - integrity sha512-CNkD51jRMdcYCqFVOkrnebqBQ6pCD3ZD5z9kO5bOC5UPZKZBkLsWdlrHGAVwosxcGxdJACbqJ0Nj+fMgIw4tNA== +"@tiptap/extension-text@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.9.tgz#a5bef0b9c5324511dbc2804a3a5ac8b9b5d5dc4c" + integrity sha512-W0pfiQUPsMkwaV5Y/wKW4cFsyXAIkyOFt7uN5u6LrZ/iW9KZ/IsDODPJDikWp0aeQnXzT9NNQULTpCjbHzzS6g== -"@tiptap/pm@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.8.tgz#b18afa77fdf69527b13614a05cfefc8b63e82224" - integrity sha512-CVhHaTG4QNHSkvuh6HHsUR4hE+nbUnk7z+VMUedaqPU8tNqkTwWGCMbiyTc+PCsz0T9Mni7vvBR+EXgEQ3+w4g== +"@tiptap/pm@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.9.tgz#f97889210374993a1ce78e9ecb23461d0e4644bf" + integrity sha512-YSUaEQVtvZnGzGjif2Tl2o9utE+6tR2Djhz0EqFUcAUEVhOMk7UYUO+r/aPfcCRraIoKKuDQzyCpjKmJicjCUA== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7554,13 +7554,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.9" -"@tiptap/react@2.5.8": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.8.tgz#d6bc68710f084fe0f02855376cf869f8ca2cf6fd" - integrity sha512-twUMm8HV7scUgR/E1hYS9N6JDtKPl7cgDiPjxTynNHc5S5f5Ecv4ns/BZRq3TMZ/JDrp4rghLvgq+ImQsLvPOA== +"@tiptap/react@2.5.9": + version "2.5.9" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.9.tgz#43f94a2bf1d4c55e82d97ef9d1c97ba20206f7f0" + integrity sha512-NZYAslIb79oxIOFHx9T9ey5oX0aJ1uRbtT2vvrvvyRaO6fKWgAwMYN92bOu5/f2oUVGUp6l7wkYZGdjz/XP5bA== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.8" - "@tiptap/extension-floating-menu" "^2.5.8" + "@tiptap/extension-bubble-menu" "^2.5.9" + "@tiptap/extension-floating-menu" "^2.5.9" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From eb7024da340a9d5c8d69f2f3f1aeb7110242bd81 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 08:24:44 +0000 Subject: [PATCH 120/164] Update testing-library monorepo --- apps/admin-x-activitypub/package.json | 2 +- apps/admin-x-demo/package.json | 2 +- apps/admin-x-design-system/package.json | 2 +- apps/admin-x-framework/package.json | 2 +- apps/admin-x-settings/package.json | 2 +- apps/comments-ui/package.json | 2 +- apps/sodo-search/package.json | 2 +- yarn.lock | 36 ++++++------------------- 8 files changed, 15 insertions(+), 35 deletions(-) diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json index 0782704d0b..19146b51d3 100644 --- a/apps/admin-x-activitypub/package.json +++ b/apps/admin-x-activitypub/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@playwright/test": "1.38.1", - "@testing-library/react": "14.1.0", + "@testing-library/react": "14.3.1", "@tryghost/admin-x-design-system": "0.0.0", "@tryghost/admin-x-framework": "0.0.0", "@types/jest": "29.5.12", diff --git a/apps/admin-x-demo/package.json b/apps/admin-x-demo/package.json index 426e969f5c..abb1fe1b9f 100644 --- a/apps/admin-x-demo/package.json +++ b/apps/admin-x-demo/package.json @@ -32,7 +32,7 @@ "preview": "vite preview" }, "devDependencies": { - "@testing-library/react": "14.1.0", + "@testing-library/react": "14.3.1", "@tryghost/admin-x-design-system": "0.0.0", "@tryghost/admin-x-framework": "0.0.0", "@types/react": "18.3.3", diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 124ee2b4e6..9c73c08281 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -36,7 +36,7 @@ "@storybook/react": "7.6.20", "@storybook/react-vite": "7.6.4", "@storybook/testing-library": "0.2.2", - "@testing-library/react": "14.1.0", + "@testing-library/react": "14.3.1", "@testing-library/react-hooks" : "8.0.1", "@vitejs/plugin-react": "4.2.1", "c8": "8.0.1", diff --git a/apps/admin-x-framework/package.json b/apps/admin-x-framework/package.json index f8ccf034da..ed983ab571 100644 --- a/apps/admin-x-framework/package.json +++ b/apps/admin-x-framework/package.json @@ -68,7 +68,7 @@ "types" ], "devDependencies": { - "@testing-library/react": "14.1.0", + "@testing-library/react": "14.3.1", "@types/mocha": "10.0.1", "c8": "8.0.1", "eslint-plugin-react-hooks": "4.6.0", diff --git a/apps/admin-x-settings/package.json b/apps/admin-x-settings/package.json index 63f09ab344..449d99ee67 100644 --- a/apps/admin-x-settings/package.json +++ b/apps/admin-x-settings/package.json @@ -49,7 +49,7 @@ }, "devDependencies": { "@playwright/test": "1.38.1", - "@testing-library/react": "14.1.0", + "@testing-library/react": "14.3.1", "@tryghost/admin-x-design-system": "0.0.0", "@tryghost/admin-x-framework": "0.0.0", "@types/react": "18.3.3", diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 71725a17a6..4be6217a89 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -62,7 +62,7 @@ "@playwright/test": "1.38.1", "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "12.1.5", - "@testing-library/user-event": "14.4.3", + "@testing-library/user-event": "14.5.2", "@tryghost/i18n": "0.0.0", "@vitejs/plugin-react": "4.2.1", "@vitest/coverage-v8": "0.34.3", diff --git a/apps/sodo-search/package.json b/apps/sodo-search/package.json index f7c29adc72..6dbebc5983 100644 --- a/apps/sodo-search/package.json +++ b/apps/sodo-search/package.json @@ -82,7 +82,7 @@ ] }, "devDependencies": { - "@testing-library/jest-dom": "5.16.5", + "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "12.1.5", "@vitejs/plugin-react": "4.2.1", "jsdom": "24.1.1", diff --git a/yarn.lock b/yarn.lock index b02945160e..7e54e52b93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7408,21 +7408,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@5.16.5": - version "5.16.5" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" - integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== - dependencies: - "@adobe/css-tools" "^4.0.1" - "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" - aria-query "^5.0.0" - chalk "^3.0.0" - css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" - lodash "^4.17.15" - redent "^3.0.0" - "@testing-library/jest-dom@5.17.0": version "5.17.0" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" @@ -7455,24 +7440,19 @@ "@testing-library/dom" "^8.0.0" "@types/react-dom" "<18.0.0" -"@testing-library/react@14.1.0": - version "14.1.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.1.0.tgz#01d64915111db99b50f8361d51d7217606805989" - integrity sha512-hcvfZEEyO0xQoZeHmUbuMs7APJCGELpilL7bY+BaJaMP57aWc6q1etFwScnoZDheYjk4ESdlzPdQ33IbsKAK/A== +"@testing-library/react@14.3.1": + version "14.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.3.1.tgz#29513fc3770d6fb75245c4e1245c470e4ffdd830" + integrity sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ== dependencies: "@babel/runtime" "^7.12.5" "@testing-library/dom" "^9.0.0" "@types/react-dom" "^18.0.0" -"@testing-library/user-event@14.4.3": - version "14.4.3" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591" - integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q== - -"@testing-library/user-event@^14.4.0": - version "14.5.1" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.1.tgz#27337d72046d5236b32fd977edee3f74c71d332f" - integrity sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg== +"@testing-library/user-event@14.5.2", "@testing-library/user-event@^14.4.0": + version "14.5.2" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" + integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== "@tiptap/core@2.5.9": version "2.5.9" From 0d1916505f94d4f687058c4b16e459d81acaea92 Mon Sep 17 00:00:00 2001 From: Steffo <41108856+SteffoSpieler@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:09:54 +0200 Subject: [PATCH 121/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20Content-Type=20f?= =?UTF-8?q?or=20RSS=20feed=20(#20670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes https://github.com/TryGhost/Ghost/issues/20634 - this sets the correct Content-Type (`application/rss+xml; charset=utf-8`) for the RSS Endpoint as mentioned in the referenced issue - references for this Content-Type: https://datatracker.ietf.org/doc/id/draft-nottingham-rss-media-type-00 and https://stackoverflow.com/questions/595616/what-is-the-correct-mime-type-to-use-for-an-rss-feed --- ghost/core/core/frontend/services/rss/renderer.js | 2 +- ghost/core/test/e2e-frontend/default_routes.test.js | 10 +++++----- .../mock-express-style/api-vs-frontend.test.js | 6 +++--- .../test/unit/frontend/services/rss/renderer.test.js | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ghost/core/core/frontend/services/rss/renderer.js b/ghost/core/core/frontend/services/rss/renderer.js index 9a15afc918..ab6be75a63 100644 --- a/ghost/core/core/frontend/services/rss/renderer.js +++ b/ghost/core/core/frontend/services/rss/renderer.js @@ -9,7 +9,7 @@ module.exports.render = function render(res, baseUrl, data) { return rssCache .getXML(baseUrl, rssData) .then(function then(feedXml) { - res.set('Content-Type', 'text/xml; charset=UTF-8'); + res.set('Content-Type', 'application/rss+xml; charset=UTF-8'); res.send(feedXml); }); }; diff --git a/ghost/core/test/e2e-frontend/default_routes.test.js b/ghost/core/test/e2e-frontend/default_routes.test.js index 3e03a48b10..7fcc50b8ce 100644 --- a/ghost/core/test/e2e-frontend/default_routes.test.js +++ b/ghost/core/test/e2e-frontend/default_routes.test.js @@ -271,7 +271,7 @@ describe('Default Frontend routing', function () { await request.get('/rss/') .expect(200) .expect('Cache-Control', testUtils.cacheRules.public) - .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Content-Type', 'application/rss+xml; charset=utf-8') .expect(assertCorrectFrontendHeaders) .expect((res) => { res.text.should.match(//); @@ -283,7 +283,7 @@ describe('Default Frontend routing', function () { await request.get('/author/ghost/rss/') .expect(200) .expect('Cache-Control', testUtils.cacheRules.public) - .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Content-Type', 'application/rss+xml; charset=utf-8') .expect(assertCorrectFrontendHeaders) .expect((res) => { res.text.should.match(//); @@ -295,7 +295,7 @@ describe('Default Frontend routing', function () { await request.get('/tag/getting-started/rss/') .expect(200) .expect('Cache-Control', testUtils.cacheRules.public) - .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Content-Type', 'application/rss+xml; charset=utf-8') .expect(assertCorrectFrontendHeaders) .expect((res) => { res.text.should.match(//); @@ -461,7 +461,7 @@ describe('Default Frontend routing', function () { await request.get(`/${settingsCache.get('public_hash')}/rss/`) .expect(200) .expect('Cache-Control', testUtils.cacheRules.private) - .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Content-Type', 'application/rss+xml; charset=utf-8') .expect(assertCorrectFrontendHeaders) .expect((res) => { res.text.should.match(//); @@ -472,7 +472,7 @@ describe('Default Frontend routing', function () { await request.get(`/tag/getting-started/${settingsCache.get('public_hash')}/rss/`) .expect(200) .expect('Cache-Control', testUtils.cacheRules.private) - .expect('Content-Type', 'text/xml; charset=utf-8') + .expect('Content-Type', 'application/rss+xml; charset=utf-8') .expect(assertCorrectFrontendHeaders) .expect((res) => { res.text.should.match(//); diff --git a/ghost/core/test/regression/mock-express-style/api-vs-frontend.test.js b/ghost/core/test/regression/mock-express-style/api-vs-frontend.test.js index b82ea19546..3090aee06a 100644 --- a/ghost/core/test/regression/mock-express-style/api-vs-frontend.test.js +++ b/ghost/core/test/regression/mock-express-style/api-vs-frontend.test.js @@ -1294,7 +1294,7 @@ describe('Frontend behavior tests', function () { return localUtils.mockExpress.invoke(app, req) .then(function (response) { response.statusCode.should.eql(200); - response.headers['content-type'].should.eql('text/xml; charset=UTF-8'); + response.headers['content-type'].should.eql('application/rss+xml; charset=UTF-8'); }); }); @@ -1448,7 +1448,7 @@ describe('Frontend behavior tests', function () { routes: { '/podcast/rss/': { templates: ['podcast/rss'], - content_type: 'text/xml' + content_type: 'application/rss+xml' }, '/cooking/': { controller: 'channel', @@ -1560,7 +1560,7 @@ describe('Frontend behavior tests', function () { .then(function (response) { response.statusCode.should.eql(200); response.template.should.eql('podcast/rss'); - response.headers['content-type'].should.eql('text/xml; charset=utf-8'); + response.headers['content-type'].should.eql('application/rss+xml'); response.body.match(//g).length.should.eql(2); }); }); diff --git a/ghost/core/test/unit/frontend/services/rss/renderer.test.js b/ghost/core/test/unit/frontend/services/rss/renderer.test.js index 7bf55e1789..2641c7c1c5 100644 --- a/ghost/core/test/unit/frontend/services/rss/renderer.test.js +++ b/ghost/core/test/unit/frontend/services/rss/renderer.test.js @@ -32,7 +32,7 @@ describe('RSS: Renderer', function () { rssCacheStub.firstCall.args.should.eql(['/rss/', {}]); res.set.calledOnce.should.be.true(); - res.set.calledWith('Content-Type', 'text/xml; charset=UTF-8').should.be.true(); + res.set.calledWith('Content-Type', 'application/rss+xml; charset=UTF-8').should.be.true(); res.send.calledOnce.should.be.true(); res.send.calledWith('dummyxml').should.be.true(); @@ -51,7 +51,7 @@ describe('RSS: Renderer', function () { rssCacheStub.firstCall.args.should.eql(['/rss/', {foo: 'bar'}]); res.set.calledOnce.should.be.true(); - res.set.calledWith('Content-Type', 'text/xml; charset=UTF-8').should.be.true(); + res.set.calledWith('Content-Type', 'application/rss+xml; charset=UTF-8').should.be.true(); res.send.calledOnce.should.be.true(); res.send.calledWith('dummyxml').should.be.true(); @@ -71,7 +71,7 @@ describe('RSS: Renderer', function () { rssCacheStub.firstCall.args.should.eql(['/rss/', {foo: 'baz', fizz: 'buzz'}]); res.set.calledOnce.should.be.true(); - res.set.calledWith('Content-Type', 'text/xml; charset=UTF-8').should.be.true(); + res.set.calledWith('Content-Type', 'application/rss+xml; charset=UTF-8').should.be.true(); res.send.calledOnce.should.be.true(); res.send.calledWith('dummyxml').should.be.true(); From b2284383bf7f3e8674804996b64041472b15d15c Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Wed, 7 Aug 2024 12:30:36 +0200 Subject: [PATCH 122/164] Fixed matching ignored errors refs https://ghost-foundation.sentry.io/issues/4907452370/ - we want to ignore these errors but the caret is stopping us from doing so because the errors usually start with AbortError - we can remove the caret to do so and clean up Sentry --- ghost/admin/app/routes/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/admin/app/routes/application.js b/ghost/admin/app/routes/application.js index 24a5f7ad57..49a3362a03 100644 --- a/ghost/admin/app/routes/application.js +++ b/ghost/admin/app/routes/application.js @@ -186,7 +186,7 @@ export default Route.extend(ShortcutsRoute, { beforeSend, ignoreErrors: [ // Browser autoplay policies (this regex covers a few) - /^The play\(\) request was interrupted.*/, + /The play\(\) request was interrupted.*/, /The request is not allowed by the user agent or the platform in the current context/, // Network errors that we don't control From 184af30a1cd7c16e11c9d19323c9d75fcbc03261 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:30:40 +0000 Subject: [PATCH 123/164] Update dependency terser to v5.31.4 --- ghost/minifier/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/minifier/package.json b/ghost/minifier/package.json index e9db19f5b7..4467414227 100644 --- a/ghost/minifier/package.json +++ b/ghost/minifier/package.json @@ -28,7 +28,7 @@ "@tryghost/errors": "1.3.5", "@tryghost/tpl": "0.1.32", "csso": "5.0.5", - "terser": "5.31.3", + "terser": "5.31.4", "tiny-glob": "0.2.9" } } diff --git a/yarn.lock b/yarn.lock index 7e54e52b93..0dec567e68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30115,10 +30115,10 @@ terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@5.31.3, terser@^5.26.0, terser@^5.7.0: - version "5.31.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.3.tgz#b24b7beb46062f4653f049eea4f0cd165d0f0c38" - integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA== +terser@5.31.4, terser@^5.26.0, terser@^5.7.0: + version "5.31.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.4.tgz#60274c4d3e20eb9a6468526a8878aba8e8428c5f" + integrity sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" From c361bf1afccf49037c09b7c79d3b206587d2d5b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:37:31 +0000 Subject: [PATCH 124/164] Update dependency terser to v5.31.5 --- ghost/minifier/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/minifier/package.json b/ghost/minifier/package.json index 4467414227..431ca3cfc0 100644 --- a/ghost/minifier/package.json +++ b/ghost/minifier/package.json @@ -28,7 +28,7 @@ "@tryghost/errors": "1.3.5", "@tryghost/tpl": "0.1.32", "csso": "5.0.5", - "terser": "5.31.4", + "terser": "5.31.5", "tiny-glob": "0.2.9" } } diff --git a/yarn.lock b/yarn.lock index 0dec567e68..f8f14ebad1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30115,10 +30115,10 @@ terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@5.31.4, terser@^5.26.0, terser@^5.7.0: - version "5.31.4" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.4.tgz#60274c4d3e20eb9a6468526a8878aba8e8428c5f" - integrity sha512-3OU03GgblDgu0g+sdnsVzhBPxnjV+WJuMmocN1qBBZDQ3ia7jZQSAkePeKbPlYAejGXUTYe1CmSaUeV51mvaIw== +terser@5.31.5, terser@^5.26.0, terser@^5.7.0: + version "5.31.5" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.5.tgz#e48b7c65f32d2808e7dad803e4586a0bc3829b87" + integrity sha512-YPmas0L0rE1UyLL/llTWA0SiDOqIcAQYLeUj7cJYzXHlRTAnMSg9pPe4VJ5PlKvTrPQsdVFuiRiwyeNlYgwh2Q== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" From bdff3d7b5c579590ae2b946ae12b95311498f63b Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:36:43 +0200 Subject: [PATCH 125/164] Updated paid cancellation notification email design (#20726) REF DES-572 --- .../email-templates/new-paid-cancellation.hbs | 118 ++++++++---------- .../staff-service/test/staff-service.test.js | 26 +--- 2 files changed, 56 insertions(+), 88 deletions(-) diff --git a/ghost/staff-service/lib/email-templates/new-paid-cancellation.hbs b/ghost/staff-service/lib/email-templates/new-paid-cancellation.hbs index 3a44cff119..f9a33bbfe1 100644 --- a/ghost/staff-service/lib/email-templates/new-paid-cancellation.hbs +++ b/ghost/staff-service/lib/email-templates/new-paid-cancellation.hbs @@ -30,71 +30,49 @@
    - +
    + + + + +
    +

    Name:

    + +

    Tier:

    + + {{#if offerData}} +

    Offer:

    + + {{/if}} + {{#if referrerSource}} +

    Source:

    +

    {{referrerSource}}

    + {{#if attributionTitle}} +

    Page:

    + + {{/if}} + {{/if}} +
    + - +
    View member + + + + + + +
    View member
    +
    @@ -104,21 +79,29 @@
    -
    -

    You can also copy & paste this URL into your browser:

    - + + + + + + + +
    +

    Or copy and paste this URL into your browser:

    + +
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +
    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    + {{#if siteIconUrl}} + + + + {{/if}} diff --git a/ghost/staff-service/lib/email-templates/recommendation-received.hbs b/ghost/staff-service/lib/email-templates/recommendation-received.hbs index e87e54ee14..ca81863d79 100644 --- a/ghost/staff-service/lib/email-templates/recommendation-received.hbs +++ b/ghost/staff-service/lib/email-templates/recommendation-received.hbs @@ -21,31 +21,34 @@
    {{siteTitle}}
    -

    Hey there,

    -

    A paid member's subscription has just been canceled.

    - - - - - - - - - - - - - {{#if subscriptionData.cancellationReason}} - - - - {{/if}} - -
    - - - - - -
    -
    - {{memberData.initials}} -
    -
    -

    {{memberData.name}}

    - {{#if memberData.showEmail}} - - {{/if}} - {{#unless subscriptionData.cancelNow}} -

    Canceled on {{subscriptionData.canceledAt}}

    - {{/unless}} -
    -
    -
    -

    Tier

    -

    {{tierData.name}} - {{tierData.details}}

    -
    - {{#if subscriptionData.cancelNow}} -

    Subscription expired on

    - {{else}} -

    Subscription will expire on

    - {{/if}} -

    {{subscriptionData.expiryAt}}

    -
    -

    Cancellation reason

    -

    {{subscriptionData.cancellationReason}}

    -
    - - +

    A paid member's subscription has just been canceled

    +
    - - - diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index 9540897a69..7570986cd2 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -732,13 +732,9 @@ describe('StaffService', function () { mailStub.calledOnce.should.be.true(); testCommonPaidSubCancelMailData(stubs); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Canceled on 5 Aug 2022')) - ).should.be.true(); - // Expiration sentence is in the future tense mailStub.calledWith( - sinon.match.has('html', sinon.match('Subscription will expire on')) + sinon.match.has('html', sinon.match('Expires on')) ).should.be.true(); mailStub.calledWith( @@ -752,24 +748,17 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('Reason: Changed my mind!')) ).should.be.true(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Cancellation reason')) - ).should.be.true(); }); it('sends paid subscription cancel alert when sub is canceled without reason', async function () { - await service.emails.notifyPaidSubscriptionCanceled({member, tier, subscription, expiryAt, canceledAt, cancelNow}, options); + await service.emails.notifyPaidSubscriptionCanceled({member, tier, subscription, expiryAt, cancelNow}, options); mailStub.calledOnce.should.be.true(); testCommonPaidSubCancelMailData(stubs); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Canceled on 5 Aug 2022')) - ).should.be.true(); - // Expiration sentence is in the future tense mailStub.calledWith( - sinon.match.has('html', sinon.match('Subscription will expire on')) + sinon.match.has('html', sinon.match('Expires on')) ).should.be.true(); mailStub.calledWith( @@ -780,9 +769,6 @@ describe('StaffService', function () { mailStub.calledWith( sinon.match.has('html', sinon.match('Reason: ')) ).should.be.false(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Cancellation reason')) - ).should.be.false(); }); it('sends paid subscription cancel alert when subscription is canceled immediately', async function () { @@ -802,7 +788,7 @@ describe('StaffService', function () { // Expiration sentence is in the past tense mailStub.calledWith( - sinon.match.has('html', sinon.match('Subscription expired on')) + sinon.match.has('html', sinon.match('Expired on')) ).should.be.true(); mailStub.calledWith( @@ -813,10 +799,6 @@ describe('StaffService', function () { sinon.match.has('html', 'Offer') ).should.be.false(); - mailStub.calledWith( - sinon.match.has('html', sinon.match('Cancellation reason')) - ).should.be.true(); - mailStub.calledWith( sinon.match.has('html', sinon.match('Reason: Payment failed')) ).should.be.true(); From e0a07c6813a36915c4e2f3d1c7363e264a85a535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Winden?= Date: Thu, 8 Aug 2024 14:48:54 +0200 Subject: [PATCH 126/164] Hide overflow on member list container (#20729) Fixes https://linear.app/tryghost/issue/DES-561/horizontal-scroll-on-members-index-shows-on-both-sides-of-table On resolutions where the members list needed horizontal scrollbars, the table was overflowing on the left side of the list. This hides that overflow and fixes the issue. --- ghost/admin/app/styles/layouts/members.css | 1 + 1 file changed, 1 insertion(+) diff --git a/ghost/admin/app/styles/layouts/members.css b/ghost/admin/app/styles/layouts/members.css index e9c7383ee5..bfbd1ce2e3 100644 --- a/ghost/admin/app/styles/layouts/members.css +++ b/ghost/admin/app/styles/layouts/members.css @@ -68,6 +68,7 @@ @media (max-width: 1450px) { .members-list-container-stretch { min-height: calc(100vh - 176px); + overflow: hidden; } } From 0d9955538d588fd577f479b25eeeac8ec3c80d4a Mon Sep 17 00:00:00 2001 From: Sag Date: Thu, 8 Aug 2024 17:47:51 +0200 Subject: [PATCH 127/164] Removed fallback Ghost icon in staff notifications (#20731) ref https://linear.app/tryghost/issue/PLG-150 - if the publication has no custom icon, staff notifications do not render the Ghost icon as fallback anymore --- ghost/core/core/frontend/meta/blog-logo.js | 2 +- ghost/core/core/server/lib/image/BlogIcon.js | 18 +++++++++++++----- ghost/core/core/server/services/slack.js | 6 +++--- .../unit/server/lib/image/blog-icon.test.js | 16 +++++++++++++--- ghost/staff-service/lib/StaffServiceEmails.js | 10 +++++----- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/ghost/core/core/frontend/meta/blog-logo.js b/ghost/core/core/frontend/meta/blog-logo.js index e472b68be3..dd054ec8e3 100644 --- a/ghost/core/core/frontend/meta/blog-logo.js +++ b/ghost/core/core/frontend/meta/blog-logo.js @@ -11,7 +11,7 @@ function getBlogLogo() { // CASE: no publication logo is updated. We can try to use either an uploaded publication icon // or use the default one to make // Google happy with it. See https://github.com/TryGhost/Ghost/issues/7558 - logo.url = blogIcon.getIconUrl(true); + logo.url = blogIcon.getIconUrl({absolute: true}); } return logo; diff --git a/ghost/core/core/server/lib/image/BlogIcon.js b/ghost/core/core/server/lib/image/BlogIcon.js index af673419c0..2238dfc04f 100644 --- a/ghost/core/core/server/lib/image/BlogIcon.js +++ b/ghost/core/core/server/lib/image/BlogIcon.js @@ -98,13 +98,17 @@ class BlogIcon { } /** - * Return URL for Blog icon: [subdirectory or not]favicon.[ico, jpeg, or png] - * Always returns {string} getIconUrl - * @returns {string} [subdirectory or not]favicon.[ico, jpeg, or png] + * Return URL for blog icon, if available: [subdirectory or not]favicon.[ico, jpeg, or png] + * Otherwise, fallbacks to the default Ghost favicon.ico file, if requested + * Otherwise, returns null + * @param {Object} [options] + * @param {boolean} [options.absolute] - if true, return absolute URL. Default: false + * @param {boolean} [options.fallbackToDefault] - if true, fallbacks to Ghost's default favicon.ico when no blog icon is found. Default: true + * @returns {string|null} [subdirectory or not]favicon.[ico, jpeg, or png] or null * @description Checks if we have a custom uploaded icon and the extension of it. If no custom uploaded icon * exists, we're returning the default `favicon.ico` */ - getIconUrl(absolute) { + getIconUrl({absolute = false, fallbackToDefault = true} = {}) { const blogIcon = this.settingsCache.get('icon'); if (blogIcon) { @@ -124,9 +128,13 @@ class BlogIcon { const sizedIcon = blogIcon.replace(/\/content\/images\//, '/content/images/size/w256h256/'); return this.urlUtils.urlFor({relativeUrl: sizedIcon}, absolute ? true : undefined); - } else { + } + + if (fallbackToDefault) { return this.urlUtils.urlFor({relativeUrl: '/favicon.ico'}, absolute ? true : undefined); } + + return null; } /** diff --git a/ghost/core/core/server/services/slack.js b/ghost/core/core/server/services/slack.js index be9916e4bc..e25d2a34a3 100644 --- a/ghost/core/core/server/services/slack.js +++ b/ghost/core/core/server/services/slack.js @@ -110,7 +110,7 @@ function ping(post) { // if it is a post or a test message to check webhook working. text: `Notification from *${blogTitle}* :ghost:`, unfurl_links: true, - icon_url: blogIcon.getIconUrl(true), + icon_url: blogIcon.getIconUrl({absolute: true}), username: slackSettings.username, // We don't want to send attachment if it is a test notification. attachments: [ @@ -141,7 +141,7 @@ function ping(post) { } ], footer: blogTitle, - footer_icon: blogIcon.getIconUrl(true), + footer_icon: blogIcon.getIconUrl({absolute: true}), ts: moment().unix() } ] @@ -150,7 +150,7 @@ function ping(post) { slackData = { text: message, unfurl_links: true, - icon_url: blogIcon.getIconUrl(true), + icon_url: blogIcon.getIconUrl({absolute: true}), username: slackSettings.username }; } diff --git a/ghost/core/test/unit/server/lib/image/blog-icon.test.js b/ghost/core/test/unit/server/lib/image/blog-icon.test.js index 5d3e9de9c1..4e9e9996f2 100644 --- a/ghost/core/test/unit/server/lib/image/blog-icon.test.js +++ b/ghost/core/test/unit/server/lib/image/blog-icon.test.js @@ -51,7 +51,7 @@ describe('lib/image: blog icon', function () { } } }}); - blogIcon.getIconUrl(true).should.deepEqual([{relativeUrl: '/content/images/2017/04/my-icon.ico'}, true]); + blogIcon.getIconUrl({absolute: true}).should.deepEqual([{relativeUrl: '/content/images/2017/04/my-icon.ico'}, true]); }); it('custom uploaded png blog icon', function () { @@ -64,7 +64,7 @@ describe('lib/image: blog icon', function () { } } }}); - blogIcon.getIconUrl(true).should.deepEqual([{relativeUrl: '/content/images/size/w256h256/2017/04/my-icon.png'}, true]); + blogIcon.getIconUrl({absolute: true}).should.deepEqual([{relativeUrl: '/content/images/size/w256h256/2017/04/my-icon.png'}, true]); }); it('default ico blog icon', function () { @@ -73,7 +73,17 @@ describe('lib/image: blog icon', function () { }, settingsCache: { get: () => {} }}); - blogIcon.getIconUrl(true).should.deepEqual([{relativeUrl: '/favicon.ico'}, true]); + blogIcon.getIconUrl({absolute: true}).should.deepEqual([{relativeUrl: '/favicon.ico'}, true]); + }); + + it('returns null if no fallback is requested', function () { + const blogIcon = new BlogIcon({config: {}, storageUtils: {}, urlUtils: { + urlFor: (key, boolean) => [key, boolean] + }, settingsCache: { + get: () => {} + }}); + + should.equal(blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), null); }); }); }); diff --git a/ghost/staff-service/lib/StaffServiceEmails.js b/ghost/staff-service/lib/StaffServiceEmails.js index fa15117087..4022de8ae6 100644 --- a/ghost/staff-service/lib/StaffServiceEmails.js +++ b/ghost/staff-service/lib/StaffServiceEmails.js @@ -45,7 +45,7 @@ class StaffServiceEmails { attributionUrl: attribution?.url || '', referrerSource: attribution?.referrerSource, siteTitle: this.settingsCache.get('title'), - siteIconUrl: this.blogIcon.getIconUrl(true), + siteIconUrl: this.blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -105,7 +105,7 @@ class StaffServiceEmails { offerData, subscriptionData, siteTitle: this.settingsCache.get('title'), - siteIconUrl: this.blogIcon.getIconUrl(true), + siteIconUrl: this.blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -156,7 +156,7 @@ class StaffServiceEmails { tierData, subscriptionData, siteTitle: this.settingsCache.get('title'), - siteIconUrl: this.blogIcon.getIconUrl(true), + siteIconUrl: this.blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -186,7 +186,7 @@ class StaffServiceEmails { return { siteTitle: this.settingsCache.get('title'), - siteIconUrl: this.blogIcon.getIconUrl(true), + siteIconUrl: this.blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, accentColor: this.settingsCache.get('accent_color'), @@ -287,7 +287,7 @@ class StaffServiceEmails { const templateData = { siteTitle: this.settingsCache.get('title'), siteUrl: this.urlUtils.getSiteUrl(), - siteIconUrl: this.blogIcon.getIconUrl(true), + siteIconUrl: this.blogIcon.getIconUrl({absolute: true, fallbackToDefault: false}), siteDomain: this.siteDomain, fromEmail: this.fromEmailAddress, toEmail: to, From b629dc02ac8bfc769b27015e18c598bc83e332c3 Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:05:52 +0000 Subject: [PATCH 128/164] v5.89.1 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 155b57a412..bad40e20c7 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.89.0", + "version": "5.89.1", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index 3bcac936c0..7e0286876a 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.89.0", + "version": "5.89.1", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org", From da691bf0d777be684f6c75f8df5ffc5b22302df6 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Mon, 12 Aug 2024 13:00:25 +0700 Subject: [PATCH 129/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20newsletter=20but?= =?UTF-8?q?ton=20not=20hidden=20in=20Portal=20(#20732)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref ONC-225 - Wires up the `editor_default_email_recipients` key to the settings public / content api endpoint. - This key is then wired up to Portal to determine whether it's hiding or showing the Member subscribe toggle --- .../pages/AccountHomePage/AccountHomePage.test.js | 7 +++++++ .../AccountHomePage/components/AccountActions.js | 12 ++++++++++-- apps/portal/src/utils/fixtures-generator.js | 4 +++- apps/portal/src/utils/helpers.js | 4 ++++ ghost/core/core/shared/settings-cache/public.js | 3 ++- .../content/__snapshots__/settings.test.js.snap | 1 + .../shared/__snapshots__/version.test.js.snap | 2 ++ 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js b/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js index ac6679bf47..6b4c6aa143 100644 --- a/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js +++ b/apps/portal/src/components/pages/AccountHomePage/AccountHomePage.test.js @@ -48,4 +48,11 @@ describe('Account Home Page', () => { fireEvent.click(manageBtn); expect(mockOnActionFn).toHaveBeenCalledWith('switchPage', {lastPage: 'accountHome', page: 'accountEmail'}); }); + + test('hides Newsletter toggle if newsletters are disabled', () => { + const siteData = getSiteData({editorDefaultEmailRecipients: 'disabled'}); + const {logoutBtn, utils} = setup({site: siteData}); + expect(logoutBtn).toBeInTheDocument(); + expect(utils.queryByText('Email newsletter')).not.toBeInTheDocument(); + }); }); diff --git a/apps/portal/src/components/pages/AccountHomePage/components/AccountActions.js b/apps/portal/src/components/pages/AccountHomePage/components/AccountActions.js index 6a0a03402f..f243f431ae 100644 --- a/apps/portal/src/components/pages/AccountHomePage/components/AccountActions.js +++ b/apps/portal/src/components/pages/AccountHomePage/components/AccountActions.js @@ -1,6 +1,6 @@ import AppContext from '../../../../AppContext'; import {useContext} from 'react'; -import {hasCommentsEnabled, hasMultipleNewsletters, isEmailSuppressed} from '../../../../utils/helpers'; +import {hasCommentsEnabled, hasMultipleNewsletters, isEmailSuppressed, hasNewsletterSendingEnabled} from '../../../../utils/helpers'; import PaidAccountActions from './PaidAccountActions'; import EmailNewsletterAction from './EmailNewsletterAction'; @@ -19,6 +19,8 @@ const AccountActions = () => { const showEmailPreferences = hasMultipleNewsletters({site}) || hasCommentsEnabled({site}) || isEmailSuppressed({member}); + const showEmailUnsubscribe = hasNewsletterSendingEnabled({site}); + return (
    @@ -40,7 +42,13 @@ const AccountActions = () => { { showEmailPreferences ? - : + : <> + } + + { + showEmailUnsubscribe && !showEmailPreferences + ? + : <> }
    diff --git a/apps/portal/src/utils/fixtures-generator.js b/apps/portal/src/utils/fixtures-generator.js index 4fda8d6de3..74adfd1369 100644 --- a/apps/portal/src/utils/fixtures-generator.js +++ b/apps/portal/src/utils/fixtures-generator.js @@ -39,6 +39,7 @@ export function getSiteData({ portalButtonSignupText: portal_button_signup_text = 'Subscribe now', portalButtonStyle: portal_button_style = 'icon-and-text', membersSupportAddress: members_support_address = 'support@example.com', + editorDefaultEmailRecipients: editor_default_email_recipients = 'visibility', newsletters = [], commentsEnabled, recommendations = [], @@ -69,7 +70,8 @@ export function getSiteData({ comments_enabled: commentsEnabled !== 'off', newsletters, recommendations, - recommendations_enabled: !!recommendationsEnabled + recommendations_enabled: !!recommendationsEnabled, + editor_default_email_recipients }; } diff --git a/apps/portal/src/utils/helpers.js b/apps/portal/src/utils/helpers.js index 0b45f80893..c178c4c11a 100644 --- a/apps/portal/src/utils/helpers.js +++ b/apps/portal/src/utils/helpers.js @@ -86,6 +86,10 @@ export function getNewsletterFromUuid({site, uuid}) { }); } +export function hasNewsletterSendingEnabled({site}) { + return site?.editor_default_email_recipients === 'visibility'; +} + export function allowCompMemberUpgrade({member}) { return member?.subscriptions?.[0]?.tier?.expiry_at !== undefined; } diff --git a/ghost/core/core/shared/settings-cache/public.js b/ghost/core/core/shared/settings-cache/public.js index fde8a8667e..30fe80f346 100644 --- a/ghost/core/core/shared/settings-cache/public.js +++ b/ghost/core/core/shared/settings-cache/public.js @@ -45,5 +45,6 @@ module.exports = { recommendations_enabled: 'recommendations_enabled', outbound_link_tagging: 'outbound_link_tagging', default_email_address: 'default_email_address', - support_email_address: 'support_email_address' + support_email_address: 'support_email_address', + editor_default_email_recipients: 'editor_default_email_recipients' }; diff --git a/ghost/core/test/e2e-api/content/__snapshots__/settings.test.js.snap b/ghost/core/test/e2e-api/content/__snapshots__/settings.test.js.snap index a103edbdab..c61c0a5f86 100644 --- a/ghost/core/test/e2e-api/content/__snapshots__/settings.test.js.snap +++ b/ghost/core/test/e2e-api/content/__snapshots__/settings.test.js.snap @@ -12,6 +12,7 @@ Object { "cover_image": "https://static.ghost.org/v5.0.0/images/publication-cover.jpg", "default_email_address": "noreply@127.0.0.1", "description": "Thoughts, stories and ideas", + "editor_default_email_recipients": "visibility", "facebook": "ghost", "firstpromoter_account": null, "icon": null, diff --git a/ghost/core/test/e2e-api/shared/__snapshots__/version.test.js.snap b/ghost/core/test/e2e-api/shared/__snapshots__/version.test.js.snap index 2e9ea09beb..d2d688d07a 100644 --- a/ghost/core/test/e2e-api/shared/__snapshots__/version.test.js.snap +++ b/ghost/core/test/e2e-api/shared/__snapshots__/version.test.js.snap @@ -1364,6 +1364,7 @@ Object { "cover_image": "https://static.ghost.org/v5.0.0/images/publication-cover.jpg", "default_email_address": "noreply@127.0.0.1", "description": "Thoughts, stories and ideas", + "editor_default_email_recipients": "visibility", "facebook": "ghost", "firstpromoter_account": null, "icon": null, @@ -1466,6 +1467,7 @@ Object { "cover_image": "https://static.ghost.org/v5.0.0/images/publication-cover.jpg", "default_email_address": "noreply@127.0.0.1", "description": "Thoughts, stories and ideas", + "editor_default_email_recipients": "visibility", "facebook": "ghost", "firstpromoter_account": null, "icon": null, From 85aed302c1d8a124446c8edc6a29ac65f211de34 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Mon, 12 Aug 2024 14:41:39 +0700 Subject: [PATCH 130/164] Bumped Portal to 2.38.0 (#20733) ref ONC-225 Bumped it a minor since it contains breaking changes. --- apps/portal/package.json | 2 +- ghost/core/core/shared/config/defaults.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/portal/package.json b/apps/portal/package.json index 395f38a128..af19aa4ec6 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/portal", - "version": "2.37.10", + "version": "2.38.0", "license": "MIT", "repository": { "type": "git", diff --git a/ghost/core/core/shared/config/defaults.json b/ghost/core/core/shared/config/defaults.json index 40c28eb70f..006a350acc 100644 --- a/ghost/core/core/shared/config/defaults.json +++ b/ghost/core/core/shared/config/defaults.json @@ -182,7 +182,7 @@ }, "portal": { "url": "https://cdn.jsdelivr.net/ghost/portal@~{version}/umd/portal.min.js", - "version": "2.37" + "version": "2.38" }, "sodoSearch": { "url": "https://cdn.jsdelivr.net/ghost/sodo-search@~{version}/umd/sodo-search.min.js", From 0412decaec7264ff6c069ef5e28f78d0577f3393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20der=20Winden?= Date: Mon, 12 Aug 2024 11:35:21 +0200 Subject: [PATCH 131/164] Excerpt form error fix (#20730) fixes https://linear.app/tryghost/issue/DES-435/excerpt-in-post-settings-has-an-inconsistent-error-state The excerpt form field seemed to not be properly handling errors. However, it was a case of the error styling being overruled by the regular styling, causing the red border to only show upon `:focus` when there is an error in the excerpt. I've rewritten the logic to be slightly less obfuscated and added some CSS to circumvent the issue. --- ghost/admin/app/styles/patterns/forms.css | 2 +- ghost/admin/app/validators/post.js | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ghost/admin/app/styles/patterns/forms.css b/ghost/admin/app/styles/patterns/forms.css index 4eba90853a..bf0a74b085 100644 --- a/ghost/admin/app/styles/patterns/forms.css +++ b/ghost/admin/app/styles/patterns/forms.css @@ -225,7 +225,7 @@ select { .gh-select.error, .error .gh-input-append, select.error { - border-color: var(--red); + border-color: var(--red)!important; } .gh-input:focus, diff --git a/ghost/admin/app/validators/post.js b/ghost/admin/app/validators/post.js index f157e4d2f6..24da716349 100644 --- a/ghost/admin/app/validators/post.js +++ b/ghost/admin/app/validators/post.js @@ -62,12 +62,11 @@ export default BaseValidator.create({ customExcerpt(model) { if (!validator.isLength(model.customExcerpt || '', 0, 300)) { - if (model.feature.editorExcerpt) { - model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.'); - } else { - model.errors.add('customExcerpt', 'Excerpt cannot be longer than 300 characters.'); - } + const errorMessage = 'Excerpt cannot be longer than 300 characters.'; + model.errors.add('customExcerpt', errorMessage); this.invalidate(); + } else { + model.errors.remove('customExcerpt'); } }, From a86f9dbddaa7223bd767524019252fac9c043eba Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Mon, 12 Aug 2024 11:17:38 +0100 Subject: [PATCH 132/164] Cleaned up internalLinking labs flag closes https://linear.app/tryghost/issue/PLG-15 - removed `internalLinking` GA labs flag - renamed search providers to `flex` and `basic` - keeps old search provider around as it can handle non-English languages unlike the faster flex provider - updated `search` service to switch from `flex` to `basic` when the site's locale is not english - bumped Koenig packages to switch from a feature flag for toggling internal linking features to the presence of the `searchLinks` function in card config - updated tests to correctly switch between flex and basic providers in respective suites --- apps/admin-x-settings/package.json | 2 +- .../app/components/koenig-lexical-editor.js | 4 +- ghost/admin/app/services/feature.js | 1 - ...h-provider.js => search-provider-basic.js} | 2 +- ...ovider-beta.js => search-provider-flex.js} | 2 +- ghost/admin/app/services/search.js | 10 +- ghost/admin/package.json | 4 +- ghost/admin/tests/acceptance/search-test.js | 27 +++- .../tests/integration/services/search-test.js | 35 ++++- ghost/core/core/shared/labs.js | 3 +- ghost/core/package.json | 10 +- .../admin/__snapshots__/settings.test.js.snap | 2 +- ghost/email-service/package.json | 2 +- ghost/importer-revue/package.json | 2 +- yarn.lock | 125 +++++++++--------- 15 files changed, 136 insertions(+), 95 deletions(-) rename ghost/admin/app/services/{search-provider.js => search-provider-basic.js} (98%) rename ghost/admin/app/services/{search-provider-beta.js => search-provider-flex.js} (98%) diff --git a/apps/admin-x-settings/package.json b/apps/admin-x-settings/package.json index 449d99ee67..4db5eb155f 100644 --- a/apps/admin-x-settings/package.json +++ b/apps/admin-x-settings/package.json @@ -39,7 +39,7 @@ "dependencies": { "@codemirror/lang-html": "6.4.9", "@tryghost/color-utils": "0.2.2", - "@tryghost/kg-unsplash-selector": "0.2.1", + "@tryghost/kg-unsplash-selector": "0.2.2", "@tryghost/limit-service": "1.2.14", "@tryghost/nql": "0.12.3", "@tryghost/timezone-data": "0.4.3", diff --git a/ghost/admin/app/components/koenig-lexical-editor.js b/ghost/admin/app/components/koenig-lexical-editor.js index 8f91477f74..b63e16157f 100644 --- a/ghost/admin/app/components/koenig-lexical-editor.js +++ b/ghost/admin/app/components/koenig-lexical-editor.js @@ -452,8 +452,6 @@ export default class KoenigLexicalEditor extends Component { feature: { collectionsCard: this.feature.collectionsCard, collections: this.feature.collections, - internalLinking: this.feature.internalLinking, - internalLinkingAtLinks: this.feature.internalLinking, contentVisibility: this.feature.contentVisibility }, deprecated: { // todo fix typo @@ -705,7 +703,7 @@ export default class KoenigLexicalEditor extends Component { Loading editor...

    }> - +
    diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index 2af398c3b0..09f4042a45 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -76,7 +76,6 @@ export default class FeatureService extends Service { @feature('lexicalIndicators') lexicalIndicators; @feature('adminXDemo') adminXDemo; @feature('ActivityPub') ActivityPub; - @feature('internalLinking') internalLinking; @feature('editorExcerpt') editorExcerpt; @feature('contentVisibility') contentVisibility; diff --git a/ghost/admin/app/services/search-provider.js b/ghost/admin/app/services/search-provider-basic.js similarity index 98% rename from ghost/admin/app/services/search-provider.js rename to ghost/admin/app/services/search-provider-basic.js index 2bef46af0c..cb73ee2820 100644 --- a/ghost/admin/app/services/search-provider.js +++ b/ghost/admin/app/services/search-provider-basic.js @@ -36,7 +36,7 @@ export const SEARCHABLES = [ } ]; -export default class SearchProviderService extends Service { +export default class SearchProviderBasicService extends Service { @service ajax; @service notifications; @service store; diff --git a/ghost/admin/app/services/search-provider-beta.js b/ghost/admin/app/services/search-provider-flex.js similarity index 98% rename from ghost/admin/app/services/search-provider-beta.js rename to ghost/admin/app/services/search-provider-flex.js index ce40a1800c..92a1b5e6d1 100644 --- a/ghost/admin/app/services/search-provider-beta.js +++ b/ghost/admin/app/services/search-provider-flex.js @@ -45,7 +45,7 @@ export const SEARCHABLES = [ } ]; -export default class SearchProviderService extends Service { +export default class SearchProviderFlexService extends Service { @service ajax; @service notifications; @service store; diff --git a/ghost/admin/app/services/search.js b/ghost/admin/app/services/search.js index 2ba14e6367..f8e3f9e500 100644 --- a/ghost/admin/app/services/search.js +++ b/ghost/admin/app/services/search.js @@ -8,16 +8,16 @@ export default class SearchService extends Service { @service ajax; @service feature; @service notifications; - @service searchProvider; - @service searchProviderBeta; + @service searchProviderBasic; + @service searchProviderFlex; + @service settings; @service store; isContentStale = true; get provider() { - return this.feature.internalLinking - ? this.searchProviderBeta - : this.searchProvider; + const isEnglish = this.settings.locale?.toLowerCase().startsWith('en') ?? true; + return isEnglish ? this.searchProviderFlex : this.searchProviderBasic; } @action diff --git a/ghost/admin/package.json b/ghost/admin/package.json index bad40e20c7..893fdf1420 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -47,9 +47,9 @@ "@tryghost/color-utils": "0.2.2", "@tryghost/ember-promise-modals": "2.0.1", "@tryghost/helpers": "1.1.90", - "@tryghost/kg-clean-basic-html": "4.1.1", + "@tryghost/kg-clean-basic-html": "4.1.2", "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.13", + "@tryghost/koenig-lexical": "1.3.14", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/admin/tests/acceptance/search-test.js b/ghost/admin/tests/acceptance/search-test.js index 3e025285be..ca6871402b 100644 --- a/ghost/admin/tests/acceptance/search-test.js +++ b/ghost/admin/tests/acceptance/search-test.js @@ -2,22 +2,34 @@ import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd'; import {authenticateSession} from 'ember-simple-auth/test-support'; import {click, currentURL, find, findAll, triggerKeyEvent, visit} from '@ember/test-helpers'; import {describe, it} from 'mocha'; -import {enableLabsFlag} from '../helpers/labs-flag'; import {expect} from 'chai'; import {getPosts} from '../../mirage/config/posts'; import {setupApplicationTest} from 'ember-mocha'; import {setupMirage} from 'ember-cli-mirage/test-support'; import {typeInSearch} from 'ember-power-select/test-support/helpers'; +// we have two search providers +// - "flex" which uses the flexsearch engine but is limited to english only +// - "basic" which uses exact string matches in a less performant way but is language agnostic const suites = [{ - name: 'Acceptance: Search', + name: 'Acceptance: Search (flex)', beforeEach() { - // noop + // noop - default locale is 'en' + }, + confirmProvider() { + const searchService = this.owner.lookup('service:search'); + expect(searchService.provider.constructor.name, 'provider name').to.equal('SearchProviderFlexService'); } }, { - name: 'Acceptance: Search (beta)', + name: 'Acceptance: Search (basic)', beforeEach() { - enableLabsFlag(this.server, 'internalLinking'); + this.server.db.settings.update({key: 'locale'}, {value: 'de'}); + }, + confirmProvider() { + const settingsService = this.owner.lookup('service:settings'); + expect(settingsService.locale, 'settings.locale').to.equal('de'); + const searchService = this.owner.lookup('service:search'); + expect(searchService.provider.constructor.name, 'provider name').to.equal('SearchProviderBasicService'); } }]; @@ -48,6 +60,11 @@ suites.forEach((suite) => { return await authenticateSession(); }); + it('is using correct provider', async function () { + await visit('/dashboard'); + suite.confirmProvider.bind(this)(); + }); + it('opens search modal when clicking icon', async function () { await visit('/dashboard'); expect(currentURL(), 'currentURL').to.equal('/dashboard'); diff --git a/ghost/admin/tests/integration/services/search-test.js b/ghost/admin/tests/integration/services/search-test.js index ce453fdeda..cebc03a52e 100644 --- a/ghost/admin/tests/integration/services/search-test.js +++ b/ghost/admin/tests/integration/services/search-test.js @@ -1,18 +1,31 @@ +import {authenticateSession} from 'ember-simple-auth/test-support'; import {describe, it} from 'mocha'; -import {enableLabsFlag} from '../../helpers/labs-flag'; import {expect} from 'chai'; import {setupMirage} from 'ember-cli-mirage/test-support'; import {setupTest} from 'ember-mocha'; +// we have two search providers +// - "flex" which uses the flexsearch engine but is limited to english only +// - "basic" which uses exact string matches in a less performant way but is language agnostic const suites = [{ - name: 'Integration: Service: Search', + name: 'Integration: Service: Search (flex)', beforeEach() { - // noop + // noop - default locale is 'en' + }, + confirmProvider() { + const searchService = this.owner.lookup('service:search'); + expect(searchService.provider.constructor.name, 'provider name').to.equal('SearchProviderFlexService'); } }, { - name: 'Integration: Service: Search (beta)', + name: 'Integration: Service: Search (basic)', beforeEach() { - enableLabsFlag(this.server, 'internalLinking'); + this.server.db.settings.update({key: 'locale'}, {value: 'de'}); + }, + confirmProvider() { + const settingsService = this.owner.lookup('service:settings'); + expect(settingsService.locale, 'settings.locale').to.equal('de'); + const searchService = this.owner.lookup('service:search'); + expect(searchService.provider.constructor.name, 'provider name').to.equal('SearchProviderBasicService'); } }]; @@ -25,9 +38,15 @@ suites.forEach((suite) => { // eslint-disable-next-line no-unused-vars let firstUser, firstPost, secondPost, firstPage, firstTag; - beforeEach(function () { + beforeEach(async function () { + this.server.loadFixtures(); + await authenticateSession(); + suite.beforeEach.bind(this)(); + const settings = this.owner.lookup('service:settings'); + await settings.fetch(); + search = this.owner.lookup('service:search'); // populate store with data we'll be searching @@ -37,6 +56,10 @@ suites.forEach((suite) => { firstTag = this.server.create('tag', {name: 'First tag', slug: 'first-tag'}); }); + it('is using correct provider', async function () { + suite.confirmProvider.bind(this)(); + }); + it('returns urls for search results', async function () { const results = await search.searchTask.perform('first'); diff --git a/ghost/core/core/shared/labs.js b/ghost/core/core/shared/labs.js index e8b68426b1..e9a1a7ad68 100644 --- a/ghost/core/core/shared/labs.js +++ b/ghost/core/core/shared/labs.js @@ -19,8 +19,7 @@ const GA_FEATURES = [ 'themeErrorsNotification', 'outboundLinkTagging', 'announcementBar', - 'newEmailAddresses', - 'internalLinking' + 'newEmailAddresses' ]; // NOTE: this allowlist is meant to be used to filter out any unexpected diff --git a/ghost/core/package.json b/ghost/core/package.json index 7e0286876a..705adc5349 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -110,11 +110,11 @@ "@tryghost/kg-card-factory": "5.0.4", "@tryghost/kg-converters": "1.0.5", "@tryghost/kg-default-atoms": "5.0.3", - "@tryghost/kg-default-cards": "10.0.6", - "@tryghost/kg-default-nodes": "1.1.9", - "@tryghost/kg-html-to-lexical": "1.1.10", - "@tryghost/kg-lexical-html-renderer": "1.1.12", - "@tryghost/kg-mobiledoc-html-renderer": "7.0.4", + "@tryghost/kg-default-cards": "10.0.7", + "@tryghost/kg-default-nodes": "1.1.10", + "@tryghost/kg-html-to-lexical": "1.1.11", + "@tryghost/kg-lexical-html-renderer": "1.1.13", + "@tryghost/kg-mobiledoc-html-renderer": "7.0.5", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", "@tryghost/link-replacer": "0.0.0", diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/settings.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/settings.test.js.snap index 75c28d354b..8adfdddb9f 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/settings.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/settings.test.js.snap @@ -1155,7 +1155,7 @@ exports[`Settings API Edit Can edit a setting 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "4454", + "content-length": "4429", "content-type": "application/json; charset=utf-8", "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, diff --git a/ghost/email-service/package.json b/ghost/email-service/package.json index b8e2d30751..1bc7db379e 100644 --- a/ghost/email-service/package.json +++ b/ghost/email-service/package.json @@ -29,7 +29,7 @@ "@tryghost/email-events": "0.0.0", "@tryghost/errors": "1.3.5", "@tryghost/html-to-plaintext": "0.0.0", - "@tryghost/kg-default-cards": "10.0.6", + "@tryghost/kg-default-cards": "10.0.7", "@tryghost/logging": "2.4.18", "@tryghost/tpl": "0.1.32", "@tryghost/validator": "0.2.14", diff --git a/ghost/importer-revue/package.json b/ghost/importer-revue/package.json index c2e5e14a2a..1172ff9dd6 100644 --- a/ghost/importer-revue/package.json +++ b/ghost/importer-revue/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@tryghost/debug": "0.1.32", - "@tryghost/kg-default-cards": "10.0.6", + "@tryghost/kg-default-cards": "10.0.7", "@tryghost/string": "0.2.12", "lodash": "4.17.21", "papaparse": "5.3.2", diff --git a/yarn.lock b/yarn.lock index f8f14ebad1..83f5d8c190 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7817,6 +7817,11 @@ resolved "https://registry.yarnpkg.com/@tryghost/kg-clean-basic-html/-/kg-clean-basic-html-4.1.1.tgz#132a019abc6b6b6a0948c7e2d3e3ce37d18983b7" integrity sha512-R654qIHRf//FP/1hHLkehTYxZz/Zp5NXomfEuQSezw4uDmOwGn1ME4yZD5TDi5+8ism71tfMeGVVI5XmLOeDLg== +"@tryghost/kg-clean-basic-html@4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@tryghost/kg-clean-basic-html/-/kg-clean-basic-html-4.1.2.tgz#05cab6a2ed2de43b4f784e0641bfc301402379e8" + integrity sha512-TCCRU1TVvSrOYXceQDaMbPwHJuBMPfYPSLm/FsRkhDsIZK14Svxswcr4oPW+zgXUsHUPKYQtnH/igln2b5wXgA== + "@tryghost/kg-converters@1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@tryghost/kg-converters/-/kg-converters-1.0.5.tgz#8deb6591b91d0c5e89c529bc39b1f9d0870bec37" @@ -7829,51 +7834,51 @@ resolved "https://registry.yarnpkg.com/@tryghost/kg-default-atoms/-/kg-default-atoms-5.0.3.tgz#7d0e5af2191f2e0c61ae11d1666905bf924f67ac" integrity sha512-uPE69rKxaiiMEa1vFsEbfX+LCBG2H5D/nqTkuIPAslEGIHYjQUruaXpBfnwtyVHn+dMLuWcO+wDJl5qET8gKEQ== -"@tryghost/kg-default-cards@10.0.6": - version "10.0.6" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.6.tgz#17f10a711814196719c8fa2c6506a7d2ea9f3d7f" - integrity sha512-wOjaqFj8G9hFr6bI85CUAO55aD9B2NoLIVaeqZgzzU6Ix09ZhP4M4abrwQup7hp89PSrGz7noqmac9NTb/Lvxg== +"@tryghost/kg-default-cards@10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.7.tgz#73f541155e646988ec3719e8acbbd051958b4d47" + integrity sha512-J5eEsBZm4H0GBWvqqlwKGYuYdQsIoC3lK3dyGfrw6/dOF0RNFYoaN6k90Td2bnMbbW6OxX5cabaOosHdj5uvqg== dependencies: - "@tryghost/kg-markdown-html-renderer" "7.0.5" + "@tryghost/kg-markdown-html-renderer" "7.0.6" "@tryghost/string" "0.2.12" "@tryghost/url-utils" "4.4.8" handlebars "^4.7.6" juice "^10.0.0" lodash "^4.17.21" - luxon "^3.0.0" + luxon "^3.5.0" -"@tryghost/kg-default-nodes@1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.9.tgz#2f8e0851735ad9daf8ba103fe3d42d9119a366eb" - integrity sha512-xTIkOfusnTHub/pU/Pdw8S5rQ8GLf5ONfS1y8x5v2cqXln1e08lV8wa5gv44WSxuyDzKjVHKKceGkT3rbdVBXg== +"@tryghost/kg-default-nodes@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.10.tgz#8cf29a94868b978c31d09b476dbfc70b21460e3f" + integrity sha512-8Yro0F1Y0nN4jkDaK7/0YnKRsZKMwCk6YHuhNoNpICaKwH9EO2Isqen2/0FLaAGo8c0uvlPaYv5AfGjWJ0/ukg== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/selection" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-clean-basic-html" "4.1.1" - "@tryghost/kg-markdown-html-renderer" "7.0.5" + "@tryghost/kg-clean-basic-html" "4.1.2" + "@tryghost/kg-markdown-html-renderer" "7.0.6" html-minifier "^4.0.0" - jsdom "^24.0.0" + jsdom "^24.1.0" lexical "0.13.1" lodash "^4.17.21" - luxon "^3.3.0" + luxon "^3.5.0" -"@tryghost/kg-default-transforms@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.10.tgz#7f4f648e2a8eec8d4af43028111e766feb3975ea" - integrity sha512-T9OZau2npHwtxKw77hXqRWNEErxdM/WKldWtmLKGJiKT7Czx8v9eEpsNmGgJYnXenkyzehJmU1okJlAUqMbcsA== +"@tryghost/kg-default-transforms@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.11.tgz#021b18cacabe83bdbe61761027bd87870c110ab5" + integrity sha512-rxxlDTa9Omzr57WscCr+kK/UBGukfGzeoTc6jlR+/eOg6p0hGSk5b64EFPgcwEwNQKDv/IkZ1DZg0UdrX7BIqw== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.9" + "@tryghost/kg-default-nodes" "1.1.10" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.10.tgz#89dcd98e3933485bb0f33ab725dac4080f5a01fe" - integrity sha512-ja0DRLEzQhhOzK4n7HQqUttr0dbDsMaueyXb6+bxovHyog3HFo3A5NYz0DX9UU3qdUhAKBLwrgY5SwrtJ/ysVw== +"@tryghost/kg-html-to-lexical@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.11.tgz#d2569450c891f590be3b8856fb58616435da67cc" + integrity sha512-aVvsuX7cmmMgWqSdS0L9+wHcYESStqVdWUEaJVNOt0xuxkN23NOMmTLB0HVSDZv6WJ7HxsgY768bPDLWIesp9g== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7881,15 +7886,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.9" - "@tryghost/kg-default-transforms" "1.1.10" - jsdom "^24.0.0" + "@tryghost/kg-default-nodes" "1.1.10" + "@tryghost/kg-default-transforms" "1.1.11" + jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.12.tgz#3234331f18b0dfe65e52e8b5821ef2bbc4a7909a" - integrity sha512-AEV+A1ZxSSVjTse7YonMz8AF9pqppEpfAHnwKH6BarSTJAVL8Gbvv/zcISO03UVrUqfqCZnnmmXS2h8iOBrbSA== +"@tryghost/kg-lexical-html-renderer@1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.13.tgz#c1c495aa462f17bf55fff8d4546231c49f77261e" + integrity sha512-EzOiWEUR8x6ub9tCHPtoROQFuXvL6rX7LMW9TdqZAMSv+xk9/yD6kgOmlp0aJ4XbuxM2iCrdq6HDHpd86ZYBwg== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -7897,17 +7902,17 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.9" - "@tryghost/kg-default-transforms" "1.1.10" - jsdom "^24.0.0" + "@tryghost/kg-default-nodes" "1.1.10" + "@tryghost/kg-default-transforms" "1.1.11" + jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-markdown-html-renderer@7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.0.5.tgz#96fb7440291ab9188b1e32c7cc71f6aa5fdec48e" - integrity sha512-Y1f2tKenyfrYIOfOVTI+9/hIVAed2MN/96B5pRBXpn/Bv2/+Sq8RCAFqlLmNwX4o7XeNp51BHZK96Mu+sU/Ltg== +"@tryghost/kg-markdown-html-renderer@7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.0.6.tgz#6d0cdcd74bc3c87da72fa7c9fb58f44ffee61eb4" + integrity sha512-EkVgUleBCQ8/FAlQ/mxxu4Erhq+CQK+ujqBRLVUsMxGvYiUop1u9q3M0VN8KoAo8t4plueHu7nkaoggLZOgiJw== dependencies: - "@tryghost/kg-utils" "1.0.26" + "@tryghost/kg-utils" "1.0.27" markdown-it "^14.0.0" markdown-it-footnote "^4.0.0" markdown-it-image-lazy-loading "^2.0.0" @@ -7915,14 +7920,14 @@ markdown-it-mark "^4.0.0" markdown-it-sub "^2.0.0" markdown-it-sup "^2.0.0" - semver "^7.3.5" + semver "^7.6.2" -"@tryghost/kg-mobiledoc-html-renderer@7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@tryghost/kg-mobiledoc-html-renderer/-/kg-mobiledoc-html-renderer-7.0.4.tgz#d5a433d9ebed76a1e74ff5792cab2d7f4b844e55" - integrity sha512-C0ncnXc5vsLPQmsEw4xfUmdJnJTL9WsoACpI6970R4/jvAWE3h99TVpSFWVnHwrc/asWxaXZlpcKKH4PJs3VzA== +"@tryghost/kg-mobiledoc-html-renderer@7.0.5": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@tryghost/kg-mobiledoc-html-renderer/-/kg-mobiledoc-html-renderer-7.0.5.tgz#5e1801f4280df357020f8ab2f42cfd79abb54d6a" + integrity sha512-PNURuXzP2lO/0b2ugdxGlyqS1+ZScdlZIvnP0lREV9H+66hu8J16xvb3YxUZvVsm14ImbLF92R3cURHOvrXwIw== dependencies: - "@tryghost/kg-utils" "1.0.26" + "@tryghost/kg-utils" "1.0.27" mobiledoc-dom-renderer "^0.7.0" simple-dom "^1.4.0" @@ -7933,22 +7938,22 @@ dependencies: "@tryghost/kg-clean-basic-html" "4.1.1" -"@tryghost/kg-unsplash-selector@0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.2.1.tgz#c9e1658ed8d9469a26ca78c1aec1848fd7d6007c" - integrity sha512-LzgKE7UJ24bvID0c94teXSCrfbAX2/jo8sKaxEKR9P4E4P8COlCme/aygdOUCTblCBgE4KxyO9a+bD3uYW2Qyg== +"@tryghost/kg-unsplash-selector@0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.2.2.tgz#fcd4445f39c1ac879662b45eb863e2bb8ba95625" + integrity sha512-t0NkercfjQnKFVDs0I4VM8Aksabv4ZB/qYFyN3mLefA4Pt5LK99XZrwuYKmL6LOoWNZLMbKPUvBxuuo1NTW5GA== -"@tryghost/kg-utils@1.0.26": - version "1.0.26" - resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.26.tgz#48e18b05d8bcf2c0d3c94f3d500e9b267d6723e6" - integrity sha512-DXx/qJwMYB6mjyhBk1mOg2hA5sXGNVcP2R0FyhkmHKtdNEwP0WIpV7UZt7DIYDT4c4uWFddya+u6yVpqU9Nhxg== +"@tryghost/kg-utils@1.0.27": + version "1.0.27" + resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.27.tgz#77442c9cce7ee2d0e6fbfa669ef733f52dec7d82" + integrity sha512-pZ9qcuOKijTJx1qTU/Pju3ZJOXTQeGBl1qKmCBMfwoacVepN0qPzn/GierOqgvPzEw5xHexJPrQVn0hE0w9bdA== dependencies: - semver "^7.3.5" + semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.13": - version "1.3.13" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.13.tgz#7ffa158d1f28f4f75d0fce763c0a884f98a23f13" - integrity sha512-tjLouQMCPPAXdvBYWVtHp4SeDylT3tF5nh0cy3JPOmG0eJRYGdGwxiEabk6jmzOEjjhkD0kwT+6m3G+vv70dcw== +"@tryghost/koenig-lexical@1.3.14": + version "1.3.14" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.14.tgz#bb8cc0cf336821b21500ba24d12e4e555cad340c" + integrity sha512-V5udyPS3WVjX4adOeuW5jXUpYtW+vYKHILS0JMtiyf8oiokRpN8W2m8y0n7w7GfptEgzcSLs9X+Sx0ujTaNURA== "@tryghost/limit-service@1.2.14": version "1.2.14" @@ -21410,7 +21415,7 @@ jscodeshift@^0.15.1: temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@24.1.1, jsdom@^24.0.0, jsdom@~24.1.0: +jsdom@24.1.1, jsdom@^24.0.0, jsdom@^24.1.0, jsdom@~24.1.0: version "24.1.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.1.1.tgz#f41df8f4f3b2fbfa7e1bdc5df62c9804fd14a9d0" integrity sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ== @@ -22752,7 +22757,7 @@ ltgt@^2.1.2: resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== -luxon@3.5.0, luxon@^3.0.0, luxon@^3.3.0: +luxon@3.5.0, luxon@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== @@ -28444,7 +28449,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@7.6.3, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.6.3, semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== From fa9bc4d446064f345e6851f6081291dbeddf1089 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:34:41 +0000 Subject: [PATCH 133/164] Update Koenig packages --- apps/admin-x-settings/package.json | 2 +- ghost/admin/package.json | 6 +- ghost/core/package.json | 14 ++-- ghost/email-service/package.json | 2 +- ghost/importer-revue/package.json | 2 +- yarn.lock | 124 ++++++++++++++--------------- 6 files changed, 75 insertions(+), 75 deletions(-) diff --git a/apps/admin-x-settings/package.json b/apps/admin-x-settings/package.json index 4db5eb155f..7200fa4819 100644 --- a/apps/admin-x-settings/package.json +++ b/apps/admin-x-settings/package.json @@ -39,7 +39,7 @@ "dependencies": { "@codemirror/lang-html": "6.4.9", "@tryghost/color-utils": "0.2.2", - "@tryghost/kg-unsplash-selector": "0.2.2", + "@tryghost/kg-unsplash-selector": "0.2.3", "@tryghost/limit-service": "1.2.14", "@tryghost/nql": "0.12.3", "@tryghost/timezone-data": "0.4.3", diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 893fdf1420..6d68a7e749 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -47,9 +47,9 @@ "@tryghost/color-utils": "0.2.2", "@tryghost/ember-promise-modals": "2.0.1", "@tryghost/helpers": "1.1.90", - "@tryghost/kg-clean-basic-html": "4.1.2", - "@tryghost/kg-converters": "1.0.5", - "@tryghost/koenig-lexical": "1.3.14", + "@tryghost/kg-clean-basic-html": "4.1.3", + "@tryghost/kg-converters": "1.0.6", + "@tryghost/koenig-lexical": "1.3.15", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/package.json b/ghost/core/package.json index 705adc5349..5187d8053f 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -107,14 +107,14 @@ "@tryghost/importer-handler-content-files": "0.0.0", "@tryghost/importer-revue": "0.0.0", "@tryghost/job-manager": "0.0.0", - "@tryghost/kg-card-factory": "5.0.4", - "@tryghost/kg-converters": "1.0.5", + "@tryghost/kg-card-factory": "5.0.5", + "@tryghost/kg-converters": "1.0.6", "@tryghost/kg-default-atoms": "5.0.3", - "@tryghost/kg-default-cards": "10.0.7", - "@tryghost/kg-default-nodes": "1.1.10", - "@tryghost/kg-html-to-lexical": "1.1.11", - "@tryghost/kg-lexical-html-renderer": "1.1.13", - "@tryghost/kg-mobiledoc-html-renderer": "7.0.5", + "@tryghost/kg-default-cards": "10.0.8", + "@tryghost/kg-default-nodes": "1.1.11", + "@tryghost/kg-html-to-lexical": "1.1.12", + "@tryghost/kg-lexical-html-renderer": "1.1.14", + "@tryghost/kg-mobiledoc-html-renderer": "7.0.6", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", "@tryghost/link-replacer": "0.0.0", diff --git a/ghost/email-service/package.json b/ghost/email-service/package.json index 1bc7db379e..6b66e4fb5d 100644 --- a/ghost/email-service/package.json +++ b/ghost/email-service/package.json @@ -29,7 +29,7 @@ "@tryghost/email-events": "0.0.0", "@tryghost/errors": "1.3.5", "@tryghost/html-to-plaintext": "0.0.0", - "@tryghost/kg-default-cards": "10.0.7", + "@tryghost/kg-default-cards": "10.0.8", "@tryghost/logging": "2.4.18", "@tryghost/tpl": "0.1.32", "@tryghost/validator": "0.2.14", diff --git a/ghost/importer-revue/package.json b/ghost/importer-revue/package.json index 1172ff9dd6..3eb22f5075 100644 --- a/ghost/importer-revue/package.json +++ b/ghost/importer-revue/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@tryghost/debug": "0.1.32", - "@tryghost/kg-default-cards": "10.0.7", + "@tryghost/kg-default-cards": "10.0.8", "@tryghost/string": "0.2.12", "lodash": "4.17.21", "papaparse": "5.3.2", diff --git a/yarn.lock b/yarn.lock index 83f5d8c190..5748e1baae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7807,25 +7807,25 @@ "@tryghost/errors" "^1.3.5" jest-snapshot "^29.0.0" -"@tryghost/kg-card-factory@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@tryghost/kg-card-factory/-/kg-card-factory-5.0.4.tgz#b2de98eaf01edbd5629fb1f4b06eca3a5f95d0ad" - integrity sha512-KcNM4QJONSSOJeQlv9no5wFx+uV2mESX3bYBL2y3c0DqB26NlMaUx0QIAFSbCSinUlCvRFOwEEBQyaACtCOvzQ== +"@tryghost/kg-card-factory@5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@tryghost/kg-card-factory/-/kg-card-factory-5.0.5.tgz#2a7552b5f7ac3bbae99af9dca3da11b95fb4ae1b" + integrity sha512-fZumHdEkR6pzc2BLukMNOnSC/tru9SJ+UP5rEaECN9lpLnLGya6B0fU51y8+Jjn+xl1CDCzeWtvII50CSBNLOQ== "@tryghost/kg-clean-basic-html@4.1.1": version "4.1.1" resolved "https://registry.yarnpkg.com/@tryghost/kg-clean-basic-html/-/kg-clean-basic-html-4.1.1.tgz#132a019abc6b6b6a0948c7e2d3e3ce37d18983b7" integrity sha512-R654qIHRf//FP/1hHLkehTYxZz/Zp5NXomfEuQSezw4uDmOwGn1ME4yZD5TDi5+8ism71tfMeGVVI5XmLOeDLg== -"@tryghost/kg-clean-basic-html@4.1.2": - version "4.1.2" - resolved "https://registry.yarnpkg.com/@tryghost/kg-clean-basic-html/-/kg-clean-basic-html-4.1.2.tgz#05cab6a2ed2de43b4f784e0641bfc301402379e8" - integrity sha512-TCCRU1TVvSrOYXceQDaMbPwHJuBMPfYPSLm/FsRkhDsIZK14Svxswcr4oPW+zgXUsHUPKYQtnH/igln2b5wXgA== +"@tryghost/kg-clean-basic-html@4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@tryghost/kg-clean-basic-html/-/kg-clean-basic-html-4.1.3.tgz#be0724ac222a76af1f6a6e2c7e1ea1d41f69c2d1" + integrity sha512-z2TLpPTMDR8onNGV177/B1BUdGIumwJ9Pd8i2GXfBbX8LtTnqswK465iR1CUVrKlwZFXFsqvj1ZFPnt51KWQKQ== -"@tryghost/kg-converters@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@tryghost/kg-converters/-/kg-converters-1.0.5.tgz#8deb6591b91d0c5e89c529bc39b1f9d0870bec37" - integrity sha512-TpKH0oAlA+yFrQk7d8N0DYlxxQ8bcmoc7Waf3F1tDuioBhi7sFOz88TVxKt5VkPu1/PKGR52c+xhEeDA7BU85A== +"@tryghost/kg-converters@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@tryghost/kg-converters/-/kg-converters-1.0.6.tgz#55353fa9241c4406671fbb82c3f06829d8b750fa" + integrity sha512-QI4o7hTvcs/6qyxY7wD2epLExYlRUCpqi3AEUfgvn35UsNYg4TuMJ6Kxr9Jwdex4DHFwJMEQ0ozrzjxnZEMoLA== dependencies: lodash "^4.17.21" @@ -7834,12 +7834,12 @@ resolved "https://registry.yarnpkg.com/@tryghost/kg-default-atoms/-/kg-default-atoms-5.0.3.tgz#7d0e5af2191f2e0c61ae11d1666905bf924f67ac" integrity sha512-uPE69rKxaiiMEa1vFsEbfX+LCBG2H5D/nqTkuIPAslEGIHYjQUruaXpBfnwtyVHn+dMLuWcO+wDJl5qET8gKEQ== -"@tryghost/kg-default-cards@10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.7.tgz#73f541155e646988ec3719e8acbbd051958b4d47" - integrity sha512-J5eEsBZm4H0GBWvqqlwKGYuYdQsIoC3lK3dyGfrw6/dOF0RNFYoaN6k90Td2bnMbbW6OxX5cabaOosHdj5uvqg== +"@tryghost/kg-default-cards@10.0.8": + version "10.0.8" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.8.tgz#ed67a90c1b5fe045c3e2822be8e14f43ccc2ee3a" + integrity sha512-yLao7TDpDDDHjiTqbl+6wmISpx//iuXY00gS7JqNrZ8X7hZ6IvxUdXu1Hq9AZuyzZ6+nqvyqRYgvZ1VycSVtTQ== dependencies: - "@tryghost/kg-markdown-html-renderer" "7.0.6" + "@tryghost/kg-markdown-html-renderer" "7.0.7" "@tryghost/string" "0.2.12" "@tryghost/url-utils" "4.4.8" handlebars "^4.7.6" @@ -7847,38 +7847,38 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-nodes@1.1.10": - version "1.1.10" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.10.tgz#8cf29a94868b978c31d09b476dbfc70b21460e3f" - integrity sha512-8Yro0F1Y0nN4jkDaK7/0YnKRsZKMwCk6YHuhNoNpICaKwH9EO2Isqen2/0FLaAGo8c0uvlPaYv5AfGjWJ0/ukg== +"@tryghost/kg-default-nodes@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.11.tgz#8f4238868ba2a8c880a2a78b6416c1700c90b015" + integrity sha512-Xh462ufG3wKEVtk/5QLRFanG87/EbTUjeS6glBiMJ63s5aHgrpwUPrKv2H52fWuJt9meg0hmOl6HRrKnYQs88g== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/selection" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-clean-basic-html" "4.1.2" - "@tryghost/kg-markdown-html-renderer" "7.0.6" + "@tryghost/kg-clean-basic-html" "4.1.3" + "@tryghost/kg-markdown-html-renderer" "7.0.7" html-minifier "^4.0.0" jsdom "^24.1.0" lexical "0.13.1" lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-transforms@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.11.tgz#021b18cacabe83bdbe61761027bd87870c110ab5" - integrity sha512-rxxlDTa9Omzr57WscCr+kK/UBGukfGzeoTc6jlR+/eOg6p0hGSk5b64EFPgcwEwNQKDv/IkZ1DZg0UdrX7BIqw== +"@tryghost/kg-default-transforms@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.12.tgz#49b84fb6c4df866f1bfc7aadf19015b14136f4de" + integrity sha512-vr12KCgo6amxoVYt65ra88v6c9lcK2Umyv01eGdoURWr1ipUhlbxOj8zEVaMHJd3Wsnp4TiCyvIag3Bb+WwqKQ== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.10" + "@tryghost/kg-default-nodes" "1.1.11" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.11.tgz#d2569450c891f590be3b8856fb58616435da67cc" - integrity sha512-aVvsuX7cmmMgWqSdS0L9+wHcYESStqVdWUEaJVNOt0xuxkN23NOMmTLB0HVSDZv6WJ7HxsgY768bPDLWIesp9g== +"@tryghost/kg-html-to-lexical@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.12.tgz#a62459127a20b852afbf72aef7a7a8c8bfe63ddb" + integrity sha512-TtHwzvkA9oMNLBI44C3YiD95oNtNItfH0t6UXkQeq7gkcqoMAv5/fqLaQtSYPs1dycgz0EnZD82nEP8rivP43g== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7886,15 +7886,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.10" - "@tryghost/kg-default-transforms" "1.1.11" + "@tryghost/kg-default-nodes" "1.1.11" + "@tryghost/kg-default-transforms" "1.1.12" jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.13": - version "1.1.13" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.13.tgz#c1c495aa462f17bf55fff8d4546231c49f77261e" - integrity sha512-EzOiWEUR8x6ub9tCHPtoROQFuXvL6rX7LMW9TdqZAMSv+xk9/yD6kgOmlp0aJ4XbuxM2iCrdq6HDHpd86ZYBwg== +"@tryghost/kg-lexical-html-renderer@1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.14.tgz#84d7c8c2e0369206780233a58e41a94455077489" + integrity sha512-ud1/Gt13jF8wO7K0MYNCeJk8QCtPxtLBlBS2bmwkRWrcRBkPEHZre5UJ0esk7Ryiq/+rHxj6IS6xClEyq5mP9g== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -7902,17 +7902,17 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.10" - "@tryghost/kg-default-transforms" "1.1.11" + "@tryghost/kg-default-nodes" "1.1.11" + "@tryghost/kg-default-transforms" "1.1.12" jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-markdown-html-renderer@7.0.6": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.0.6.tgz#6d0cdcd74bc3c87da72fa7c9fb58f44ffee61eb4" - integrity sha512-EkVgUleBCQ8/FAlQ/mxxu4Erhq+CQK+ujqBRLVUsMxGvYiUop1u9q3M0VN8KoAo8t4plueHu7nkaoggLZOgiJw== +"@tryghost/kg-markdown-html-renderer@7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@tryghost/kg-markdown-html-renderer/-/kg-markdown-html-renderer-7.0.7.tgz#c0640f0165a853a8f371edb4a731a04c48ee938e" + integrity sha512-hTTEyMeYLhDCq7vXeCiM6SqcozWZEHjK3fZelW3m6E6FxwpZHGURMoZ30UZVpgIpEp/66fMWAT6fD69+tS3TNQ== dependencies: - "@tryghost/kg-utils" "1.0.27" + "@tryghost/kg-utils" "1.0.28" markdown-it "^14.0.0" markdown-it-footnote "^4.0.0" markdown-it-image-lazy-loading "^2.0.0" @@ -7922,12 +7922,12 @@ markdown-it-sup "^2.0.0" semver "^7.6.2" -"@tryghost/kg-mobiledoc-html-renderer@7.0.5": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@tryghost/kg-mobiledoc-html-renderer/-/kg-mobiledoc-html-renderer-7.0.5.tgz#5e1801f4280df357020f8ab2f42cfd79abb54d6a" - integrity sha512-PNURuXzP2lO/0b2ugdxGlyqS1+ZScdlZIvnP0lREV9H+66hu8J16xvb3YxUZvVsm14ImbLF92R3cURHOvrXwIw== +"@tryghost/kg-mobiledoc-html-renderer@7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@tryghost/kg-mobiledoc-html-renderer/-/kg-mobiledoc-html-renderer-7.0.6.tgz#471bcc83546acb4b45dd6d6940709416f2fb28a8" + integrity sha512-jpHKpq7nda6fSO7/kEOb2dbY6Y1NOQ+w7l4snhtRV3rcW1epIZ07OhXwW36GV0hGTVFs3yxc6HMWlKlvIZS0uA== dependencies: - "@tryghost/kg-utils" "1.0.27" + "@tryghost/kg-utils" "1.0.28" mobiledoc-dom-renderer "^0.7.0" simple-dom "^1.4.0" @@ -7938,22 +7938,22 @@ dependencies: "@tryghost/kg-clean-basic-html" "4.1.1" -"@tryghost/kg-unsplash-selector@0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.2.2.tgz#fcd4445f39c1ac879662b45eb863e2bb8ba95625" - integrity sha512-t0NkercfjQnKFVDs0I4VM8Aksabv4ZB/qYFyN3mLefA4Pt5LK99XZrwuYKmL6LOoWNZLMbKPUvBxuuo1NTW5GA== +"@tryghost/kg-unsplash-selector@0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@tryghost/kg-unsplash-selector/-/kg-unsplash-selector-0.2.3.tgz#3b969e62e8410eb0fade25ee2f4944d4935e125b" + integrity sha512-FS4SWZ9hbPA+DvaIbXjLC/EPBqcrdZLWbj9kB96izNYaqyE3awyQNCDmBPcaOnnfqZ8BjVUlxQspxk8vLRH14A== -"@tryghost/kg-utils@1.0.27": - version "1.0.27" - resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.27.tgz#77442c9cce7ee2d0e6fbfa669ef733f52dec7d82" - integrity sha512-pZ9qcuOKijTJx1qTU/Pju3ZJOXTQeGBl1qKmCBMfwoacVepN0qPzn/GierOqgvPzEw5xHexJPrQVn0hE0w9bdA== +"@tryghost/kg-utils@1.0.28": + version "1.0.28" + resolved "https://registry.yarnpkg.com/@tryghost/kg-utils/-/kg-utils-1.0.28.tgz#cfdaac6f5cbe6f4375f8771e5b4a014bfb53a689" + integrity sha512-OIl+V3j7u9/bWdnkZvnx/cDbRpmCIMI4f9uJbp0sC9dfAgGq83TFg1ZuHXK9QAB6nVCnY36eR2JVawlSYx04PA== dependencies: semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.14": - version "1.3.14" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.14.tgz#bb8cc0cf336821b21500ba24d12e4e555cad340c" - integrity sha512-V5udyPS3WVjX4adOeuW5jXUpYtW+vYKHILS0JMtiyf8oiokRpN8W2m8y0n7w7GfptEgzcSLs9X+Sx0ujTaNURA== +"@tryghost/koenig-lexical@1.3.15": + version "1.3.15" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.15.tgz#4d6b2296b810a8fc6cbc5c58db04fd23ff88a48b" + integrity sha512-ANXkZg/Zh+R4roH74veaprwWZJmMMiuxZgRCxzHgEj0PU/US6+aLXTeEMK8VgWiAaKpVjRNJDJsdI0RuRbwiHA== "@tryghost/limit-service@1.2.14": version "1.2.14" From c96b1a1140057978a7936332e2d379ee8a412a26 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 00:29:47 +0000 Subject: [PATCH 134/164] Update dependency i18next to v23.12.3 --- ghost/i18n/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/i18n/package.json b/ghost/i18n/package.json index e55ed4b862..db6cdcdd70 100644 --- a/ghost/i18n/package.json +++ b/ghost/i18n/package.json @@ -30,6 +30,6 @@ "mocha": "10.2.0" }, "dependencies": { - "i18next": "23.12.2" + "i18next": "23.12.3" } } diff --git a/yarn.lock b/yarn.lock index 5748e1baae..f3a5ec3644 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19682,10 +19682,10 @@ i18next-parser@8.13.0: vinyl-fs "^4.0.0" vue-template-compiler "^2.6.11" -i18next@23.12.2, i18next@^23.5.1: - version "23.12.2" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.12.2.tgz#c5b44bb95e4d4a5908a51577fa06c63dc2f650a4" - integrity sha512-XIeh5V+bi8SJSWGL3jqbTEBW5oD6rbP5L+E7dVQh1MNTxxYef0x15rhJVcRb7oiuq4jLtgy2SD8eFlf6P2cmqg== +i18next@23.12.3, i18next@^23.5.1: + version "23.12.3" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.12.3.tgz#e0b811ef218f6d3fdb0b91f1a1eed099a1f7c48b" + integrity sha512-DyigQmrR10V9U2N6pjhbfahW13GY7n8BQD9swN09JuRRropgsksWVi4vRLeex0Qf7zCPnBfIqQfhcBzdZBQBYw== dependencies: "@babel/runtime" "^7.23.2" From 3c94ad0cb1627c1f6bdf847e99d074ba0ebb09ac Mon Sep 17 00:00:00 2001 From: Stanislav Traykov <36573153+StanTraykov@users.noreply.github.com> Date: Tue, 13 Aug 2024 04:55:26 +0300 Subject: [PATCH 135/164] Fixed errors in Bulgarian portal translations (#20691) BG translation fixes in portal.json. --- ghost/i18n/locales/bg/portal.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ghost/i18n/locales/bg/portal.json b/ghost/i18n/locales/bg/portal.json index 5984b74417..74093559c4 100644 --- a/ghost/i18n/locales/bg/portal.json +++ b/ghost/i18n/locales/bg/portal.json @@ -10,10 +10,10 @@ "{{memberEmail}} will no longer receive emails when someone replies to your comments.": "{{memberEmail}} повече няма да получава имейли, когато някой отговаря на ваш коментар.", "{{memberEmail}} will no longer receive this newsletter.": "{{memberEmail}} повече няма да получава този бюлетин.", "{{trialDays}} days free": "{{trialDays}} дни безплатен достъп", - "A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.": "Връзка за влизане беше изпратена към пощенската Ви кутия. Ако писмото не пристигне до 3 минути, провете дали не е категоризирана като нежелано писмо.", + "A login link has been sent to your inbox. If it doesn't arrive in 3 minutes, be sure to check your spam folder.": "Изпратен Ви е имейл с препратка за влизане. Ако не пристигне до 3 минути, проверете дали не е категоризиран като нежелано писмо.", "Account": "Профил", "Account settings": "Настройки на профила Ви", - "After a free trial ends, you will be charged the regular price for the tier you've chosen. You can always cancel before then.": "След прикючване на безплатния достъп ще бъдете таксувани според обявените цени. Можете да се откажете преди изтичането на безплатния достъп.", + "After a free trial ends, you will be charged the regular price for the tier you've chosen. You can always cancel before then.": "След приключване на безплатния достъп ще бъдете таксувани според обявените цени. Можете да се откажете преди изтичането на безплатния достъп.", "Already a member?": "Вече сте абонат на сайта?", "An unexpected error occured. Please try again or contact support if the error persists.": "Възникна неочаквана грешка. Моля, опитайте отново или потърсете поддръжката ако това се повтаря.", "Back": "Обратно", @@ -83,7 +83,7 @@ "Need more help? Contact support": "Още имате нужда от помощ? Потърсете поддръжката", "Newsletters can be disabled on your account for two reasons: A previous email was marked as spam, or attempting to send an email resulted in a permanent failure (bounce).": "Информационните бюлетини могат да бъдат деактивирани в профила ви по две причини: Предишен имейл е бил маркиран като спам или опитът за изпращане на имейл е довел до траен неуспех (отказ).", "Not receiving emails?": "Не получавате поща?", - "Now check your email!": "Провете си пощенската кутия!", + "Now check your email!": "Проверете си пощенската кутия!", "Once resubscribed, if you still don't see emails in your inbox, check your spam folder. Some inbox providers keep a record of previous spam complaints and will continue to flag emails. If this happens, mark the latest newsletter as 'Not spam' to move it back to your primary inbox.": "След като се абонирате отново, ако все още не виждате имейли във входящата си поща, проверете папката за спам. Някои доставчици пазят история с предишни оплаквания за спам и ще продължат да маркират имейлите. Ако вашият случай е такъв, маркирайте последния бюлетин като 'Не е спам', за да го преместите обратно в основната си пощенска кутия.", "Permanent failure (bounce)": "Постоянен проблем (отскок)", "Plan": "План", @@ -125,7 +125,7 @@ "The email address we have for you is {{memberEmail}} — if that's not correct, you can update it in your .": "Имейлът, който имаме за вас, е {{memberEmail}} - ако не е верен, можете да го актуализирате в областта за .", "There was a problem submitting your feedback. Please try again a little later.": "Имаше проблем при изпращането на обратната връзка. Моля, опитайте отново малко по-късно.", "This site is invite-only, contact the owner for access.": "Сайтът е само с покани. Свържете се със собственика за да получите достъп.", - "To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!": "За да прикючите вашата регистрация беше изпратена връзка за влизане към пощенската Ви кутия. Ако писмото не пристигне до 3 минути, провете дали не е категоризирана като нежелано писмо.", + "To complete signup, click the confirmation link in your inbox. If it doesn't arrive within 3 minutes, check your spam folder!": "За да приключите регистрацията, последвайте препратката в съобщението, изпратено Ви по имейл. Ако не пристигне до 3 минути, проверете дали не е категоризирано като нежелано писмо.", "Try free for {{amount}} days, then {{originalPrice}}.": "Тествайте безплатно за {{amount}} дни, след това {{originalPrice}}.", "Unlock access to all newsletters by becoming a paid subscriber.": "Отключете достъпа до всички бюлетини, като станете платен абонат.", "Unsubscribe from all emails": "Прекрати изпращането на всякакви писма", From 49b2803dc53902df085c1433e61c0555247ada11 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:33:38 +0000 Subject: [PATCH 136/164] Update dependency terser to v5.31.6 --- ghost/minifier/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/minifier/package.json b/ghost/minifier/package.json index 431ca3cfc0..6cafd69752 100644 --- a/ghost/minifier/package.json +++ b/ghost/minifier/package.json @@ -28,7 +28,7 @@ "@tryghost/errors": "1.3.5", "@tryghost/tpl": "0.1.32", "csso": "5.0.5", - "terser": "5.31.5", + "terser": "5.31.6", "tiny-glob": "0.2.9" } } diff --git a/yarn.lock b/yarn.lock index f3a5ec3644..a5b30d18b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30120,10 +30120,10 @@ terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@5.31.5, terser@^5.26.0, terser@^5.7.0: - version "5.31.5" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.5.tgz#e48b7c65f32d2808e7dad803e4586a0bc3829b87" - integrity sha512-YPmas0L0rE1UyLL/llTWA0SiDOqIcAQYLeUj7cJYzXHlRTAnMSg9pPe4VJ5PlKvTrPQsdVFuiRiwyeNlYgwh2Q== +terser@5.31.6, terser@^5.26.0, terser@^5.7.0: + version "5.31.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.6.tgz#c63858a0f0703988d0266a82fcbf2d7ba76422b1" + integrity sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" From 808ed29c27aad8349950beec5face642d8510661 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:34:13 +0000 Subject: [PATCH 137/164] Update tiptap monorepo to v2.6.0 --- apps/comments-ui/package.json | 20 +++---- yarn.lock | 100 +++++++++++++++++----------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 4be6217a89..26ce1c8c1d 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -44,16 +44,16 @@ }, "dependencies": { "@headlessui/react": "1.7.19", - "@tiptap/core": "2.5.9", - "@tiptap/extension-blockquote": "2.5.9", - "@tiptap/extension-document": "2.5.9", - "@tiptap/extension-hard-break": "2.5.9", - "@tiptap/extension-link": "2.5.9", - "@tiptap/extension-paragraph": "2.5.9", - "@tiptap/extension-placeholder": "2.5.9", - "@tiptap/extension-text": "2.5.9", - "@tiptap/pm": "2.5.9", - "@tiptap/react": "2.5.9", + "@tiptap/core": "2.6.0", + "@tiptap/extension-blockquote": "2.6.0", + "@tiptap/extension-document": "2.6.0", + "@tiptap/extension-hard-break": "2.6.0", + "@tiptap/extension-link": "2.6.0", + "@tiptap/extension-paragraph": "2.6.0", + "@tiptap/extension-placeholder": "2.6.0", + "@tiptap/extension-text": "2.6.0", + "@tiptap/pm": "2.6.0", + "@tiptap/react": "2.6.0", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index a5b30d18b4..c2447861ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7454,66 +7454,66 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== -"@tiptap/core@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.9.tgz#1deb0b7c748e24ec32613263e0af8d55a3b3c2ca" - integrity sha512-PPUR+0tbr+wX2G8RG4FEps4qhbnAPEeXK1FUtirLXSRh8vm+TDgafu3sms7wBc4fAyw9zTO/KNNZ90GBe04guA== +"@tiptap/core@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.6.0.tgz#c222f53043abfa69d8d41a081bb3dfc1181fc841" + integrity sha512-MG2OXwpMVaiacGuipqZ3VXi36gMvtV3i3ncrtXufsMducWvniENMi8fEonj/Q7nkeVi59OcoW8vD6kqdKkh2Gw== -"@tiptap/extension-blockquote@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.9.tgz#d873a8496fcf572c69aaac2a7a341e035fdbae22" - integrity sha512-LhGyigmd/v1OjYPeoVK8UvFHbH6ffh175ZuNvseZY4PsBd7kZhrSUiuMG8xYdNX8FxamsxAzr2YpsYnOzu3W7A== +"@tiptap/extension-blockquote@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.6.0.tgz#3f0a80ebd82e1f5d174c24f9794e22ac3184c072" + integrity sha512-/rYTYnn7fk/LBixLv7R1fEee3JDrDo3VHEJ+gBGuyLXcNaYtzWuRfqdeIraNSOuhzHffWQ5dg1oxpVFrVrd6AQ== -"@tiptap/extension-bubble-menu@^2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.9.tgz#d600bbcaa1d98a99f32b3b8b8c3d35752161200c" - integrity sha512-NddZ8Qn5dgPPa1W4yk0jdhF4tDBh0FwzBpbnDu2Xz/0TUHrA36ugB2CvR5xS1we4zUKckgpVqOqgdelrmqqFVg== +"@tiptap/extension-bubble-menu@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.0.tgz#91fb19abd94a4dcbd8816c89285d8b3a0dd6c098" + integrity sha512-d8LiaWVRCa3jc26OOCGTGLtJhbTlylokAJvKK1U0IGoX/jjzwWIypg6FZawYB2Fj0JANdTD100e7xDfLTOLLKg== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.9.tgz#13a22b2d3bdc1463844872b1f1c926633df431a8" - integrity sha512-VdNZYDyCzC3W430UdeRXR9IZzPeODSbi5Xz/JEdV93THVp8AC9CrZR7/qjqdBTgbTB54VP8Yr6bKfCoIAF0BeQ== +"@tiptap/extension-document@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.6.0.tgz#5c21fcb40dcbabdfedc54bd17f964ec2a28164c8" + integrity sha512-aKyKCaErlZSOav2mttktvbyYhpgNfQcEvrupkDuRYRsdy1CH5h66Of1Vc2F9z/NgZvkyJLWWv2T7fwyAYnMnYw== -"@tiptap/extension-floating-menu@^2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.9.tgz#b970905f3c1af49a916dcbd477a4302086187974" - integrity sha512-MWJIQQT6e5MgqHny8neeH2Dx926nVPF7sv4p84nX4E0dnkRbEYUP8mCsWYhSUvxxIif6e+yY+4654f2Q9qTx1w== +"@tiptap/extension-floating-menu@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.0.tgz#6dd656ce00e80d5312dacc7fe472d3233ccddad7" + integrity sha512-eFmDaJJgGC+PBdRwfWSTzNfJR5Z0udPjHUf4pJosz2PcIg6wzeAxJyELzprk8qWNKbrxDm0CeJ0nz4uXxECdVw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-hard-break@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.9.tgz#4f38f06dbeb5fb3e58ff7fc0c48b9db9c4ee4ecd" - integrity sha512-8hQ63SgZRG4BqHOeSfeaowG2eMr2beced018pOGbpHbE3XSYoISkMVuFz4Z8UEVR3W9dTbKo4wxNufSTducocQ== +"@tiptap/extension-hard-break@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.6.0.tgz#6a475ca5422a53c2739c0b54eaf488098972e6cc" + integrity sha512-RX5Xmj2svS7T3QkDs0X8aTekkcDxpnJ3hqHarzR1gK0GdVsrpowVKYhuEAc1zAAUirVa/IBimKXAIkXjDvuvNA== -"@tiptap/extension-link@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.9.tgz#6cb323d36b82700963ad2b9d189a7d07c81c7d6e" - integrity sha512-7v9yRsX7NuiY8DPslIsPIlFqcD8aGBMLqfEGXltJDvuG6kykdr+khEZeWcJ8ihHIL4yWR3/MAgeT2W72Z/nxiQ== +"@tiptap/extension-link@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.6.0.tgz#2a3f65742fcb2776973541e9f2cea3169ce4a204" + integrity sha512-16ckFDnwqt0tYtQRgnVhooAUTLSk7liaqcGgc3NJ35gqWZho8hSnaTOl+72dlyJqUjqwt8D24NHFJsSqB9PSxA== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-paragraph@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.9.tgz#05210b6e7a9940b1acc09fdd4ec769fc6406da2b" - integrity sha512-HDXGiHTJ/V85dbDMjcFj4XfqyTQZqry6V21ucMzgBZYX60X3gIn7VpQTQnnRjvULSgtfOASSJP6BELc5TyiK0w== +"@tiptap/extension-paragraph@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.6.0.tgz#721921477e3785c0a0abf132ea109e67bcea9fae" + integrity sha512-ztmgfCSaCGFCR4VudA91WJDEQrpXH2FirQrJSaoOOwk3OfrNSSvcbqAhTmxJHl8e7aFwuR5eH8wbIxKw9In3Kg== -"@tiptap/extension-placeholder@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.5.9.tgz#c9bebc7e2bba2b0321e360d8a7a358152ffc9137" - integrity sha512-ytKmlSiebtCBXoMPE2cup48DR0rQiekXhLKLkNyt7m8tSXkaRO4eDaFqCqPEXLeQXWdhwWEoPM6Cejaaa3ztkA== +"@tiptap/extension-placeholder@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.6.0.tgz#9110ca221dae0b68706c7feaaf6a8071639d0e64" + integrity sha512-PzAsG3y2L6ZXxIfoD5Qf1HOvuwQPdOw8C+I7tcPwvOgYow8+slJ3kZbURoXw3dT/BtiPXmZ6DequX2wHgl+bjg== -"@tiptap/extension-text@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.9.tgz#a5bef0b9c5324511dbc2804a3a5ac8b9b5d5dc4c" - integrity sha512-W0pfiQUPsMkwaV5Y/wKW4cFsyXAIkyOFt7uN5u6LrZ/iW9KZ/IsDODPJDikWp0aeQnXzT9NNQULTpCjbHzzS6g== +"@tiptap/extension-text@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.6.0.tgz#395cd44456059c11fb5f3e2215093a5c32ba59a3" + integrity sha512-Bg/FXGzj6WGvqgCVvFKzTWOpgg8Nl4X2a/bUT7nEmBafpUNAiN6enomMWnniylGTFEqZrywr5KRcvbgl8ubeDQ== -"@tiptap/pm@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.9.tgz#f97889210374993a1ce78e9ecb23461d0e4644bf" - integrity sha512-YSUaEQVtvZnGzGjif2Tl2o9utE+6tR2Djhz0EqFUcAUEVhOMk7UYUO+r/aPfcCRraIoKKuDQzyCpjKmJicjCUA== +"@tiptap/pm@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.6.0.tgz#b2ef8aba2f5a04ac3f5051e3d891ffe26f37967b" + integrity sha512-Oj912Jfowuq/K+oEkqx+pADjuVwBCXroC2hdrRMo4w18bb2gI2Nhse6St1KQ9kWWGF36O81RpDbpLe7L1YQhzQ== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -7534,13 +7534,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.9" -"@tiptap/react@2.5.9": - version "2.5.9" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.5.9.tgz#43f94a2bf1d4c55e82d97ef9d1c97ba20206f7f0" - integrity sha512-NZYAslIb79oxIOFHx9T9ey5oX0aJ1uRbtT2vvrvvyRaO6fKWgAwMYN92bOu5/f2oUVGUp6l7wkYZGdjz/XP5bA== +"@tiptap/react@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.6.0.tgz#98285416584713261fb66d8101962e5bda9350c3" + integrity sha512-oTCkR+X3qJTi3DsbJzymjhOhlrhrP/yrocXL7dc/N7TzMmdLdLcYei8sOJeds9hCPAixLZAdrG4RUfKAaz7/Qw== dependencies: - "@tiptap/extension-bubble-menu" "^2.5.9" - "@tiptap/extension-floating-menu" "^2.5.9" + "@tiptap/extension-bubble-menu" "^2.6.0" + "@tiptap/extension-floating-menu" "^2.6.0" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From 4d133defa03d55e9ae5e41cc3770d22d123783c1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:03:39 +0000 Subject: [PATCH 138/164] Update tiptap monorepo to v2.6.1 --- apps/comments-ui/package.json | 6 ++--- yarn.lock | 44 +++++++++++++++++------------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/comments-ui/package.json b/apps/comments-ui/package.json index 26ce1c8c1d..cbaf844999 100644 --- a/apps/comments-ui/package.json +++ b/apps/comments-ui/package.json @@ -46,14 +46,14 @@ "@headlessui/react": "1.7.19", "@tiptap/core": "2.6.0", "@tiptap/extension-blockquote": "2.6.0", - "@tiptap/extension-document": "2.6.0", + "@tiptap/extension-document": "2.6.1", "@tiptap/extension-hard-break": "2.6.0", "@tiptap/extension-link": "2.6.0", "@tiptap/extension-paragraph": "2.6.0", - "@tiptap/extension-placeholder": "2.6.0", + "@tiptap/extension-placeholder": "2.6.1", "@tiptap/extension-text": "2.6.0", "@tiptap/pm": "2.6.0", - "@tiptap/react": "2.6.0", + "@tiptap/react": "2.6.1", "react": "17.0.2", "react-dom": "17.0.2", "react-string-replace": "1.1.1" diff --git a/yarn.lock b/yarn.lock index c2447861ec..11a4731df9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7464,22 +7464,22 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.6.0.tgz#3f0a80ebd82e1f5d174c24f9794e22ac3184c072" integrity sha512-/rYTYnn7fk/LBixLv7R1fEee3JDrDo3VHEJ+gBGuyLXcNaYtzWuRfqdeIraNSOuhzHffWQ5dg1oxpVFrVrd6AQ== -"@tiptap/extension-bubble-menu@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.0.tgz#91fb19abd94a4dcbd8816c89285d8b3a0dd6c098" - integrity sha512-d8LiaWVRCa3jc26OOCGTGLtJhbTlylokAJvKK1U0IGoX/jjzwWIypg6FZawYB2Fj0JANdTD100e7xDfLTOLLKg== +"@tiptap/extension-bubble-menu@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.1.tgz#8a6777000cdf74d5319a66dc5c283bdac94e00b4" + integrity sha512-lIXilRlvQ2P4rnJ3zp5VM5JB3UG4w/SlSMkXHmOAf8S67aGqH9DHqDI3p2Twp+5ylsg/f0Q3evY/rftD51mNSw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-document@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.6.0.tgz#5c21fcb40dcbabdfedc54bd17f964ec2a28164c8" - integrity sha512-aKyKCaErlZSOav2mttktvbyYhpgNfQcEvrupkDuRYRsdy1CH5h66Of1Vc2F9z/NgZvkyJLWWv2T7fwyAYnMnYw== +"@tiptap/extension-document@2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.6.1.tgz#ff5797960248e32c24e1070b9bdb86349a201a37" + integrity sha512-BKuyzzmxzs9Rm/LpwriluMjBQvZhM0kcvtWsIGchzmBdoyPQwOtZxItnqSJeGzgJzqdp5otVeLb6PAP/PlPs3g== -"@tiptap/extension-floating-menu@^2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.0.tgz#6dd656ce00e80d5312dacc7fe472d3233ccddad7" - integrity sha512-eFmDaJJgGC+PBdRwfWSTzNfJR5Z0udPjHUf4pJosz2PcIg6wzeAxJyELzprk8qWNKbrxDm0CeJ0nz4uXxECdVw== +"@tiptap/extension-floating-menu@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.1.tgz#af5178fe1c180328ec10ff93434f5edbea62d073" + integrity sha512-HVTK9tpFM5umb8J1YbhwJr/Cf7UPSLqZO3dA+MulVxBt9fD4Z+zNbLkhm57rbRZ68DIA0RL57FgFvdxlFCZA4A== dependencies: tippy.js "^6.3.7" @@ -7500,10 +7500,10 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.6.0.tgz#721921477e3785c0a0abf132ea109e67bcea9fae" integrity sha512-ztmgfCSaCGFCR4VudA91WJDEQrpXH2FirQrJSaoOOwk3OfrNSSvcbqAhTmxJHl8e7aFwuR5eH8wbIxKw9In3Kg== -"@tiptap/extension-placeholder@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.6.0.tgz#9110ca221dae0b68706c7feaaf6a8071639d0e64" - integrity sha512-PzAsG3y2L6ZXxIfoD5Qf1HOvuwQPdOw8C+I7tcPwvOgYow8+slJ3kZbURoXw3dT/BtiPXmZ6DequX2wHgl+bjg== +"@tiptap/extension-placeholder@2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.6.1.tgz#01f6c83fe0560b5d5ab1e64dc238904e87317d46" + integrity sha512-YwgpKaPoZgYZ/56Yig6PylGHJwfcY+IhraifOx4sYphi/ZamUAgSGhsOYz4l9T/0zzIRLvLYlMsHNgtuN6Iupg== "@tiptap/extension-text@2.6.0": version "2.6.0" @@ -7534,13 +7534,13 @@ prosemirror-transform "^1.9.0" prosemirror-view "^1.33.9" -"@tiptap/react@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.6.0.tgz#98285416584713261fb66d8101962e5bda9350c3" - integrity sha512-oTCkR+X3qJTi3DsbJzymjhOhlrhrP/yrocXL7dc/N7TzMmdLdLcYei8sOJeds9hCPAixLZAdrG4RUfKAaz7/Qw== +"@tiptap/react@2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.6.1.tgz#77d47730fa0054aeba78488b96e8a486eea2fed2" + integrity sha512-Mq0zhPiR3YZ3IlCtGhNsrXkyK12df71qgvlbWi3KMtcQFU+JWV18qpOyDMK4PxPpPgicKTYlAn57k7HBqtTZ6w== dependencies: - "@tiptap/extension-bubble-menu" "^2.6.0" - "@tiptap/extension-floating-menu" "^2.6.0" + "@tiptap/extension-bubble-menu" "^2.6.1" + "@tiptap/extension-floating-menu" "^2.6.1" "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.2.2" From 5ba74b0243ad0ef63537b98c5b543fae587b0a97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:36:35 +0200 Subject: [PATCH 139/164] Updated Koenig packages (#20744) ref https://linear.app/tryghost/issue/ENG-1255 changelog: - https://github.com/TryGhost/Koenig/pull/1348 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 6 ++--- yarn.lock | 50 ++++++++++++++++++++-------------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 6d68a7e749..2a8370ff3b 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.3", "@tryghost/kg-converters": "1.0.6", - "@tryghost/koenig-lexical": "1.3.15", + "@tryghost/koenig-lexical": "1.3.16", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/package.json b/ghost/core/package.json index 5187d8053f..3347272a76 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -111,9 +111,9 @@ "@tryghost/kg-converters": "1.0.6", "@tryghost/kg-default-atoms": "5.0.3", "@tryghost/kg-default-cards": "10.0.8", - "@tryghost/kg-default-nodes": "1.1.11", - "@tryghost/kg-html-to-lexical": "1.1.12", - "@tryghost/kg-lexical-html-renderer": "1.1.14", + "@tryghost/kg-default-nodes": "1.1.12", + "@tryghost/kg-html-to-lexical": "1.1.13", + "@tryghost/kg-lexical-html-renderer": "1.1.15", "@tryghost/kg-mobiledoc-html-renderer": "7.0.6", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/yarn.lock b/yarn.lock index 11a4731df9..b109f22ce2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7847,10 +7847,10 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-nodes@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.11.tgz#8f4238868ba2a8c880a2a78b6416c1700c90b015" - integrity sha512-Xh462ufG3wKEVtk/5QLRFanG87/EbTUjeS6glBiMJ63s5aHgrpwUPrKv2H52fWuJt9meg0hmOl6HRrKnYQs88g== +"@tryghost/kg-default-nodes@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.12.tgz#40be11e6a898f14e4249f05142e14e72ab956068" + integrity sha512-zBeCgpcFZibehWzo10HjzKFS2RSW8AvSakShpX3h4skSDG9Tkki60b5ATczxiLFswHerEvnBacHEgZO1xzzbNQ== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" @@ -7864,21 +7864,21 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-transforms@1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.12.tgz#49b84fb6c4df866f1bfc7aadf19015b14136f4de" - integrity sha512-vr12KCgo6amxoVYt65ra88v6c9lcK2Umyv01eGdoURWr1ipUhlbxOj8zEVaMHJd3Wsnp4TiCyvIag3Bb+WwqKQ== +"@tryghost/kg-default-transforms@1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.13.tgz#6d38f687f2f363d4b85a615216e1b83aff417954" + integrity sha512-AvTpBNpUzpTI4EYtJ+xZapKxfz7mu/ZbdTgxCnuMkWRe0Cw3uOl838gaKHTMNuP76k7HCxNVW61gGm/FVofo5g== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.11" + "@tryghost/kg-default-nodes" "1.1.12" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.12.tgz#a62459127a20b852afbf72aef7a7a8c8bfe63ddb" - integrity sha512-TtHwzvkA9oMNLBI44C3YiD95oNtNItfH0t6UXkQeq7gkcqoMAv5/fqLaQtSYPs1dycgz0EnZD82nEP8rivP43g== +"@tryghost/kg-html-to-lexical@1.1.13": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.13.tgz#83f9a24b30ba0a40f07ab1c8c66eb9c99904f049" + integrity sha512-lVb0V5ckZ1SPjMzTfxzZ61ABN9koCNLQd1nCYHyNLunLH5IVVY2yRFNLt2CHtbfUMcjvGv4Egfi/g9oRwJkBiQ== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7886,15 +7886,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.11" - "@tryghost/kg-default-transforms" "1.1.12" + "@tryghost/kg-default-nodes" "1.1.12" + "@tryghost/kg-default-transforms" "1.1.13" jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.14.tgz#84d7c8c2e0369206780233a58e41a94455077489" - integrity sha512-ud1/Gt13jF8wO7K0MYNCeJk8QCtPxtLBlBS2bmwkRWrcRBkPEHZre5UJ0esk7Ryiq/+rHxj6IS6xClEyq5mP9g== +"@tryghost/kg-lexical-html-renderer@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.15.tgz#9512fc14a3ed83baf7ce5577bbf3b33cde78292b" + integrity sha512-hCx7iizJvmbwUWtrQKOEOmH+lPmxzcoOf+gpxOL3actEcfFYHO9lYWTVg9x8s6Q8eyzLQ3lIh9mpdktzmz+x9g== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -7902,8 +7902,8 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.11" - "@tryghost/kg-default-transforms" "1.1.12" + "@tryghost/kg-default-nodes" "1.1.12" + "@tryghost/kg-default-transforms" "1.1.13" jsdom "^24.1.0" lexical "0.13.1" @@ -7950,10 +7950,10 @@ dependencies: semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.15": - version "1.3.15" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.15.tgz#4d6b2296b810a8fc6cbc5c58db04fd23ff88a48b" - integrity sha512-ANXkZg/Zh+R4roH74veaprwWZJmMMiuxZgRCxzHgEj0PU/US6+aLXTeEMK8VgWiAaKpVjRNJDJsdI0RuRbwiHA== +"@tryghost/koenig-lexical@1.3.16": + version "1.3.16" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.16.tgz#b1f317e969dcfa6b01f3a7f6dce55c872bb6f833" + integrity sha512-jyLZL5QiMF+GjUIAHD/h69cb+PZrvC+9fA05lFTyBPlgVRc1L2qTu99Xo6YH+ZrE+mIBfmNfKRfmIRJRD5AR5w== "@tryghost/limit-service@1.2.14": version "1.2.14" From e9208888326007a37f91c931a83e16fa09fd4a96 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Wed, 14 Aug 2024 14:32:18 +0700 Subject: [PATCH 140/164] Wired up stripeEnabled to Koenig config (#20750) ref PLG-109 - Added a boolean config property `stripeEnabled` to the configuration. - This property is now passed down to the Koenig Editor to manage features based on Stripe connectivity. --- ghost/admin/app/components/koenig-lexical-editor.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ghost/admin/app/components/koenig-lexical-editor.js b/ghost/admin/app/components/koenig-lexical-editor.js index b63e16157f..704aac7e6d 100644 --- a/ghost/admin/app/components/koenig-lexical-editor.js +++ b/ghost/admin/app/components/koenig-lexical-editor.js @@ -441,6 +441,16 @@ export default class KoenigLexicalEditor extends Component { } }; + const checkStripeEnabled = () => { + const hasDirectKeys = !!(this.settings.stripeSecretKey && this.settings.stripePublishableKey); + const hasConnectKeys = !!(this.settings.stripeConnectSecretKey && this.settings.stripeConnectPublishableKey); + + if (this.config.stripeDirect) { + return hasDirectKeys; + } + return hasDirectKeys || hasConnectKeys; + }; + const defaultCardConfig = { unsplash: this.settings.unsplash ? unsplashConfig.defaultHeaders : null, tenor: this.config.tenor?.googleApiKey ? this.config.tenor : null, @@ -461,7 +471,8 @@ export default class KoenigLexicalEditor extends Component { searchLinks, siteTitle: this.settings.title, siteDescription: this.settings.description, - siteUrl: this.config.getSiteUrl('/') + siteUrl: this.config.getSiteUrl('/'), + stripeEnabled: checkStripeEnabled() // returns a boolean }; const cardConfig = Object.assign({}, defaultCardConfig, props.cardConfig, {pinturaConfig: this.pinturaConfig}); From 814e03be76828ed628068d39425f3d277c188749 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Wed, 14 Aug 2024 09:49:17 +0200 Subject: [PATCH 141/164] Updated recommendation received notification email design (#20748) REF PLG-162 - Added publication icon and improved overall layout --- .../recommendation-emails.test.js.snap | 140 +++++++++++------- .../lib/email-templates/donation.hbs | 11 ++ .../recommendation-received.hbs | 67 +++++---- 3 files changed, 134 insertions(+), 84 deletions(-) diff --git a/ghost/core/test/e2e-server/services/__snapshots__/recommendation-emails.test.js.snap b/ghost/core/test/e2e-server/services/__snapshots__/recommendation-emails.test.js.snap index 42a394d010..4315ec4374 100644 --- a/ghost/core/test/e2e-server/services/__snapshots__/recommendation-emails.test.js.snap +++ b/ghost/core/test/e2e-server/services/__snapshots__/recommendation-emails.test.js.snap @@ -134,17 +134,15 @@ exports[`Incoming Recommendation Emails Sends a different email if we receive a
    - +
    + + + + +
    +

    Name:

    + +

    Tier:

    + + {{#if subscriptionData.cancelNow}} +

    Expired on:

    + {{else}} +

    Expires on:

    + {{/if}} + + {{#if subscriptionData.cancellationReason}} +

    "{{subscriptionData.cancellationReason}}"

    + {{/if}} +
    + - +
    View member + + + + + + +
    View member
    +
    @@ -102,21 +80,29 @@
    -
    -

    You can also copy & paste this URL into your browser:

    - + + + + + + + +
    +

    Or copy and paste this URL into your browser:

    + +
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +
    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    - - @@ -382,17 +390,16 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
    - -

    Great news!

    -

    A site you’re recommending is now recommending you back:

    +

    A site is now recommending you back

    -
    - +
    +
    -
    +
    -
    Other Ghost Site
    +
    Other Ghost Site
    @@ -152,15 +150,15 @@ exports[`Incoming Recommendation Emails Sends a different email if we receive a
    -

    This message was sent from 127.0.0.1 to jbloggs@example.com

    +
    +

    This message was sent from 127.0.0.1 to jbloggs@example.com

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    - - @@ -608,17 +625,16 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
    - -

    Good news!

    -

    Other Ghost Site is recommending you to their audience. Now, every time someone new signs up on their site, they’ll see an option to check out Ghost as well.

    +

    A site is now recommending you to their audience

    +

    Every time someone new signs up on their site, they’ll see an option to check out Ghost as well.

    -
    - +
    +
    -
    +
    -
    Other Ghost Site
    +
    Other Ghost Site
    @@ -400,15 +407,15 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
    -

    This message was sent from 127.0.0.1 to jbloggs@example.com

    +
    +

    This message was sent from 127.0.0.1 to jbloggs@example.com

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    - - diff --git a/ghost/staff-service/lib/email-templates/donation.hbs b/ghost/staff-service/lib/email-templates/donation.hbs index 420ac9c8d7..73fcf6b827 100644 --- a/ghost/staff-service/lib/email-templates/donation.hbs +++ b/ghost/staff-service/lib/email-templates/donation.hbs @@ -50,6 +50,17 @@
    - -

    Good news!

    -

    Other Ghost Site is recommending you to their audience. Now, every time someone new signs up on their site, they’ll see an option to check out Ghost as well.

    +

    A site is now recommending you to their audience

    +

    Every time someone new signs up on their site, they’ll see an option to check out Ghost as well.

    -
    - +
    +
    -
    +
    -
    Other Ghost Site
    +
    Other Ghost Site
    @@ -626,15 +642,15 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
    -

    This message was sent from 127.0.0.1 to swellingsworth@example.com

    +
    +

    This message was sent from 127.0.0.1 to swellingsworth@example.com

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    + + + + + + + +
    +

    Or copy and paste this URL into your browser:

    + +
    + {{#if siteIconUrl}} + + + + {{/if}} - - From f3e1ec7ddf0d49ba3084aa72c958054ce6820420 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 06:06:18 +0000 Subject: [PATCH 142/164] Update dependency lint-staged to v15.2.9 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4c3dcd389a..b50208c85b 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "eslint-plugin-ghost": "3.4.0", "eslint-plugin-react": "7.33.0", "husky": "8.0.3", - "lint-staged": "15.2.8", + "lint-staged": "15.2.9", "nx": "16.8.1", "rimraf": "5.0.10", "ts-node": "10.9.2", diff --git a/yarn.lock b/yarn.lock index b109f22ce2..0a5f4012ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22118,10 +22118,10 @@ linkifyjs@^4.1.0: resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.1.1.tgz#73d427e3bbaaf4ca8e71c589ad4ffda11a9a5fde" integrity sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA== -lint-staged@15.2.8: - version "15.2.8" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.8.tgz#5e19eb7b4dbb922f56fafb4635b44ee3c92f7322" - integrity sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ== +lint-staged@15.2.9: + version "15.2.9" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.9.tgz#bf70d40b6b192df6ad756fb89822211615e0f4da" + integrity sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ== dependencies: chalk "~5.3.0" commander "~12.1.0" From b2d5304c3c6ba3975b45d382e27facaca61dd1e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:47:02 +0000 Subject: [PATCH 143/164] Update Koenig packages no issue - added passthrough of `feature.contentVisibility` when rendering Lexical to HTML to allow for labs-feature specific rendering of HTML cards - updated golden post email render test to account for labs flags being on/off --- ghost/admin/package.json | 2 +- ghost/core/core/server/lib/lexical.js | 6 +- ghost/core/package.json | 8 +- .../__snapshots__/cards.test.js.snap | 2384 +++++++++++++++++ .../services/email-service/cards.test.js | 24 +- .../utils/serializers/input/posts.test.js | 6 +- ghost/email-service/package.json | 2 +- ghost/importer-revue/package.json | 2 +- yarn.lock | 58 +- 9 files changed, 2451 insertions(+), 41 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 2a8370ff3b..ac32b8abed 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.3", "@tryghost/kg-converters": "1.0.6", - "@tryghost/koenig-lexical": "1.3.16", + "@tryghost/koenig-lexical": "1.3.18", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/core/server/lib/lexical.js b/ghost/core/core/server/lib/lexical.js index fa08d2bf7c..7c6397b4fb 100644 --- a/ghost/core/core/server/lib/lexical.js +++ b/ghost/core/core/server/lib/lexical.js @@ -2,6 +2,7 @@ const path = require('path'); const errors = require('@tryghost/errors'); const urlUtils = require('../../shared/url-utils'); const config = require('../../shared/config'); +const labs = require('../../shared/labs'); const storage = require('../adapters/storage'); let nodes; @@ -99,7 +100,10 @@ module.exports = { && imageTransform.shouldResizeFileExtension(ext) && typeof storage.getStorage('images').saveRaw === 'function'; }, - getCollectionPosts + getCollectionPosts, + feature: { + contentVisibility: labs.isSet('contentVisibility') + } }, userOptions); return await this.lexicalHtmlRenderer.render(lexical, options); diff --git a/ghost/core/package.json b/ghost/core/package.json index 3347272a76..a0c347ff3e 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -110,10 +110,10 @@ "@tryghost/kg-card-factory": "5.0.5", "@tryghost/kg-converters": "1.0.6", "@tryghost/kg-default-atoms": "5.0.3", - "@tryghost/kg-default-cards": "10.0.8", - "@tryghost/kg-default-nodes": "1.1.12", - "@tryghost/kg-html-to-lexical": "1.1.13", - "@tryghost/kg-lexical-html-renderer": "1.1.15", + "@tryghost/kg-default-cards": "10.0.9", + "@tryghost/kg-default-nodes": "1.1.14", + "@tryghost/kg-html-to-lexical": "1.1.15", + "@tryghost/kg-lexical-html-renderer": "1.1.17", "@tryghost/kg-mobiledoc-html-renderer": "7.0.6", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap index a30d6ed8ee..403143a56f 100644 --- a/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap +++ b/ghost/core/test/integration/services/email-service/__snapshots__/cards.test.js.snap @@ -1284,6 +1284,2390 @@ This is a paragraph + + + + + +Ghost © 2024 – Unsubscribe [http://127.0.0.1:2369/unsubscribe/?uuid=member-uuid&newsletter=requested-newsletter-uuid] + + + +https://ghost.org/?via=pbg-newsletter&ref=127.0.0.1 + + + + + + + + + + + +  + + + + +", +} +`; + +exports[`Can send cards via email renders the golden post correctly (labs flag: contentVisibility) 1 1`] = ` +Object { + "html": " + + + + + + A random test post + + + + This is just a simple paragraph, no frills. + +This is block quote + +This is a...different block quote + + +This is a heading! + + +Here's a smaller heading. + + +A heading + + +and a paragraph (in markdown!) + + + + +A paragraph inside an HTML card. + + +And another one, with some bold text. + + + +Ghost: Independent technology for modern publishingBeautiful, modern publishing with newsletters and premium subscriptions built-in. Used by Sky, 404Media, Lever News, Tangle, The Browser, and thousands more.Ghost - The Profes +
    {{siteTitle}}
    - {{#if recommendation.recommendingBack}} -

    Great news!

    -

    A site you’re recommending is now recommending you back:

    +

    A site is now recommending you back

    {{else}} -

    Good news!

    -

    {{recommendation.title}} is recommending you to their audience. Now, every time someone new signs up on their site, they’ll see an option to check out {{siteTitle}} as well.

    +

    A site is now recommending you to their audience

    +

    Every time someone new signs up on their site, they’ll see an option to check out {{siteTitle}} as well.

    {{/if}} -
    - +
    +
    -
    +
    {{#if recommendation.favicon}}{{/if}} -
    {{recommendation.title}}
    +
    {{recommendation.title}}
    {{#if recommendation.excerpt}} -
    {{recommendation.excerpt}}
    +
    {{recommendation.excerpt}}
    {{/if}}
    {{#if recommendation.featuredImage}} -
    +
    {{/if}} @@ -54,9 +57,9 @@
    -

    This message was sent from {{siteDomain}} to {{toEmail}}

    +
    +

    This message was sent from {{siteDomain}} to {{toEmail}}

    -

    Don’t want to receive these emails? Manage your preferences here.

    +
    +

    Don’t want to receive these emails? Manage your preferences here.

    + + + + + + + + + +
      +
    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + + + + +
    +
    + A random test post +
    + + + + + + + + +
    + By Joe Bloggs • date + + View in browser +
    + View in browser +
    +
    + +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    and a paragraph (in markdown!)

    +
    + +

    A paragraph inside an HTML card.

    +

    And another one, with some bold text.

    + +

    A gallery.

    Hey Vinz,

    💡
    I had an idea...
    +

    Spoiler alert!

    +

    Just kidding

    +
    + + + +
    + + + + + +
    + + + + + + + + + + + + + + +
    + Sample +
    + + + + + +
    + + + 1:45 • Click to play audio +
    +
    +
    +
    + + + + + + + + +
    + + +
    +
     
    +
    + + + + +

    A lovely video of a woman on the beach doing nothing.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +

    Make a blog!

    +
    + +
    +

    with Ghost

    +
    + +
    + +
    +

    Good news everyone!

    +

    This header renders properly in all email clients!

    + +
    +

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    + + + +
    + + + + + +
    + +
    + test +
    + + +
    + A tiny text file. +
    + +
    + test.txt • 16 Bytes +
    +
    + + + +
    +
    + + +
    +
    + + + + + + + + +
    Ghost © 2024 – Unsubscribe
    \\"Powered
    +
    + +
    +
     
    + + +", + "plaintext": " + + + + + + + +  + + + + + + + + + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + + + + + +Ghost [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + +Daily newsletter [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + +A random test post [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + +By Joe Bloggs • date + + +View in browser [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + +View in browser [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + + +This is just a simple paragraph, no frills. + +> This is block quote + +> This is a...different block quote + + +This is a heading! + + +Here's a smaller heading. + +A lovely cow + + +A heading + + +and a paragraph (in markdown!) + + + + +A paragraph inside an HTML card. + + +And another one, with some bold text. + + + +---------------------------------------- + +A gallery. + + + + + + +Ghost: Independent technology for modern publishing +Beautiful, modern publishing with newsletters and premium subscriptions built-in. Used by Sky, 404Media, Lever News, Tangle, The Browser, and thousands more. + + +Ghost - The Professional Publishing Platform + + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + +My favorite website. + + + + +Hey Vinz, + + + +Click me, I'm a button! [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + +💡I had an idea... + + +Spoiler alert! + + +Just kidding + + + + + + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + +Sample [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + +1:45 • Click to play audio [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + + + + + + + + + + + + + + + +  + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + +A lovely video of a woman on the beach doing nothing. + + + + + + + + + + + + + + +Make a blog! + + + + + + + + + + + + + +with Ghost + + + + + + + + +Click here [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + +Good news everyone! + + +This header renders properly in all email clients! + + + + + + + + + + + + + + +  + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + +Some text. + +> A blockquote + +Some more text. + +console.log('Hello world!'); + +A tiny little script. + + + + + + + + + + + + + +test [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + +A tiny text file. [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + +test.txt • 16 Bytes [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + + + + + + + + + + + + + + + + + + + + + +Ghost © 2024 – Unsubscribe [http://127.0.0.1:2369/unsubscribe/?uuid=member-uuid&newsletter=requested-newsletter-uuid] + + + +https://ghost.org/?via=pbg-newsletter&ref=127.0.0.1 + + + + + + + + + + + +  + + + + +", +} +`; + +exports[`Can send cards via email renders the golden post correctly (no labs flags) 1 1`] = ` +Object { + "html": " + + + + + + A random test post + + + + This is just a simple paragraph, no frills. + +This is block quote + +This is a...different block quote + + +This is a heading! + + +Here's a smaller heading. + + +A heading + + +and a paragraph (in markdown!) + + + + +A paragraph inside an HTML card. + + +And another one, with some bold text. + + + +Ghost: Independent technology for modern publishingBeautiful, modern publishing with newsletters and premium subscriptions built-in. Used by Sky, 404Media, Lever News, Tangle, The Browser, and thousands more.Ghost - The Profes + + + + + + + + + + +
      +
    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    + + + +
    + + + + + + + + +
    +
    + A random test post +
    + + + + + + + + +
    + By Joe Bloggs • date + + View in browser +
    + View in browser +
    +
    + +

    This is just a simple paragraph, no frills.

    This is block quote

    This is a...different block quote

    This is a heading!

    Here's a smaller heading.

    \\"Cows
    A lovely cow

    A heading

    +

    and a paragraph (in markdown!)

    + + +

    A paragraph inside an HTML card.

    +

    And another one, with some bold text.

    + +

    A gallery.

    Hey Vinz,

    💡
    I had an idea...
    +

    Spoiler alert!

    +

    Just kidding

    +
    + + + +
    + + + + + +
    + + + + + + + + + + + + + + +
    + Sample +
    + + + + + +
    + + + 1:45 • Click to play audio +
    +
    +
    +
    + + + + + + + + +
    + + +
    +
     
    +
    + + + + +

    A lovely video of a woman on the beach doing nothing.

    +
    + + + + + + + + + + + + + + + + + + + + + +
    + +
    +

    Make a blog!

    +
    + +
    +

    with Ghost

    +
    + +
    + +
    +

    Good news everyone!

    +

    This header renders properly in all email clients!

    + +
    +

    Some text.

    A blockquote

    Some more text.

    console.log('Hello world!');

    A tiny little script.

    + + + +
    + + + + + +
    + +
    + test +
    + + +
    + A tiny text file. +
    + +
    + test.txt • 16 Bytes +
    +
    + + + +
    +
    + + +
    +
    + + + + + + + + +
    Ghost © 2024 – Unsubscribe
    \\"Powered
    +
    + +
    +
     
    + + +", + "plaintext": " + + + + + + + +  + + + + + + + + + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + + + + + +Ghost [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + +Daily newsletter [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + +A random test post [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + +By Joe Bloggs • date + + +View in browser [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + +View in browser [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + + +This is just a simple paragraph, no frills. + +> This is block quote + +> This is a...different block quote + + +This is a heading! + + +Here's a smaller heading. + +A lovely cow + + +A heading + + +and a paragraph (in markdown!) + + + + +A paragraph inside an HTML card. + + +And another one, with some bold text. + + + +---------------------------------------- + +A gallery. + + + + + + +Ghost: Independent technology for modern publishing +Beautiful, modern publishing with newsletters and premium subscriptions built-in. Used by Sky, 404Media, Lever News, Tangle, The Browser, and thousands more. + + +Ghost - The Professional Publishing Platform + + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + +My favorite website. + + + + +Hey Vinz, + + + +Click me, I'm a button! [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + +💡I had an idea... + + +Spoiler alert! + + +Just kidding + + + + + + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + +Sample [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + +1:45 • Click to play audio [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + + + + + + + + + + + + + + + +  + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + +A lovely video of a woman on the beach doing nothing. + + + + + + + + + + + + + + +Make a blog! + + + + + + + + + + + + + +with Ghost + + + + + + + + +Click here [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + + + + +Good news everyone! + + +This header renders properly in all email clients! + + + + + + + + + + + + + + +  + + + + +[http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + +Some text. + +> A blockquote + +Some more text. + +console.log('Hello world!'); + +A tiny little script. + + + + + + + + + + + + + +test [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + +A tiny text file. [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + +test.txt • 16 Bytes [http://127.0.0.1:2369/r/xxxxxx?m=member-uuid] + + + + + + + +http://127.0.0.1:2369/r/xxxxxx?m=member-uuid + + + + + + + + + + + + + + + + + + + + diff --git a/ghost/core/test/integration/services/email-service/cards.test.js b/ghost/core/test/integration/services/email-service/cards.test.js index 0acaaf53cf..1ccf953cb0 100644 --- a/ghost/core/test/integration/services/email-service/cards.test.js +++ b/ghost/core/test/integration/services/email-service/cards.test.js @@ -1,5 +1,7 @@ +const sinon = require('sinon'); const {agentProvider, fixtureManager, mockManager} = require('../../../utils/e2e-framework'); const models = require('../../../../core/server/models'); +const labs = require('../../../../core/shared/labs'); const assert = require('assert/strict'); const configUtils = require('../../../utils/configUtils'); const {sendEmail, matchEmailSnapshot} = require('../../../utils/batch-email-utils'); @@ -76,6 +78,7 @@ describe('Can send cards via email', function () { value: false }], {context: {internal: true}}); mockManager.restore(); + sinon.restore(); }); before(async function () { @@ -143,7 +146,26 @@ describe('Can send cards via email', function () { await matchEmailSnapshot(); }); - it('renders the golden post correctly', async function () { + it('renders the golden post correctly (no labs flags)', async function () { + sinon.stub(labs, 'isSet').returns(false); + + const data = await sendEmail(agent, { + lexical: JSON.stringify(goldenPost) + }); + + splitPreheader(data); + + await matchEmailSnapshot(); + }); + + it('renders the golden post correctly (labs flag: contentVisibility)', async function () { + sinon.stub(labs, 'isSet').callsFake((key) => { + if (key === 'contentVisibility') { + return true; + } + return false; + }); + const data = await sendEmail(agent, { lexical: JSON.stringify(goldenPost) }); diff --git a/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js b/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js index 1328116f8b..961f1591f7 100644 --- a/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js +++ b/ghost/core/test/unit/api/canary/utils/serializers/input/posts.test.js @@ -141,11 +141,11 @@ describe('Unit: endpoints/utils/serializers/input/posts', function () { columns: ['id'] } }; - + serializers.input.posts.browse(apiConfig, frame); frame.options.columns.should.eql(['id', 'visibility']); }); - + it('strips mobiledoc and lexical columns from a specified selectRaw option', function () { const apiConfig = {}; const frame = { @@ -353,7 +353,7 @@ describe('Unit: endpoints/utils/serializers/input/posts', function () { serializers.input.posts.edit(apiConfig, frame); let postData = frame.data.posts[0]; - postData.lexical.should.equal('{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"this is great feature","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1},{"type":"html","version":1,"html":"
    My Custom HTML
    ","visibility":{"emailOnly":false,"segment":""}},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"custom html preserved!","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'); + postData.lexical.should.equal('{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"this is great feature","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1},{"type":"html","version":1,"html":"
    My Custom HTML
    ","visibility":{"showOnEmail":true,"showOnWeb":true,"segment":""}},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"custom html preserved!","type":"extended-text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'); }); it('throws error when HTML conversion fails', function () { diff --git a/ghost/email-service/package.json b/ghost/email-service/package.json index 6b66e4fb5d..fe4fc9f710 100644 --- a/ghost/email-service/package.json +++ b/ghost/email-service/package.json @@ -29,7 +29,7 @@ "@tryghost/email-events": "0.0.0", "@tryghost/errors": "1.3.5", "@tryghost/html-to-plaintext": "0.0.0", - "@tryghost/kg-default-cards": "10.0.8", + "@tryghost/kg-default-cards": "10.0.9", "@tryghost/logging": "2.4.18", "@tryghost/tpl": "0.1.32", "@tryghost/validator": "0.2.14", diff --git a/ghost/importer-revue/package.json b/ghost/importer-revue/package.json index 3eb22f5075..2aeba09b4f 100644 --- a/ghost/importer-revue/package.json +++ b/ghost/importer-revue/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@tryghost/debug": "0.1.32", - "@tryghost/kg-default-cards": "10.0.8", + "@tryghost/kg-default-cards": "10.0.9", "@tryghost/string": "0.2.12", "lodash": "4.17.21", "papaparse": "5.3.2", diff --git a/yarn.lock b/yarn.lock index 0a5f4012ab..bb35cf006d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7834,10 +7834,10 @@ resolved "https://registry.yarnpkg.com/@tryghost/kg-default-atoms/-/kg-default-atoms-5.0.3.tgz#7d0e5af2191f2e0c61ae11d1666905bf924f67ac" integrity sha512-uPE69rKxaiiMEa1vFsEbfX+LCBG2H5D/nqTkuIPAslEGIHYjQUruaXpBfnwtyVHn+dMLuWcO+wDJl5qET8gKEQ== -"@tryghost/kg-default-cards@10.0.8": - version "10.0.8" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.8.tgz#ed67a90c1b5fe045c3e2822be8e14f43ccc2ee3a" - integrity sha512-yLao7TDpDDDHjiTqbl+6wmISpx//iuXY00gS7JqNrZ8X7hZ6IvxUdXu1Hq9AZuyzZ6+nqvyqRYgvZ1VycSVtTQ== +"@tryghost/kg-default-cards@10.0.9": + version "10.0.9" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-cards/-/kg-default-cards-10.0.9.tgz#6ea967c3270c4fd895f9f3b7773bc1203370fb45" + integrity sha512-y5P5ybNX846PZ7N+z3Gs5curkqKSU0qNVjve0TVwQe5J3kHPwTKH4eMFcqrEQj9Tu3XgIqx0XWxXL3bSN6uWiw== dependencies: "@tryghost/kg-markdown-html-renderer" "7.0.7" "@tryghost/string" "0.2.12" @@ -7847,10 +7847,10 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-nodes@1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.12.tgz#40be11e6a898f14e4249f05142e14e72ab956068" - integrity sha512-zBeCgpcFZibehWzo10HjzKFS2RSW8AvSakShpX3h4skSDG9Tkki60b5ATczxiLFswHerEvnBacHEgZO1xzzbNQ== +"@tryghost/kg-default-nodes@1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.14.tgz#39039072451df411972e68ffa798ce1df768269d" + integrity sha512-X+aTPHgB6OuJeuQPK9xPNNcYgtL9pUIwOam0+RYzlsGswYMo0fAhxijPAo8oIgDyJ9gQRmMexHM0qWBNUvmHaA== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" @@ -7864,21 +7864,21 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-transforms@1.1.13": - version "1.1.13" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.13.tgz#6d38f687f2f363d4b85a615216e1b83aff417954" - integrity sha512-AvTpBNpUzpTI4EYtJ+xZapKxfz7mu/ZbdTgxCnuMkWRe0Cw3uOl838gaKHTMNuP76k7HCxNVW61gGm/FVofo5g== +"@tryghost/kg-default-transforms@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.15.tgz#054231b5b72934d65e694e5d81730d13594424ec" + integrity sha512-iw4G3iq1X8Rc+6Fbcd9LWZWFt/VBQrdWW5klE4IMXii98saYdSnBcy2/mo+F7UWH81edHENfX1S4Zwd2zuA9eg== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.12" + "@tryghost/kg-default-nodes" "1.1.14" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.13": - version "1.1.13" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.13.tgz#83f9a24b30ba0a40f07ab1c8c66eb9c99904f049" - integrity sha512-lVb0V5ckZ1SPjMzTfxzZ61ABN9koCNLQd1nCYHyNLunLH5IVVY2yRFNLt2CHtbfUMcjvGv4Egfi/g9oRwJkBiQ== +"@tryghost/kg-html-to-lexical@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.15.tgz#c3ba3c96051a46bce4d7c6c42cdc33570d5d6e72" + integrity sha512-KOeBy56zjMPEjk2yUd3f2UnbPv/tic0g07vkCXuOFrz2wKJFX9MILQlmuVK5lHULnqKTGiZZ0yY6SmmKiLGYIA== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7886,15 +7886,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.12" - "@tryghost/kg-default-transforms" "1.1.13" + "@tryghost/kg-default-nodes" "1.1.14" + "@tryghost/kg-default-transforms" "1.1.15" jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.15": - version "1.1.15" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.15.tgz#9512fc14a3ed83baf7ce5577bbf3b33cde78292b" - integrity sha512-hCx7iizJvmbwUWtrQKOEOmH+lPmxzcoOf+gpxOL3actEcfFYHO9lYWTVg9x8s6Q8eyzLQ3lIh9mpdktzmz+x9g== +"@tryghost/kg-lexical-html-renderer@1.1.17": + version "1.1.17" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.17.tgz#3628aa45f30b8355638bda91ba5ced908e116882" + integrity sha512-DSVQadD+N/67mhK4UzAChXPk5l5dqnOASqldY0CTzB0LhKUMRMKjZIDLpU4coo0Q8ckSqdYPqeSCP6s7I4F77Q== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -7902,8 +7902,8 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.12" - "@tryghost/kg-default-transforms" "1.1.13" + "@tryghost/kg-default-nodes" "1.1.14" + "@tryghost/kg-default-transforms" "1.1.15" jsdom "^24.1.0" lexical "0.13.1" @@ -7950,10 +7950,10 @@ dependencies: semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.16": - version "1.3.16" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.16.tgz#b1f317e969dcfa6b01f3a7f6dce55c872bb6f833" - integrity sha512-jyLZL5QiMF+GjUIAHD/h69cb+PZrvC+9fA05lFTyBPlgVRc1L2qTu99Xo6YH+ZrE+mIBfmNfKRfmIRJRD5AR5w== +"@tryghost/koenig-lexical@1.3.18": + version "1.3.18" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.18.tgz#b48b1edf8485df7f196e19b37fe6dc1e77a7fdba" + integrity sha512-UmzdzDIV44yUq3C/dhlVdQtUybimLaDXt9E6cywc33b2USR3NN3k9fta02RWZab7nhiLFwm5Dt6CedpZTE70Ag== "@tryghost/limit-service@1.2.14": version "1.2.14" From 6ae20a8d3272658e4be44f217739ef1f9d73e6d7 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Wed, 14 Aug 2024 16:07:11 +0100 Subject: [PATCH 144/164] Added relative timestamps to feed objects ref https://linear.app/tryghost/issue/AP-131/show-nice-relative-timestamps --- .../src/utils/get-relative-timestamp.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 apps/admin-x-activitypub/src/utils/get-relative-timestamp.ts diff --git a/apps/admin-x-activitypub/src/utils/get-relative-timestamp.ts b/apps/admin-x-activitypub/src/utils/get-relative-timestamp.ts new file mode 100644 index 0000000000..be70d68c24 --- /dev/null +++ b/apps/admin-x-activitypub/src/utils/get-relative-timestamp.ts @@ -0,0 +1,33 @@ +export const getRelativeTimestamp = (date: Date): string => { + const now = new Date(); + const seconds = Math.floor((now.getTime() - date.getTime()) / 1000); + + let interval = Math.floor(seconds / 31536000); + if (interval > 1) { + return `${interval}y`; + } + + interval = Math.floor(seconds / 2592000); + if (interval > 1) { + return `${interval}m`; + } + + interval = Math.floor(seconds / 86400); + if (interval >= 1) { + return `${interval}d`; + } + + interval = Math.floor(seconds / 3600); + if (interval > 1) { + return `${interval}h`; + } + + interval = Math.floor(seconds / 60); + if (interval > 1) { + return `${interval}m`; + } + + return `${seconds} seconds`; +}; + +export default getRelativeTimestamp; \ No newline at end of file From ee5a49077d64dbb2e446bfc3fce3f435af308d88 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Wed, 14 Aug 2024 16:07:49 +0100 Subject: [PATCH 145/164] Made hover state on links inside Notes easier to see ref https://linear.app/tryghost/issue/MOM-282/render-notes-in-the-frontend --- apps/admin-x-activitypub/src/styles/index.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/admin-x-activitypub/src/styles/index.css b/apps/admin-x-activitypub/src/styles/index.css index a59dd422e7..d9a471ebfb 100644 --- a/apps/admin-x-activitypub/src/styles/index.css +++ b/apps/admin-x-activitypub/src/styles/index.css @@ -29,7 +29,8 @@ animation: bump 0.3s ease-in-out; } .ap-note-content a:hover { - color: rgb(219 39 119) !important; + color: rgb(190, 25, 99) !important; + text-decoration: underline !important; } .ap-note-content p + p { From 04b600b0b8d6f39d4e1e90dc9e16d8aecc3e7757 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Wed, 14 Aug 2024 16:08:51 +0100 Subject: [PATCH 146/164] Refactored `renderAttachment` to keep it DRY ref https://linear.app/tryghost/issue/MOM-282/render-notes-in-the-frontend --- .../src/components/ListIndex.tsx | 152 ++++++++++-------- 1 file changed, 86 insertions(+), 66 deletions(-) diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/ListIndex.tsx index 9f9d1393ec..62f05786b7 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/ListIndex.tsx @@ -1,6 +1,7 @@ import ActivityPubWelcomeImage from '../assets/images/ap-welcome.png'; import React, {useEffect, useRef, useState} from 'react'; import articleBodyStyles from './articleBodyStyles'; +import getRelativeTimestamp from '../utils/get-relative-timestamp'; import getUsername from '../utils/get-username'; import {ActivityPubAPI} from '../api/activitypub'; import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; @@ -204,7 +205,7 @@ const ActivityPubComponent: React.FC = () => { title: 'Profile', contents: (
    -
    +
    updateRoute('/view-following')}> @@ -285,15 +286,15 @@ const ArticleBody: React.FC<{heading: string, image: string|undefined, html: str ${cssContent} -
    -

    ${heading}

    +
    +

    ${heading}

    ${image && - `
    - ${heading} + `
    + ${heading}
    ` }
    -
    +
    ${html}
    @@ -312,16 +313,72 @@ ${image &&
    ); }; +function renderAttachment(object: ObjectProperties) { + let attachment; + if (object.image) { + attachment = object.image; + } + + if (object.type === 'Note' && !attachment) { + attachment = object.attachment; + } + + if (!attachment) { + return null; + } + + if (Array.isArray(attachment)) { + const attachmentCount = attachment.length; + + let gridClass = ''; + if (attachmentCount === 1) { + gridClass = 'grid-cols-1'; // Single image, full width + } else if (attachmentCount === 2) { + gridClass = 'grid-cols-2'; // Two images, side by side + } else if (attachmentCount === 3 || attachmentCount === 4) { + gridClass = 'grid-cols-2'; // Three or four images, two per row + } + + return ( +
    + {attachment.map((item, index) => ( + {`attachment-${index}`} + ))} +
    + ); + } + + switch (attachment.mediaType) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + return attachment; + case 'video/mp4': + case 'video/webm': + return
    +
    ; + + case 'audio/mpeg': + case 'audio/ogg': + return
    +
    ; + default: + return null; + } +} + const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProperties, layout: string, type: string }> = ({actor, object, layout, type}) => { const parser = new DOMParser(); const doc = parser.parseFromString(object.content || '', 'text/html'); @@ -335,55 +392,11 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp previewContent = plainTextContent || ''; } - const renderAttachment = () => { - let attachment; - if (object.image) { - attachment = object.image; - } - - if (object.type === 'Note' && !attachment) { - attachment = object.attachment; - } - - // const attachment = object.attachment; - if (!attachment) { - return null; - } - - if (Array.isArray(attachment)) { - return ( -
    - {attachment.map((item, index) => ( - {`attachment-${index}`} - ))} -
    - ); - } - - switch (attachment.mediaType) { - case 'image/jpeg': - case 'image/png': - case 'image/gif': - return attachment; - case 'video/mp4': - case 'video/webm': - return
    -
    ; - - case 'audio/mpeg': - case 'audio/ogg': - return
    -
    ; - default: - return null; - } - }; - const timestamp = new Date(object?.published ?? new Date()).toLocaleDateString('default', {year: 'numeric', month: 'short', day: '2-digit'}) + ', ' + new Date(object?.published ?? new Date()).toLocaleTimeString('default', {hour: '2-digit', minute: '2-digit'}); + const date = new Date(object?.published ?? new Date()); + const [isClicked, setIsClicked] = useState(false); const [isLiked, setIsLiked] = useState(false); @@ -404,18 +417,20 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp <> {object && (
    - {(type === 'Announce' && object.type === 'Note') &&
    + {(type === 'Announce' && object.type === 'Note') &&
    {actor.name} reposted
    } -
    +
    -
    +
    - {author.name} +
    + {author.name} + {getRelativeTimestamp(date)} +
    {getUsername(author)} - {timestamp}
    @@ -423,9 +438,9 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp {object.name && {object.name}}
    {/*

    {object.content}

    */} - {renderAttachment()} + {renderAttachment(object)}
    -
    @@ -455,7 +470,7 @@ const ObjectContentDisplay: React.FC<{actor: ActorProperties, object: ObjectProp

    {previewContent}

    -
    @@ -499,14 +514,19 @@ const ViewArticle: React.FC = ({object, onBackToList}) => {
    -
    - + {object.type === 'Note' && ( +
    + {object.content &&
    } + {renderAttachment(object)} +
    )} + {object.type === 'Article' && }
    From e6254bbb932f9f7c461b52414294c4df92c645bd Mon Sep 17 00:00:00 2001 From: Sag Date: Wed, 14 Aug 2024 17:48:54 +0200 Subject: [PATCH 147/164] =?UTF-8?q?=F0=9F=8E=A8=20Removed=20member=20bulk?= =?UTF-8?q?=20deletion=20safeguard=20from=20safe=20queries=20(#20747)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes https://linear.app/tryghost/issue/ENG-1484 - in Ghost release [v5.89.0](https://github.com/TryGhost/Ghost/releases/tag/v5.89.0), we have added a safeguard around bulk member deletion, due to a limitation in NQL for member filters (commit: 2484a77) - with this change, we limit the safeguard to only the cases we know are problematic, and remove it for other useful and safe queries - more precisely, the safeguard is in place only when: - Multiple newsletters exist, and the filter contains 2 or more newsletter filters - If any of the following stripe filters are used even once: - Billing period - Stripe subscription status - Paid start date - Next billing date - Subscription started on post/page - Offers --- ghost/admin/README.md | 2 +- ghost/admin/app/components/members/filter.js | 4 + .../components/members/filters/created-at.js | 6 +- .../members/filters/email-clicked.js | 8 +- .../components/members/filters/email-count.js | 8 +- .../members/filters/email-open-rate.js | 6 +- .../members/filters/email-opened-count.js | 8 +- .../members/filters/email-opened.js | 8 +- .../app/components/members/filters/email.js | 4 +- .../app/components/members/filters/label.js | 8 +- .../components/members/filters/last-seen.js | 8 +- .../app/components/members/filters/name.js | 6 +- .../app/components/members/filters/offers.js | 2 +- .../members/filters/signup-attribution.js | 8 +- .../app/components/members/filters/status.js | 4 +- .../filters/subscription-attribution.js | 8 +- .../app/components/members/filters/tier.js | 8 +- ghost/admin/app/controllers/members.js | 40 ++++- ghost/admin/app/templates/members.hbs | 4 +- ghost/admin/mirage/factories/newsletter.js | 10 ++ ghost/admin/tests/acceptance/members-test.js | 152 ++++++++++++++---- 21 files changed, 222 insertions(+), 90 deletions(-) create mode 100644 ghost/admin/mirage/factories/newsletter.js diff --git a/ghost/admin/README.md b/ghost/admin/README.md index 73a49b566b..04fa03e8ed 100644 --- a/ghost/admin/README.md +++ b/ghost/admin/README.md @@ -12,7 +12,7 @@ Run all tests in the browser by running `yarn dev` in the Ghost monorepo and vis --- -Tip: You can use `await this.pauseTest()` in your tests to temporarily pause the execution of browser tests. Use the browser console to inspect and debug the DOM, then resume tests by running `resumeTest()` directly in the browser console ([docs](https://guides.emberjs.com/v3.28.0/testing/testing-application/#toc_debugging-your-tests)) +Tip: You can use `this.timeout(0); await this.pauseTest();` in your tests to temporarily pause the execution of browser tests. Use the browser console to inspect and debug the DOM, then resume tests by running `resumeTest()` directly in the browser console ([docs](https://guides.emberjs.com/v3.28.0/testing/testing-application/#toc_debugging-your-tests)) ### Running tests in the CLI diff --git a/ghost/admin/app/components/members/filter.js b/ghost/admin/app/components/members/filter.js index 6993d1c4e7..46da839176 100644 --- a/ghost/admin/app/components/members/filter.js +++ b/ghost/admin/app/components/members/filter.js @@ -137,6 +137,10 @@ class Filter { return this.properties.options ?? []; } + get group() { + return this.properties.group; + } + get isValid() { if (Array.isArray(this.value)) { return !!this.value.length; diff --git a/ghost/admin/app/components/members/filters/created-at.js b/ghost/admin/app/components/members/filters/created-at.js index 9ded43af78..f857771115 100644 --- a/ghost/admin/app/components/members/filters/created-at.js +++ b/ghost/admin/app/components/members/filters/created-at.js @@ -1,8 +1,8 @@ import {DATE_RELATION_OPTIONS} from './relation-options'; export const CREATED_AT_FILTER = { - label: 'Created', - name: 'created_at', - valueType: 'date', + label: 'Created', + name: 'created_at', + valueType: 'date', relationOptions: DATE_RELATION_OPTIONS }; diff --git a/ghost/admin/app/components/members/filters/email-clicked.js b/ghost/admin/app/components/members/filters/email-clicked.js index d24798b197..c78dae0c41 100644 --- a/ghost/admin/app/components/members/filters/email-clicked.js +++ b/ghost/admin/app/components/members/filters/email-clicked.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const EMAIL_CLICKED_FILTER = { - label: 'Clicked email', - name: 'clicked_links.post_id', - valueType: 'string', - resource: 'email', + label: 'Clicked email', + name: 'clicked_links.post_id', + valueType: 'string', + resource: 'email', relationOptions: MATCH_RELATION_OPTIONS, columnLabel: 'Clicked email', setting: 'emailTrackClicks', diff --git a/ghost/admin/app/components/members/filters/email-count.js b/ghost/admin/app/components/members/filters/email-count.js index ef8396c574..03a0a4242f 100644 --- a/ghost/admin/app/components/members/filters/email-count.js +++ b/ghost/admin/app/components/members/filters/email-count.js @@ -2,10 +2,10 @@ import {NUMBER_RELATION_OPTIONS} from './relation-options'; import {formatNumber} from 'ghost-admin/helpers/format-number'; export const EMAIL_COUNT_FILTER = { - label: 'Emails sent (all time)', - name: 'email_count', - columnLabel: 'Email count', - valueType: 'number', + label: 'Emails sent (all time)', + name: 'email_count', + columnLabel: 'Email count', + valueType: 'number', relationOptions: NUMBER_RELATION_OPTIONS, getColumnValue: (member) => { return { diff --git a/ghost/admin/app/components/members/filters/email-open-rate.js b/ghost/admin/app/components/members/filters/email-open-rate.js index 5990199a4d..6361239b05 100644 --- a/ghost/admin/app/components/members/filters/email-open-rate.js +++ b/ghost/admin/app/components/members/filters/email-open-rate.js @@ -1,9 +1,9 @@ import {NUMBER_RELATION_OPTIONS} from './relation-options'; export const EMAIL_OPEN_RATE_FILTER = { - label: 'Open rate (all time)', - name: 'email_open_rate', - valueType: 'number', + label: 'Open rate (all time)', + name: 'email_open_rate', + valueType: 'number', setting: 'emailTrackOpens', relationOptions: NUMBER_RELATION_OPTIONS }; diff --git a/ghost/admin/app/components/members/filters/email-opened-count.js b/ghost/admin/app/components/members/filters/email-opened-count.js index ff4d2e3363..dece4042ad 100644 --- a/ghost/admin/app/components/members/filters/email-opened-count.js +++ b/ghost/admin/app/components/members/filters/email-opened-count.js @@ -2,10 +2,10 @@ import {NUMBER_RELATION_OPTIONS} from './relation-options'; import {formatNumber} from 'ghost-admin/helpers/format-number'; export const EMAIL_OPENED_COUNT_FILTER = { - label: 'Emails opened (all time)', - name: 'email_opened_count', - columnLabel: 'Email opened count', - valueType: 'number', + label: 'Emails opened (all time)', + name: 'email_opened_count', + columnLabel: 'Email opened count', + valueType: 'number', relationOptions: NUMBER_RELATION_OPTIONS, getColumnValue: (member) => { return { diff --git a/ghost/admin/app/components/members/filters/email-opened.js b/ghost/admin/app/components/members/filters/email-opened.js index 5f284e16a5..d9535523aa 100644 --- a/ghost/admin/app/components/members/filters/email-opened.js +++ b/ghost/admin/app/components/members/filters/email-opened.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const EMAIL_OPENED_FILTER = { - label: 'Opened email', - name: 'opened_emails.post_id', - valueType: 'string', - resource: 'email', + label: 'Opened email', + name: 'opened_emails.post_id', + valueType: 'string', + resource: 'email', relationOptions: MATCH_RELATION_OPTIONS, columnLabel: 'Opened email', setting: 'emailTrackOpens', diff --git a/ghost/admin/app/components/members/filters/email.js b/ghost/admin/app/components/members/filters/email.js index 3645374c34..1aa094a395 100644 --- a/ghost/admin/app/components/members/filters/email.js +++ b/ghost/admin/app/components/members/filters/email.js @@ -1,8 +1,8 @@ import {CONTAINS_RELATION_OPTIONS} from './relation-options'; export const EMAIL_FILTER = { - label: 'Email', + label: 'Email', name: 'email', - valueType: 'string', + valueType: 'string', relationOptions: CONTAINS_RELATION_OPTIONS }; diff --git a/ghost/admin/app/components/members/filters/label.js b/ghost/admin/app/components/members/filters/label.js index 7538d60df5..857845a2db 100644 --- a/ghost/admin/app/components/members/filters/label.js +++ b/ghost/admin/app/components/members/filters/label.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const LABEL_FILTER = { - label: 'Label', - name: 'label', - valueType: 'array', - columnLabel: 'Label', + label: 'Label', + name: 'label', + valueType: 'array', + columnLabel: 'Label', relationOptions: MATCH_RELATION_OPTIONS, getColumnValue: (member) => { return { diff --git a/ghost/admin/app/components/members/filters/last-seen.js b/ghost/admin/app/components/members/filters/last-seen.js index 22eb950daf..d0667dbda3 100644 --- a/ghost/admin/app/components/members/filters/last-seen.js +++ b/ghost/admin/app/components/members/filters/last-seen.js @@ -2,10 +2,10 @@ import {DATE_RELATION_OPTIONS} from './relation-options'; import {getDateColumnValue} from './columns/date-column'; export const LAST_SEEN_FILTER = { - label: 'Last seen', - name: 'last_seen_at', - valueType: 'date', - columnLabel: 'Last seen at', + label: 'Last seen', + name: 'last_seen_at', + valueType: 'date', + columnLabel: 'Last seen at', relationOptions: DATE_RELATION_OPTIONS, getColumnValue: (member, filter) => { return getDateColumnValue(member.lastSeenAtUTC, filter); diff --git a/ghost/admin/app/components/members/filters/name.js b/ghost/admin/app/components/members/filters/name.js index 875d50e5fa..c5571d5567 100644 --- a/ghost/admin/app/components/members/filters/name.js +++ b/ghost/admin/app/components/members/filters/name.js @@ -1,8 +1,8 @@ import {CONTAINS_RELATION_OPTIONS} from './relation-options'; export const NAME_FILTER = { - label: 'Name', - name: 'name', - valueType: 'string', + label: 'Name', + name: 'name', + valueType: 'string', relationOptions: CONTAINS_RELATION_OPTIONS }; diff --git a/ghost/admin/app/components/members/filters/offers.js b/ghost/admin/app/components/members/filters/offers.js index 0039f04c19..854c46e4e7 100644 --- a/ghost/admin/app/components/members/filters/offers.js +++ b/ghost/admin/app/components/members/filters/offers.js @@ -1,7 +1,7 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const OFFERS_FILTER = { - label: 'Offers', + label: 'Offers', name: 'offer_redemptions', group: 'Subscription', relationOptions: MATCH_RELATION_OPTIONS, diff --git a/ghost/admin/app/components/members/filters/signup-attribution.js b/ghost/admin/app/components/members/filters/signup-attribution.js index 49a396e071..6bcf2b2762 100644 --- a/ghost/admin/app/components/members/filters/signup-attribution.js +++ b/ghost/admin/app/components/members/filters/signup-attribution.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const SIGNUP_ATTRIBUTION_FILTER = { - label: 'Signed up on post/page', - name: 'signup', - valueType: 'string', - resource: 'post', + label: 'Signed up on post/page', + name: 'signup', + valueType: 'string', + resource: 'post', relationOptions: MATCH_RELATION_OPTIONS, columnLabel: 'Signed up on', setting: 'membersTrackSources', diff --git a/ghost/admin/app/components/members/filters/status.js b/ghost/admin/app/components/members/filters/status.js index e6d31dd697..08cbbef1a9 100644 --- a/ghost/admin/app/components/members/filters/status.js +++ b/ghost/admin/app/components/members/filters/status.js @@ -1,8 +1,8 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const STATUS_FILTER = { - label: 'Member status', - name: 'status', + label: 'Member status', + name: 'status', relationOptions: MATCH_RELATION_OPTIONS, valueType: 'options', options: [ diff --git a/ghost/admin/app/components/members/filters/subscription-attribution.js b/ghost/admin/app/components/members/filters/subscription-attribution.js index 84688dac5d..373b51c0b3 100644 --- a/ghost/admin/app/components/members/filters/subscription-attribution.js +++ b/ghost/admin/app/components/members/filters/subscription-attribution.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const SUBSCRIPTION_ATTRIBUTION_FILTER = { - label: 'Subscription started on post/page', - name: 'conversion', - valueType: 'string', - resource: 'post', + label: 'Subscription started on post/page', + name: 'conversion', + valueType: 'string', + resource: 'post', relationOptions: MATCH_RELATION_OPTIONS, columnLabel: 'Subscription started on', setting: 'membersTrackSources', diff --git a/ghost/admin/app/components/members/filters/tier.js b/ghost/admin/app/components/members/filters/tier.js index b219d0fc4f..bbd1c00e2d 100644 --- a/ghost/admin/app/components/members/filters/tier.js +++ b/ghost/admin/app/components/members/filters/tier.js @@ -1,10 +1,10 @@ import {MATCH_RELATION_OPTIONS} from './relation-options'; export const TIER_FILTER = { - label: 'Membership tier', - name: 'tier_id', - valueType: 'array', - columnLabel: 'Membership tier', + label: 'Membership tier', + name: 'tier_id', + valueType: 'array', + columnLabel: 'Membership tier', relationOptions: MATCH_RELATION_OPTIONS, getColumnValue: (member) => { return { diff --git a/ghost/admin/app/controllers/members.js b/ghost/admin/app/controllers/members.js index 1270b5a06a..9279418d23 100644 --- a/ghost/admin/app/controllers/members.js +++ b/ghost/admin/app/controllers/members.js @@ -209,8 +209,44 @@ export default class MembersController extends Controller { return uniqueColumns.splice(0, 2); // Maximum 2 columns } - get isMultiFiltered() { - return this.isFiltered && this.filters.length >= 2; + /* Due to a limitation with NQL when multiple member filters are used in combination, we currently have a safeguard around member bulk deletion. + * Member bulk deletion is not permitted when: + * 1) Multiple newsletters exist, and 2 or more newsletter filters are in use + * 2) If any of the following Stripe filters are used, even once: + * - Billing period + * - Stripe subscription status + * - Paid start date + * - Next billing date + * - Subscription started on post/page + * - Offers + * + * See issue https://linear.app/tryghost/issue/ENG-1484 for more context + */ + get isBulkDeletePermitted() { + if (!this.isFiltered) { + return false; + } + + const newsletterFilters = this.filters.filter(f => f.group === 'Newsletters'); + + if (newsletterFilters && newsletterFilters.length >= 2) { + return false; + } + + const stripeFilters = this.filters.filter(f => [ + 'subscriptions.plan_interval', + 'subscriptions.status', + 'subscriptions.start_date', + 'subscriptions.current_period_end', + 'conversion', + 'offer_redemptions' + ].includes(f.type)); + + if (stripeFilters && stripeFilters.length >= 1) { + return false; + } + + return true; } includeTierQuery() { diff --git a/ghost/admin/app/templates/members.hbs b/ghost/admin/app/templates/members.hbs index d72c91a904..c965f8389b 100644 --- a/ghost/admin/app/templates/members.hbs +++ b/ghost/admin/app/templates/members.hbs @@ -104,14 +104,14 @@ {{/if}} - {{#unless this.isMultiFiltered}} + {{#if this.isBulkDeletePermitted}}
  • - {{/unless}} + {{/if}} {{/if}} diff --git a/ghost/admin/mirage/factories/newsletter.js b/ghost/admin/mirage/factories/newsletter.js new file mode 100644 index 0000000000..b6b0ac2ad4 --- /dev/null +++ b/ghost/admin/mirage/factories/newsletter.js @@ -0,0 +1,10 @@ +import moment from 'moment-timezone'; +import {Factory} from 'miragejs'; + +export default Factory.extend({ + name(i) { return `Newsletter ${i}`; }, + slug(i) { return `newsletter-${i}`; }, + status() { return 'active'; }, + createdAt() { return moment.utc().toISOString(); }, + updatedAt() { return moment.utc().toISOString(); } +}); diff --git a/ghost/admin/tests/acceptance/members-test.js b/ghost/admin/tests/acceptance/members-test.js index b59f9514ca..ba9f0c9ef8 100644 --- a/ghost/admin/tests/acceptance/members-test.js +++ b/ghost/admin/tests/acceptance/members-test.js @@ -143,51 +143,133 @@ describe('Acceptance: Members', function () { .to.equal('example@domain.com'); }); - /* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation. - * Delete this test once we have fixed the root NQL limitation. - * See https://linear.app/tryghost/issue/ONC-203 + /* Due to a limitation with NQL when multiple member filters are used in combination, we currently have a safeguard around member bulk deletion. + * Member bulk deletion is not permitted when: + * 1) Multiple newsletters exist, and 2 or more newsletter filters are in use + * 2) If any of the following Stripe filters are used, even once: + * - Billing period + * - Stripe subscription status + * - Paid start date + * - Next billing date + * - Subscription started on post/page + * - Offers + * + * See code: ghost/admin/app/controllers/members.js:isBulkDeletePermitted + * See issue https://linear.app/tryghost/issue/ENG-1484 for more context + * + * TODO: delete this block of tests once the guardrail has been removed */ - it('cannot bulk delete members if more than 1 filter is selected', async function () { - // Members with label - const labelOne = this.server.create('label'); - const labelTwo = this.server.create('label'); - this.server.createList('member', 2, {labels: [labelOne]}); - this.server.createList('member', 2, {labels: [labelOne, labelTwo]}); + describe('[Temp] Guardrail against bulk deletion', function () { + it('cannot bulk delete members if more than 1 newsletter filter is used', async function () { + // Create two newsletters and members subscribed to 1 or 2 newsletters + const newsletterOne = this.server.create('newsletter'); + const newsletterTwo = this.server.create('newsletter'); + this.server.createList('member', 2).forEach(member => member.update({newsletters: [newsletterOne], email_disabled: 0})); + this.server.createList('member', 2).forEach(member => member.update({newsletters: [newsletterOne, newsletterTwo], email_disabled: 0})); - await visit('/members'); - expect(findAll('[data-test-member]').length).to.equal(4); + await visit('/members'); + expect(findAll('[data-test-member]').length).to.equal(4); - // The delete button should not be visible by default - await click('[data-test-button="members-actions"]'); - expect(find('[data-test-button="delete-selected"]')).to.not.exist; + // The delete button should not be visible by default + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; - // Apply a single filter - await click('[data-test-button="members-filter-actions"]'); - await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', 'label'); - await click('.gh-member-label-input input'); - await click(`[data-test-label-filter="${labelOne.name}"]`); - await click(`[data-test-button="members-apply-filter"]`); + // Apply a first filter + await click('[data-test-button="members-filter-actions"]'); + await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', `newsletters.slug:${newsletterOne.slug}`); + await click(`[data-test-button="members-apply-filter"]`); - expect(findAll('[data-test-member]').length).to.equal(4); - expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D`); + expect(findAll('[data-test-member]').length).to.equal(4); + expect(currentURL()).to.equal(`/members?filter=(newsletters.slug%3A${newsletterOne.slug}%2Bemail_disabled%3A0)`); - await click('[data-test-button="members-actions"]'); - expect(find('[data-test-button="delete-selected"]')).to.exist; + // Bulk deletion is permitted + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.exist; - // Apply a second filter - await click('[data-test-button="members-filter-actions"]'); - await click('[data-test-button="add-members-filter"]'); + // Apply a second filter + await click('[data-test-button="members-filter-actions"]'); + await click('[data-test-button="add-members-filter"]'); + await fillIn('[data-test-members-filter="1"] [data-test-select="members-filter"]', `newsletters.slug:${newsletterTwo.slug}`); + await click(`[data-test-button="members-apply-filter"]`); - await fillIn('[data-test-members-filter="1"] [data-test-select="members-filter"]', 'label'); - await click('[data-test-members-filter="1"] .gh-member-label-input input'); - await click(`[data-test-members-filter="1"] [data-test-label-filter="${labelTwo.name}"]`); - await click(`[data-test-button="members-apply-filter"]`); + expect(findAll('[data-test-member]').length).to.equal(2); + expect(currentURL()).to.equal(`/members?filter=(newsletters.slug%3A${newsletterOne.slug}%2Bemail_disabled%3A0)%2B(newsletters.slug%3A${newsletterTwo.slug}%2Bemail_disabled%3A0)`); - expect(findAll('[data-test-member]').length).to.equal(2); - expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D%2Blabel%3A%5B${labelTwo.slug}%5D`); + // Bulk deletion is not permitted anymore + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + }); - await click('[data-test-button="members-actions"]'); - expect(find('[data-test-button="delete-selected"]')).to.not.exist; + it('can bulk delete members if a non-Stripe subscription filter is in use (member tier, status)', async function () { + const tier = this.server.create('tier', {id: 'qwerty123456789'}); + this.server.createList('member', 2, {status: 'free'}); + this.server.createList('member', 2, {status: 'paid', tiers: [tier]}); + + await visit('/members'); + expect(findAll('[data-test-member]').length).to.equal(4); + + // The delete button should not be visible by default + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 1) Membership tier filter: permitted + await visit(`/members?filter=tier_id:[${tier.id}]`); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.exist; + + // 2) Member status filter: permitted + await visit('/members?filter=status%3Afree'); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.exist; + }); + + it('cannot bulk delete members if a Stripe subscription filter is in use', async function () { + // Create free and paid members + const tier = this.server.create('tier'); + const offer = this.server.create('offer', {tier: {id: tier.id}, createdAt: moment.utc().subtract(1, 'day').valueOf()}); + this.server.createList('member', 2, {status: 'free'}); + this.server.createList('member', 2, {status: 'paid'}).forEach(member => this.server.create('subscription', {member, planInterval: 'month', status: 'active', start_date: '2000-01-01T00:00:00.000Z', current_period_end: '2000-02-01T00:00:00.000Z', offer: offer, tier: tier})); + this.server.createList('member', 2, {status: 'paid'}).forEach(member => this.server.create('subscription', {member, planInterval: 'year', status: 'active'})); + + await visit('/members'); + expect(findAll('[data-test-member]').length).to.equal(6); + + // The delete button should not be visible by default + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 1) Stripe billing period filter: not permitted + await visit('/members?filter=subscriptions.plan_interval%3Amonth'); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 2) Stripe subscription status filter: not permitted + await visit('/members?filter=subscriptions.status%3Aactive'); + expect(findAll('[data-test-member]').length).to.equal(4); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 3) Stripe paid start date filter: not permitted + await visit(`/members?filter=subscriptions.start_date%3A>'1999-01-01%2005%3A59%3A59'`); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 4) Next billing date filter: not permitted + await visit(`/members?filter=subscriptions.current_period_end%3A>'2000-01-01%2005%3A59%3A59'`); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + + // 5) Offers redeemed filter: not permitted + await visit('/members?filter=' + encodeURIComponent(`offer_redemptions:'${offer.id}'`)); + expect(findAll('[data-test-member]').length).to.equal(2); + await click('[data-test-button="members-actions"]'); + expect(find('[data-test-button="delete-selected"]')).to.not.exist; + }); }); it('can bulk delete members', async function () { From 0eb569ad89559d2bef028fa09c80043de5a93ced Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:32:56 +0000 Subject: [PATCH 148/164] Update Koenig packages --- ghost/admin/package.json | 2 +- ghost/core/package.json | 6 ++--- yarn.lock | 50 ++++++++++++++++++++-------------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index ac32b8abed..dfaa0b7600 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.3", "@tryghost/kg-converters": "1.0.6", - "@tryghost/koenig-lexical": "1.3.18", + "@tryghost/koenig-lexical": "1.3.19", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/ghost/core/package.json b/ghost/core/package.json index a0c347ff3e..2e9125f7a1 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -111,9 +111,9 @@ "@tryghost/kg-converters": "1.0.6", "@tryghost/kg-default-atoms": "5.0.3", "@tryghost/kg-default-cards": "10.0.9", - "@tryghost/kg-default-nodes": "1.1.14", - "@tryghost/kg-html-to-lexical": "1.1.15", - "@tryghost/kg-lexical-html-renderer": "1.1.17", + "@tryghost/kg-default-nodes": "1.1.15", + "@tryghost/kg-html-to-lexical": "1.1.16", + "@tryghost/kg-lexical-html-renderer": "1.1.18", "@tryghost/kg-mobiledoc-html-renderer": "7.0.6", "@tryghost/limit-service": "1.2.14", "@tryghost/link-redirects": "0.0.0", diff --git a/yarn.lock b/yarn.lock index bb35cf006d..0d9c919ffb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7847,10 +7847,10 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-nodes@1.1.14": - version "1.1.14" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.14.tgz#39039072451df411972e68ffa798ce1df768269d" - integrity sha512-X+aTPHgB6OuJeuQPK9xPNNcYgtL9pUIwOam0+RYzlsGswYMo0fAhxijPAo8oIgDyJ9gQRmMexHM0qWBNUvmHaA== +"@tryghost/kg-default-nodes@1.1.15": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-nodes/-/kg-default-nodes-1.1.15.tgz#eeaed10aade6a0f4d5031c2530290c8dc73be06d" + integrity sha512-RN5UVt75od0inG93kDlarl7cJ1BpVsGP3pLCBKyIxYOQVTotclQXIm0/Y4N+KklhVoUs6YQZV8ZC4W30jV1HlQ== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/rich-text" "0.13.1" @@ -7864,21 +7864,21 @@ lodash "^4.17.21" luxon "^3.5.0" -"@tryghost/kg-default-transforms@1.1.15": - version "1.1.15" - resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.15.tgz#054231b5b72934d65e694e5d81730d13594424ec" - integrity sha512-iw4G3iq1X8Rc+6Fbcd9LWZWFt/VBQrdWW5klE4IMXii98saYdSnBcy2/mo+F7UWH81edHENfX1S4Zwd2zuA9eg== +"@tryghost/kg-default-transforms@1.1.16": + version "1.1.16" + resolved "https://registry.yarnpkg.com/@tryghost/kg-default-transforms/-/kg-default-transforms-1.1.16.tgz#e95cfa76267aaf34243e445b99c9e212001db3f9" + integrity sha512-WMNYehmUg0u7R+tYm3WIxP8WDA51MYxibTL2LNo17VjDHJp/VFCoDhc6I2tAGeH7loVBtp421llMr+33aa5DHQ== dependencies: "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" "@lexical/utils" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.14" + "@tryghost/kg-default-nodes" "1.1.15" lexical "0.13.1" -"@tryghost/kg-html-to-lexical@1.1.15": - version "1.1.15" - resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.15.tgz#c3ba3c96051a46bce4d7c6c42cdc33570d5d6e72" - integrity sha512-KOeBy56zjMPEjk2yUd3f2UnbPv/tic0g07vkCXuOFrz2wKJFX9MILQlmuVK5lHULnqKTGiZZ0yY6SmmKiLGYIA== +"@tryghost/kg-html-to-lexical@1.1.16": + version "1.1.16" + resolved "https://registry.yarnpkg.com/@tryghost/kg-html-to-lexical/-/kg-html-to-lexical-1.1.16.tgz#f2048bd42a5faafd28e9380b940e7a69a7ebc4b7" + integrity sha512-LLnZ+1jPonbDJ0DbBqfJwOVfBw1HfLjsyvM2reZO7GRq7J+ScLUxgc2AMe3q/bT9Dlny0qfCDSO/0kW/nUMqGA== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/headless" "0.13.1" @@ -7886,15 +7886,15 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.14" - "@tryghost/kg-default-transforms" "1.1.15" + "@tryghost/kg-default-nodes" "1.1.15" + "@tryghost/kg-default-transforms" "1.1.16" jsdom "^24.1.0" lexical "0.13.1" -"@tryghost/kg-lexical-html-renderer@1.1.17": - version "1.1.17" - resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.17.tgz#3628aa45f30b8355638bda91ba5ced908e116882" - integrity sha512-DSVQadD+N/67mhK4UzAChXPk5l5dqnOASqldY0CTzB0LhKUMRMKjZIDLpU4coo0Q8ckSqdYPqeSCP6s7I4F77Q== +"@tryghost/kg-lexical-html-renderer@1.1.18": + version "1.1.18" + resolved "https://registry.yarnpkg.com/@tryghost/kg-lexical-html-renderer/-/kg-lexical-html-renderer-1.1.18.tgz#b2475194f2e64126be3a36ee59457065e52507f9" + integrity sha512-36IHSXx373rWIitpPIx6bN5uPZRi7lFuv5JY6IYPnB519UruV5V+YJvSFZhNv6YYZgrV6P1Zcum6TB4bGY9+LA== dependencies: "@lexical/clipboard" "0.13.1" "@lexical/code" "0.13.1" @@ -7902,8 +7902,8 @@ "@lexical/link" "0.13.1" "@lexical/list" "0.13.1" "@lexical/rich-text" "0.13.1" - "@tryghost/kg-default-nodes" "1.1.14" - "@tryghost/kg-default-transforms" "1.1.15" + "@tryghost/kg-default-nodes" "1.1.15" + "@tryghost/kg-default-transforms" "1.1.16" jsdom "^24.1.0" lexical "0.13.1" @@ -7950,10 +7950,10 @@ dependencies: semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.18": - version "1.3.18" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.18.tgz#b48b1edf8485df7f196e19b37fe6dc1e77a7fdba" - integrity sha512-UmzdzDIV44yUq3C/dhlVdQtUybimLaDXt9E6cywc33b2USR3NN3k9fta02RWZab7nhiLFwm5Dt6CedpZTE70Ag== +"@tryghost/koenig-lexical@1.3.19": + version "1.3.19" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.19.tgz#f3766260da782cfd14bd74ffca01009a1786c80b" + integrity sha512-X8oYWGl65cJ2XLDopWiTZtkc2sVgWerOSBrTFBvq6+oh0FKpYUKEYGBVN3xZm7wEnwwtQgqpbB6MwSiMDjGCag== "@tryghost/limit-service@1.2.14": version "1.2.14" From 37fd9eaad5d713df314e6fad5fd988e110fe4241 Mon Sep 17 00:00:00 2001 From: Ronald Langeveld Date: Thu, 15 Aug 2024 16:37:04 +0700 Subject: [PATCH 149/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20additional=20whi?= =?UTF-8?q?te=20space=20appearing=20at=20bottom=20of=20editor=20(#20757)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref ENG-1490 - Regression from the secondary lexical instance. - Replaced the visibility and positioning styles with width, height, and overflow to prevent layout space issues. - Ensured the element takes up no space and is not visible, while allowing proper initialisation. --- .../app/components/koenig-lexical-editor.js | 2 +- .../test/e2e-browser/admin/publishing.spec.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ghost/admin/app/components/koenig-lexical-editor.js b/ghost/admin/app/components/koenig-lexical-editor.js index 704aac7e6d..f2a471a463 100644 --- a/ghost/admin/app/components/koenig-lexical-editor.js +++ b/ghost/admin/app/components/koenig-lexical-editor.js @@ -680,7 +680,7 @@ export default class KoenigLexicalEditor extends Component { const KGEditorComponent = ({isInitInstance}) => { return ( -
    +
    { }); }); + test.describe('Lexical Rendering', () => { + test.describe.configure({retries: 1}); + + test('Renders Lexical editor', async ({sharedPage: adminPage}) => { + await adminPage.goto('/ghost'); + + await createPostDraft(adminPage, {title: 'Lexical editor test', body: 'This is my post body.'}); + + // Check if the lexical editor is present + expect(await adminPage.locator('[data-kg="editor"]').first()).toBeVisible(); + }); + + test('Renders secondary hidden lexical editor', async ({sharedPage: adminPage}) => { + await adminPage.goto('/ghost'); + + await createPostDraft(adminPage, {title: 'Secondary lexical editor test', body: 'This is my post body.'}); + + // Check if the secondary lexical editor exists but is hidden. + expect(await adminPage.locator('[data-secondary-instance="true"]')).toBeHidden(); + }); + }); + test.describe('Update post', () => { test.describe.configure({retries: 1}); From 5ee67892dc2494d741b59b0789c8f6f8c3bf8e60 Mon Sep 17 00:00:00 2001 From: Sodbileg Gansukh Date: Thu, 15 Aug 2024 18:22:32 +0800 Subject: [PATCH 150/164] Improved publishing flow end screen (#20701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref DES-594 - this update introduces some improvements to the publishing flow end screen - everything's behind a feature flag — publishFlowEndScreen --- .../src/assets/icons/share.svg | 1 + .../settings/advanced/labs/AlphaFeatures.tsx | 8 + .../components/editor/modals/publish-flow.hbs | 14 +- .../editor/modals/publish-flow/confirm.js | 10 ++ .../app/components/modal-post-success.hbs | 152 ++++++++++++++++++ .../app/components/modal-post-success.js | 95 +++++++++++ .../admin/app/components/posts-list/list.hbs | 4 +- ghost/admin/app/components/posts-list/list.js | 32 ++++ .../admin/app/components/posts/analytics.hbs | 60 ++++++- ghost/admin/app/components/posts/analytics.js | 76 +++++++++ ghost/admin/app/services/feature.js | 2 + .../admin/app/styles/components/dropdowns.css | 3 +- .../app/styles/components/publishmenu.css | 120 ++++++++++++++ ghost/admin/app/styles/layouts/content.css | 15 ++ ghost/admin/app/templates/posts.hbs | 2 +- ghost/admin/public/assets/icons/reload.svg | 2 +- ghost/admin/public/assets/icons/share.svg | 1 + .../public/assets/icons/social-threads.svg | 10 ++ ghost/core/core/shared/labs.js | 4 +- 19 files changed, 595 insertions(+), 16 deletions(-) create mode 100644 apps/admin-x-design-system/src/assets/icons/share.svg create mode 100644 ghost/admin/app/components/modal-post-success.hbs create mode 100644 ghost/admin/app/components/modal-post-success.js create mode 100644 ghost/admin/public/assets/icons/share.svg create mode 100644 ghost/admin/public/assets/icons/social-threads.svg diff --git a/apps/admin-x-design-system/src/assets/icons/share.svg b/apps/admin-x-design-system/src/assets/icons/share.svg new file mode 100644 index 0000000000..6feac81448 --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/share.svg @@ -0,0 +1 @@ +Share 1 Streamline Icon: https://streamlinehq.comshare-1 \ No newline at end of file diff --git a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx index f23705753d..39dd01a525 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx @@ -59,6 +59,14 @@ const features = [{ title: 'Content Visibility', description: 'Enables content visibility in Emails', flag: 'contentVisibility' +},{ + title: 'Publish Flow — End Screen', + description: 'Enables improved publish flow', + flag: 'publishFlowEndScreen' +},{ + title: 'Post Analytics — Refresh', + description: 'Adds a refresh button to the post analytics screen', + flag: 'postAnalyticsRefresh' }]; const AlphaFeatures: React.FC = () => { diff --git a/ghost/admin/app/components/editor/modals/publish-flow.hbs b/ghost/admin/app/components/editor/modals/publish-flow.hbs index e8264e4746..e202eb7bff 100644 --- a/ghost/admin/app/components/editor/modals/publish-flow.hbs +++ b/ghost/admin/app/components/editor/modals/publish-flow.hbs @@ -45,12 +45,14 @@ @close={{@close}} /> {{else if this.isComplete}} - + {{#unless (feature "publishFlowEndScreen")}} + + {{/unless}} {{else}} + {{#if this.post.featureImage}} + + {{else if this.post.twitterImage}} + + {{else if this.post.ogImage}} + + {{/if}} + + + + + + + +
    + {{#if (and this.post.isPublished (not this.post.emailOnly))}} + + + + + + {{else}} + {{#if (and this.post.isScheduled (not this.post.emailOnly))}} + + {{/if}} + + + {{/if}} +
    +
    diff --git a/ghost/admin/app/components/modal-post-success.js b/ghost/admin/app/components/modal-post-success.js new file mode 100644 index 0000000000..32ed28216c --- /dev/null +++ b/ghost/admin/app/components/modal-post-success.js @@ -0,0 +1,95 @@ +import Component from '@glimmer/component'; +import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard'; +import {action} from '@ember/object'; +import {capitalize} from '@ember/string'; +import {inject as service} from '@ember/service'; +import {task, timeout} from 'ember-concurrency'; + +export default class PostSuccessModal extends Component { + @service store; + @service router; + @service notifications; + + static modalOptions = { + className: 'fullscreen-modal-wide fullscreen-modal-action modal-post-success' + }; + + get post() { + return this.args.data.post; + } + + get postCount() { + return this.args.data.postCount; + } + + get showPostCount() { + return this.args.data.showPostCount; + } + + @action + handleTwitter() { + window.open(`https://twitter.com/intent/tweet?url=${encodeURI(this.post.url)}`, '_blank'); + } + + @action + handleThreads() { + window.open(`https://threads.net/intent/post?text=${encodeURI(this.post.url)}`, '_blank'); + } + + @action + handleFacebook() { + window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURI(this.post.url)}`, '_blank'); + } + + @action + handleLinkedIn() { + window.open(`http://www.linkedin.com/shareArticle?mini=true&url=${encodeURI(this.post.url)}`, '_blank'); + } + + @action + viewInBrowser() { + window.open(this.post.url, '_blank'); + } + + @task + *handleCopyLink() { + copyTextToClipboard(this.post.url); + yield timeout(1000); + return true; + } + + @task + *handleCopyPreviewLink() { + copyTextToClipboard(this.post.previewUrl); + yield timeout(1000); + return true; + } + + @task + *revertToDraftTask() { + const currentPost = this.post; + const originalStatus = currentPost.status; + const originalPublishedAtUTC = currentPost.publishedAtUTC; + + try { + if (currentPost.isScheduled) { + currentPost.publishedAtUTC = null; + } + + currentPost.status = 'draft'; + currentPost.emailOnly = false; + + yield currentPost.save(); + this.router.transitionTo('lexical-editor.edit', 'post', currentPost.id); + + const postType = capitalize(currentPost.displayName); + this.notifications.showNotification(`${postType} reverted to a draft.`, {type: 'success'}); + + return true; + } catch (e) { + currentPost.status = originalStatus; + currentPost.publishedAtUTC = originalPublishedAtUTC; + throw e; + } + } +} diff --git a/ghost/admin/app/components/posts-list/list.hbs b/ghost/admin/app/components/posts-list/list.hbs index 99e0dcf7d5..c9cf59e331 100644 --- a/ghost/admin/app/components/posts-list/list.hbs +++ b/ghost/admin/app/components/posts-list/list.hbs @@ -1,5 +1,5 @@ - {{!-- always order as scheduled, draft, remainder --}} + {{!-- always order as scheduled, draft, remainder --}} {{#if (or @model.scheduledInfinityModel (or @model.draftInfinityModel @model.publishedAndSentInfinityModel))}} {{#if @model.scheduledInfinityModel}} {{#each @model.scheduledInfinityModel as |post|}} @@ -42,4 +42,4 @@ as |menu| > - + \ No newline at end of file diff --git a/ghost/admin/app/components/posts-list/list.js b/ghost/admin/app/components/posts-list/list.js index acf7d30687..d0ecbb7a42 100644 --- a/ghost/admin/app/components/posts-list/list.js +++ b/ghost/admin/app/components/posts-list/list.js @@ -1,7 +1,39 @@ import Component from '@glimmer/component'; +import PostSuccessModal from '../modal-post-success'; +import {inject as service} from '@ember/service'; +import {task} from 'ember-concurrency'; export default class PostsList extends Component { + @service store; + @service modals; + @service feature; + + latestScheduledPost = null; + + constructor() { + super(...arguments); + if (this.feature.publishFlowEndScreen) { + this.checkPublishFlowModal(); + } + } + + async checkPublishFlowModal() { + if (localStorage.getItem('ghost-last-scheduled-post')) { + await this.getLatestScheduledPost.perform(); + this.modals.open(PostSuccessModal, { + post: this.latestScheduledPost + }); + localStorage.removeItem('ghost-last-scheduled-post'); + } + } + get list() { return this.args.list; } + + @task + *getLatestScheduledPost() { + const result = yield this.store.query('post', {filter: `id:${localStorage.getItem('ghost-last-scheduled-post')}`, limit: 1}); + this.latestScheduledPost = result.toArray()[0]; + } } diff --git a/ghost/admin/app/components/posts/analytics.hbs b/ghost/admin/app/components/posts/analytics.hbs index c08e6aeb71..6f20b826af 100644 --- a/ghost/admin/app/components/posts/analytics.hbs +++ b/ghost/admin/app/components/posts/analytics.hbs @@ -34,9 +34,61 @@ {{moment-format publishedAt "HH:mm"}} {{/let}}
    - - {{svg-jar "pen" title=""}}Edit post - + {{#if (feature "publishFlowEndScreen")}} +
    + {{#if (feature "postAnalyticsRefresh")}} + + {{/if}} + {{#unless this.post.emailOnly}} + + {{/unless}} + + + + + {{svg-jar "dotdotdot"}} + + + + +
  • + Edit post +
  • +
  • + View in browser +
  • +
  • + +
  • +
    +
    +
    + {{else}} + + {{svg-jar "pen" title=""}}Edit post + + {{/if}}
    @@ -201,4 +253,4 @@
    {{/if}} - + \ No newline at end of file diff --git a/ghost/admin/app/components/posts/analytics.js b/ghost/admin/app/components/posts/analytics.js index 0f81f7454d..3fe7e136d3 100644 --- a/ghost/admin/app/components/posts/analytics.js +++ b/ghost/admin/app/components/posts/analytics.js @@ -1,4 +1,6 @@ import Component from '@glimmer/component'; +import DeletePostModal from '../modals/delete-post'; +import PostSuccessModal from '../modal-post-success'; import {action} from '@ember/object'; import {didCancel, task} from 'ember-concurrency'; import {inject as service} from '@ember/service'; @@ -24,6 +26,9 @@ export default class Analytics extends Component { @service utils; @service feature; @service store; + @service router; + @service modals; + @service notifications; @tracked sources = null; @tracked links = null; @@ -31,12 +36,47 @@ export default class Analytics extends Component { @tracked sortColumn = 'signups'; @tracked showSuccess; @tracked updateLinkId; + @tracked _post = null; + @tracked postCount = null; + @tracked showPostCount = false; displayOptions = DISPLAY_OPTIONS; + constructor() { + super(...arguments); + if (this.feature.publishFlowEndScreen) { + this.checkPublishFlowModal(); + } + } + + openPublishFlowModal() { + this.modals.open(PostSuccessModal, { + post: this.post, + postCount: this.postCount, + showPostCount: this.showPostCount + }); + } + + async checkPublishFlowModal() { + if (localStorage.getItem('ghost-last-published-post')) { + await this.fetchPostCountTask.perform(); + this.showPostCount = true; + this.openPublishFlowModal(); + localStorage.removeItem('ghost-last-published-post'); + } + } + get post() { + if (this.feature.publishFlowEndScreen) { + return this._post ?? this.args.post; + } + return this.args.post; } + set post(value) { + this._post = value; + } + get allowedDisplayOptions() { if (!this.hasPaidConversionData) { return this.displayOptions.filter(d => d.value === 'signups'); @@ -142,6 +182,19 @@ export default class Analytics extends Component { } } + @action + togglePublishFlowModal() { + this.showPostCount = false; + this.openPublishFlowModal(); + } + + @action + confirmDeleteMember() { + this.modals.open(DeletePostModal, { + post: this.post + }); + } + updateLinkData(linksData) { let updatedLinks; if (this.links?.length) { @@ -302,6 +355,29 @@ export default class Analytics extends Component { this.mentions = yield this.store.query('mention', {limit: 5, order: 'created_at desc', filter}); } + @task + *fetchPostCountTask() { + if (!this.post.emailOnly) { + const result = yield this.store.query('post', {filter: 'status:published', limit: 1}); + let count = result.meta.pagination.total; + + this.postCount = count; + } + } + + @task + *fetchPostTask() { + const result = yield this.store.query('post', {filter: `id:${this.post.id}`, limit: 1}); + this.post = result.toArray()[0]; + + if (this.post.email) { + this.notifications.showNotification('Post analytics refreshing', { + description: 'It can take up to five minutes for all data to show.', + type: 'success' + }); + } + } + get showLinks() { return this.post.showEmailClickAnalytics; } diff --git a/ghost/admin/app/services/feature.js b/ghost/admin/app/services/feature.js index 09f4042a45..14be59d768 100644 --- a/ghost/admin/app/services/feature.js +++ b/ghost/admin/app/services/feature.js @@ -78,6 +78,8 @@ export default class FeatureService extends Service { @feature('ActivityPub') ActivityPub; @feature('editorExcerpt') editorExcerpt; @feature('contentVisibility') contentVisibility; + @feature('publishFlowEndScreen') publishFlowEndScreen; + @feature('postAnalyticsRefresh') postAnalyticsRefresh; _user = null; diff --git a/ghost/admin/app/styles/components/dropdowns.css b/ghost/admin/app/styles/components/dropdowns.css index 099644e81d..96a1b808a1 100644 --- a/ghost/admin/app/styles/components/dropdowns.css +++ b/ghost/admin/app/styles/components/dropdowns.css @@ -393,7 +393,8 @@ Post context menu stroke-width: 1.8px; } -.gh-posts-context-menu li:last-child::before { +.gh-posts-context-menu li:last-child::before, +.gh-analytics-actions-menu li:last-child::before { display: block; position: relative; content: ""; diff --git a/ghost/admin/app/styles/components/publishmenu.css b/ghost/admin/app/styles/components/publishmenu.css index 6a781a9d63..d1e164c81e 100644 --- a/ghost/admin/app/styles/components/publishmenu.css +++ b/ghost/admin/app/styles/components/publishmenu.css @@ -880,3 +880,123 @@ height: 20px; margin-right: 6px; } + +/* Publish flow modal +/* ---------------------------------------------------------- */ + +.modal-post-success { + max-width: 640px; + --padding: 40px; + --radius: 12px; +} + +.modal-post-success .modal-content { + padding: var(--padding); + border-radius: var(--radius); +} + +.modal-post-success .modal-image { + aspect-ratio: 16 / 7.55; + overflow: hidden; + margin: calc(var(--padding) * -1) calc(var(--padding) * -1) var(--padding); +} + +.modal-post-success .modal-image img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: var(--radius) var(--radius) 0 0; +} + +.modal-post-success .modal-header { + margin: 0; +} + +.modal-post-success .modal-header h1 { + display: flex; + flex-direction: column; + margin: 0; + font-size: 3.6rem; + font-weight: 700; + letter-spacing: -0.03em; +} + +.modal-post-success .modal-header h1 span:has(+ span) { + color: var(--green); +} + +.modal-post-success .modal-body { + margin-top: 16px; + font-size: 1.8rem; + line-height: 1.4; + letter-spacing: -0.002em; +} + +.modal-post-success .modal-footer { + gap: 16px; + margin-top: var(--padding); +} + +.modal-post-success .modal-footer .gh-btn { + min-width: 64px; + height: 44px; + border-radius: 4px; +} + +.modal-post-success .modal-footer .gh-btn:not(:first-child) { + margin: 0; +} + +.modal-post-success .modal-footer .gh-btn span { + padding-inline: 18px; + font-size: 1.6rem; +} + +.modal-post-success .modal-footer .gh-btn-primary { + min-width: 80px; +} + +.modal-post-success .modal-footer:has(.twitter) .gh-btn-primary { + flex-grow: 1; +} + +.modal-post-success .modal-footer .gh-btn:is(.twitter, .threads, .facebook, .linkedin) { + width: 56px; +} + +.modal-post-success .modal-footer .gh-btn:is(.twitter, .threads, .facebook, .linkedin) span { + font-size: 0; +} + +.modal-post-success .modal-footer .gh-btn svg { + width: 18px; + height: 18px; +} + +.modal-post-success .modal-footer .gh-btn.twitter svg path { + fill: black; +} + +.modal-post-success:has(.modal-image) .close { + display: flex; + justify-content: center; + align-items: center; + width: 32px; + height: 32px; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 50%; +} + +.modal-post-success:has(.modal-image) .close:hover { + background-color: rgba(0, 0, 0, 0.25); +} + +.modal-post-success:has(.modal-image) .close svg { + width: 14px; + height: 14px; +} + +.modal-post-success:has(.modal-image) .close svg path { + fill: white; +} diff --git a/ghost/admin/app/styles/layouts/content.css b/ghost/admin/app/styles/layouts/content.css index a4e6f6aa5c..3964ce4374 100644 --- a/ghost/admin/app/styles/layouts/content.css +++ b/ghost/admin/app/styles/layouts/content.css @@ -776,6 +776,17 @@ border-radius: var(--border-radius); } +.gh-analytics-actions-menu { + top: calc(100% + 6px); + left: auto; + right: 0; +} + +.gh-analytics-actions-menu.fade-out { + animation-duration: .001s; + pointer-events: none; +} + .feature-audienceFeedback .gh-post-analytics-box.gh-post-analytics-newsletter-clicks, .feature-audienceFeedback .gh-post-analytics-box.gh-post-analytics-source-attribution, .gh-post-analytics-box.gh-post-analytics-mentions { @@ -1523,6 +1534,10 @@ transition: all .1s linear; } +span.dropdown .gh-post-list-cta > span { + padding: 0; +} + .gh-post-list-cta.edit.is-hovered > *, .gh-post-list-cta.edit.is-hovered:hover > *, .gh-post-list-cta.edit:not(.is-hovered):hover > * { diff --git a/ghost/admin/app/templates/posts.hbs b/ghost/admin/app/templates/posts.hbs index 3d99d9ee66..dc3e26361d 100644 --- a/ghost/admin/app/templates/posts.hbs +++ b/ghost/admin/app/templates/posts.hbs @@ -73,4 +73,4 @@ {{outlet}} - + \ No newline at end of file diff --git a/ghost/admin/public/assets/icons/reload.svg b/ghost/admin/public/assets/icons/reload.svg index a094276a30..5d17641a8a 100644 --- a/ghost/admin/public/assets/icons/reload.svg +++ b/ghost/admin/public/assets/icons/reload.svg @@ -1,6 +1,6 @@ reload - + diff --git a/ghost/admin/public/assets/icons/share.svg b/ghost/admin/public/assets/icons/share.svg new file mode 100644 index 0000000000..6feac81448 --- /dev/null +++ b/ghost/admin/public/assets/icons/share.svg @@ -0,0 +1 @@ +Share 1 Streamline Icon: https://streamlinehq.comshare-1 \ No newline at end of file diff --git a/ghost/admin/public/assets/icons/social-threads.svg b/ghost/admin/public/assets/icons/social-threads.svg new file mode 100644 index 0000000000..dc95af6271 --- /dev/null +++ b/ghost/admin/public/assets/icons/social-threads.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/ghost/core/core/shared/labs.js b/ghost/core/core/shared/labs.js index e9a1a7ad68..4f242c4ccc 100644 --- a/ghost/core/core/shared/labs.js +++ b/ghost/core/core/shared/labs.js @@ -45,7 +45,9 @@ const ALPHA_FEATURES = [ 'importMemberTier', 'lexicalIndicators', 'adminXDemo', - 'contentVisibility' + 'contentVisibility', + 'publishFlowEndScreen', + 'postAnalyticsRefresh' ]; module.exports.GA_KEYS = [...GA_FEATURES]; From 108c9f60c8cac17247869b575a63099a2e3faaef Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 15 Aug 2024 12:28:08 +0200 Subject: [PATCH 151/164] Add reusable UI components to ActivityPub (#20758) AP-348 ATM the top navigation and the article drawer components are missing for ActivityPub UI. They are both part of the next phase so we need to add them. --- apps/admin-x-activitypub/src/App.tsx | 4 +- apps/admin-x-activitypub/src/MainContent.tsx | 43 ++++- .../src/components/Activities.tsx | 52 +++++ .../src/components/Inbox.tsx | 83 ++++++++ .../src/components/Profile.tsx | 72 +++++++ .../src/components/Search.tsx | 18 ++ .../{ListIndex.tsx => _ObsoleteListIndex.tsx} | 86 +++++---- .../components/activities/ActivityItem.tsx | 27 +++ .../src/components/feed/ArticleModal.tsx | 94 +++++++++ .../src/components/feed/FeedItem.tsx | 179 ++++++++++++++++++ .../src/components/global/APAvatar.tsx | 17 ++ .../FollowSiteModal.tsx} | 2 +- .../src/components/modals.tsx | 6 +- .../src/components/navigation/MainHeader.tsx | 17 ++ .../components/navigation/MainNavigation.tsx | 29 +++ .../ViewFollowersModal.tsx} | 6 +- .../ViewFollowingModal.tsx} | 6 +- .../src/assets/icons/bell.svg | 1 + .../src/assets/icons/home.svg | 1 + .../src/assets/icons/user.svg | 1 + .../admin-x-design-system/src/global/Icon.tsx | 30 +-- .../src/global/modal/Modal.stories.tsx | 26 +++ .../src/global/modal/Modal.tsx | 10 +- .../admin-x-design-system/tailwind.config.cjs | 11 ++ 24 files changed, 752 insertions(+), 69 deletions(-) create mode 100644 apps/admin-x-activitypub/src/components/Activities.tsx create mode 100644 apps/admin-x-activitypub/src/components/Inbox.tsx create mode 100644 apps/admin-x-activitypub/src/components/Profile.tsx create mode 100644 apps/admin-x-activitypub/src/components/Search.tsx rename apps/admin-x-activitypub/src/components/{ListIndex.tsx => _ObsoleteListIndex.tsx} (92%) create mode 100644 apps/admin-x-activitypub/src/components/activities/ActivityItem.tsx create mode 100644 apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx create mode 100644 apps/admin-x-activitypub/src/components/feed/FeedItem.tsx create mode 100644 apps/admin-x-activitypub/src/components/global/APAvatar.tsx rename apps/admin-x-activitypub/src/components/{FollowSite.tsx => inbox/FollowSiteModal.tsx} (97%) create mode 100644 apps/admin-x-activitypub/src/components/navigation/MainHeader.tsx create mode 100644 apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx rename apps/admin-x-activitypub/src/components/{ViewFollowers.tsx => profile/ViewFollowersModal.tsx} (94%) rename apps/admin-x-activitypub/src/components/{ViewFollowing.tsx => profile/ViewFollowingModal.tsx} (95%) create mode 100644 apps/admin-x-design-system/src/assets/icons/bell.svg create mode 100644 apps/admin-x-design-system/src/assets/icons/home.svg create mode 100644 apps/admin-x-design-system/src/assets/icons/user.svg diff --git a/apps/admin-x-activitypub/src/App.tsx b/apps/admin-x-activitypub/src/App.tsx index 983751ea45..62145ea0ea 100644 --- a/apps/admin-x-activitypub/src/App.tsx +++ b/apps/admin-x-activitypub/src/App.tsx @@ -11,8 +11,8 @@ interface AppProps { const modals = { paths: { 'follow-site': 'FollowSite', - 'view-following': 'ViewFollowing', - 'view-followers': 'ViewFollowers' + 'profile/following': 'ViewFollowing', + 'profile/followers': 'ViewFollowers' }, load: async () => import('./components/modals') }; diff --git a/apps/admin-x-activitypub/src/MainContent.tsx b/apps/admin-x-activitypub/src/MainContent.tsx index 31ddedf3f8..e8f05cba87 100644 --- a/apps/admin-x-activitypub/src/MainContent.tsx +++ b/apps/admin-x-activitypub/src/MainContent.tsx @@ -1,7 +1,46 @@ -import ActivityPubComponent from './components/ListIndex'; +import Activities from './components/Activities'; +import Inbox from './components/Inbox'; +import Profile from './components/Profile'; +import Search from './components/Search'; +import {ActivityPubAPI} from './api/activitypub'; +import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; +import {useQuery} from '@tanstack/react-query'; +import {useRouting} from '@tryghost/admin-x-framework/routing'; + +export function useBrowseInboxForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`inbox:${handle}`], + async queryFn() { + return api.getInbox(); + } + }); +} const MainContent = () => { - return ; + const {route} = useRouting(); + const mainRoute = route.split('/')[0]; + switch (mainRoute) { + case 'search': + return ; + break; + case 'activity': + return ; + break; + case 'profile': + return ; + break; + default: + return ; + break; + } }; export default MainContent; diff --git a/apps/admin-x-activitypub/src/components/Activities.tsx b/apps/admin-x-activitypub/src/components/Activities.tsx new file mode 100644 index 0000000000..14363cea24 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/Activities.tsx @@ -0,0 +1,52 @@ +import APAvatar from './global/APAvatar'; +import ActivityItem from './activities/ActivityItem'; +import MainNavigation from './navigation/MainNavigation'; +import React from 'react'; + +interface ActivitiesProps {} + +const Activities: React.FC = ({}) => { + // const fakeAuthor = + return ( + <> + +
    +
    + + +
    +
    Lydia Mango @username@domain.com
    +
    Followed you
    +
    +
    + + + +
    +
    Tiana Passaquindici Arcand @username@domain.com
    +
    Followed you
    +
    +
    + + + +
    +
    Gretchen Press @username@domain.com
    +
    Followed you
    +
    +
    + + + +
    +
    Leo Lubin @username@domain.com
    +
    Followed you
    +
    +
    +
    +
    + + ); +}; + +export default Activities; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/Inbox.tsx b/apps/admin-x-activitypub/src/components/Inbox.tsx new file mode 100644 index 0000000000..f4256444df --- /dev/null +++ b/apps/admin-x-activitypub/src/components/Inbox.tsx @@ -0,0 +1,83 @@ +import ActivityPubWelcomeImage from '../assets/images/ap-welcome.png'; +import ArticleModal from './feed/ArticleModal'; +import FeedItem from './feed/FeedItem'; +import MainNavigation from './navigation/MainNavigation'; +import NiceModal from '@ebay/nice-modal-react'; +import React, {useState} from 'react'; +import {Activity} from './activities/ActivityItem'; +import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; +import {Button, Heading} from '@tryghost/admin-x-design-system'; +import {useBrowseInboxForUser} from '../MainContent'; + +interface InboxProps {} + +const Inbox: React.FC = ({}) => { + const {data: activities = []} = useBrowseInboxForUser('index'); + const [, setArticleContent] = useState(null); + const [, setArticleActor] = useState(null); + + const inboxTabActivities = activities.filter((activity: Activity) => { + const isCreate = activity.type === 'Create' && ['Article', 'Note'].includes(activity.object.type); + const isAnnounce = activity.type === 'Announce' && activity.object.type === 'Note'; + + return isCreate || isAnnounce; + }); + + const handleViewContent = (object: ObjectProperties, actor: ActorProperties) => { + setArticleContent(object); + setArticleActor(actor); + NiceModal.show(ArticleModal, { + object: object + }); + }; + + return ( + <> + +
    +
    + {inboxTabActivities.length > 0 ? ( +
      + {inboxTabActivities.reverse().map(activity => ( +
    • handleViewContent(activity.object, activity.actor)} + > + +
    • + ))} +
    + ) : ( +
    +
    + Ghost site logos + + Welcome to ActivityPub + +

    + We’re so glad to have you on board! At the moment, you can follow other Ghost sites and enjoy their content right here inside Ghost. +

    +

    + You can see all of the users on the right—find your favorite ones and give them a follow. +

    +
    +
    + )} +
    +
    + + ); +}; + +export default Inbox; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/Profile.tsx b/apps/admin-x-activitypub/src/components/Profile.tsx new file mode 100644 index 0000000000..f0d00f3c89 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/Profile.tsx @@ -0,0 +1,72 @@ +import MainNavigation from './navigation/MainNavigation'; +import React from 'react'; +import {ActivityPubAPI} from '../api/activitypub'; +import {SettingValue} from '@tryghost/admin-x-design-system'; +import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; +import {useQuery} from '@tanstack/react-query'; +import {useRouting} from '@tryghost/admin-x-framework/routing'; + +interface ProfileProps {} + +function useFollowersCountForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`followersCount:${handle}`], + async queryFn() { + return api.getFollowersCount(); + } + }); +} + +function useFollowingCountForUser(handle: string) { + const site = useBrowseSite(); + const siteData = site.data?.site; + const siteUrl = siteData?.url ?? window.location.origin; + const api = new ActivityPubAPI( + new URL(siteUrl), + new URL('/ghost/api/admin/identities/', window.location.origin), + handle + ); + return useQuery({ + queryKey: [`followingCount:${handle}`], + async queryFn() { + return api.getFollowingCount(); + } + }); +} + +const Profile: React.FC = ({}) => { + const {updateRoute} = useRouting(); + const {data: followersCount = 0} = useFollowersCountForUser('index'); + const {data: followingCount = 0} = useFollowingCountForUser('index'); + + return ( + <> + +
    +
    +
    +
    +
    updateRoute('/profile/following')}> + {followingCount} + Following +
    +
    updateRoute('/profile/followers')}> + {followersCount} + Followers +
    +
    +
    +
    + + ); +}; + +export default Profile; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/Search.tsx b/apps/admin-x-activitypub/src/components/Search.tsx new file mode 100644 index 0000000000..8ed44b58e5 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/Search.tsx @@ -0,0 +1,18 @@ +import MainNavigation from './navigation/MainNavigation'; +import React from 'react'; +import {Icon} from '@tryghost/admin-x-design-system'; + +interface SearchProps {} + +const Search: React.FC = ({}) => { + return ( + <> + +
    +
    Search the Fediverse
    +
    + + ); +}; + +export default Search; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/ListIndex.tsx b/apps/admin-x-activitypub/src/components/_ObsoleteListIndex.tsx similarity index 92% rename from apps/admin-x-activitypub/src/components/ListIndex.tsx rename to apps/admin-x-activitypub/src/components/_ObsoleteListIndex.tsx index 62f05786b7..021cbbf1b6 100644 --- a/apps/admin-x-activitypub/src/components/ListIndex.tsx +++ b/apps/admin-x-activitypub/src/components/_ObsoleteListIndex.tsx @@ -22,7 +22,7 @@ type Activity = { } } -function useBrowseInboxForUser(handle: string) { +export function useBrowseInboxForUser(handle: string) { const site = useBrowseSite(); const siteData = site.data?.site; const siteUrl = siteData?.url ?? window.location.origin; @@ -224,51 +224,53 @@ const ActivityPubComponent: React.FC = () => { ]; return ( - - {!articleContent ? ( - { - setSelectedOption({label: 'Feed', value: 'feed'}); + <> + + {!articleContent ? ( + { + setSelectedOption({label: 'Feed', value: 'feed'}); + } + + }, + { + icon: 'cardview', + size: 'sm', + iconColorClass: selectedOption.value === 'inbox' ? 'text-black' : 'text-grey-500', + onClick: () => { + setSelectedOption({label: 'Inbox', value: 'inbox'}); + } } - - }, - { - icon: 'cardview', - size: 'sm', - iconColorClass: selectedOption.value === 'inbox' ? 'text-black' : 'text-grey-500', + ]} clearBg={true} link outlineOnMobile />]} + firstOnPage={true} + primaryAction={{ + title: 'Follow', onClick: () => { - setSelectedOption({label: 'Inbox', value: 'inbox'}); - } - } - ]} clearBg={true} link outlineOnMobile />]} - firstOnPage={true} - primaryAction={{ - title: 'Follow', - onClick: () => { - updateRoute('follow-site'); - }, - icon: 'add' - }} - selectedTab={selectedTab} - stickyHeader={true} - tabs={tabs} - title='ActivityPub' - toolbarBorder={true} - type='page' - onTabChange={setSelectedTab} - > - + updateRoute('follow-site'); + }, + icon: 'add' + }} + selectedTab={selectedTab} + stickyHeader={true} + tabs={tabs} + title='ActivityPub' + toolbarBorder={true} + type='page' + onTabChange={setSelectedTab} + > + - ) : ( - - )} + ) : ( + + )} - + + ); }; diff --git a/apps/admin-x-activitypub/src/components/activities/ActivityItem.tsx b/apps/admin-x-activitypub/src/components/activities/ActivityItem.tsx new file mode 100644 index 0000000000..af3ea48b3a --- /dev/null +++ b/apps/admin-x-activitypub/src/components/activities/ActivityItem.tsx @@ -0,0 +1,27 @@ +import React, {ReactNode} from 'react'; + +export type Activity = { + type: string, + object: { + type: string + } +} + +interface ActivityItemProps { + children?: ReactNode; +} + +const ActivityItem: React.FC = ({children}) => { + const childrenArray = React.Children.toArray(children); + + return ( +
    +
    + {childrenArray[0]} + {childrenArray[1]} +
    +
    + ); +}; + +export default ActivityItem; diff --git a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx new file mode 100644 index 0000000000..9da2d3f10f --- /dev/null +++ b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx @@ -0,0 +1,94 @@ +import MainHeader from '../navigation/MainHeader'; +import NiceModal, {useModal} from '@ebay/nice-modal-react'; +import React, {useEffect, useRef} from 'react'; +import articleBodyStyles from '../articleBodyStyles'; +import {Button, Modal} from '@tryghost/admin-x-design-system'; +import {ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; +import {renderAttachment} from './FeedItem'; +import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; + +interface ArticleModalProps { + object: ObjectProperties; +} + +const ArticleBody: React.FC<{heading: string, image: string|undefined, html: string}> = ({heading, image, html}) => { + const site = useBrowseSite(); + const siteData = site.data?.site; + + const iframeRef = useRef(null); + + const cssContent = articleBodyStyles(siteData?.url.replace(/\/$/, '')); + + const htmlContent = ` + + + ${cssContent} + + +
    +

    ${heading}

    +${image && + `
    + ${heading} +
    ` +} +
    +
    + ${html} +
    + + +`; + + useEffect(() => { + const iframe = iframeRef.current; + if (iframe) { + iframe.srcdoc = htmlContent; + } + }, [htmlContent]); + + return ( +
    + +
    + ); +}; + +const ArticleModal: React.FC = ({object}) => { + const modal = useModal(); + return ( + } + height={'full'} + padding={false} + size='bleed' + width={640} + > + +
    +
    +
    +
    + {object.type === 'Note' && ( +
    + {object.content &&
    } + {renderAttachment(object)} +
    )} + {object.type === 'Article' && } +
    +
    + ); +}; + +export default NiceModal.create(ArticleModal); \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/feed/FeedItem.tsx b/apps/admin-x-activitypub/src/components/feed/FeedItem.tsx new file mode 100644 index 0000000000..e35f2edd64 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/feed/FeedItem.tsx @@ -0,0 +1,179 @@ +import APAvatar from '../global/APAvatar'; +import React, {useState} from 'react'; +import getRelativeTimestamp from '../../utils/get-relative-timestamp'; +import getUsername from '../../utils/get-username'; +import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; +import {Button, Heading, Icon} from '@tryghost/admin-x-design-system'; + +export function renderAttachment(object: ObjectProperties) { + let attachment; + if (object.image) { + attachment = object.image; + } + + if (object.type === 'Note' && !attachment) { + attachment = object.attachment; + } + + if (!attachment) { + return null; + } + + if (Array.isArray(attachment)) { + const attachmentCount = attachment.length; + + let gridClass = ''; + if (attachmentCount === 1) { + gridClass = 'grid-cols-1'; // Single image, full width + } else if (attachmentCount === 2) { + gridClass = 'grid-cols-2'; // Two images, side by side + } else if (attachmentCount === 3 || attachmentCount === 4) { + gridClass = 'grid-cols-2'; // Three or four images, two per row + } + + return ( +
    + {attachment.map((item, index) => ( + {`attachment-${index}`} + ))} +
    + ); + } + + switch (attachment.mediaType) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + return attachment; + case 'video/mp4': + case 'video/webm': + return
    +
    ; + + case 'audio/mpeg': + case 'audio/ogg': + return
    +
    ; + default: + return null; + } +} + +interface FeedItemProps { + actor: ActorProperties; + object: ObjectProperties; + layout: string; + type: string; +} + +const FeedItem: React.FC = ({actor, object, layout, type}) => { + const parser = new DOMParser(); + const doc = parser.parseFromString(object.content || '', 'text/html'); + + const plainTextContent = doc.body.textContent; + let previewContent = ''; + if (object.preview) { + const previewDoc = parser.parseFromString(object.preview.content || '', 'text/html'); + previewContent = previewDoc.body.textContent || ''; + } else if (object.type === 'Note') { + previewContent = plainTextContent || ''; + } + + const timestamp = + new Date(object?.published ?? new Date()).toLocaleDateString('default', {year: 'numeric', month: 'short', day: '2-digit'}) + ', ' + new Date(object?.published ?? new Date()).toLocaleTimeString('default', {hour: '2-digit', minute: '2-digit'}); + + const date = new Date(object?.published ?? new Date()); + + const [isClicked, setIsClicked] = useState(false); + const [isLiked, setIsLiked] = useState(false); + + const handleLikeClick = (event: React.MouseEvent | undefined) => { + event?.stopPropagation(); + setIsClicked(true); + setIsLiked(!isLiked); + setTimeout(() => setIsClicked(false), 300); // Reset the animation class after 300ms + }; + + let author = actor; + if (type === 'Announce' && object.type === 'Note') { + author = typeof object.attributedTo === 'object' ? object.attributedTo as ActorProperties : actor; + } + + if (layout === 'feed') { + return ( + <> + {object && ( +
    + {(type === 'Announce' && object.type === 'Note') &&
    +
    + {actor.name} reposted +
    } +
    + +
    +
    +
    + {author.name} + {getRelativeTimestamp(date)} +
    +
    + {getUsername(author)} +
    +
    +
    +
    + {object.name && {object.name}} +
    + {/*

    {object.content}

    */} + {renderAttachment(object)} +
    +
    +
    +
    +
    +
    +
    +
    + )} + + ); + } else if (layout === 'inbox') { + return ( + <> + {object && ( +
    +
    + + {actor.name} + {/* {getUsername(actor)} */} + {timestamp} +
    +
    +
    +
    + {object.name} +
    +

    {previewContent}

    +
    +
    +
    + {/* {image &&
    + +
    } */} +
    +
    + {/*
    */} +
    + )} + + ); + } +}; + +export default FeedItem; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/global/APAvatar.tsx b/apps/admin-x-activitypub/src/components/global/APAvatar.tsx new file mode 100644 index 0000000000..1bfe40176d --- /dev/null +++ b/apps/admin-x-activitypub/src/components/global/APAvatar.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import {ActorProperties} from '@tryghost/admin-x-framework/api/activitypub'; +import {Icon} from '@tryghost/admin-x-design-system'; + +interface APAvatarProps { + author?: ActorProperties; +} + +const APAvatar: React.FC = ({author}) => { + return ( + <> + {author && author!.icon?.url ? :
    } + + ); +}; + +export default APAvatar; diff --git a/apps/admin-x-activitypub/src/components/FollowSite.tsx b/apps/admin-x-activitypub/src/components/inbox/FollowSiteModal.tsx similarity index 97% rename from apps/admin-x-activitypub/src/components/FollowSite.tsx rename to apps/admin-x-activitypub/src/components/inbox/FollowSiteModal.tsx index 55f613d18a..76ea0e4bb2 100644 --- a/apps/admin-x-activitypub/src/components/FollowSite.tsx +++ b/apps/admin-x-activitypub/src/components/inbox/FollowSiteModal.tsx @@ -1,5 +1,5 @@ import NiceModal from '@ebay/nice-modal-react'; -import {ActivityPubAPI} from '../api/activitypub'; +import {ActivityPubAPI} from '../../api/activitypub'; import {Modal, TextField, showToast} from '@tryghost/admin-x-design-system'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; import {useMutation} from '@tanstack/react-query'; diff --git a/apps/admin-x-activitypub/src/components/modals.tsx b/apps/admin-x-activitypub/src/components/modals.tsx index 5764840ba3..93361b1c0a 100644 --- a/apps/admin-x-activitypub/src/components/modals.tsx +++ b/apps/admin-x-activitypub/src/components/modals.tsx @@ -1,6 +1,6 @@ -import FollowSite from './FollowSite'; -import ViewFollowers from './ViewFollowers'; -import ViewFollowing from './ViewFollowing'; +import FollowSite from './inbox/FollowSiteModal'; +import ViewFollowers from './profile/ViewFollowersModal'; +import ViewFollowing from './profile/ViewFollowingModal'; import {ModalComponent} from '@tryghost/admin-x-framework/routing'; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/apps/admin-x-activitypub/src/components/navigation/MainHeader.tsx b/apps/admin-x-activitypub/src/components/navigation/MainHeader.tsx new file mode 100644 index 0000000000..db4ab93ba7 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/navigation/MainHeader.tsx @@ -0,0 +1,17 @@ +import React, {ReactNode} from 'react'; + +interface MainHeaderProps { + children?: ReactNode; +} + +const MainHeader: React.FC = ({children}) => { + return ( +
    +
    + {children} +
    +
    + ); +}; + +export default MainHeader; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx b/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx new file mode 100644 index 0000000000..a3e9736cf6 --- /dev/null +++ b/apps/admin-x-activitypub/src/components/navigation/MainNavigation.tsx @@ -0,0 +1,29 @@ +import MainHeader from './MainHeader'; +import React from 'react'; +import {Button} from '@tryghost/admin-x-design-system'; +import {useRouting} from '@tryghost/admin-x-framework/routing'; + +interface MainNavigationProps {} + +const MainNavigation: React.FC = ({}) => { + const {route, updateRoute} = useRouting(); + const mainRoute = route.split('/')[0]; + + return ( + +
    +
    +
    +
    +
    + ); +}; + +export default MainNavigation; \ No newline at end of file diff --git a/apps/admin-x-activitypub/src/components/ViewFollowers.tsx b/apps/admin-x-activitypub/src/components/profile/ViewFollowersModal.tsx similarity index 94% rename from apps/admin-x-activitypub/src/components/ViewFollowers.tsx rename to apps/admin-x-activitypub/src/components/profile/ViewFollowersModal.tsx index 99cc96fa17..db0dd424f3 100644 --- a/apps/admin-x-activitypub/src/components/ViewFollowers.tsx +++ b/apps/admin-x-activitypub/src/components/profile/ViewFollowersModal.tsx @@ -1,6 +1,6 @@ import NiceModal from '@ebay/nice-modal-react'; -import getUsername from '../utils/get-username'; -import {ActivityPubAPI} from '../api/activitypub'; +import getUsername from '../../utils/get-username'; +import {ActivityPubAPI} from '../../api/activitypub'; import {Avatar, Button, List, ListItem, Modal} from '@tryghost/admin-x-design-system'; import {RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; @@ -51,7 +51,7 @@ const ViewFollowersModal: React.FC = ({}) => { { mutation.reset(); - updateRoute(''); + updateRoute('profile'); }} cancelLabel='' footer={false} diff --git a/apps/admin-x-activitypub/src/components/ViewFollowing.tsx b/apps/admin-x-activitypub/src/components/profile/ViewFollowingModal.tsx similarity index 95% rename from apps/admin-x-activitypub/src/components/ViewFollowing.tsx rename to apps/admin-x-activitypub/src/components/profile/ViewFollowingModal.tsx index 4fc3de36c4..6b58dc94bd 100644 --- a/apps/admin-x-activitypub/src/components/ViewFollowing.tsx +++ b/apps/admin-x-activitypub/src/components/profile/ViewFollowingModal.tsx @@ -1,6 +1,6 @@ import NiceModal from '@ebay/nice-modal-react'; -import getUsername from '../utils/get-username'; -import {ActivityPubAPI} from '../api/activitypub'; +import getUsername from '../../utils/get-username'; +import {ActivityPubAPI} from '../../api/activitypub'; import {Avatar, Button, List, ListItem, Modal} from '@tryghost/admin-x-design-system'; import {RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {useBrowseSite} from '@tryghost/admin-x-framework/api/site'; @@ -32,7 +32,7 @@ const ViewFollowingModal: React.FC = ({}) => { return ( { - updateRoute(''); + updateRoute('profile'); }} cancelLabel='' footer={false} diff --git a/apps/admin-x-design-system/src/assets/icons/bell.svg b/apps/admin-x-design-system/src/assets/icons/bell.svg new file mode 100644 index 0000000000..e9b771d42d --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/bell.svg @@ -0,0 +1 @@ +Alarm Bell Streamline Icon: https://streamlinehq.comalarm-bell \ No newline at end of file diff --git a/apps/admin-x-design-system/src/assets/icons/home.svg b/apps/admin-x-design-system/src/assets/icons/home.svg new file mode 100644 index 0000000000..9f44dc3fac --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/home.svg @@ -0,0 +1 @@ +House Entrance Streamline Icon: https://streamlinehq.comhouse-entrance \ No newline at end of file diff --git a/apps/admin-x-design-system/src/assets/icons/user.svg b/apps/admin-x-design-system/src/assets/icons/user.svg new file mode 100644 index 0000000000..e954801d94 --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/user.svg @@ -0,0 +1 @@ +Single Neutral Streamline Icon: https://streamlinehq.comsingle-neutral \ No newline at end of file diff --git a/apps/admin-x-design-system/src/global/Icon.tsx b/apps/admin-x-design-system/src/global/Icon.tsx index 61e1621f8d..47fc5e7eaa 100644 --- a/apps/admin-x-design-system/src/global/Icon.tsx +++ b/apps/admin-x-design-system/src/global/Icon.tsx @@ -30,42 +30,50 @@ export interface IconProps { const Icon: React.FC = ({name, size = 'md', colorClass = '', className = ''}) => { const {ReactComponent: SvgComponent} = icons[`../assets/icons/${name}.svg`]; - let styles = ''; + let classes = ''; + let styles = {}; - if (!styles) { + if (typeof size === 'number') { + styles = { + width: `${size}px`, + height: `${size}px` + }; + } + + if (!classes) { switch (size) { case 'custom': break; case '2xs': - styles = 'w-2 h-2'; + classes = 'w-2 h-2'; break; case 'xs': - styles = 'w-3 h-3'; + classes = 'w-3 h-3'; break; case 'sm': - styles = 'w-4 h-4'; + classes = 'w-4 h-4'; break; case 'lg': - styles = 'w-8 h-8'; + classes = 'w-8 h-8'; break; case 'xl': - styles = 'w-10 h-10'; + classes = 'w-10 h-10'; break; default: - styles = 'w-5 h-5'; + classes = 'w-5 h-5'; break; } } - styles = clsx( - styles, + classes = clsx( + classes, colorClass ); if (SvgComponent) { return ( - + ); } return null; diff --git a/apps/admin-x-design-system/src/global/modal/Modal.stories.tsx b/apps/admin-x-design-system/src/global/modal/Modal.stories.tsx index e5ea357cf7..ddf397d0f2 100644 --- a/apps/admin-x-design-system/src/global/modal/Modal.stories.tsx +++ b/apps/admin-x-design-system/src/global/modal/Modal.stories.tsx @@ -203,6 +203,32 @@ export const CustomButtons: Story = { } }; +export const RightDrawer: Story = { + args: { + size: 'bleed', + align: 'right', + animate: false, + width: 600, + footer: <>, + children: <> +

    This is a drawer style on the right

    + + } +}; + +export const LeftDrawer: Story = { + args: { + size: 'bleed', + align: 'left', + animate: false, + width: 600, + footer: <>, + children: <> +

    This is a drawer style on the right

    + + } +}; + const longContent = ( <>

    Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.

    diff --git a/apps/admin-x-design-system/src/global/modal/Modal.tsx b/apps/admin-x-design-system/src/global/modal/Modal.tsx index e959a3c89f..36bd69bc42 100644 --- a/apps/admin-x-design-system/src/global/modal/Modal.tsx +++ b/apps/admin-x-design-system/src/global/modal/Modal.tsx @@ -18,6 +18,7 @@ export interface ModalProps { size?: ModalSize; width?: 'full' | number; height?: 'full' | number; + align?: 'center' | 'left' | 'right'; testId?: string; title?: string; @@ -52,6 +53,7 @@ export const topLevelBackdropClasses = 'bg-[rgba(98,109,121,0.2)] backdrop-blur- const Modal: React.FC = ({ size = 'md', + align = 'center', width, height, testId, @@ -188,10 +190,14 @@ const Modal: React.FC = ({ } let modalClasses = clsx( - 'relative z-50 mx-auto flex max-h-[100%] w-full flex-col justify-between overflow-x-hidden bg-white dark:bg-black', + 'relative z-50 flex max-h-[100%] w-full flex-col justify-between overflow-x-hidden bg-white dark:bg-black', + align === 'center' && 'mx-auto', + align === 'left' && 'mr-auto', + align === 'right' && 'ml-auto', size !== 'bleed' && 'rounded', formSheet ? 'shadow-md' : 'shadow-xl', - (animate && !formSheet && !animationFinished) && 'animate-modal-in', + (animate && !formSheet && !animationFinished && align === 'center') && 'animate-modal-in', + (animate && !formSheet && !animationFinished && align === 'right') && 'animate-modal-in-from-right', (formSheet && !animationFinished) && 'animate-modal-in-reverse', scrolling ? 'overflow-y-auto' : 'overflow-y-hidden' ); diff --git a/apps/admin-x-design-system/tailwind.config.cjs b/apps/admin-x-design-system/tailwind.config.cjs index 59a302365e..712560fc06 100644 --- a/apps/admin-x-design-system/tailwind.config.cjs +++ b/apps/admin-x-design-system/tailwind.config.cjs @@ -166,6 +166,16 @@ module.exports = { transform: 'translateY(0px)' } }, + modalInFromRight: { + '0%': { + transform: 'translateX(32px)', + opacity: '0' + }, + '100%': { + transform: 'translateX(0px)', + opacity: '1' + } + }, modalInReverse: { '0%': { transform: 'translateY(-32px)' @@ -192,6 +202,7 @@ module.exports = { 'setting-highlight-fade-out': 'fadeOut 0.2s 1.4s ease forwards', 'modal-backdrop-in': 'fadeIn 0.15s ease forwards', 'modal-in': 'modalIn 0.25s ease forwards', + 'modal-in-from-right': 'modalInFromRight 0.25s ease forwards', 'modal-in-reverse': 'modalInReverse 0.25s ease forwards', spin: 'spin 1s linear infinite' }, From d86bcd2873c14b5e3421c146601c84aa7c6e08c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 10:56:44 +0000 Subject: [PATCH 152/164] Update dependency @tryghost/koenig-lexical to v1.3.20 --- ghost/admin/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index dfaa0b7600..2f17c77474 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -49,7 +49,7 @@ "@tryghost/helpers": "1.1.90", "@tryghost/kg-clean-basic-html": "4.1.3", "@tryghost/kg-converters": "1.0.6", - "@tryghost/koenig-lexical": "1.3.19", + "@tryghost/koenig-lexical": "1.3.20", "@tryghost/limit-service": "1.2.14", "@tryghost/members-csv": "0.0.0", "@tryghost/nql": "0.12.3", diff --git a/yarn.lock b/yarn.lock index 0d9c919ffb..611475c50e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7950,10 +7950,10 @@ dependencies: semver "^7.6.2" -"@tryghost/koenig-lexical@1.3.19": - version "1.3.19" - resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.19.tgz#f3766260da782cfd14bd74ffca01009a1786c80b" - integrity sha512-X8oYWGl65cJ2XLDopWiTZtkc2sVgWerOSBrTFBvq6+oh0FKpYUKEYGBVN3xZm7wEnwwtQgqpbB6MwSiMDjGCag== +"@tryghost/koenig-lexical@1.3.20": + version "1.3.20" + resolved "https://registry.yarnpkg.com/@tryghost/koenig-lexical/-/koenig-lexical-1.3.20.tgz#cf4b818dfb7807fe5f3ff3bb17803d68f981428f" + integrity sha512-zZiNqorah/RcavTfjMHPsf/IZF1iomZInRy8tLJQhG9iUdO8WeQQ0M5KJGF7NwQEUzMs/1iBjjlELe+MrYY9SA== "@tryghost/limit-service@1.2.14": version "1.2.14" From 46f93ad55542044f5b0c162d7295e5222dad6981 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 15 Aug 2024 14:54:50 +0200 Subject: [PATCH 153/164] Refined details for new publish flow (#20761) DES-686 Typography for the new modal was a bit off, needed refinement. --- .../app/components/modal-post-success.hbs | 2 +- .../app/styles/components/publishmenu.css | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ghost/admin/app/components/modal-post-success.hbs b/ghost/admin/app/components/modal-post-success.hbs index 2f8715db6d..8fd086b91b 100644 --- a/ghost/admin/app/components/modal-post-success.hbs +++ b/ghost/admin/app/components/modal-post-success.hbs @@ -66,7 +66,7 @@ {{#if (or this.post.hasEmail this.post.willEmail)}} {{#let (members-count-fetcher query=(hash filter=this.post.fullRecipientFilter)) as |countFetcher|}} - + {{if (eq @recipientType "all") "all"}} {{format-number countFetcher.count}} diff --git a/ghost/admin/app/styles/components/publishmenu.css b/ghost/admin/app/styles/components/publishmenu.css index d1e164c81e..ee328e50b2 100644 --- a/ghost/admin/app/styles/components/publishmenu.css +++ b/ghost/admin/app/styles/components/publishmenu.css @@ -885,8 +885,8 @@ /* ---------------------------------------------------------- */ .modal-post-success { - max-width: 640px; - --padding: 40px; + max-width: 600px; + --padding: 36px; --radius: 12px; } @@ -917,7 +917,7 @@ display: flex; flex-direction: column; margin: 0; - font-size: 3.6rem; + font-size: 3.2rem; font-weight: 700; letter-spacing: -0.03em; } @@ -927,10 +927,15 @@ } .modal-post-success .modal-body { - margin-top: 16px; - font-size: 1.8rem; + margin-top: 10px; + font-size: 1.6rem; line-height: 1.4; - letter-spacing: -0.002em; + letter-spacing: -0.01em; + text-wrap: pretty; +} + +.modal-post-success .modal-body strong.nowrap { + text-wrap: nowrap; } .modal-post-success .modal-footer { @@ -940,7 +945,7 @@ .modal-post-success .modal-footer .gh-btn { min-width: 64px; - height: 44px; + height: 40px; border-radius: 4px; } @@ -950,7 +955,7 @@ .modal-post-success .modal-footer .gh-btn span { padding-inline: 18px; - font-size: 1.6rem; + font-size: 1.4rem; } .modal-post-success .modal-footer .gh-btn-primary { From dc7abe47125e056700b3d133a17c06333f3e271a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:39:58 +0000 Subject: [PATCH 154/164] Update sentry-javascript monorepo to v7.119.0 --- apps/admin-x-design-system/package.json | 2 +- apps/admin-x-framework/package.json | 2 +- apps/portal/package.json | 2 +- ghost/admin/package.json | 2 +- ghost/core/package.json | 4 +- yarn.lock | 186 ++++++++++++------------ 6 files changed, 99 insertions(+), 99 deletions(-) diff --git a/apps/admin-x-design-system/package.json b/apps/admin-x-design-system/package.json index 9c73c08281..5f4fd67719 100644 --- a/apps/admin-x-design-system/package.json +++ b/apps/admin-x-design-system/package.json @@ -60,7 +60,7 @@ "@dnd-kit/core": "6.1.0", "@dnd-kit/sortable": "7.0.2", "@ebay/nice-modal-react": "1.2.13", - "@sentry/react": "7.118.0", + "@sentry/react": "7.119.0", "@tailwindcss/forms": "0.5.7", "@tailwindcss/line-clamp": "0.4.4", "@uiw/react-codemirror": "4.23.0", diff --git a/apps/admin-x-framework/package.json b/apps/admin-x-framework/package.json index ed983ab571..7e8f9ac1c9 100644 --- a/apps/admin-x-framework/package.json +++ b/apps/admin-x-framework/package.json @@ -82,7 +82,7 @@ "typescript": "5.4.5" }, "dependencies": { - "@sentry/react": "7.118.0", + "@sentry/react": "7.119.0", "@tanstack/react-query": "4.36.1", "@tryghost/admin-x-design-system": "0.0.0", "@types/react": "18.3.3", diff --git a/apps/portal/package.json b/apps/portal/package.json index af19aa4ec6..c1f630e2ef 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -80,7 +80,7 @@ "devDependencies": { "@babel/eslint-parser": "7.23.3", "@doist/react-interpolate": "1.1.1", - "@sentry/react": "7.118.0", + "@sentry/react": "7.119.0", "@sentry/tracing": "7.114.0", "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "12.1.5", diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 2f17c77474..d1125b025b 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -41,7 +41,7 @@ "@faker-js/faker": "7.6.0", "@glimmer/component": "1.1.2", "@html-next/vertical-collection": "3.0.0", - "@sentry/ember": "7.118.0", + "@sentry/ember": "7.119.0", "@sentry/integrations": "7.114.0", "@sentry/replay": "7.116.0", "@tryghost/color-utils": "0.2.2", diff --git a/ghost/core/package.json b/ghost/core/package.json index 2e9125f7a1..c2a73af9d3 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -67,7 +67,7 @@ "@opentelemetry/sdk-metrics": "1.25.1", "@opentelemetry/sdk-node": "0.52.1", "@opentelemetry/sdk-trace-node": "1.25.1", - "@sentry/node": "7.118.0", + "@sentry/node": "7.119.0", "@tryghost/adapter-base-cache": "0.1.12", "@tryghost/adapter-cache-redis": "0.0.0", "@tryghost/adapter-manager": "0.0.0", @@ -230,7 +230,7 @@ "yjs": "13.6.18" }, "optionalDependencies": { - "@sentry/profiling-node": "7.118.0", + "@sentry/profiling-node": "7.119.0", "@tryghost/html-to-mobiledoc": "3.1.2", "sqlite3": "5.1.7" }, diff --git a/yarn.lock b/yarn.lock index 611475c50e..b819220b53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5174,24 +5174,24 @@ domhandler "^4.2.0" selderee "^0.6.0" -"@sentry-internal/feedback@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.118.0.tgz#5b4b13ba514452d07a22ec8c66c2e4bc2091d8e6" - integrity sha512-IYOGRcqIqKJJpMwBBv+0JTu0FPpXnakJYvOx/XEa/SNyF5+l7b9gGEjUVWh1ok50kTLW/XPnpnXNAGQcoKHg+w== +"@sentry-internal/feedback@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.119.0.tgz#429b3ea0fd34e928d2e7de5dcbe9377272a3f221" + integrity sha512-om8TkAU5CQGO8nkmr7qsSBVkP+/vfeS4JgtW3sjoTK0fhj26+DljR6RlfCGWtYQdPSP6XV7atcPTjbSnsmG9FQ== dependencies: - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" -"@sentry-internal/replay-canvas@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.118.0.tgz#d9741962439a85525e660973042c801c569ea9e4" - integrity sha512-XxHlCClvrxmVKpiZetFYyiBaPQNiojoBGFFVgbbWBIAPc+fWeLJ2BMoQEBjn/0NA/8u8T6lErK5YQo/eIx9+XQ== +"@sentry-internal/replay-canvas@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.119.0.tgz#85669d184ba79150e64d05de02f5e2b616e68371" + integrity sha512-NL02VQx6ekPxtVRcsdp1bp5Tb5w6vnfBKSIfMKuDRBy5A10Uc3GSoy/c3mPyHjOxB84452A+xZSx6bliEzAnuA== dependencies: - "@sentry/core" "7.118.0" - "@sentry/replay" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/core" "7.119.0" + "@sentry/replay" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" "@sentry-internal/tracing@7.114.0": version "7.114.0" @@ -5211,28 +5211,28 @@ "@sentry/types" "7.116.0" "@sentry/utils" "7.116.0" -"@sentry-internal/tracing@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.118.0.tgz#1a96ea745db818e20c2f8273d317f284a416a90a" - integrity sha512-dERAshKlQLrBscHSarhHyUeGsu652bDTUN1FK0m4e3X48M3I5/s+0N880Qjpe5MprNLcINlaIgdQ9jkisvxjfw== +"@sentry-internal/tracing@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.119.0.tgz#201561af2a4ad1837333287c26050a5e688537ca" + integrity sha512-oKdFJnn+56f0DHUADlL8o9l8jTib3VDLbWQBVkjD9EprxfaCwt2m8L5ACRBdQ8hmpxCEo4I8/6traZ7qAdBUqA== dependencies: - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" -"@sentry/browser@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.118.0.tgz#2395b47d693f7e49057552997d5125fc1a3d3448" - integrity sha512-8onDOFV1VLEoBuqA5yaJeR3FF1JNuxr5C7p1oN3OwY724iTVqQnOLmZKZaSnHV3RkY67wKDGQkQIie14sc+42g== +"@sentry/browser@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.119.0.tgz#65004015c107be5d2f49a852ebcffc5d19d90e0d" + integrity sha512-WwmW1Y4D764kVGeKmdsNvQESZiAn9t8LmCWO0ucBksrjL2zw9gBPtOpRcO6l064sCLeSxxzCN+kIxhRm1gDFEA== dependencies: - "@sentry-internal/feedback" "7.118.0" - "@sentry-internal/replay-canvas" "7.118.0" - "@sentry-internal/tracing" "7.118.0" - "@sentry/core" "7.118.0" - "@sentry/integrations" "7.118.0" - "@sentry/replay" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry-internal/feedback" "7.119.0" + "@sentry-internal/replay-canvas" "7.119.0" + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/replay" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" "@sentry/core@7.114.0": version "7.114.0" @@ -5250,24 +5250,24 @@ "@sentry/types" "7.116.0" "@sentry/utils" "7.116.0" -"@sentry/core@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.118.0.tgz#1549b49621bc05a8df16c3546793a299b0638559" - integrity sha512-ol0xBdp3/K11IMAYSQE0FMxBOOH9hMsb/rjxXWe0hfM5c72CqYWL3ol7voPci0GELJ5CZG+9ImEU1V9r6gK64g== +"@sentry/core@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.119.0.tgz#a6e41119bb03ec27689f9ad04e79d1fba5b7fc37" + integrity sha512-CS2kUv9rAJJEjiRat6wle3JATHypB0SyD7pt4cpX5y0dN5dZ1JrF57oLHRMnga9fxRivydHz7tMTuBhSSwhzjw== dependencies: - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" -"@sentry/ember@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/ember/-/ember-7.118.0.tgz#202fafed91b183d1d83c1cfe1f847ad812118be3" - integrity sha512-z3uqmd7HPZDy5n9TTnBDlBrMsV5+tnuVBOXzu/hrVlKRK9WGUprAmPDivOL+owojLPF4gMwyUWhIlPy/auCMkg== +"@sentry/ember@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/ember/-/ember-7.119.0.tgz#b795df5c7dcb3fa35b9db3cfdc0d148f1c66ccc2" + integrity sha512-1c3sM+FkfNkrtVwIAIeX9iaJAGlk6EbQadoswO4dtLE8XgbMu/Mp0L0HCdWFqpTfBOJKf+LYjl0SS0iLQD6NQg== dependencies: "@embroider/macros" "^1.9.0" - "@sentry/browser" "7.118.0" - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/browser" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" ember-auto-import "^1.12.1 || ^2.4.3" ember-cli-babel "^7.26.11" ember-cli-htmlbars "^6.1.1" @@ -5283,44 +5283,44 @@ "@sentry/utils" "7.114.0" localforage "^1.8.1" -"@sentry/integrations@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.118.0.tgz#f090db621979785c6dc44406da1f72653fa0617c" - integrity sha512-C2rR4NvIMjokF8jP5qzSf1o2zxDx7IeYnr8u15Kb2+HdZtX559owALR0hfgwnfeElqMhGlJBaKUWZ48lXJMzCQ== +"@sentry/integrations@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.119.0.tgz#5b25c603026dbacfe1ae7bb8d768506a129149fb" + integrity sha512-OHShvtsRW0A+ZL/ZbMnMqDEtJddPasndjq+1aQXw40mN+zeP7At/V1yPZyFaURy86iX7Ucxw5BtmzuNy7hLyTA== dependencies: - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" localforage "^1.8.1" -"@sentry/node@7.118.0", "@sentry/node@^7.73.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.118.0.tgz#c0b78cabc737eb8a638e82f338fc533455552117" - integrity sha512-79N63DvYKkNPqzmc0cjO+vMZ/nU7+CbE3K3COQNiV7gk58+666G9mRZQJuZVOVebatq5wM5UR0G4LPkwD+J84g== +"@sentry/node@7.119.0", "@sentry/node@^7.73.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.119.0.tgz#d429ecbba9ce22caf05e811cb666a045e167b6d7" + integrity sha512-9PFzN8xS6U0oZCflpVxS2SSIsHkCaj7qYBlsvHj4CTGWfao9ImwrU6+smy4qoG6oxwPfoVb5pOOMb4WpWOvXcQ== dependencies: - "@sentry-internal/tracing" "7.118.0" - "@sentry/core" "7.118.0" - "@sentry/integrations" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/integrations" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" -"@sentry/profiling-node@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-7.118.0.tgz#7ca512d085e10facb634c857904e3d6032ae96be" - integrity sha512-CHxNwufyBJN44CrwFubYCj0g0h4CLQpx08mcny+b01TRgSdplJ0cMBChVLrQVsoL5i4nmifyyqpjuSRcvMMaiA== +"@sentry/profiling-node@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-7.119.0.tgz#021263e3167a416b5eb868f5994feddbaadbdf28" + integrity sha512-KEJ2kFVWDmg2qi7y24PuSqerlW+iDK4C6pDXpXV6h+GEekUXsnRSAqtKc5H6LJqVGe8ljgFqPy75XIkM1qPBzw== dependencies: detect-libc "^2.0.2" node-abi "^3.61.0" -"@sentry/react@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.118.0.tgz#88ae04e1abec1fd9a242ad6d852a1fe5e6851ad4" - integrity sha512-oEYe5TGk8S7YzPsFqDf4xDHjfzs35/QFE+dou3S2d24OYpso8Tq4C5f1VzYmnOOyy85T7JNicYLSo0n0NSJvQg== +"@sentry/react@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.119.0.tgz#79f2c9d94322a3afbfa8af9f5b872f7c2e9b0820" + integrity sha512-cf8Cei+qdSA26gx+IMAuc/k44PeBImNzIpXi3930SLhUe44ypT5OZ/44L6xTODHZzTIyMSJPduf59vT2+eW9yg== dependencies: - "@sentry/browser" "7.118.0" - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry/browser" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" hoist-non-react-statics "^3.3.2" "@sentry/replay@7.116.0": @@ -5333,15 +5333,15 @@ "@sentry/types" "7.116.0" "@sentry/utils" "7.116.0" -"@sentry/replay@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.118.0.tgz#ae55b7e14b85f22256dcc5a96bf3e63b252c1acf" - integrity sha512-boQfCL+1L/tSZ9Huwi00+VtU+Ih1Lcg8HtxBuAsBCJR9pQgUL5jp7ECYdTeeHyCh/RJO7JqV1CEoGTgohe10mA== +"@sentry/replay@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.119.0.tgz#50881079d013c77f87a994331d8bcad1d49e0960" + integrity sha512-BnNsYL+X5I4WCH6wOpY6HQtp4MgVt0NVlhLUsEyrvMUiTs0bPkDBrulsgZQBUKJsbOr3l9nHrFoNVB/0i6WNLA== dependencies: - "@sentry-internal/tracing" "7.118.0" - "@sentry/core" "7.118.0" - "@sentry/types" "7.118.0" - "@sentry/utils" "7.118.0" + "@sentry-internal/tracing" "7.119.0" + "@sentry/core" "7.119.0" + "@sentry/types" "7.119.0" + "@sentry/utils" "7.119.0" "@sentry/tracing@7.114.0": version "7.114.0" @@ -5360,10 +5360,10 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.116.0.tgz#0be3434e7e53c86db4993e668af1c3a65bfb7519" integrity sha512-QCCvG5QuQrwgKzV11lolNQPP2k67Q6HHD9vllZ/C4dkxkjoIym8Gy+1OgAN3wjsR0f/kG9o5iZyglgNpUVRapQ== -"@sentry/types@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.118.0.tgz#ca3ab06912f60bc2a7ccf2d2e5ccf43985851aef" - integrity sha512-2drqrD2+6kgeg+W/ycmiti3G4lJrV3hGjY9PpJ3bJeXrh6T2+LxKPzlgSEnKFaeQWkXdZ4eaUbtTXVebMjb5JA== +"@sentry/types@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.119.0.tgz#8b3d7a1405c362e75cd900d46089df4e70919d2a" + integrity sha512-27qQbutDBPKGbuJHROxhIWc1i0HJaGLA90tjMu11wt0E4UNxXRX+UQl4Twu68v4EV3CPvQcEpQfgsViYcXmq+w== "@sentry/utils@7.114.0": version "7.114.0" @@ -5379,12 +5379,12 @@ dependencies: "@sentry/types" "7.116.0" -"@sentry/utils@7.118.0": - version "7.118.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.118.0.tgz#bfc60826fe3d5d2ae7338ec6ac1f06c20beb179e" - integrity sha512-43qItc/ydxZV1Zb3Kn2M54RwL9XXFa3IAYBO8S82Qvq5YUYmU2AmJ1jgg7DabXlVSWgMA1HntwqnOV3JLaEnTQ== +"@sentry/utils@7.119.0": + version "7.119.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.119.0.tgz#debe29020f6ef3786a5bd855cf1b97116b7be826" + integrity sha512-ZwyXexWn2ZIe2bBoYnXJVPc2esCSbKpdc6+0WJa8eutXfHq3FRKg4ohkfCBpfxljQGEfP1+kfin945lA21Ka+A== dependencies: - "@sentry/types" "7.118.0" + "@sentry/types" "7.119.0" "@sidvind/better-ajv-errors@2.1.3": version "2.1.3" From ae628d752010041e4a749c142d082ab34c240cd3 Mon Sep 17 00:00:00 2001 From: Sanne de Vries <65487235+sanne-san@users.noreply.github.com> Date: Thu, 15 Aug 2024 16:09:48 +0200 Subject: [PATCH 155/164] =?UTF-8?q?=F0=9F=8E=A8=20Added=20=E2=80=9CCopy=20?= =?UTF-8?q?post=20link=E2=80=9D=20to=20posts=20list=20context=20menu=20(#2?= =?UTF-8?q?0760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit REF DES-321 - Added a "Copy post link" button to the context menu to copy the post URL for published posts, and a "Copy preview link" for draft and scheduled posts. --------- Co-authored-by: Kevin Ansfield --- .../components/posts-list/context-menu.hbs | 15 ++ .../app/components/posts-list/context-menu.js | 35 ++++- ghost/admin/mirage/factories/post.js | 7 + ghost/admin/tests/acceptance/content-test.js | 133 ++++++++++++++---- 4 files changed, 160 insertions(+), 30 deletions(-) diff --git a/ghost/admin/app/components/posts-list/context-menu.hbs b/ghost/admin/app/components/posts-list/context-menu.hbs index 36e8077e69..a7277b4161 100644 --- a/ghost/admin/app/components/posts-list/context-menu.hbs +++ b/ghost/admin/app/components/posts-list/context-menu.hbs @@ -1,11 +1,26 @@
    diff --git a/ghost/admin/app/components/modal-post-history.js b/ghost/admin/app/components/modal-post-history.js index 4dfa987e2e..05aba6646e 100644 --- a/ghost/admin/app/components/modal-post-history.js +++ b/ghost/admin/app/components/modal-post-history.js @@ -31,7 +31,6 @@ export default class ModalPostHistory extends Component { super(...arguments); this.post = this.args.model.post; this.editorAPI = this.args.model.editorAPI; - this.secondaryEditorAPI = this.args.model.secondaryEditorAPI; this.toggleSettingsMenu = this.args.model.toggleSettingsMenu; } @@ -102,11 +101,6 @@ export default class ModalPostHistory extends Component { this.selectedEditor = api; } - @action - registerSecondarySelectedEditorApi(api) { - this.secondarySelectedEditor = api; - } - @action registerComparisonEditorApi(api) { this.comparisonEditor = api; @@ -136,7 +130,6 @@ export default class ModalPostHistory extends Component { updateEditor: () => { const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical); this.editorAPI.editorInstance.setEditorState(state); - this.secondaryEditorAPI.editorInstance.setEditorState(state); }, closePostHistoryModal: () => { this.closeModal(); diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index 2b8342afd9..71c6331437 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -297,11 +297,6 @@ export default class LexicalEditorController extends Controller { this._timedSaveTask.perform(); } - @action - updateSecondaryInstanceModel(lexical) { - this.set('post.secondaryLexicalState', JSON.stringify(lexical)); - } - @action updateTitleScratch(title) { this.set('post.titleScratch', title); @@ -428,11 +423,6 @@ export default class LexicalEditorController extends Controller { this.editorAPI = API; } - @action - registerSecondaryEditorAPI(API) { - this.secondaryEditorAPI = API; - } - @action clearFeatureImage() { this.post.set('featureImage', null); @@ -1231,6 +1221,7 @@ export default class LexicalEditorController extends Controller { _timedSaveTask; /* Private methods -------------------------------------------------------*/ + _hasDirtyAttributes() { let post = this.post; @@ -1238,7 +1229,8 @@ export default class LexicalEditorController extends Controller { return false; } - // If the Adapter failed to save the post, isError will be true, and we should consider the post still dirty. + // if the Adapter failed to save the post isError will be true + // and we should consider the post still dirty. if (post.get('isError')) { this._leaveModalReason = {reason: 'isError', context: post.errors.messages}; return true; @@ -1253,32 +1245,37 @@ export default class LexicalEditorController extends Controller { return true; } - // Title scratch comparison + // titleScratch isn't an attr so needs a manual dirty check if (post.titleScratch !== post.title) { this._leaveModalReason = {reason: 'title is different', context: {current: post.title, scratch: post.titleScratch}}; return true; } - // Lexical and scratch comparison + // scratch isn't an attr so needs a manual dirty check let lexical = post.get('lexical'); let scratch = post.get('lexicalScratch'); - let secondaryLexical = post.get('secondaryLexicalState'); + // additional guard in case we are trying to compare null with undefined + if (scratch || lexical) { + if (scratch !== lexical) { + // lexical can dynamically set direction on loading editor state (e.g. "rtl"/"ltr") per the DOM context + // and we need to ignore this as a change from the user; see https://github.com/facebook/lexical/issues/4998 + const scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; + const lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; - let lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; - let scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; - let secondaryLexicalChildNodes = secondaryLexical ? JSON.parse(secondaryLexical).root?.children : []; + // // nullling is typically faster than delete + scratchChildNodes.forEach(child => child.direction = null); + lexicalChildNodes.forEach(child => child.direction = null); - lexicalChildNodes.forEach(child => child.direction = null); - scratchChildNodes.forEach(child => child.direction = null); - secondaryLexicalChildNodes.forEach(child => child.direction = null); + if (JSON.stringify(scratchChildNodes) === JSON.stringify(lexicalChildNodes)) { + return false; + } - // Compare initLexical with scratch - let isSecondaryDirty = secondaryLexical && scratch && JSON.stringify(secondaryLexicalChildNodes) !== JSON.stringify(scratchChildNodes); + this._leaveModalReason = {reason: 'lexical is different', context: {current: lexical, scratch}}; + return true; + } + } - // Compare lexical with scratch - let isLexicalDirty = lexical && scratch && JSON.stringify(lexicalChildNodes) !== JSON.stringify(scratchChildNodes); - - // New+unsaved posts always return `hasDirtyAttributes: true` + // new+unsaved posts always return `hasDirtyAttributes: true` // so we need a manual check to see if any if (post.get('isNew')) { let changedAttributes = Object.keys(post.changedAttributes()); @@ -1289,26 +1286,15 @@ export default class LexicalEditorController extends Controller { return changedAttributes.length ? true : false; } - // We've covered all the non-tracked cases we care about so fall + // we've covered all the non-tracked cases we care about so fall // back on Ember Data's default dirty attribute checks let {hasDirtyAttributes} = post; + if (hasDirtyAttributes) { this._leaveModalReason = {reason: 'post.hasDirtyAttributes === true', context: post.changedAttributes()}; - return true; } - // If either comparison is not dirty, return false, because scratch is always up to date. - if (!isSecondaryDirty || !isLexicalDirty) { - return false; - } - - // If both comparisons are dirty, consider the post dirty - if (isSecondaryDirty && isLexicalDirty) { - this._leaveModalReason = {reason: 'initLexical and lexical are different from scratch', context: {secondaryLexical, lexical, scratch}}; - return true; - } - - return false; + return hasDirtyAttributes; } _showSaveNotification(prevStatus, status, delayed) { diff --git a/ghost/admin/app/models/post.js b/ghost/admin/app/models/post.js index 835d24d0a2..1ffb06d8d0 100644 --- a/ghost/admin/app/models/post.js +++ b/ghost/admin/app/models/post.js @@ -136,9 +136,6 @@ export default Model.extend(Comparable, ValidationEngine, { scratch: null, lexicalScratch: null, titleScratch: null, - //This is used to store the initial lexical state from the - // secondary editor to get the schema up to date in case its outdated - secondaryLexicalState: null, // For use by date/time pickers - will be validated then converted to UTC // on save. Updated by an observer whenever publishedAtUTC changes. diff --git a/ghost/admin/app/templates/lexical-editor.hbs b/ghost/admin/app/templates/lexical-editor.hbs index b7ee9a4a74..bd9d5d51e7 100644 --- a/ghost/admin/app/templates/lexical-editor.hbs +++ b/ghost/admin/app/templates/lexical-editor.hbs @@ -73,7 +73,6 @@ @body={{readonly this.post.lexicalScratch}} @bodyPlaceholder={{concat "Begin writing your " this.post.displayName "..."}} @onBodyChange={{this.updateScratch}} - @updateSecondaryInstanceModel={{this.updateSecondaryInstanceModel}} @headerOffset={{editor.headerHeight}} @scrollContainerSelector=".gh-koenig-editor" @scrollOffsetBottomSelector=".gh-mobile-nav-bar" @@ -98,7 +97,6 @@ }} @postType={{this.post.displayName}} @registerAPI={{this.registerEditorAPI}} - @registerSecondaryAPI={{this.registerSecondaryEditorAPI}} @savePostTask={{this.savePostTask}} /> @@ -138,7 +136,6 @@ @updateSlugTask={{this.updateSlugTask}} @savePostTask={{this.savePostTask}} @editorAPI={{this.editorAPI}} - @secondaryEditorAPI={{this.secondaryEditorAPI}} @toggleSettingsMenu={{this.toggleSettingsMenu}} /> {{/if}} diff --git a/ghost/admin/tests/unit/controllers/editor-test.js b/ghost/admin/tests/unit/controllers/editor-test.js index acf7a5e95b..088f22391d 100644 --- a/ghost/admin/tests/unit/controllers/editor-test.js +++ b/ghost/admin/tests/unit/controllers/editor-test.js @@ -208,8 +208,7 @@ describe('Unit: Controller: lexical-editor', function () { titleScratch: 'this is a title', status: 'published', lexical: initialLexicalString, - lexicalScratch: initialLexicalString, - secondaryLexicalState: initialLexicalString + lexicalScratch: initialLexicalString })); // synthetically update the lexicalScratch as if the editor itself made the modifications on loading the initial editorState @@ -226,47 +225,5 @@ describe('Unit: Controller: lexical-editor', function () { isDirty = controller.get('hasDirtyAttributes'); expect(isDirty).to.be.true; }); - - it('dirty is false if secondaryLexical and scratch matches, but lexical is outdated', async function () { - const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - - let controller = this.owner.lookup('controller:lexical-editor'); - controller.set('post', EmberObject.create({ - title: 'this is a title', - titleScratch: 'this is a title', - status: 'published', - lexical: initialLexicalString, - lexicalScratch: lexicalScratch, - secondaryLexicalState: secondLexicalInstance - })); - - let isDirty = controller.get('hasDirtyAttributes'); - - expect(isDirty).to.be.false; - }); - - it('dirty is true if secondaryLexical and lexical does not match scratch', async function () { - const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content1234","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - - let controller = this.owner.lookup('controller:lexical-editor'); - controller.set('post', EmberObject.create({ - title: 'this is a title', - titleScratch: 'this is a title', - status: 'published', - lexical: initialLexicalString, - lexicalScratch: lexicalScratch, - secondaryLexicalState: secondLexicalInstance - })); - - controller.send('updateScratch',JSON.parse(lexicalScratch)); - - let isDirty = controller.get('hasDirtyAttributes'); - - expect(isDirty).to.be.true; - }); }); }); From 93cbb94b90987eb5f410858436a14a2173d0bfb1 Mon Sep 17 00:00:00 2001 From: Chris Raible Date: Fri, 16 Aug 2024 12:09:11 -0700 Subject: [PATCH 163/164] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20a=20bug=20causin?= =?UTF-8?q?g=20new=20drafts=20to=20only=20save=20if=20the=20title=20is=20p?= =?UTF-8?q?opulated=20(#20769)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/ONC-253/drafts-only-save-if-the-title-is-populated - A [commit](https://github.com/TryGhost/Ghost/commit/c8ba9e8027bd8269f8fb0ff3303da6d1e34b9696) in `v5.89.1` introduced a bug that caused new drafts to only save if the post title was populated, causing potential data loss if a user is working on a new draft without setting the title. - This commit reverts the one that introduced this bug to prevent data loss. This reverts commit c8ba9e8027bd8269f8fb0ff3303da6d1e34b9696. --- .../components/gh-koenig-editor-lexical.hbs | 2 - .../components/gh-koenig-editor-lexical.js | 7 -- .../app/components/gh-post-settings-menu.hbs | 1 - .../app/components/koenig-lexical-editor.js | 57 +++++++--------- .../app/components/modal-post-history.hbs | 1 - .../app/components/modal-post-history.js | 7 -- ghost/admin/app/controllers/lexical-editor.js | 66 ++++++++----------- ghost/admin/app/models/post.js | 3 - ghost/admin/app/templates/lexical-editor.hbs | 3 - .../tests/unit/controllers/editor-test.js | 45 +------------ 10 files changed, 51 insertions(+), 141 deletions(-) diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs index 3ad4aae7a0..af3524b998 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.hbs +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.hbs @@ -95,9 +95,7 @@ @placeholder={{@bodyPlaceholder}} @cardConfig={{@cardOptions}} @onChange={{@onBodyChange}} - @updateSecondaryInstanceModel={{@updateSecondaryInstanceModel}} @registerAPI={{this.registerEditorAPI}} - @registerSecondaryAPI={{this.registerSecondaryEditorAPI}} @cursorDidExitAtTop={{if this.feature.editorExcerpt this.focusExcerpt this.focusTitle}} @updateWordCount={{@updateWordCount}} @updatePostTkCount={{@updatePostTkCount}} diff --git a/ghost/admin/app/components/gh-koenig-editor-lexical.js b/ghost/admin/app/components/gh-koenig-editor-lexical.js index be28f73a5f..2319ae9c8a 100644 --- a/ghost/admin/app/components/gh-koenig-editor-lexical.js +++ b/ghost/admin/app/components/gh-koenig-editor-lexical.js @@ -15,7 +15,6 @@ export default class GhKoenigEditorLexical extends Component { uploadUrl = `${ghostPaths().apiRoot}/images/upload/`; editorAPI = null; - secondaryEditorAPI = null; skipFocusEditor = false; @tracked titleIsHovered = false; @@ -233,12 +232,6 @@ export default class GhKoenigEditorLexical extends Component { this.args.registerAPI(API); } - @action - registerSecondaryEditorAPI(API) { - this.secondaryEditorAPI = API; - this.args.registerSecondaryAPI(API); - } - // focus the editor when the editor canvas is clicked below the editor content, // otherwise the browser will defocus the editor and the cursor will disappear @action diff --git a/ghost/admin/app/components/gh-post-settings-menu.hbs b/ghost/admin/app/components/gh-post-settings-menu.hbs index ee91b2af80..1d01dcc5bc 100644 --- a/ghost/admin/app/components/gh-post-settings-menu.hbs +++ b/ghost/admin/app/components/gh-post-settings-menu.hbs @@ -853,7 +853,6 @@ post=this.post editorAPI=this.editorAPI toggleSettingsMenu=this.toggleSettingsMenu - secondaryEditorAPI=this.secondaryEditorAPI }} @close={{this.closePostHistory}} @modifier="total-overlay post-history" /> diff --git a/ghost/admin/app/components/koenig-lexical-editor.js b/ghost/admin/app/components/koenig-lexical-editor.js index f2a471a463..ad1c769da6 100644 --- a/ghost/admin/app/components/koenig-lexical-editor.js +++ b/ghost/admin/app/components/koenig-lexical-editor.js @@ -678,43 +678,34 @@ export default class KoenigLexicalEditor extends Component { const multiplayerDocId = cardConfig.post.id; const multiplayerUsername = this.session.user.name; - const KGEditorComponent = ({isInitInstance}) => { - return ( -
    - - - {} : this.args.updateWordCount} /> - {} : this.args.updatePostTkCount} /> - -
    - ); - }; - return (
    Loading editor...

    }> - - + + + + +
    diff --git a/ghost/admin/app/components/modal-post-history.hbs b/ghost/admin/app/components/modal-post-history.hbs index f7f8ab1261..3989c29db5 100644 --- a/ghost/admin/app/components/modal-post-history.hbs +++ b/ghost/admin/app/components/modal-post-history.hbs @@ -33,7 +33,6 @@ @lexical={{this.selectedRevision.lexical}} @cardConfig={{this.cardConfig}} @registerAPI={{this.registerSelectedEditorApi}} - @registerSecondaryAPI={{this.registerSecondarySelectedEditorApi}} />
    diff --git a/ghost/admin/app/components/modal-post-history.js b/ghost/admin/app/components/modal-post-history.js index 4dfa987e2e..05aba6646e 100644 --- a/ghost/admin/app/components/modal-post-history.js +++ b/ghost/admin/app/components/modal-post-history.js @@ -31,7 +31,6 @@ export default class ModalPostHistory extends Component { super(...arguments); this.post = this.args.model.post; this.editorAPI = this.args.model.editorAPI; - this.secondaryEditorAPI = this.args.model.secondaryEditorAPI; this.toggleSettingsMenu = this.args.model.toggleSettingsMenu; } @@ -102,11 +101,6 @@ export default class ModalPostHistory extends Component { this.selectedEditor = api; } - @action - registerSecondarySelectedEditorApi(api) { - this.secondarySelectedEditor = api; - } - @action registerComparisonEditorApi(api) { this.comparisonEditor = api; @@ -136,7 +130,6 @@ export default class ModalPostHistory extends Component { updateEditor: () => { const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical); this.editorAPI.editorInstance.setEditorState(state); - this.secondaryEditorAPI.editorInstance.setEditorState(state); }, closePostHistoryModal: () => { this.closeModal(); diff --git a/ghost/admin/app/controllers/lexical-editor.js b/ghost/admin/app/controllers/lexical-editor.js index 2b8342afd9..71c6331437 100644 --- a/ghost/admin/app/controllers/lexical-editor.js +++ b/ghost/admin/app/controllers/lexical-editor.js @@ -297,11 +297,6 @@ export default class LexicalEditorController extends Controller { this._timedSaveTask.perform(); } - @action - updateSecondaryInstanceModel(lexical) { - this.set('post.secondaryLexicalState', JSON.stringify(lexical)); - } - @action updateTitleScratch(title) { this.set('post.titleScratch', title); @@ -428,11 +423,6 @@ export default class LexicalEditorController extends Controller { this.editorAPI = API; } - @action - registerSecondaryEditorAPI(API) { - this.secondaryEditorAPI = API; - } - @action clearFeatureImage() { this.post.set('featureImage', null); @@ -1231,6 +1221,7 @@ export default class LexicalEditorController extends Controller { _timedSaveTask; /* Private methods -------------------------------------------------------*/ + _hasDirtyAttributes() { let post = this.post; @@ -1238,7 +1229,8 @@ export default class LexicalEditorController extends Controller { return false; } - // If the Adapter failed to save the post, isError will be true, and we should consider the post still dirty. + // if the Adapter failed to save the post isError will be true + // and we should consider the post still dirty. if (post.get('isError')) { this._leaveModalReason = {reason: 'isError', context: post.errors.messages}; return true; @@ -1253,32 +1245,37 @@ export default class LexicalEditorController extends Controller { return true; } - // Title scratch comparison + // titleScratch isn't an attr so needs a manual dirty check if (post.titleScratch !== post.title) { this._leaveModalReason = {reason: 'title is different', context: {current: post.title, scratch: post.titleScratch}}; return true; } - // Lexical and scratch comparison + // scratch isn't an attr so needs a manual dirty check let lexical = post.get('lexical'); let scratch = post.get('lexicalScratch'); - let secondaryLexical = post.get('secondaryLexicalState'); + // additional guard in case we are trying to compare null with undefined + if (scratch || lexical) { + if (scratch !== lexical) { + // lexical can dynamically set direction on loading editor state (e.g. "rtl"/"ltr") per the DOM context + // and we need to ignore this as a change from the user; see https://github.com/facebook/lexical/issues/4998 + const scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; + const lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; - let lexicalChildNodes = lexical ? JSON.parse(lexical).root?.children : []; - let scratchChildNodes = scratch ? JSON.parse(scratch).root?.children : []; - let secondaryLexicalChildNodes = secondaryLexical ? JSON.parse(secondaryLexical).root?.children : []; + // // nullling is typically faster than delete + scratchChildNodes.forEach(child => child.direction = null); + lexicalChildNodes.forEach(child => child.direction = null); - lexicalChildNodes.forEach(child => child.direction = null); - scratchChildNodes.forEach(child => child.direction = null); - secondaryLexicalChildNodes.forEach(child => child.direction = null); + if (JSON.stringify(scratchChildNodes) === JSON.stringify(lexicalChildNodes)) { + return false; + } - // Compare initLexical with scratch - let isSecondaryDirty = secondaryLexical && scratch && JSON.stringify(secondaryLexicalChildNodes) !== JSON.stringify(scratchChildNodes); + this._leaveModalReason = {reason: 'lexical is different', context: {current: lexical, scratch}}; + return true; + } + } - // Compare lexical with scratch - let isLexicalDirty = lexical && scratch && JSON.stringify(lexicalChildNodes) !== JSON.stringify(scratchChildNodes); - - // New+unsaved posts always return `hasDirtyAttributes: true` + // new+unsaved posts always return `hasDirtyAttributes: true` // so we need a manual check to see if any if (post.get('isNew')) { let changedAttributes = Object.keys(post.changedAttributes()); @@ -1289,26 +1286,15 @@ export default class LexicalEditorController extends Controller { return changedAttributes.length ? true : false; } - // We've covered all the non-tracked cases we care about so fall + // we've covered all the non-tracked cases we care about so fall // back on Ember Data's default dirty attribute checks let {hasDirtyAttributes} = post; + if (hasDirtyAttributes) { this._leaveModalReason = {reason: 'post.hasDirtyAttributes === true', context: post.changedAttributes()}; - return true; } - // If either comparison is not dirty, return false, because scratch is always up to date. - if (!isSecondaryDirty || !isLexicalDirty) { - return false; - } - - // If both comparisons are dirty, consider the post dirty - if (isSecondaryDirty && isLexicalDirty) { - this._leaveModalReason = {reason: 'initLexical and lexical are different from scratch', context: {secondaryLexical, lexical, scratch}}; - return true; - } - - return false; + return hasDirtyAttributes; } _showSaveNotification(prevStatus, status, delayed) { diff --git a/ghost/admin/app/models/post.js b/ghost/admin/app/models/post.js index 835d24d0a2..1ffb06d8d0 100644 --- a/ghost/admin/app/models/post.js +++ b/ghost/admin/app/models/post.js @@ -136,9 +136,6 @@ export default Model.extend(Comparable, ValidationEngine, { scratch: null, lexicalScratch: null, titleScratch: null, - //This is used to store the initial lexical state from the - // secondary editor to get the schema up to date in case its outdated - secondaryLexicalState: null, // For use by date/time pickers - will be validated then converted to UTC // on save. Updated by an observer whenever publishedAtUTC changes. diff --git a/ghost/admin/app/templates/lexical-editor.hbs b/ghost/admin/app/templates/lexical-editor.hbs index b7ee9a4a74..bd9d5d51e7 100644 --- a/ghost/admin/app/templates/lexical-editor.hbs +++ b/ghost/admin/app/templates/lexical-editor.hbs @@ -73,7 +73,6 @@ @body={{readonly this.post.lexicalScratch}} @bodyPlaceholder={{concat "Begin writing your " this.post.displayName "..."}} @onBodyChange={{this.updateScratch}} - @updateSecondaryInstanceModel={{this.updateSecondaryInstanceModel}} @headerOffset={{editor.headerHeight}} @scrollContainerSelector=".gh-koenig-editor" @scrollOffsetBottomSelector=".gh-mobile-nav-bar" @@ -98,7 +97,6 @@ }} @postType={{this.post.displayName}} @registerAPI={{this.registerEditorAPI}} - @registerSecondaryAPI={{this.registerSecondaryEditorAPI}} @savePostTask={{this.savePostTask}} /> @@ -138,7 +136,6 @@ @updateSlugTask={{this.updateSlugTask}} @savePostTask={{this.savePostTask}} @editorAPI={{this.editorAPI}} - @secondaryEditorAPI={{this.secondaryEditorAPI}} @toggleSettingsMenu={{this.toggleSettingsMenu}} /> {{/if}} diff --git a/ghost/admin/tests/unit/controllers/editor-test.js b/ghost/admin/tests/unit/controllers/editor-test.js index acf7a5e95b..088f22391d 100644 --- a/ghost/admin/tests/unit/controllers/editor-test.js +++ b/ghost/admin/tests/unit/controllers/editor-test.js @@ -208,8 +208,7 @@ describe('Unit: Controller: lexical-editor', function () { titleScratch: 'this is a title', status: 'published', lexical: initialLexicalString, - lexicalScratch: initialLexicalString, - secondaryLexicalState: initialLexicalString + lexicalScratch: initialLexicalString })); // synthetically update the lexicalScratch as if the editor itself made the modifications on loading the initial editorState @@ -226,47 +225,5 @@ describe('Unit: Controller: lexical-editor', function () { isDirty = controller.get('hasDirtyAttributes'); expect(isDirty).to.be.true; }); - - it('dirty is false if secondaryLexical and scratch matches, but lexical is outdated', async function () { - const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - - let controller = this.owner.lookup('controller:lexical-editor'); - controller.set('post', EmberObject.create({ - title: 'this is a title', - titleScratch: 'this is a title', - status: 'published', - lexical: initialLexicalString, - lexicalScratch: lexicalScratch, - secondaryLexicalState: secondLexicalInstance - })); - - let isDirty = controller.get('hasDirtyAttributes'); - - expect(isDirty).to.be.false; - }); - - it('dirty is true if secondaryLexical and lexical does not match scratch', async function () { - const initialLexicalString = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content","type": "extended-text","version": 1}],"direction": null,"format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const lexicalScratch = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Sample content1234","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - const secondLexicalInstance = `{"root":{"children":[{"children": [{"detail": 0,"format": 0,"mode": "normal","style": "","text": "Here's some new text","type": "extended-text","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "paragraph","version": 1}],"direction": "ltr","format": "","indent": 0,"type": "root","version": 1}}`; - - let controller = this.owner.lookup('controller:lexical-editor'); - controller.set('post', EmberObject.create({ - title: 'this is a title', - titleScratch: 'this is a title', - status: 'published', - lexical: initialLexicalString, - lexicalScratch: lexicalScratch, - secondaryLexicalState: secondLexicalInstance - })); - - controller.send('updateScratch',JSON.parse(lexicalScratch)); - - let isDirty = controller.get('hasDirtyAttributes'); - - expect(isDirty).to.be.true; - }); }); }); From 64f92fb46051aa3c3e3cff96c095ff4c8db45cc7 Mon Sep 17 00:00:00 2001 From: Ghost CI <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:50:28 +0000 Subject: [PATCH 164/164] v5.89.3 --- ghost/admin/package.json | 2 +- ghost/core/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/admin/package.json b/ghost/admin/package.json index 7e20f90a60..e788d0ef19 100644 --- a/ghost/admin/package.json +++ b/ghost/admin/package.json @@ -1,6 +1,6 @@ { "name": "ghost-admin", - "version": "5.89.2", + "version": "5.89.3", "description": "Ember.js admin client for Ghost", "author": "Ghost Foundation", "homepage": "http://ghost.org", diff --git a/ghost/core/package.json b/ghost/core/package.json index fca7c13a0b..02e44e94b8 100644 --- a/ghost/core/package.json +++ b/ghost/core/package.json @@ -1,6 +1,6 @@ { "name": "ghost", - "version": "5.89.2", + "version": "5.89.3", "description": "The professional publishing platform", "author": "Ghost Foundation", "homepage": "https://ghost.org",