ghost 6.61.0 → 6.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ghost-storage-base-3.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-cache-0.2.0.tgz +0 -0
- package/components/tryghost-adapter-base-jobs-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-redirects-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-route-settings-0.0.0.tgz +0 -0
- package/components/tryghost-adapter-base-scheduling-0.2.3.tgz +0 -0
- package/components/tryghost-adapter-base-sso-0.1.3.tgz +0 -0
- package/components/tryghost-admin-api-schema-0.0.0.tgz +0 -0
- package/components/tryghost-checkout-0.0.0.tgz +0 -0
- package/components/tryghost-custom-field-types-0.0.0.tgz +0 -0
- package/components/tryghost-i18n-0.0.0.tgz +0 -0
- package/components/tryghost-kg-card-factory-5.2.4.tgz +0 -0
- package/components/tryghost-kg-clean-basic-html-4.3.4.tgz +0 -0
- package/components/tryghost-kg-converters-1.2.5.tgz +0 -0
- package/components/tryghost-kg-default-cards-10.3.5.tgz +0 -0
- package/components/tryghost-kg-default-nodes-2.2.1.tgz +0 -0
- package/components/tryghost-kg-default-transforms-1.3.4.tgz +0 -0
- package/components/tryghost-kg-html-to-lexical-1.4.0.tgz +0 -0
- package/components/tryghost-kg-lexical-html-renderer-1.5.0.tgz +0 -0
- package/components/tryghost-kg-markdown-html-renderer-7.2.4.tgz +0 -0
- package/components/tryghost-kg-utils-1.1.4.tgz +0 -0
- package/components/tryghost-nql-string-0.0.0.tgz +0 -0
- package/components/tryghost-parse-email-address-0.0.0.tgz +0 -0
- package/content/settings/README.md +1 -1
- package/content/themes/casper/.gitignore +28 -0
- package/content/themes/casper/assets/built/casper.js +1 -1
- package/content/themes/casper/assets/built/screen.css +1 -1
- package/content/themes/casper/assets/css/screen.css +2 -1
- package/content/themes/casper/assets/js/lightbox.js +63 -46
- package/content/themes/casper/package.json +6 -6
- package/content/themes/casper/page.hbs +1 -0
- package/content/themes/casper/post.hbs +1 -0
- package/content/themes/source/.gitignore +28 -0
- package/content/themes/source/assets/built/screen.css +1 -1
- package/content/themes/source/assets/built/source.js +1 -1
- package/content/themes/source/assets/css/screen.css +2 -1
- package/content/themes/source/assets/js/lightbox.js +62 -45
- package/content/themes/source/assets/js/main.js +2 -2
- package/content/themes/source/package.json +4 -4
- package/content/themes/source/partials/feature-image.hbs +1 -0
- package/core/boot.js +54 -22
- package/core/built/admin/assets/Combination-B0l4MKVh.js +41 -0
- package/core/built/admin/assets/InterVariable-DiVDrmQJ.woff2 +0 -0
- package/core/built/admin/assets/InterVariable-Italic-FCBEiFp6.woff2 +0 -0
- package/core/built/admin/assets/{about-Bve7IJS2.js → about-Dyae_UT6.js} +1 -1
- package/core/built/admin/assets/account-migration-CESa0mnX.js +1 -0
- package/core/built/admin/assets/activitypub/{account-migration-C-aej4x_.js → account-migration-By69CgCp.js} +16 -16
- package/core/built/admin/assets/activitypub/activitypub.js +9 -9
- package/core/built/admin/assets/activitypub/{bluesky-sharing-BXkA5mqV.js → bluesky-sharing-BJcPMrDd.js} +36 -36
- package/core/built/admin/assets/activitypub/{content-formatters-CHcyMFk4.js → content-formatters-3rTHg3mp.js} +3 -3
- package/core/built/admin/assets/activitypub/{domain-B3ihkPde.js → domain-DUR4xSkV.js} +121 -121
- package/core/built/admin/assets/activitypub/{edit-profile-DsZM3rTX.js → edit-profile-BqtfzxCp.js} +40 -40
- package/core/built/admin/assets/activitypub/{explore-BodNrZgR.js → explore-BxuJd-J4.js} +26 -26
- package/core/built/admin/assets/activitypub/{feed-PwH1iJ84.js → feed-BM1TcksA.js} +48 -48
- package/core/built/admin/assets/activitypub/{field-D06-kmOJ.js → field-Dn7HHx2-.js} +2 -2
- package/core/built/admin/assets/activitypub/inbox-CCjkuow2.js +1284 -0
- package/core/built/admin/assets/activitypub/label-1AA_s9Fa.js +16 -0
- package/core/built/admin/assets/activitypub/layout-2-0bw6HN.js +1 -0
- package/core/built/admin/assets/activitypub/{moderation-D1Yapsod.js → moderation-CnmxEB8w.js} +91 -91
- package/core/built/admin/assets/activitypub/{note-C5zjqq5C.js → note-ZKc_Bnba.js} +23 -23
- package/core/built/admin/assets/activitypub/{notifications-B9p8eCHD.js → notifications-CB2tJZau.js} +48 -48
- package/core/built/admin/assets/activitypub/{onboarding-CNelD_uF.js → onboarding-BsJ9E5qf.js} +2 -2
- package/core/built/admin/assets/activitypub/{onboarding-mat5Hrl0.js → onboarding-CSUJJIlh.js} +2 -2
- package/core/built/admin/assets/activitypub/{preferences-BCqUdC3v.js → preferences-0-JZnUkI.js} +1519 -1519
- package/core/built/admin/assets/activitypub/{profile-BRCWoFem.js → profile-C35cFIhM.js} +183 -183
- package/core/built/admin/assets/activitypub/{routes-BSXT992i.js → routes-BD4vscyc.js} +740 -731
- package/core/built/admin/assets/activitypub/settings-BsMROdqh.js +315 -0
- package/core/built/admin/assets/activitypub/{site-DHoMupSB.js → site-O0RL9YCN.js} +2 -2
- package/core/built/admin/assets/activitypub/{step-1-B-YfsVeO.js → step-1-BMSx7c_X.js} +4 -4
- package/core/built/admin/assets/activitypub/{step-3-D6BFMXL_.js → step-3-CLlOZTyt.js} +3 -3
- package/core/built/admin/assets/activitypub/{tabs-GpNecvfB.js → tabs-Dxk-b9M-.js} +306 -306
- package/core/built/admin/assets/activitypub/{topic-filter-BPDeQQys.js → topic-filter-5yZH6tId.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-activity-pub-queries-BoVOxXkV.js → use-activity-pub-queries-CV50qe03.js} +2 -2
- package/core/built/admin/assets/activitypub/{use-reply-chain-data-wVBX7swM.js → use-reply-chain-data-Cbvzgjzd.js} +30 -30
- package/core/built/admin/assets/activitypub/{users-CoX6FT_K.js → users-BjcoP_HC.js} +7 -5
- package/core/built/admin/assets/add-integration-modal-BlF6rs_f.js +1 -0
- package/core/built/admin/assets/add-newsletter-modal-CglVQqah.js +1 -0
- package/core/built/admin/assets/add-recommendation-modal-wz7BsuaG.js +1 -0
- package/core/built/admin/assets/analytics-C4HE1gBg.js +1 -0
- package/core/built/admin/assets/{announcement-bar-modal-DM3cYOpo.js → announcement-bar-modal-Ci4zz8jO.js} +1 -1
- package/core/built/admin/assets/{api-keys-BLm2NDBh.js → api-keys-Ck-Yteiv.js} +1 -1
- package/core/built/admin/assets/{audience-9mwU5TW6.js → audience-CtbA54Sn.js} +1 -1
- package/core/built/admin/assets/{automated-emails-te1D5kq-.js → automated-emails-D5VVMND9.js} +1 -1
- package/core/built/admin/assets/automations-CzKt5fb8.js +1 -0
- package/core/built/admin/assets/bluesky-sharing-BEtJ_gL_.js +1 -0
- package/core/built/admin/assets/{breadcrumb-DLiqCyFc.js → breadcrumb-zUrZrBtW.js} +1 -1
- package/core/built/admin/assets/{button-B2jcu6bk.js → button-eNMKQWXr.js} +1 -1
- package/core/built/admin/assets/{chart-helpers-Fo3bUL3K.js → chart-helpers-CfBKt0vT.js} +1 -1
- package/core/built/admin/assets/{checkbox-BfKktMph.js → checkbox-CgOn_tKW.js} +1 -1
- package/core/built/admin/assets/{chunk.524.60ca4692d91c6022042f.js → chunk.524.a697dee88db67fdf6021.js} +4 -4
- package/core/built/admin/assets/{chunk.582.525c7617c2d9e94c940a.js → chunk.582.5ed5d124cfca9c259b9c.js} +6 -6
- package/core/built/admin/assets/{chunk.896.435fe0637944d4a69810.js → chunk.598.7e056fabb47ce7dbd73f.js} +14 -14
- package/core/built/admin/assets/{code-editor-view-CYVu4EW6.js → code-editor-view-BYNoAoNV.js} +1 -1
- package/core/built/admin/assets/{code-editor-view-DCQJxsfU.js → code-editor-view-K2wI-I5f.js} +1 -1
- package/core/built/admin/assets/color-picker-Ddw80xX6.js +3 -0
- package/core/built/admin/assets/color-picker-field-D9nUhvA7.js +1 -0
- package/core/built/admin/assets/combobox-w9j8PPQT.js +1 -0
- package/core/built/admin/assets/command-BfpHORBv.js +1 -0
- package/core/built/admin/assets/comments-BLmCmyCs.js +1 -0
- package/core/built/admin/assets/confirmation-context-CBM8G0Vj.js +1 -0
- package/core/built/admin/assets/{content-api-modal-CyQEC3fr.js → content-api-modal-TXRz3U7S.js} +1 -1
- package/core/built/admin/assets/context-menu-_QMdd3kp.js +1 -0
- package/core/built/admin/assets/{copy-field-B3M1s78L.js → copy-field-6N01S07E.js} +1 -1
- package/core/built/admin/assets/create-ghost-browse-value-source-qIBymvvf.js +1 -0
- package/core/built/admin/assets/custom-field-type-option-BddFlJFa.js +1 -0
- package/core/built/admin/assets/custom-integration-modal-DmNquxUV.js +1 -0
- package/core/built/admin/assets/{date-range-select-DD2RKkgB.js → date-range-select-uvz_DtPh.js} +1 -1
- package/core/built/admin/assets/design-and-theme-modal-BSmo12Op.js +2 -0
- package/core/built/admin/assets/dirty-confirm-dialog-BZ1iytEd.js +1 -0
- package/core/built/admin/assets/{disabled-sources-indicator-Cvy47bE2.js → disabled-sources-indicator-DMRrxsiJ.js} +1 -1
- package/core/built/admin/assets/dist-Dcsslyo72.js +1 -0
- package/core/built/admin/assets/domain-BNYrt0aD.js +1 -0
- package/core/built/admin/assets/dropzone-D7nFdAe5.js +5 -0
- package/core/built/admin/assets/edit-profile-C5Asm4LA.js +1 -0
- package/core/built/admin/assets/editor-REokrzci.js +8 -0
- package/core/built/admin/assets/{embed-signup-form-modal-DeBMLxlL.js → embed-signup-form-modal-Ds6mPEi5.js} +1 -1
- package/core/built/admin/assets/en-B1jX3WMs.js +1 -0
- package/core/built/admin/assets/explore-UY10Egsp.js +1 -0
- package/core/built/admin/assets/feature-image-placeholder-D1iF_8bc.js +1 -0
- package/core/built/admin/assets/feed-81C8mJWR.js +1 -0
- package/core/built/admin/assets/{field-Cj8lmDOo.js → field-Dihp5p18.js} +1 -1
- package/core/built/admin/assets/filter-bar-Da1qxybu.js +1 -0
- package/core/built/admin/assets/filter-sources-BO9xUPj7.js +1 -0
- package/core/built/admin/assets/filters-CRUWecfY.js +1 -0
- package/core/built/admin/assets/filters-DirqNXjH.js +1 -0
- package/core/built/admin/assets/first-promoter-modal-CT6MxYZb.js +1 -0
- package/core/built/admin/assets/{ghost-c8807202a07c20030acb719ee0114815.css → ghost-8eabfc1ece81b03cf7b90780d31487e8.css} +1 -1
- package/core/built/admin/assets/{ghost-3894eedcc6968f658578c0abfe942aab.js → ghost-c2848508292937415f20f5a830eea59e.js} +31 -28
- package/core/built/admin/assets/{ghost-dark-fbf1e384aaef3ae315e540f0eb7373a7.css → ghost-dark-a7bebe2c561f55369dcf29f4c820f7ff.css} +1 -1
- package/core/built/admin/assets/gift-link-modal-CQVAye17.js +1 -0
- package/core/built/admin/assets/growth-BWIu-6xj.js +3 -0
- package/core/built/admin/assets/growth-DU29oxeA.js +1 -0
- package/core/built/admin/assets/history-modal-CFd1Hvx6.js +1 -0
- package/core/built/admin/assets/{html-field-C8qVGGC1.js → html-field-CJBCLTvv.js} +1 -1
- package/core/built/admin/assets/{images-h8VZEQ9k.js → images-scfkWZiK.js} +1 -1
- package/core/built/admin/assets/{inbox-BYd1M06Q.js → inbox-Mcp4DXZN.js} +14 -14
- package/core/built/admin/assets/index-6MkPdCtr.js +4 -0
- package/core/built/admin/assets/index-Bn7QhcZu.css +2 -0
- package/core/built/admin/assets/index.es-CNnuNH0d.js +1 -0
- package/core/built/admin/assets/{input-group-D8sCXRF5.js → input-group-CMhe2DvY.js} +1 -1
- package/core/built/admin/assets/invite-user-modal-DfheMXs0.js +1 -0
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.js +14 -14
- package/core/built/admin/assets/koenig-lexical/koenig-lexical.umd.js +3 -3
- package/core/built/admin/assets/koenig-lexical/style.css +1 -1
- package/core/built/admin/assets/{koenig-lexical-_-IsVI7E.js → koenig-lexical-DBSqAuiI.js} +3 -3
- package/core/built/admin/assets/koenig-loader-aFQNlJlD.js +2 -0
- package/core/built/admin/assets/{kpi-card-DZQk_0W8.js → kpi-card-DBRMfGOW.js} +1 -1
- package/core/built/admin/assets/{kpi-card-DW_2y3ug.js → kpi-card-DFafD1qq.js} +1 -1
- package/core/built/admin/assets/{kpi-helpers-PueTKuA6.js → kpi-helpers-Cv8i8hns.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-CojDs-Qz.js → kpi-tabs-BJItvLYm.js} +1 -1
- package/core/built/admin/assets/{label-DjbT91GM.js → label-BnSCvffu.js} +1 -1
- package/core/built/admin/assets/label-picker-KVoo91sh.js +1 -0
- package/core/built/admin/assets/layout-Bjv2Ll-5.js +1 -0
- package/core/built/admin/assets/member-custom-fields-DDQV6QeG.js +1 -0
- package/core/built/admin/assets/member-detail-CdJF6Agw.js +1 -0
- package/core/built/admin/assets/members-DjQbq9Z2.js +4 -0
- package/core/built/admin/assets/moderation-AgCIEso4.js +1 -0
- package/core/built/admin/assets/{multi-select-combobox-B6LiRzDO.js → multi-select-combobox-BcITkUVr.js} +1 -1
- package/core/built/admin/assets/navigation-modal-BkT7b1M2.js +1 -0
- package/core/built/admin/assets/newsletter-BMMAuvgq.js +1 -0
- package/core/built/admin/assets/newsletter-detail-modal-0SCJ7Zpm.js +1 -0
- package/core/built/admin/assets/{newsletter-emails-CFeLlt4z.js → newsletter-emails-DKzwafuh.js} +1 -1
- package/core/built/admin/assets/newsletters-BRBb_uQC.js +1 -0
- package/core/built/admin/assets/note-DyGHtS9a.js +1 -0
- package/core/built/admin/assets/notifications-ZgWniKna.js +1 -0
- package/core/built/admin/assets/nql-BLpCn7qX.js +21 -0
- package/core/built/admin/assets/offers-container-modal-B-E8TIWr.js +1 -0
- package/core/built/admin/assets/{onboarding-B4YhlZ0i.js → onboarding-CzIsre-d.js} +1 -1
- package/core/built/admin/assets/onboarding-qg8Akovm.js +1 -0
- package/core/built/admin/assets/{onboarding-route-Dmc4IPm3.js → onboarding-route-CaavZulg.js} +1 -1
- package/core/built/admin/assets/overview-BW8GLeYi.js +1 -0
- package/core/built/admin/assets/overview-BkXZNzaZ.js +1 -0
- package/core/built/admin/assets/{page-header-BR_nbIPp.js → page-header-De0VzKBi.js} +1 -1
- package/core/built/admin/assets/pagemenu-B5KKCpbA.js +1 -0
- package/core/built/admin/assets/pages-jgaFm69d.js +1 -0
- package/core/built/admin/assets/pages-route-CIUAQxtY.js +1 -0
- package/core/built/admin/assets/papaparse.min-cQwnxGCG.js +10 -0
- package/core/built/admin/assets/{pintura-modal-BR9hEvS8.js → pintura-modal-BZ2J9w3n.js} +1 -1
- package/core/built/admin/assets/portal-frame-B6Hr9wHc.js +1 -0
- package/core/built/admin/assets/portal-modal-D4ceei3t.js +1 -0
- package/core/built/admin/assets/post-analytics-header-BYCsT0GP.js +1 -0
- package/core/built/admin/assets/post-analytics-ot7f6pih.js +1 -0
- package/core/built/admin/assets/{post-analytics-provider-CLfKGWZy.js → post-analytics-provider-Bo1gmYaP.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-Cl0NXRJr.js → post-share-modal-C-PaYZ_e.js} +1 -1
- package/core/built/admin/assets/posts-DuGazgTb.js +1 -0
- package/core/built/admin/assets/posts-list-screen-CBGGgjjj.js +2 -0
- package/core/built/admin/assets/posts-route-D6xmS7Vf.js +1 -0
- package/core/built/admin/assets/preferences-C5YJLp74.js +5 -0
- package/core/built/admin/assets/{preview-modal-C-BL-F9k.js → preview-modal-BfeFqi5y.js} +1 -1
- package/core/built/admin/assets/profile-B8mx2yAu.js +1 -0
- package/core/built/admin/assets/radio-group-DczlLKOn.js +1 -0
- package/core/built/admin/assets/{schemas-4UPic-oD.js → schemas-DppqmpeJ.js} +1 -1
- package/core/built/admin/assets/select-cb0t_TAJ.js +1 -0
- package/core/built/admin/assets/settings-DZqxR0Da.js +1 -0
- package/core/built/admin/assets/settings-DvfTNdHQ.js +4 -0
- package/core/built/admin/assets/{settings-breadcrumbs-CyP_9zTo.js → settings-breadcrumbs-DLzbRRSt.js} +1 -1
- package/core/built/admin/assets/share-modal-D2-Bz1_I.js +1 -0
- package/core/built/admin/assets/slack-modal-DYDTUria.js +1 -0
- package/core/built/admin/assets/{sort-button-Cc-Z96sA.js → sort-button-VVeQjjbu.js} +1 -1
- package/core/built/admin/assets/{source-icon-v6PUnmyM.js → source-icon-Ck5_5Kgq.js} +1 -1
- package/core/built/admin/assets/stats-Rjmk5owg.js +1 -0
- package/core/built/admin/assets/stats-view-BGGmkmgv.js +1 -0
- package/core/built/admin/assets/{step-1-DSRp2vBj.js → step-1-CsaBjeN4.js} +1 -1
- package/core/built/admin/assets/{step-2-COqv2g0u.js → step-2-D2vnxOOM.js} +1 -1
- package/core/built/admin/assets/{step-3-6W1NMVQc.js → step-3-wBoLdGEb.js} +2 -2
- package/core/built/admin/assets/{stripe-button-D2K42Uty.js → stripe-button-CDgLz0S0.js} +1 -1
- package/core/built/admin/assets/stripe-connect-modal-CSjMJetP.js +1 -0
- package/core/built/admin/assets/{table-B2pIKjVh.js → table-Do6iQ83Q.js} +1 -1
- package/core/built/admin/assets/{tabs-SaW_UT-Q.js → tabs-D2yWLMp_.js} +1 -1
- package/core/built/admin/assets/tag-detail-BhXlOsCa.js +2 -0
- package/core/built/admin/assets/tags-ldukRApe.js +1 -0
- package/core/built/admin/assets/testimonials-modal-CtnZBJKl.js +1 -0
- package/core/built/admin/assets/tier-detail-modal-BOs8e_G8.js +1 -0
- package/core/built/admin/assets/{toggle-group-EM9WpESu.js → toggle-group-ByUJsxwD.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DY4gNHn2.js → topic-filter-BgC3g5_v.js} +1 -1
- package/core/built/admin/assets/{transistor-modal-CpyPG-MH.js → transistor-modal-Drr0SNfo.js} +1 -1
- package/core/built/admin/assets/{trend-badge-ByMkuAuH.js → trend-badge-6ye93YAW.js} +1 -1
- package/core/built/admin/assets/{unsplash-modal-Cv2Q6GtY.js → unsplash-modal-p6AoQoWp.js} +1 -1
- package/core/built/admin/assets/{use-analytics-data-CHvjMYuT.js → use-analytics-data-CWCMAESo.js} +1 -1
- package/core/built/admin/assets/use-check-theme-limit-error-B8bjF-gO.css +1 -0
- package/core/built/admin/assets/{use-check-theme-limit-error-Qv4SVXqw.js → use-check-theme-limit-error-Cw3WQHy8.js} +1 -1
- package/core/built/admin/assets/use-filter-params-BIni9taJ.js +1 -0
- package/core/built/admin/assets/use-growth-stats-DCVG3s9w.js +1 -0
- package/core/built/admin/assets/{use-limiter-cwJvsNBo.js → use-limiter-D2WRRx0H.js} +1 -1
- package/core/built/admin/assets/{use-pintura-config-CzTfnufz.js → use-pintura-config-h-J0Lvta.js} +1 -1
- package/core/built/admin/assets/{use-pintura-editor-BDBXWUYq.js → use-pintura-editor-BfN1UwzH.js} +1 -1
- package/core/built/admin/assets/use-post-referrers-Czg47m8s.js +1 -0
- package/core/built/admin/assets/use-reply-chain-data-ns0caK8k.js +1 -0
- package/core/built/admin/assets/{use-router-history-entry-Bg6BhBQQ.js → use-router-history-entry-uldn7zJO.js} +1 -1
- package/core/built/admin/assets/{use-setting-group-VM7FQwMY.js → use-setting-group-DpcuCUDg.js} +1 -1
- package/core/built/admin/assets/use-settings-navigation-CZjGrhsp.js +1 -0
- package/core/built/admin/assets/{use-simple-pagination-Dum6Irbs.js → use-simple-pagination-DzJWobPR.js} +1 -1
- package/core/built/admin/assets/{use-staff-users-cZg4ugOF.js → use-staff-users-Csipb-O0.js} +1 -1
- package/core/built/admin/assets/use-tinybird-query-CSnGXzZh.js +1 -0
- package/core/built/admin/assets/use-tinybird-token-DTOKRR7e.js +1 -0
- package/core/built/admin/assets/{use-unsaved-changes-guard-BAy0RANR.js → use-unsaved-changes-guard-CBzs-KPi.js} +1 -1
- package/core/built/admin/assets/{use-url-input-Df5QzNa7.js → use-url-input-DFr8lCbS.js} +1 -1
- package/core/built/admin/assets/user-detail-modal-B_iUClI6.js +1 -0
- package/core/built/admin/assets/users-Cg-OeGzC.js +1 -0
- package/core/built/admin/assets/{virtual-list-DEx8WgiO.js → virtual-list-BW-padMe.js} +1 -1
- package/core/built/admin/assets/web-C1v33ehA.js +2 -0
- package/core/built/admin/assets/web-C4BKzn2T.js +1 -0
- package/core/built/admin/assets/{zapier-modal-Cw3BTBUu.js → zapier-modal-BBFfI6Ml.js} +1 -1
- package/core/built/admin/index.html +17 -22
- package/core/frontend/apps/private-blogging/lib/views/private.hbs +44 -43
- package/core/frontend/helpers/cancel_link.js +2 -2
- package/core/frontend/helpers/pagination.js +2 -2
- package/core/frontend/helpers/social_accounts.js +2 -2
- package/core/frontend/public/ghost.css +1 -1
- package/core/frontend/public/ghost.min.css +1 -1
- package/core/frontend/services/sitemap/user-map-generator.js +9 -2
- package/core/server/api/endpoints/index.js +2 -2
- package/core/server/api/endpoints/mail.js +1 -1
- package/core/server/api/endpoints/{member-custom-fields.js → member-metafields.js} +27 -28
- package/core/server/api/endpoints/members.js +4 -4
- package/core/server/api/endpoints/posts.js +3 -3
- package/core/server/api/endpoints/utils/serializers/input/members.js +4 -7
- package/core/server/api/endpoints/utils/serializers/output/index.js +2 -2
- package/core/server/api/endpoints/utils/serializers/output/{member-custom-fields.js → member-metafields.js} +2 -4
- package/core/server/api/endpoints/utils/serializers/output/members.js +2 -4
- package/core/server/api/endpoints/utils/validators/input/invitations.js +0 -5
- package/core/server/api/endpoints/utils/validators/input/pages.js +1 -1
- package/core/server/api/endpoints/utils/validators/input/posts.js +1 -1
- package/core/server/data/importer/importers/data/data-importer.js +3 -3
- package/core/server/data/migrations/utils/migrations.js +1 -1
- package/core/server/lib/image/image-size.js +24 -0
- package/core/server/lib/package-json/parse.js +2 -2
- package/core/server/models/member.js +6 -17
- package/core/server/models/post.js +2 -2
- package/core/server/models/relations/authors.js +2 -16
- package/core/server/models/user.js +9 -2
- package/core/server/services/auth/passwordreset.js +13 -0
- package/core/server/services/comments/comments-controller.js +30 -9
- package/core/server/services/comments/comments-service.js +55 -5
- package/core/server/services/content-import/csv/formula.js +12 -0
- package/core/server/services/content-import/csv/index.js +6 -1
- package/core/server/services/content-import/csv/parse.js +27 -6
- package/core/server/services/content-import/csv/serialize.js +13 -0
- package/core/server/services/content-import/import/completion-email.js +164 -0
- package/core/server/services/content-import/import/errors-file.js +36 -0
- package/core/server/services/content-import/import/importer.js +160 -53
- package/core/server/services/content-import/import/local-media-url.js +63 -0
- package/core/server/services/content-import/import/media.js +254 -0
- package/core/server/services/content-import/import/post-data.js +0 -1
- package/core/server/services/content-import/import/post-link.js +11 -0
- package/core/server/services/content-import/import/post-repository.js +25 -3
- package/core/server/services/content-import/import/reader.js +9 -2
- package/core/server/services/content-import/import/report.js +34 -0
- package/core/server/services/content-import/import/row.js +0 -2
- package/core/server/services/content-import/import/schema.js +1 -0
- package/core/server/services/content-import/import/staged-file.js +29 -0
- package/core/server/services/content-import/import/store.js +25 -2
- package/core/server/services/content-import/index.js +52 -6
- package/core/server/services/content-import/jobs/content-csv-import-job.js +20 -0
- package/core/server/services/custom-redirects/redirect-config-parser.js +2 -2
- package/core/server/services/custom-redirects/redirects-service.js +1 -1
- package/core/server/services/custom-redirects/validation.js +1 -1
- package/core/server/services/email-address/email-address-parser.js +30 -38
- package/core/server/services/email-address/email-address-service.js +37 -4
- package/core/server/services/email-analytics/index.js +2 -7
- package/core/server/services/email-service/batch-sending-service.js +0 -4
- package/core/server/services/email-service/email-renderer.js +14 -4
- package/core/server/services/email-service/email-service-wrapper.js +0 -9
- package/core/server/services/email-service/mailgun-email-provider.js +20 -9
- package/core/server/services/gifts/email-templates/gift-delivery.hbs +3 -3
- package/core/server/services/gifts/gift-email-service.js +0 -1
- package/core/server/services/gifts/index.js +3 -3
- package/core/server/services/gifts/jobs/index.js +4 -4
- package/core/server/services/indexnow-ping/indexnow-ping-service.js +2 -2
- package/core/server/services/invitations/accept.js +4 -2
- package/core/server/services/jobs/job-service.js +0 -9
- package/core/server/services/jobs-service/register-job-handlers.js +21 -11
- package/core/server/services/mail/ghost-mailer.js +4 -4
- package/core/server/services/member-welcome-emails/service.js +7 -7
- package/core/server/services/members/api.js +1 -0
- package/core/server/services/members/import-export/csv/parse.js +0 -7
- package/core/server/services/members/import-export/import/completion-email.js +2 -5
- package/core/server/services/members/import-export/import/row.js +0 -3
- package/core/server/services/members/import-export/index.js +4 -11
- package/core/server/services/members/jobs/clean-expired-comped-job.js +7 -0
- package/core/server/services/members/jobs/clean-expired-comped-task.js +145 -0
- package/core/server/services/members/jobs/index.js +48 -24
- package/core/server/services/members/members-api/controllers/member-controller.js +11 -0
- package/core/server/services/members/members-api/controllers/router-controller.js +3 -6
- package/core/server/services/members/members-api/members-api.js +4 -6
- package/core/server/services/members/members-api/repositories/event-repository.js +0 -4
- package/core/server/services/members/members-api/services/member-bread-service.js +34 -62
- package/core/server/services/members/members-api/services/payments-service.js +1 -1
- package/core/server/services/members/service.js +1 -5
- package/core/server/services/members-custom-fields/bindings-service.js +2 -1
- package/core/server/services/members-custom-fields/codec.js +3 -4
- package/core/server/services/members-custom-fields/definitions-service.js +62 -10
- package/core/server/services/members-custom-fields/filter.js +75 -93
- package/core/server/services/members-custom-fields/key.js +2 -1
- package/core/server/services/members-custom-fields/models.js +1 -1
- package/core/server/services/members-custom-fields/serializers.js +3 -5
- package/core/server/services/members-custom-fields/values-service.js +50 -28
- package/core/server/services/mentions/mention-controller.js +23 -21
- package/core/server/services/mentions/process-webmention-job.js +16 -0
- package/core/server/services/mentions/service.js +8 -4
- package/core/server/services/route-settings/validation-errors.js +1 -1
- package/core/server/services/route-settings/yaml-parser.js +1 -1
- package/core/server/services/settings/settings-service.js +1 -8
- package/core/server/services/settings-helpers/settings-helpers.js +4 -6
- package/core/server/services/slack-ping/slack-ping-service.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +2 -2
- package/core/server/services/stripe/config.js +14 -8
- package/core/server/services/stripe/service.js +10 -1
- package/core/server/services/stripe/services/checkout/completed-session.js +2 -2
- package/core/server/services/stripe/services/checkout/session-options.js +18 -15
- package/core/server/services/stripe/services/webhook/checkout-session-event-service.js +26 -5
- package/core/server/services/stripe/stripe-api.js +2 -0
- package/core/server/services/stripe/stripe-service.js +20 -10
- package/core/server/services/stripe/webhook-controller.js +10 -0
- package/core/server/services/tier-checkout-config/codec.js +9 -5
- package/core/server/services/tier-checkout-config/models.js +9 -3
- package/core/server/services/tier-checkout-config/queries.js +4 -4
- package/core/server/services/tier-checkout-config/serializers.js +16 -10
- package/core/server/services/tier-checkout-config/service.js +13 -12
- package/core/server/services/update-check/index.js +13 -13
- package/core/server/services/update-check/jobs/update-check-job.js +7 -0
- package/core/server/services/update-check/update-check-service.js +2 -2
- package/core/server/web/admin/controller.js +1 -1
- package/core/server/web/api/endpoints/admin/routes.js +19 -16
- package/core/shared/config/env/config.testing-mysql.json +1 -1
- package/core/shared/labs.js +5 -2
- package/package.json +24 -23
- package/pnpm-lock.yaml +704 -69
- package/pnpm-workspace.yaml +8 -5
- package/components/tryghost-kg-default-nodes-2.2.0.tgz +0 -0
- package/core/built/admin/assets/Combination-5fYEzgbs.js +0 -41
- package/core/built/admin/assets/account-migration-DRhAehfw.js +0 -1
- package/core/built/admin/assets/activitypub/inbox-BluD2ucT.js +0 -1284
- package/core/built/admin/assets/activitypub/label-FHSh6ZAw.js +0 -16
- package/core/built/admin/assets/activitypub/layout-Cq4NG0Xr.js +0 -1
- package/core/built/admin/assets/activitypub/settings-DQDkr06X.js +0 -315
- package/core/built/admin/assets/add-integration-modal-BC0jExDZ.js +0 -1
- package/core/built/admin/assets/add-newsletter-modal-DchTkA7l.js +0 -1
- package/core/built/admin/assets/add-recommendation-modal-DMvaJZKp.js +0 -1
- package/core/built/admin/assets/alert-dialog-QhXzdHfc.js +0 -1
- package/core/built/admin/assets/analytics-No1DDY3A.js +0 -1
- package/core/built/admin/assets/automations-DpUNUdcZ.js +0 -1
- package/core/built/admin/assets/bluesky-sharing-Cjrv_f62.js +0 -1
- package/core/built/admin/assets/color-picker-BTY_NdEm.js +0 -3
- package/core/built/admin/assets/color-picker-field-CPlkzOH0.js +0 -1
- package/core/built/admin/assets/combobox-DrMtv_4P.js +0 -1
- package/core/built/admin/assets/command-ZdgYJpoI.js +0 -1
- package/core/built/admin/assets/comments-kidj60-h.js +0 -1
- package/core/built/admin/assets/confirmation-context-BhbmQQEl.js +0 -1
- package/core/built/admin/assets/custom-field-type-option-CsGIZk1J.js +0 -10
- package/core/built/admin/assets/custom-integration-modal-BY37O_9X.js +0 -1
- package/core/built/admin/assets/design-and-theme-modal-DUSKM4uC.js +0 -22
- package/core/built/admin/assets/dialog-B44iRwlc.js +0 -1
- package/core/built/admin/assets/dirty-confirm-dialog-BxgQgQ6H.js +0 -1
- package/core/built/admin/assets/dist-Cy8tfrp_.js +0 -1
- package/core/built/admin/assets/domain-Ca6KhoQt.js +0 -1
- package/core/built/admin/assets/dropzone-Bb-5zQTk.js +0 -5
- package/core/built/admin/assets/edit-profile-BdupLlus.js +0 -1
- package/core/built/admin/assets/editor-BP883Lpn.js +0 -8
- package/core/built/admin/assets/explore-FB-CimXw.js +0 -1
- package/core/built/admin/assets/feed-C1GxsmI2.js +0 -1
- package/core/built/admin/assets/filter-sources-D5n5bFA2.js +0 -1
- package/core/built/admin/assets/filters-DJEzkMUK.js +0 -1
- package/core/built/admin/assets/filters-MZXvdH5D.js +0 -1
- package/core/built/admin/assets/first-promoter-modal-CGTGLCr5.js +0 -1
- package/core/built/admin/assets/gift-link-modal-B6v0tnRN.js +0 -1
- package/core/built/admin/assets/growth-BGAJD1rs.js +0 -3
- package/core/built/admin/assets/growth-D6mhcx3q.js +0 -1
- package/core/built/admin/assets/history-modal-iKwUY3XR.js +0 -1
- package/core/built/admin/assets/index-DjQe-e5s.js +0 -4
- package/core/built/admin/assets/index-Dqf2x-ry.css +0 -1
- package/core/built/admin/assets/index.es-VihPwfeH.js +0 -1
- package/core/built/admin/assets/input--W-rCTrX.js +0 -1
- package/core/built/admin/assets/invite-user-modal-MdImLCaJ.js +0 -1
- package/core/built/admin/assets/koenig-loader-DYZpm6El.js +0 -2
- package/core/built/admin/assets/label-picker-cB51xl1r.js +0 -1
- package/core/built/admin/assets/layout-c_B_1hax.js +0 -1
- package/core/built/admin/assets/loading-indicator-BpFWTVgh.js +0 -1
- package/core/built/admin/assets/member-custom-fields-qs0dIkxM.js +0 -1
- package/core/built/admin/assets/member-detail-B6uec2a6.js +0 -1
- package/core/built/admin/assets/members-BQF3CqhI.js +0 -2
- package/core/built/admin/assets/moderation-DY4wF1Q3.js +0 -1
- package/core/built/admin/assets/navigation-modal-DX-gNF3X.js +0 -1
- package/core/built/admin/assets/newsletter-CyQA7m3m.js +0 -1
- package/core/built/admin/assets/newsletter-detail-modal-BDrptuxn.js +0 -1
- package/core/built/admin/assets/newsletters-DXuNmC42.js +0 -1
- package/core/built/admin/assets/note-Cgo2LCJ6.js +0 -1
- package/core/built/admin/assets/notifications-BC50AMku.js +0 -1
- package/core/built/admin/assets/offers-container-modal-DWycIMzT.js +0 -1
- package/core/built/admin/assets/onboarding-DGQuV7Ew.js +0 -1
- package/core/built/admin/assets/overview-CVbFtn-k.js +0 -1
- package/core/built/admin/assets/overview-YKj7SZaK.js +0 -1
- package/core/built/admin/assets/pages-B6vr9F_D.js +0 -1
- package/core/built/admin/assets/popover-CCy0h5rq.js +0 -1
- package/core/built/admin/assets/portal-frame-dc4XKr0e.js +0 -1
- package/core/built/admin/assets/portal-modal-BvvdF_B9.js +0 -1
- package/core/built/admin/assets/post-analytics-DX470WRG.js +0 -1
- package/core/built/admin/assets/post-analytics-header-CXpJmdDv.js +0 -1
- package/core/built/admin/assets/posts-KY3aUhVV.js +0 -1
- package/core/built/admin/assets/preferences-DO0UUWFp.js +0 -5
- package/core/built/admin/assets/profile-CxT-0l4d.js +0 -1
- package/core/built/admin/assets/radio-group-Bgz60i81.js +0 -1
- package/core/built/admin/assets/select-DuDUFdPq.js +0 -1
- package/core/built/admin/assets/settings-BjCii7BM.js +0 -1
- package/core/built/admin/assets/settings-b8kNzZSQ.js +0 -4
- package/core/built/admin/assets/share-modal-BV70mr9k.js +0 -1
- package/core/built/admin/assets/shim-ClF_m5ux.js +0 -1
- package/core/built/admin/assets/slack-modal-Be2_KK-z.js +0 -1
- package/core/built/admin/assets/stats-C4reGh9O.js +0 -1
- package/core/built/admin/assets/stats-view-DP9pQ62k.js +0 -1
- package/core/built/admin/assets/stripe-connect-modal-Z-UT_BGc.js +0 -1
- package/core/built/admin/assets/tag-detail-Dc0nTOtU.js +0 -2
- package/core/built/admin/assets/tags-BLaXBwnb.js +0 -1
- package/core/built/admin/assets/testimonials-modal-sFwRnNLh.js +0 -1
- package/core/built/admin/assets/tier-detail-modal-BCFpWwUS.js +0 -1
- package/core/built/admin/assets/use-filter-params-CAvfDvf1.js +0 -1
- package/core/built/admin/assets/use-growth-stats-D6mZXGhK.js +0 -1
- package/core/built/admin/assets/use-post-referrers-DbY-mzci.js +0 -1
- package/core/built/admin/assets/use-reply-chain-data-BeQwz8oF.js +0 -1
- package/core/built/admin/assets/use-settings-navigation-BbrSUVcf.js +0 -1
- package/core/built/admin/assets/use-tinybird-query-B1IMyAcv.js +0 -39
- package/core/built/admin/assets/use-tinybird-token-Bow0Zwn8.js +0 -1
- package/core/built/admin/assets/user-detail-modal-yipSoQfv.js +0 -1
- package/core/built/admin/assets/users-5msE1WMs.js +0 -1
- package/core/built/admin/assets/web-CKcXAu86.js +0 -1
- package/core/built/admin/assets/web-DR4TqM3E.js +0 -2
- package/core/server/services/jobs/worker-model-event-bridge.js +0 -149
- package/core/server/services/members/jobs/clean-expired-comped.js +0 -132
- package/core/server/services/stripe/services/checkout/allowed-countries.js +0 -267
- package/core/server/services/stripe/services/checkout/field-ports.js +0 -34
- package/core/server/services/tier-checkout-config/destinations.js +0 -17
- package/core/server/services/update-check/run-update-check.js +0 -63
|
@@ -13,7 +13,7 @@ function isUnsplashImage(url) {
|
|
|
13
13
|
}
|
|
14
14
|
const { DateTime } = require('luxon');
|
|
15
15
|
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
|
|
16
|
-
const
|
|
16
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
17
17
|
const { getEmailDesign } = require('../email-rendering/email-design');
|
|
18
18
|
const { registerHelpers } = require('./helpers/register-helpers');
|
|
19
19
|
const crypto = require('crypto');
|
|
@@ -229,6 +229,8 @@ function cheerioLoad(html) {
|
|
|
229
229
|
* @prop {string} id
|
|
230
230
|
* @prop {RegExp} token
|
|
231
231
|
* @prop {(member: MemberLike) => string} getValue
|
|
232
|
+
* @prop {boolean} [trusted] - Value is server-generated, so it is inserted into the
|
|
233
|
+
* html body as-is instead of being HTML-escaped. Defaults to false (escaped).
|
|
232
234
|
*/
|
|
233
235
|
|
|
234
236
|
/**
|
|
@@ -337,7 +339,7 @@ class EmailRenderer {
|
|
|
337
339
|
}
|
|
338
340
|
|
|
339
341
|
#getRawFromAddress(post, newsletter) {
|
|
340
|
-
// Pass the raw name through;
|
|
342
|
+
// Pass the raw name through; emailAddressParser.stringify() is the single
|
|
341
343
|
// point that escapes it for the RFC5322 quoted-string From header. Escaping
|
|
342
344
|
// here too would double-escape (e.g. a title containing a double quote).
|
|
343
345
|
let senderName = this.#settingsCache.get('title') || '';
|
|
@@ -394,7 +396,7 @@ class EmailRenderer {
|
|
|
394
396
|
{ useFallbackAddress },
|
|
395
397
|
);
|
|
396
398
|
|
|
397
|
-
return
|
|
399
|
+
return emailAddressParser.stringify(addresses.from);
|
|
398
400
|
}
|
|
399
401
|
|
|
400
402
|
/**
|
|
@@ -423,7 +425,7 @@ class EmailRenderer {
|
|
|
423
425
|
);
|
|
424
426
|
|
|
425
427
|
if (addresses.replyTo) {
|
|
426
|
-
return
|
|
428
|
+
return emailAddressParser.stringify(addresses.replyTo);
|
|
427
429
|
}
|
|
428
430
|
return null;
|
|
429
431
|
}
|
|
@@ -920,18 +922,21 @@ class EmailRenderer {
|
|
|
920
922
|
getValue: (member) => {
|
|
921
923
|
return this.createUnsubscribeUrl(member.uuid, { newsletterUuid });
|
|
922
924
|
},
|
|
925
|
+
trusted: true, // Server-generated URL, must not be HTML-escaped
|
|
923
926
|
},
|
|
924
927
|
{
|
|
925
928
|
id: 'manage_account_url',
|
|
926
929
|
getValue: () => {
|
|
927
930
|
return this.createManageAccountUrl();
|
|
928
931
|
},
|
|
932
|
+
trusted: true, // Server-generated URL, must not be HTML-escaped
|
|
929
933
|
},
|
|
930
934
|
{
|
|
931
935
|
id: 'uuid',
|
|
932
936
|
getValue: (member) => {
|
|
933
937
|
return member.uuid;
|
|
934
938
|
},
|
|
939
|
+
trusted: true, // Server-generated identifier
|
|
935
940
|
},
|
|
936
941
|
{
|
|
937
942
|
id: 'key',
|
|
@@ -941,6 +946,7 @@ class EmailRenderer {
|
|
|
941
946
|
.update(member.uuid)
|
|
942
947
|
.digest('hex');
|
|
943
948
|
},
|
|
949
|
+
trusted: true, // Server-generated hmac
|
|
944
950
|
},
|
|
945
951
|
{
|
|
946
952
|
id: 'first_name',
|
|
@@ -1000,6 +1006,7 @@ class EmailRenderer {
|
|
|
1000
1006
|
return this.createUnsubscribeUrl(member.uuid, { newsletterUuid });
|
|
1001
1007
|
},
|
|
1002
1008
|
required: true, // Used in email headers
|
|
1009
|
+
trusted: true, // Server-generated URL, must not be HTML-escaped
|
|
1003
1010
|
},
|
|
1004
1011
|
// Unique ID used for ad images to bypass ESP image proxies
|
|
1005
1012
|
{
|
|
@@ -1007,6 +1014,7 @@ class EmailRenderer {
|
|
|
1007
1014
|
getValue: () => {
|
|
1008
1015
|
return crypto.randomUUID();
|
|
1009
1016
|
},
|
|
1017
|
+
trusted: true, // Server-generated identifier
|
|
1010
1018
|
},
|
|
1011
1019
|
];
|
|
1012
1020
|
|
|
@@ -1043,6 +1051,7 @@ class EmailRenderer {
|
|
|
1043
1051
|
getValue: fallback
|
|
1044
1052
|
? (member) => definition.getValue(member) || fallback
|
|
1045
1053
|
: definition.getValue,
|
|
1054
|
+
trusted: definition.trusted === true,
|
|
1046
1055
|
});
|
|
1047
1056
|
}
|
|
1048
1057
|
}
|
|
@@ -1056,6 +1065,7 @@ class EmailRenderer {
|
|
|
1056
1065
|
originalId: definition.id,
|
|
1057
1066
|
token: new RegExp(`%%\\{${definition.id}\\}%%`, 'g'),
|
|
1058
1067
|
getValue: definition.getValue,
|
|
1068
|
+
trusted: definition.trusted === true,
|
|
1059
1069
|
});
|
|
1060
1070
|
}
|
|
1061
1071
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('i18n');
|
|
2
|
-
const logging = require('@tryghost/logging');
|
|
3
2
|
const url = require('../../api/endpoints/utils/serializers/output/utils/url');
|
|
4
3
|
const events = require('../../lib/common/events');
|
|
5
4
|
|
|
@@ -55,12 +54,6 @@ class EmailServiceWrapper {
|
|
|
55
54
|
const emailAnalyticsJobs = require('../email-analytics/jobs');
|
|
56
55
|
const { cachedImageSizeFromUrl } = require('../../lib/image');
|
|
57
56
|
|
|
58
|
-
// capture errors from mailgun client and log them in sentry
|
|
59
|
-
const errorHandler = (error) => {
|
|
60
|
-
logging.info(`Capturing error for mailgun email provider service`);
|
|
61
|
-
sentry.captureException(error);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
57
|
// Mailgun client instance for email provider
|
|
65
58
|
const mailgunClient = new MailgunClient({
|
|
66
59
|
config: configService,
|
|
@@ -78,7 +71,6 @@ class EmailServiceWrapper {
|
|
|
78
71
|
const mailgunEmailProvider = new MailgunEmailProvider({
|
|
79
72
|
mailgunClient,
|
|
80
73
|
config: configService,
|
|
81
|
-
errorHandler,
|
|
82
74
|
});
|
|
83
75
|
|
|
84
76
|
const emailRenderer = new EmailRenderer({
|
|
@@ -134,7 +126,6 @@ class EmailServiceWrapper {
|
|
|
134
126
|
db,
|
|
135
127
|
sentry,
|
|
136
128
|
getRequiredUrlRelations,
|
|
137
|
-
debugStorageFilePath: configService.getContentPath('data'),
|
|
138
129
|
});
|
|
139
130
|
|
|
140
131
|
if (ghostServer) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const logging = require('@tryghost/logging');
|
|
2
2
|
const errors = require('@tryghost/errors');
|
|
3
3
|
const debug = require('@tryghost/debug')('email-service:mailgun-provider-service');
|
|
4
|
+
const { escapeExpression } = require('handlebars');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {object} Config
|
|
@@ -35,35 +36,41 @@ const debug = require('@tryghost/debug')('email-service:mailgun-provider-service
|
|
|
35
36
|
class MailgunEmailProvider {
|
|
36
37
|
#mailgunClient;
|
|
37
38
|
#config;
|
|
38
|
-
#errorHandler;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* @param {object} dependencies
|
|
42
42
|
* @param {import('../lib/mailgun-client')} dependencies.mailgunClient - mailgun client to send emails
|
|
43
43
|
* @param {Config} dependencies.config
|
|
44
|
-
* @param {Function} [dependencies.errorHandler] - custom error handler for logging exceptions
|
|
45
44
|
*/
|
|
46
|
-
constructor({ mailgunClient, config
|
|
45
|
+
constructor({ mailgunClient, config }) {
|
|
47
46
|
this.#mailgunClient = mailgunClient;
|
|
48
47
|
this.#config = config;
|
|
49
|
-
this.#errorHandler = errorHandler;
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
#createRecipientData(replacements) {
|
|
50
|
+
#createRecipientData(replacements, htmlEscapedIds) {
|
|
53
51
|
let recipientData = {};
|
|
54
52
|
|
|
55
53
|
recipientData = replacements.reduce((acc, replacement) => {
|
|
56
54
|
const { id, value } = replacement;
|
|
57
55
|
acc[id] = value;
|
|
56
|
+
// Mailgun's recipient-variables substitution is a simple string replace with
|
|
57
|
+
// no HTML awareness, and the same value is used for both the html and plaintext
|
|
58
|
+
// bodies. Member-controlled values get an extra, escaped variant that only the
|
|
59
|
+
// html body points at. Trusted server-generated values (urls, uuids, hmacs) are
|
|
60
|
+
// skipped so their URLs don't end up entity-encoded inside hrefs.
|
|
61
|
+
if (htmlEscapedIds.has(id)) {
|
|
62
|
+
acc[`${id}_html`] = typeof value === 'string' ? escapeExpression(value) : value;
|
|
63
|
+
}
|
|
58
64
|
return acc;
|
|
59
65
|
}, {});
|
|
60
66
|
|
|
61
67
|
return recipientData;
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
#updateRecipientVariables(data, replacementDefinitions) {
|
|
70
|
+
#updateRecipientVariables(data, replacementDefinitions, isHtml) {
|
|
65
71
|
for (const def of replacementDefinitions) {
|
|
66
|
-
|
|
72
|
+
const useHtmlVariant = isHtml && !def.trusted;
|
|
73
|
+
data = data.replace(def.token, `%recipient.${def.id}${useHtmlVariant ? '_html' : ''}%`);
|
|
67
74
|
}
|
|
68
75
|
return data;
|
|
69
76
|
}
|
|
@@ -128,8 +135,11 @@ class MailgunEmailProvider {
|
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
// create recipient data for Mailgun using replacement definitions
|
|
138
|
+
const htmlEscapedIds = new Set(
|
|
139
|
+
replacementDefinitions.filter((def) => !def.trusted).map((def) => def.id),
|
|
140
|
+
);
|
|
131
141
|
const recipientData = recipients.reduce((acc, recipient) => {
|
|
132
|
-
acc[recipient.email] = this.#createRecipientData(recipient.replacements);
|
|
142
|
+
acc[recipient.email] = this.#createRecipientData(recipient.replacements, htmlEscapedIds);
|
|
133
143
|
return acc;
|
|
134
144
|
}, {});
|
|
135
145
|
|
|
@@ -139,6 +149,7 @@ class MailgunEmailProvider {
|
|
|
139
149
|
messageData[key] = this.#updateRecipientVariables(
|
|
140
150
|
messageData[key],
|
|
141
151
|
replacementDefinitions,
|
|
152
|
+
key === 'html',
|
|
142
153
|
);
|
|
143
154
|
}
|
|
144
155
|
});
|
|
@@ -165,7 +176,7 @@ class MailgunEmailProvider {
|
|
|
165
176
|
message: this.#createMailgunErrorMessage(error),
|
|
166
177
|
errorDetails: JSON.stringify({ error, messageData }),
|
|
167
178
|
context: `Mailgun Error ${error.status}: ${error.details}`,
|
|
168
|
-
help: `https://ghost.org/
|
|
179
|
+
help: `https://docs.ghost.org/newsletters/#bulk-email-configuration`,
|
|
169
180
|
code: 'BULK_EMAIL_SEND_FAILED',
|
|
170
181
|
});
|
|
171
182
|
} else {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
<tr>
|
|
32
32
|
<td align="left" style="padding-bottom: 22px; text-align: left;">
|
|
33
33
|
{{#if siteIconUrl}}
|
|
34
|
-
<
|
|
34
|
+
<img src="{{siteIconUrl}}" alt="{{siteTitle}}" border="0" width="48" height="48" style="border-radius: 4px;">
|
|
35
35
|
{{else}}
|
|
36
|
-
<
|
|
36
|
+
<span style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 17px; font-weight: 700; color: {{accentColor}};">{{siteTitle}}</span>
|
|
37
37
|
{{/if}}
|
|
38
38
|
</td>
|
|
39
39
|
</tr>
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
</tr>
|
|
93
93
|
<tr>
|
|
94
94
|
<td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 14px; vertical-align: top; padding-top: 40px;">
|
|
95
|
-
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 16px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0;">{{t 'This message was sent from {siteDomain} to {email} on behalf of {buyerName} ({buyerEmail}).' siteDomain=
|
|
95
|
+
<p class="small" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 16px; font-size: 11px; color: #738A94; font-weight: normal; margin: 0;">{{t 'This message was sent from {siteDomain} to {email} on behalf of {buyerName} ({buyerEmail}).' siteDomain=siteDomain email=toEmail buyerName=buyerName buyerEmail=buyerEmail}}</p>
|
|
96
96
|
</td>
|
|
97
97
|
</tr>
|
|
98
98
|
</table>
|
|
@@ -182,7 +182,6 @@ class GiftEmailService {
|
|
|
182
182
|
const giftLink = `${siteUrl.replace(/\/$/, '')}/gift/${token}`;
|
|
183
183
|
const { html, text } = await this.renderer.renderDelivery({
|
|
184
184
|
siteTitle,
|
|
185
|
-
siteUrl,
|
|
186
185
|
siteIconUrl: this.blogIcon.getIconUrl({ absolute: true, fallbackToDefault: false }),
|
|
187
186
|
siteDomain,
|
|
188
187
|
accentColor: this.accentColor,
|
|
@@ -14,7 +14,7 @@ const send_gift_delivery_event_1 = require("./events/send-gift-delivery-event");
|
|
|
14
14
|
// Persistent scheduling adapters keep their queue across restarts and opt out
|
|
15
15
|
// of boot-time rebuilds (same contract post scheduling honours in boot.js).
|
|
16
16
|
let rescheduleDeliveriesOnBoot = true;
|
|
17
|
-
|
|
17
|
+
function init(options) {
|
|
18
18
|
if (exports.service) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
@@ -37,7 +37,7 @@ async function init(options) {
|
|
|
37
37
|
const labsService = require('../../../shared/labs');
|
|
38
38
|
const urlUtils = require('../../../shared/url-utils').default;
|
|
39
39
|
const settingsHelpers = require('../settings-helpers');
|
|
40
|
-
const
|
|
40
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
41
41
|
const { blogIcon } = require('../../../server/lib/image');
|
|
42
42
|
const { t } = require('../i18n');
|
|
43
43
|
const repository = new gift_bookshelf_repository_1.GiftBookshelfRepository({
|
|
@@ -57,7 +57,7 @@ async function init(options) {
|
|
|
57
57
|
bulkMailer: new MailgunClient({ config, settings: settingsCache }),
|
|
58
58
|
settingsCache,
|
|
59
59
|
urlUtils,
|
|
60
|
-
getFromAddress: () =>
|
|
60
|
+
getFromAddress: () => emailAddressParser.stringify(settingsHelpers.getDefaultEmail()),
|
|
61
61
|
getReplyToAddress: () => settingsHelpers.getMembersSupportAddress(),
|
|
62
62
|
blogIcon,
|
|
63
63
|
t,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const logging = require('@tryghost/logging');
|
|
3
|
-
const
|
|
3
|
+
const jobManager = require('../../jobs');
|
|
4
4
|
const CleanGiftsJob = require('./clean-gifts-job').default;
|
|
5
5
|
|
|
6
6
|
let hasScheduled = {
|
|
@@ -31,7 +31,7 @@ function scheduleJob(key, name, jobFile) {
|
|
|
31
31
|
const at = randomOffPeakDailyCron();
|
|
32
32
|
|
|
33
33
|
logging.info(`[Background Job] ${name} scheduled at ${at}`);
|
|
34
|
-
|
|
34
|
+
jobManager.addJob({
|
|
35
35
|
at,
|
|
36
36
|
job: path.resolve(__dirname, jobFile),
|
|
37
37
|
name,
|
|
@@ -43,14 +43,14 @@ function scheduleJob(key, name, jobFile) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
module.exports = {
|
|
46
|
-
async scheduleGiftCleanupJob(
|
|
46
|
+
async scheduleGiftCleanupJob(jobsService) {
|
|
47
47
|
if (alreadyScheduledOrTest('cleanup')) {
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const cron = randomOffPeakDailyCron();
|
|
52
52
|
logging.info(`[Background Job] clean-gifts scheduled at ${cron}`);
|
|
53
|
-
await
|
|
53
|
+
await jobsService.scheduleRecurring(new CleanGiftsJob(), { cron });
|
|
54
54
|
|
|
55
55
|
hasScheduled.cleanup = true;
|
|
56
56
|
},
|
|
@@ -160,7 +160,7 @@ class IndexNowPingService {
|
|
|
160
160
|
err,
|
|
161
161
|
message: err.message,
|
|
162
162
|
context: (0, tpl_1.default)(constants_1.messages.requestFailedError, { service: 'IndexNow' }),
|
|
163
|
-
help: (0, tpl_1.default)(constants_1.messages.requestFailedHelp, { url: 'https://ghost.org/
|
|
163
|
+
help: (0, tpl_1.default)(constants_1.messages.requestFailedHelp, { url: 'https://docs.ghost.org/' }),
|
|
164
164
|
}),
|
|
165
165
|
};
|
|
166
166
|
}
|
|
@@ -181,7 +181,7 @@ class IndexNowPingService {
|
|
|
181
181
|
err,
|
|
182
182
|
message: err.message,
|
|
183
183
|
context: (0, tpl_1.default)(constants_1.messages.requestFailedError, { service: 'IndexNow' }),
|
|
184
|
-
help: (0, tpl_1.default)(constants_1.messages.requestFailedHelp, { url: 'https://ghost.org/
|
|
184
|
+
help: (0, tpl_1.default)(constants_1.messages.requestFailedHelp, { url: 'https://docs.ghost.org/' }),
|
|
185
185
|
}),
|
|
186
186
|
};
|
|
187
187
|
}
|
|
@@ -30,7 +30,9 @@ async function accept(invitation) {
|
|
|
30
30
|
throw new errors.NotFoundError({ message: tpl(messages.inviteExpired) });
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
const email = invite.get('email');
|
|
34
|
+
|
|
35
|
+
let user = await models.User.findOne({ email }, options);
|
|
34
36
|
if (user) {
|
|
35
37
|
throw new errors.ValidationError({
|
|
36
38
|
message: tpl(messages.inviteEmailAlreadyExist.message),
|
|
@@ -45,7 +47,7 @@ async function accept(invitation) {
|
|
|
45
47
|
|
|
46
48
|
await models.User.add(
|
|
47
49
|
{
|
|
48
|
-
email
|
|
50
|
+
email,
|
|
49
51
|
name: data.name,
|
|
50
52
|
password: data.password,
|
|
51
53
|
roles: [roleId],
|
|
@@ -9,22 +9,13 @@ const models = require('../../models');
|
|
|
9
9
|
const sentry = require('../../../shared/sentry');
|
|
10
10
|
const domainEvents = require('@tryghost/domain-events');
|
|
11
11
|
const config = require('../../../shared/config');
|
|
12
|
-
const WorkerModelEventBridge = require('./worker-model-event-bridge');
|
|
13
12
|
const errorHandler = (error, workerMeta) => {
|
|
14
13
|
logging.error(error, `[Background Job] ${workerMeta.name} failed`);
|
|
15
14
|
sentry.captureException(error);
|
|
16
15
|
};
|
|
17
16
|
const events = require('../../lib/common/events');
|
|
18
|
-
const workerModelEventBridge = new WorkerModelEventBridge({ models, events, logging, sentry });
|
|
19
17
|
|
|
20
18
|
const workerMessageHandler = ({ name, message }) => {
|
|
21
|
-
if (workerModelEventBridge.isModelEventMessage(message)) {
|
|
22
|
-
// Carries its own `eventName` rather than job-manager's reserved `event` key,
|
|
23
|
-
// so it routes through the bridge instead of being dispatched as a raw domain event.
|
|
24
|
-
workerModelEventBridge.handle(message, { jobName: name });
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
19
|
if (typeof message === 'string' && !['done', 'cancelled'].includes(message)) {
|
|
29
20
|
logging.info(`[Background Job] ${name}: ${message}`);
|
|
30
21
|
}
|
|
@@ -37,25 +37,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.default = registerJobHandlers;
|
|
40
|
-
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
41
40
|
const clean_tokens_job_1 = __importDefault(require("../members/jobs/clean-tokens-job"));
|
|
42
|
-
const
|
|
43
|
-
const gifts = __importStar(require("../gifts"));
|
|
41
|
+
const clean_expired_comped_job_1 = __importDefault(require("../members/jobs/clean-expired-comped-job"));
|
|
44
42
|
const clean_gifts_job_1 = __importDefault(require("../gifts/jobs/clean-gifts-job"));
|
|
45
43
|
const external_media_inliner_job_1 = __importDefault(require("../media-inliner/external-media-inliner-job"));
|
|
46
|
-
|
|
44
|
+
const content_csv_import_job_1 = __importDefault(require("../content-import/jobs/content-csv-import-job"));
|
|
45
|
+
const contentImport = __importStar(require("../content-import"));
|
|
46
|
+
const update_check_job_1 = __importDefault(require("../update-check/jobs/update-check-job"));
|
|
47
|
+
const process_webmention_job_1 = __importDefault(require("../mentions/process-webmention-job"));
|
|
48
|
+
const updateCheck = require('../update-check');
|
|
49
|
+
function registerJobHandlers({ jobsService, memberJobs, giftService, mediaInliner, mentionsController, }) {
|
|
47
50
|
jobsService.handle(clean_tokens_job_1.default, async () => {
|
|
48
|
-
await
|
|
51
|
+
await memberJobs.cleanTokens();
|
|
52
|
+
});
|
|
53
|
+
jobsService.handle(clean_expired_comped_job_1.default, async () => {
|
|
54
|
+
await memberJobs.cleanExpiredComped();
|
|
49
55
|
});
|
|
50
56
|
jobsService.handle(clean_gifts_job_1.default, async () => {
|
|
51
|
-
|
|
52
|
-
throw new errors_1.default.IncorrectUsageError({
|
|
53
|
-
message: 'clean-gifts ran before the gifts service was initialised',
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
await gifts.service.cleanup();
|
|
57
|
+
await giftService.cleanup();
|
|
57
58
|
});
|
|
58
59
|
jobsService.handle(external_media_inliner_job_1.default, async (job) => {
|
|
59
60
|
await mediaInliner.inline(job.domains);
|
|
60
61
|
});
|
|
62
|
+
jobsService.handle(content_csv_import_job_1.default, async (job) => {
|
|
63
|
+
await contentImport.handleJob(job);
|
|
64
|
+
});
|
|
65
|
+
jobsService.handle(update_check_job_1.default, async () => {
|
|
66
|
+
await updateCheck({ rethrowErrors: true });
|
|
67
|
+
});
|
|
68
|
+
jobsService.handle(process_webmention_job_1.default, async (job) => {
|
|
69
|
+
await mentionsController.processWebmention(job);
|
|
70
|
+
});
|
|
61
71
|
}
|
|
@@ -17,7 +17,7 @@ const messages = {
|
|
|
17
17
|
reason: ' Reason: {reason}.',
|
|
18
18
|
messageSent: 'Message sent. Double check inbox and spam folder!',
|
|
19
19
|
};
|
|
20
|
-
const
|
|
20
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
21
21
|
const DEFAULT_TAGS = ['ghost-email', 'transactional-email'];
|
|
22
22
|
const MAX_MAILGUN_TAGS = 10;
|
|
23
23
|
|
|
@@ -54,8 +54,8 @@ function getFromAddress(requestedFromAddress, requestedReplyToAddress) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
return {
|
|
57
|
-
from:
|
|
58
|
-
replyTo: addresses.replyTo ?
|
|
57
|
+
from: emailAddressParser.stringify(addresses.from),
|
|
58
|
+
replyTo: addresses.replyTo ? emailAddressParser.stringify(addresses.replyTo) : null,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -100,7 +100,7 @@ function createMessage(message) {
|
|
|
100
100
|
*/
|
|
101
101
|
function createMailError({ message, err, ignoreDefaultMessage } = { message: '' }) {
|
|
102
102
|
const helpMessage = tpl(messages.checkEmailConfigInstructions, {
|
|
103
|
-
url: 'https://ghost.org/
|
|
103
|
+
url: 'https://docs.ghost.org/config/#mail',
|
|
104
104
|
});
|
|
105
105
|
const defaultErrorMessage = tpl(messages.failedSendingEmailError);
|
|
106
106
|
|
|
@@ -7,7 +7,7 @@ const MagicLink = require('../lib/magic-link/magic-link');
|
|
|
7
7
|
const sentry = require('../../../shared/sentry');
|
|
8
8
|
const emailAddressService = require('../email-address');
|
|
9
9
|
const settingsHelpers = require('../settings-helpers');
|
|
10
|
-
const
|
|
10
|
+
const emailAddressParser = require('../email-address/email-address-parser');
|
|
11
11
|
const mail = require('../mail');
|
|
12
12
|
const MailgunClient = require('../lib/mailgun-client');
|
|
13
13
|
const config = require('../../../shared/config');
|
|
@@ -152,7 +152,7 @@ class MemberWelcomeEmailService {
|
|
|
152
152
|
},
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
const from =
|
|
155
|
+
const from = emailAddressParser.stringify(fromAddresses.from);
|
|
156
156
|
const replyToSetting = newsletter.get('sender_reply_to');
|
|
157
157
|
let replyTo = null;
|
|
158
158
|
|
|
@@ -173,7 +173,7 @@ class MemberWelcomeEmailService {
|
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
if (addresses.replyTo) {
|
|
176
|
-
replyTo =
|
|
176
|
+
replyTo = emailAddressParser.stringify(addresses.replyTo);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -199,10 +199,10 @@ class MemberWelcomeEmailService {
|
|
|
199
199
|
async #getEffectiveSenderOptions(automatedSender = {}) {
|
|
200
200
|
const defaultOptions = await this.#getSenderOptions();
|
|
201
201
|
const defaultFrom =
|
|
202
|
-
|
|
202
|
+
emailAddressParser.parse(defaultOptions.from || '') ||
|
|
203
203
|
emailAddressService.service.defaultFromEmail;
|
|
204
204
|
const defaultReplyTo = defaultOptions.replyTo
|
|
205
|
-
?
|
|
205
|
+
? emailAddressParser.parse(defaultOptions.replyTo)
|
|
206
206
|
: undefined;
|
|
207
207
|
|
|
208
208
|
const senderName = trimValue(automatedSender.senderName) || defaultFrom?.name || undefined;
|
|
@@ -218,10 +218,10 @@ class MemberWelcomeEmailService {
|
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
return {
|
|
221
|
-
from:
|
|
221
|
+
from: emailAddressParser.stringify(addresses.from),
|
|
222
222
|
...(addresses.replyTo
|
|
223
223
|
? {
|
|
224
|
-
replyTo:
|
|
224
|
+
replyTo: emailAddressParser.stringify(addresses.replyTo),
|
|
225
225
|
}
|
|
226
226
|
: {}),
|
|
227
227
|
};
|
|
@@ -274,6 +274,7 @@ function createApiInstance(config) {
|
|
|
274
274
|
// two together in boot order. Custom fields is initialised in initCore, the
|
|
275
275
|
// members API is built in initServices, so this is always the live instance.
|
|
276
276
|
customFieldValues: customFieldsService.values,
|
|
277
|
+
customFieldDefinitions: customFieldsService.definitions,
|
|
277
278
|
});
|
|
278
279
|
|
|
279
280
|
return membersApiInstance;
|
|
@@ -43,19 +43,12 @@ function parse(path, headerMapping) {
|
|
|
43
43
|
// hasOwn, not `in`: a prototype-named header would otherwise match an
|
|
44
44
|
// inherited method on the mapping and take a function as its mapped name.
|
|
45
45
|
if (headerMapping && Object.hasOwn(headerMapping, header)) {
|
|
46
|
-
// An empty target is the caller naming a column to leave out, which
|
|
47
|
-
// it has to name: an unnamed column carries through below, and for a
|
|
48
|
-
// custom_fields.* column carrying through means being imported. So
|
|
49
|
-
// omitting a column from the mapping is the opposite of excluding it.
|
|
50
46
|
if (!headerMapping[header]) {
|
|
51
47
|
continue;
|
|
52
48
|
}
|
|
53
49
|
row[headerMapping[header]] = value;
|
|
54
50
|
}
|
|
55
51
|
else if (isSafeColumnName(header)) {
|
|
56
|
-
// Carry any unmapped column through untouched, so the import is not
|
|
57
|
-
// constrained to a known vocabulary: a custom_fields.* column
|
|
58
|
-
// survives parsing even though nothing consumes it yet.
|
|
59
52
|
row[header] = value;
|
|
60
53
|
}
|
|
61
54
|
}
|
|
@@ -55,8 +55,6 @@ function humaniseError(row) {
|
|
|
55
55
|
function stringifyLabels(labels) {
|
|
56
56
|
return labels.map((label) => (typeof label === 'string' ? label : label.name)).join(',');
|
|
57
57
|
}
|
|
58
|
-
// The custom_fields.* cells a submitted row carried, echoed untouched so a manager can
|
|
59
|
-
// fix a failed row and re-upload the values they mapped.
|
|
60
58
|
function customFieldCells(row) {
|
|
61
59
|
return Object.fromEntries(Object.entries(row).filter(([column]) => (0, csv_2.isCustomFieldColumn)(column)));
|
|
62
60
|
}
|
|
@@ -82,9 +80,8 @@ function toErrorReportRow(row) {
|
|
|
82
80
|
}
|
|
83
81
|
// The error report attached to the completion email: the failed rows as CSV, called only
|
|
84
82
|
// when there are rows to list. It shares the serialiser with the export but not the
|
|
85
|
-
// shaping -- the export writes db members, this echoes submitted rows. Member columns
|
|
86
|
-
//
|
|
87
|
-
// before the last error column, and each row's custom cells merged on.
|
|
83
|
+
// shaping -- the export writes db members, this echoes submitted rows. Member columns
|
|
84
|
+
// come from the shaper's keys, so the type stays the single source.
|
|
88
85
|
function buildErrorReport(errors) {
|
|
89
86
|
const memberColumns = Object.keys(toErrorReportRow(errors[0])).filter((column) => column !== 'error');
|
|
90
87
|
const customColumns = [...new Set(errors.flatMap((row) => Object.keys(customFieldCells(row))))];
|
|
@@ -16,9 +16,6 @@ const optionalCell = zod_1.z
|
|
|
16
16
|
// not defaulted), so a CSV lacking these columns leaves the member's state untouched.
|
|
17
17
|
const isSubscribed = (cell) => cell.toLowerCase() !== 'false';
|
|
18
18
|
const isComplimentary = (cell) => cell.toLowerCase() === 'true';
|
|
19
|
-
// The member import vocabulary as a schema: the single source of truth that coerces the
|
|
20
|
-
// raw string Row into typed member fields. Unknown columns (custom_fields.*, and anything
|
|
21
|
-
// else a source carries) pass through via .loose() so nothing is lost before the kernel.
|
|
22
19
|
exports.memberImportRowSchema = zod_1.z
|
|
23
20
|
.object({
|
|
24
21
|
id: optionalCell,
|
|
@@ -12,7 +12,6 @@ const exporter_1 = __importDefault(require("./export/exporter"));
|
|
|
12
12
|
const MembersCSVImporterStripeUtils = require('./import/stripe-utils');
|
|
13
13
|
const db = require('../../../data/db');
|
|
14
14
|
const models = require('../../../models');
|
|
15
|
-
const labs = require('../../../../shared/labs');
|
|
16
15
|
const logging = require('@tryghost/logging');
|
|
17
16
|
const sentry = require('../../../../shared/sentry');
|
|
18
17
|
// Build the members CSV importer. This is the composition root: today's models and
|
|
@@ -57,11 +56,8 @@ function makeImporter(deps) {
|
|
|
57
56
|
transacting: options.transacting,
|
|
58
57
|
}),
|
|
59
58
|
};
|
|
60
|
-
// Gated by the same labs flag as the export, so the two halves round-trip or stay
|
|
61
|
-
// silent together: off, activeFields resolves empty and every custom_fields.* column
|
|
62
|
-
// is dropped.
|
|
63
59
|
const customFields = {
|
|
64
|
-
activeFields: async () =>
|
|
60
|
+
activeFields: async () => deps.customFields.definitions.browse(),
|
|
65
61
|
planWrite: (values) => deps.customFields.values.planWrite(values),
|
|
66
62
|
// Every value the import writes came out of the file, whichever column carried it.
|
|
67
63
|
// An import has no id to give until runs are tracked, so it names its kind only.
|
|
@@ -105,9 +101,7 @@ function makeImporter(deps) {
|
|
|
105
101
|
}
|
|
106
102
|
// Build the members CSV exporter. The same composition root from the other direction:
|
|
107
103
|
// knex and the members id lookup are wired here, and the custom fields definitions and
|
|
108
|
-
// values services are injected (boot builds them before this one).
|
|
109
|
-
// decides whether custom field columns appear, so nothing flag-shaped leaks into the
|
|
110
|
-
// exporter itself.
|
|
104
|
+
// values services are injected (boot builds them before this one).
|
|
111
105
|
function makeExporter({ definitions, values, }) {
|
|
112
106
|
const exporter = new exporter_1.default({
|
|
113
107
|
knex: db.knex,
|
|
@@ -126,9 +120,8 @@ function makeExporter({ definitions, values, }) {
|
|
|
126
120
|
},
|
|
127
121
|
customFields: {
|
|
128
122
|
// Boot builds the definitions and values services before this one, so they
|
|
129
|
-
// are always present -- no not-initialised state to guard.
|
|
130
|
-
|
|
131
|
-
activeDefinitions: async () => labs.isSet('membersCustomFields') ? definitions.browse() : [],
|
|
123
|
+
// are always present -- no not-initialised state to guard.
|
|
124
|
+
activeDefinitions: async () => definitions.browse(),
|
|
132
125
|
valuesForMembers: (memberIds) => values.getValuesForMembers(memberIds),
|
|
133
126
|
},
|
|
134
127
|
});
|