From 680651aa586be1d3cd17411ccd3cf6bbeaafacd8 Mon Sep 17 00:00:00 2001 From: Chris Raible Date: Thu, 30 May 2024 15:55:45 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20admin=20error=20when=20t?= =?UTF-8?q?rying=20to=20overwrite=20a=20default=20theme=20(#20299)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/ONC-57/trying-to-overwrite-the-default-source-theme-crashes-ghost - When attempting to overwrite a default theme (source or casper), admin would crash because this is not allowed by the API and the error was not handled - This fixes that by showing a modal with an error message when the user tries to overwrite a default theme, and instructs the user to rename the zip file --- .../components/settings/site/ThemeModal.tsx | 17 +++++++- .../test/acceptance/site/theme.test.ts | 38 ++++++++++++++++++ .../test/utils/responses/source.zip | Bin 0 -> 4195 bytes 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 apps/admin-x-settings/test/utils/responses/source.zip diff --git a/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx b/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx index 1af12ccf68..21aad85d7a 100644 --- a/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx @@ -90,7 +90,22 @@ const ThemeToolbar: React.FC = ({ const onThemeUpload = async (file: File) => { const themeFileName = file?.name.replace(/\.zip$/, ''); const existingThemeNames = themes.map(t => t.name); - if (existingThemeNames.includes(themeFileName)) { + if (isDefaultOrLegacyTheme({name: themeFileName})) { + NiceModal.show(ConfirmationModal, { + title: 'Cannot overwrite theme', + cancelLabel: 'Cancel', + okLabel: '', + prompt: ( + <> +

Sorry, {themeFileName} is a default theme and cannot be overwritten.

+

Please rename your zip file and try again.

+ + ), + onOk: async (confirmModal) => { + confirmModal?.remove(); + } + }); + } else if (existingThemeNames.includes(themeFileName)) { NiceModal.show(ConfirmationModal, { title: 'Overwrite theme', prompt: ( diff --git a/apps/admin-x-settings/test/acceptance/site/theme.test.ts b/apps/admin-x-settings/test/acceptance/site/theme.test.ts index 386ed97945..6dbcbe44d1 100644 --- a/apps/admin-x-settings/test/acceptance/site/theme.test.ts +++ b/apps/admin-x-settings/test/acceptance/site/theme.test.ts @@ -207,4 +207,42 @@ test.describe('Theme settings', async () => { await expect(page.getByTestId('limit-modal')).toHaveText(/Upgrade to enable custom themes/); }); + + test('Prevents overwriting the default theme', async ({page}) => { + await mockApi({page, requests: { + ...globalDataRequests, + browseThemes: {method: 'GET', path: '/themes/', response: responseFixtures.themes}, + uploadTheme: {method: 'POST', path: '/themes/upload/', response: { + themes: [{ + name: 'mytheme', + package: {}, + active: false, + templates: [] + }] + }} + }}); + + await page.goto('/'); + + const designSection = page.getByTestId('design'); + + await designSection.getByRole('button', {name: 'Customize'}).click(); + + const designModal = page.getByTestId('design-modal'); + + await designModal.getByTestId('change-theme').click(); + + const modal = page.getByTestId('theme-modal'); + + await modal.getByRole('button', {name: 'Upload theme'}).click(); + + const fileChooserPromise = page.waitForEvent('filechooser'); + + await page.getByTestId('confirmation-modal').locator('label[for=theme-upload]').click(); + + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(`${__dirname}/../../utils/responses/source.zip`); + + await expect(page.getByTestId('confirmation-modal')).toHaveText(/Cannot overwrite theme/); + }); }); diff --git a/apps/admin-x-settings/test/utils/responses/source.zip b/apps/admin-x-settings/test/utils/responses/source.zip new file mode 100644 index 0000000000000000000000000000000000000000..8fde66b696644c0672528c5159028dedf0894b0f GIT binary patch literal 4195 zcmd5O^S1h zQ=n97X{?}#O`QmmYCx!hBc`34p5dFC+i^vvN-OM!&`+&WRXZaTIvDixBeF$Z@aS+B|;N6M#NYuO6Z~S1&%UTMlOj4 z%0XO<68SkcGO)_5H14_%>GXHIgSF};bLA&&%lYd*`8!A|1pR(GX_`B3WIn6?W=CWt zp7`*l;vXiq=BY{V?!}E|E!^v_criRZl}OBb`cpNU@1?b1Y{@>KEn&{~NLI*(0Gh;K zdHd4eb%^1)F|84HV(~3HBgYq?6Mv7(n-Qd!q8uNzk*P*y1akvDS$>LG)$+d=8Ld&88_R9AFpZ zadn1+0sbY}18IqB;!3i?a9USL&1Xth%$c~j^ZWVzOs6z7{nD&n`L^9*j!@KeCO6)uf3DkWWBtt6hd(i7SwF)d(?-ecl{oSHze|pwo`g{ zpZ&K9d?>`gw{H-`2K;(dtok*fMdX|*ScY)uicEVKDw zLu8ZesY4UVo8qE!WrC5N(TM?jq_y1X*WnXk!+uBUOK#cSuW`KUVYn@?PtJ4FylzJM&M3|FKF=F(uTJNuh~`a} zuK2QSgd&6BWO%!sWD%_5qH|R4E|8$-PG?vzKr+WZ8S^GP)qVKw(feuU!GsQxXlvfu z!(ufeBFXcPoC!DG=vh+C4`};yp_>mtw;%hIKh<RegQc zHn@u$dRf$yuBMF?BZZyhw{LYV5$KE=N_LqM7uSsKx?K3gs`1rv3+x^p{H(P4M7-PW zhPtG2wC?DY3q7~12aI6Iu)2&YRth{D;p>mXpt!mx=B+t)kv1gC{X^ByP;M~9jzN#e zpUU!uzKA$+KgXI_dB4}-+DwdFpWS4eaut+fnpo_J(e-do*E>q<*3X6S2|iz8@}41u zGowCkx%2CusmywniH~0P=dwQdP8_wL{~+L7-u6+dWz2$fFU{%GoBY0;ma>QbvQLdX zB+`@GZs=WmDX1mNZDHz?wu6ahn1e^=pS21?N&T|VbZAeZS}3YU^_W^T95rO4(#iV@ zEhKc#T*)Si)Kz=R<9G-!(-l@GPNI85MX{X`E&i$xNtDCanay!Aya^B$R3eX*)8~SK zBljx&>TfZunCc@E?=urx7@}&npHMHhUhNl?E-nq9Pm-bcq$x(Wz?EnLW65cJx;iZ< zA86=j(mu55#&gb)e;#%e5LOrQD4e`P8PtiLM)Jw;*J*^y962L=xG;QBeM*H=*6f)D zo6A(@G)_5_S2m~}U|Xt;xFOt|VjAQymQ2f;ntDWiir+3Zr##m^u(xqi+GOLo)Zsvh zK{zb*ZV5D$>(+>+TWaZ-sZYepah1uz`le* zSd&yhg$Aw!Djh%bYLx!`nr6sb{!&J#Pgz?>Vb|GV(do*Ul~2KTUYrzSf#3wHE>K(7 zsJcKs-nwf4ShE1HtE2xfJBR^*g?G7wr~nP{Tjv0lR%`pXW+fA*Qo-TJZEZ$aR)Xwo z@kfOWo$Q)fmiWgS*;(dCgKWL+xiT8L2P$NCXo$*3U$?Q3w0pq0*qvFef?7I&UU1*F z*%eup-S3S1zv;^yP+{2G2M~}$P#9S7{$C$Qz9?Nfkg(72;H0)B3^GXLx{oIoxMlU= zL4bXL1rGvCLL)12e6xAwq`Jnj;79H311LBIFA0rIg|ZpH{*jn{BXFK&AN<%^0${+F zVXU0+K!a8pkbYFpK8=CXa>RrH literal 0 HcmV?d00001