ghost 6.37.1 → 6.38.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/tryghost-i18n-0.0.0.tgz +0 -0
- package/core/boot.js +13 -9
- package/core/bridge.js +10 -6
- package/core/built/admin/assets/{PolarAngleAxis-Cavik65s.js → PolarAngleAxis-CB-FJBDa.js} +1 -1
- package/core/built/admin/assets/{_baseAssignValue-CnqN6ZMT.js → _baseAssignValue-BEAjPAfy.js} +1 -1
- package/core/built/admin/assets/{a-large-small-Wi4LtZX-.js → a-large-small-L1BJa6mG.js} +1 -1
- package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
- package/core/built/admin/assets/admin-x-settings/{code-editor-view-Cf8T5Jfd.mjs → code-editor-view-bOZzJ3a1.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-BMWEh-vR.mjs → index-BRuMxaCz.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{index-Aj-hFSnY.mjs → index-CTAQj489.mjs} +5 -5
- package/core/built/admin/assets/admin-x-settings/{index-DjBJrXYn.mjs → index-WoKKBOe4.mjs} +2 -2
- package/core/built/admin/assets/admin-x-settings/{modals-8WpWZZdI.mjs → modals-BQ5M-RTA.mjs} +3 -3
- package/core/built/admin/assets/{arrow-right-BiAOKOto.js → arrow-right-B2j-ecEK.js} +1 -1
- package/core/built/admin/assets/{at-sign-z5gBMxDW.js → at-sign-CtAw8_lw.js} +1 -1
- package/core/built/admin/assets/{audience-DxNilLIv.js → audience-CvSOqHuo.js} +1 -1
- package/core/built/admin/assets/automations-BVUNuVOh.js +1 -0
- package/core/built/admin/assets/automations-Bi9RJq5E.js +1 -0
- package/core/built/admin/assets/{avatar-flipboard-5OzseZFV.js → avatar-flipboard-DocLTXLh.js} +1 -1
- package/core/built/admin/assets/{bluesky-sharing-D9YcSpW8.js → bluesky-sharing-DafX2lE1.js} +1 -1
- package/core/built/admin/assets/chart-BRQeD9eh.js +67 -0
- package/core/built/admin/assets/{chevron-up-DVt6fhV3.js → chevron-up-v4_QwmnL.js} +1 -1
- package/core/built/admin/assets/{chunk.524.fcd26ef812d73e2b0620.js → chunk.524.c3d844070c24d7984fd7.js} +4 -4
- package/core/built/admin/assets/{chunk.582.b61ffaad1c2fa4aba820.js → chunk.582.0e0ede1eac12926acceb.js} +6 -6
- package/core/built/admin/assets/{chunk.383.adb5a3fe32b6b98c43ff.js → chunk.995.4b32363ab39da3e7f5d7.js} +3 -3
- package/core/built/admin/assets/circle-alert-Dbz54fYb.js +1 -0
- package/core/built/admin/assets/{code-editor-view-CbQIqTrK.js → code-editor-view-D-pYCcdu.js} +1 -1
- package/core/built/admin/assets/comments-DSv09jRn.js +1 -0
- package/core/built/admin/assets/{content-helpers-jANWGIfA.js → content-helpers-DQdPPDGb.js} +1 -1
- package/core/built/admin/assets/{copy-BBLeLEnG.js → copy-BULQsPZ4.js} +1 -1
- package/core/built/admin/assets/{data-list-BveeOSGI.js → data-list-WxXC0iKE.js} +1 -1
- package/core/built/admin/assets/{deleted-feed-item-DupAEk4P.js → deleted-feed-item-ZBLfgRkK.js} +1 -1
- package/core/built/admin/assets/{dropzone-D3PSdAHN.js → dropzone-B8mAoWrn.js} +1 -1
- package/core/built/admin/assets/{edit-profile-NrCR9_bi.js → edit-profile-Bj3th8H0.js} +1 -1
- package/core/built/admin/assets/editor-BZV40eAE.css +1 -0
- package/core/built/admin/assets/editor-u20VuPzn.js +7 -0
- package/core/built/admin/assets/{empty-indicator-Dhi_KeYX.js → empty-indicator-P3IsUguf.js} +1 -1
- package/core/built/admin/assets/{en-BontI3SP.js → en-C9bUnuy7.js} +1 -1
- package/core/built/admin/assets/{feed-B3Kfiz5R.js → feed-B94mO944.js} +1 -1
- package/core/built/admin/assets/{filter-query-core-Dwh4Qulu.js → filter-query-core-CM0KKIr2.js} +1 -1
- package/core/built/admin/assets/filters-D52FTE0z.js +1 -0
- package/core/built/admin/assets/gh-chart-Xa6Nvzwz.js +1 -0
- package/core/built/admin/assets/{ghost-c25434d5bbf5a5429883d4c3590f1946.js → ghost-82f1c4ffe0e1cd058ce4b66b5253b620.js} +159 -201
- package/core/built/admin/assets/{ghost-dark-52df27573e5f2a75e6cf4b1d06eea9e4.css → ghost-dark-9897cf5102772faedf566e5e54d4fbee.css} +1 -1
- package/core/built/admin/assets/{ghost-fcf460028e04192f392f16e173f2e3a8.css → ghost-e11f1814be0f38e98bb77b49eba11178.css} +1 -1
- package/core/built/admin/assets/{growth-DRH8zUIJ.js → growth-D3TW3JIB.js} +1 -1
- package/core/built/admin/assets/{hash-UvX5iOa9.js → hash-CstXS-iB.js} +1 -1
- package/core/built/admin/assets/{header-G1a38jnf.js → header-StkTITw-.js} +1 -1
- package/core/built/admin/assets/{inbox-By7HZskI.js → inbox-D1RM3kVF.js} +1 -1
- package/core/built/admin/assets/index-BEkJ9xtY.css +1 -0
- package/core/built/admin/assets/{index-CS_ZmI8F.js → index-BJx5Uas5.js} +1 -1
- package/core/built/admin/assets/index-BKzg6OE7.js +1 -0
- package/core/built/admin/assets/{index-D7JyKf1m.js → index-BM-MBdD0.js} +1 -1
- package/core/built/admin/assets/index-BRzvVuXt.js +2 -0
- package/core/built/admin/assets/index-B_3xqeM9.js +1 -0
- package/core/built/admin/assets/{index-ydyrczVl.js → index-C-OdR2wJ.js} +1 -1
- package/core/built/admin/assets/{index-X-MG1ikg.js → index-C59QlJqT.js} +1 -1
- package/core/built/admin/assets/{index-CJo0Qh4V.js → index-CCq_1tCo.js} +1 -1
- package/core/built/admin/assets/{index-qHUvPjQB.js → index-CYyEyBYw.js} +11 -11
- package/core/built/admin/assets/{index-CX4ztLSk.js → index-DHbiBjyq.js} +1 -1
- package/core/built/admin/assets/{index-BP6hBbPK.js → index-DTVgSMyL.js} +2 -2
- package/core/built/admin/assets/{index-vZMbI8e8.js → index-DiUjEgRw.js} +1 -1
- package/core/built/admin/assets/{index-YM4ZplGj.js → index-DugxKzA6.js} +1 -1
- package/core/built/admin/assets/{index-D8InZCr2.js → index-DzVHlhO1.js} +1 -1
- package/core/built/admin/assets/{index-D4b7U6K-.js → index-LsaD9Cq7.js} +1 -1
- package/core/built/admin/assets/{index-DRUPF8nN.js → index-x0hNfyqU.js} +1 -1
- package/core/built/admin/assets/{inline-Bc9zHR7e.js → inline-B4cWemrJ.js} +1 -1
- package/core/built/admin/assets/{koenig-lexical-B-ZnqRE1.js → koenig-lexical-BOdeH3QJ.js} +1 -1
- package/core/built/admin/assets/{kpi-card-zyi5Onb0.js → kpi-card-P7zSpcx0.js} +1 -1
- package/core/built/admin/assets/{kpi-card-CzlPIEPE.js → kpi-card-plhYOEtA.js} +1 -1
- package/core/built/admin/assets/{kpi-tabs-Dsa5ldXK.js → kpi-tabs-DZw2Qktb.js} +1 -1
- package/core/built/admin/assets/{kpis-Cw5ZRQhk.js → kpis-CDHM0xhW.js} +1 -1
- package/core/built/admin/assets/{label-BvxmsD1M.js → label-Ckacdx14.js} +1 -1
- package/core/built/admin/assets/{links-CiS5BPKS.js → links-BtXE5d0Y.js} +1 -1
- package/core/built/admin/assets/{list-filter-zmkB7rg4.js → list-filter-DLjjiktn.js} +1 -1
- package/core/built/admin/assets/{list-header-Cp779e3H.js → list-header-Cc7Ia3nr.js} +1 -1
- package/core/built/admin/assets/loader-circle-DLJd-dNm.js +1 -0
- package/core/built/admin/assets/{mail-i2qs8CIG.js → mail-B97w28e2.js} +1 -1
- package/core/built/admin/assets/{main-layout-KCvKOX8F.js → main-layout-bwWvzc9B.js} +1 -1
- package/core/built/admin/assets/{members-w601v5JJ.js → members-CokS8MI2.js} +5 -5
- package/core/built/admin/assets/{message-square-text-bG9IoWIt.js → message-square-text-D3rsjDFR.js} +1 -1
- package/core/built/admin/assets/minus-BzXQVmMw.js +1 -0
- package/core/built/admin/assets/{modals-lNzhiL0o.js → modals-D0lip-iy.js} +2 -2
- package/core/built/admin/assets/{moderation-CyfNRSWj.js → moderation-Dv7AadCV.js} +1 -1
- package/core/built/admin/assets/newsletter-BazC5rIj.js +1 -0
- package/core/built/admin/assets/{newsletters-B8xSXSr_.js → newsletters-m6HQgU5J.js} +1 -1
- package/core/built/admin/assets/{note-dNfKEpxw.js → note-CCGiEKz-.js} +1 -1
- package/core/built/admin/assets/{onboarding-route-Drbw0QtT.js → onboarding-route-Cbt7qWNE.js} +1 -1
- package/core/built/admin/assets/overview-TN50tzUh.js +1 -0
- package/core/built/admin/assets/{pagemenu-VJXMoMB4.js → pagemenu-BXrHWtsU.js} +1 -1
- package/core/built/admin/assets/{pencil-DHNmlLjO.js → pencil-BL1r8KVy.js} +1 -1
- package/core/built/admin/assets/{post-analytics-PsqLsS2e.js → post-analytics-BqEXi6Ps.js} +1 -1
- package/core/built/admin/assets/{post-analytics-context-QLZsZCFg.js → post-analytics-context-DekYNlb3.js} +1 -1
- package/core/built/admin/assets/{post-analytics-header-PecXsIT-.js → post-analytics-header-AkNbn6uc.js} +1 -1
- package/core/built/admin/assets/{post-share-modal-B4Rvo7W1.js → post-share-modal-CXDGVKoQ.js} +1 -1
- package/core/built/admin/assets/posts/{app-utils-DIc5TmxO.mjs → app-utils-DxA7edI_.mjs} +2 -2
- package/core/built/admin/assets/posts/{automations-BZpT35kJ.mjs → automations-Cwu_gHl4.mjs} +64 -67
- package/core/built/admin/assets/posts/automations-D81hV_2c.mjs +13 -0
- package/core/built/admin/assets/posts/{avatar-K63cvqSS.mjs → avatar-IkjEzEjI.mjs} +19 -19
- package/core/built/admin/assets/posts/{button-ufGBCcsV.mjs → button-KaJZKcou.mjs} +21 -21
- package/core/built/admin/assets/posts/{check-IcEnuTpD.mjs → check-Cf74RvZ1.mjs} +21 -21
- package/core/built/admin/assets/posts/{comments-CyJU9Ums.mjs → comments-pdKhYbYG.mjs} +54 -52
- package/core/built/admin/assets/posts/createLucideIcon-Cj7h3r9g.mjs +320 -0
- package/core/built/admin/assets/posts/{dialog-D6_lBvtT.mjs → dialog-Ci3W8fjx.mjs} +71 -76
- package/core/built/admin/assets/posts/{dropdown-menu-BSSmAory.mjs → dropdown-menu-DZ9XCHSw.mjs} +5 -5
- package/core/built/admin/assets/posts/editor-Bb-xle0o.mjs +6084 -0
- package/core/built/admin/assets/posts/ellipsis-ChhZo9aT.mjs +10 -0
- package/core/built/admin/assets/posts/{empty-indicator-DvGCvRAX.mjs → empty-indicator-DnFs7hDy.mjs} +2 -2
- package/core/built/admin/assets/posts/{filters-BLBaHhJz.mjs → filters-CO_FUPX-.mjs} +42 -40
- package/core/built/admin/assets/posts/get-site-timezone-C21yKZT_.mjs +87 -0
- package/core/built/admin/assets/posts/{growth-Bz1ChJNB.mjs → growth-CtX9Efm2.mjs} +12 -12
- package/core/built/admin/assets/posts/heading-DZ_KtDTL.mjs +138 -0
- package/core/built/admin/assets/posts/{hooks-BEngBys9.mjs → hooks-DVhBDVlA.mjs} +3 -2
- package/core/built/admin/assets/posts/{index-BAF0YXsp.mjs → index-B4db8w_H.mjs} +181 -172
- package/core/built/admin/assets/posts/{inline-C2YFQAdU.mjs → inline-BfB-SNk0.mjs} +2 -2
- package/core/built/admin/assets/posts/{input-surface-TqJlNSp_.mjs → input-surface-CNwZJloi.mjs} +2 -2
- package/core/built/admin/assets/posts/{kpis-DAX5_0-Q.mjs → kpis-BvY_5Y1u.mjs} +8792 -8803
- package/core/built/admin/assets/posts/{links-A1YTsBbF.mjs → links-Ck-h7SKg.mjs} +57 -57
- package/core/built/admin/assets/posts/{loading-indicator-BZsjmp8g.mjs → loading-indicator-B_rU9Lie.mjs} +3 -3
- package/core/built/admin/assets/posts/mail-DiZ3oKEW.mjs +9 -0
- package/core/built/admin/assets/posts/{main-layout-D_HHTP_n.mjs → main-layout-aYo6qOBZ.mjs} +2 -2
- package/core/built/admin/assets/posts/{newsletter-BRzgjOIa.mjs → newsletter-BQc53TF2.mjs} +31 -30
- package/core/built/admin/assets/posts/{overview-C41VFjn7.mjs → overview-blz9Ak9q.mjs} +42 -41
- package/core/built/admin/assets/posts/plus-YVjtYaK3.mjs +15 -0
- package/core/built/admin/assets/posts/{post-analytics-DGgMkFaf.mjs → post-analytics-BQbWGoeu.mjs} +6 -6
- package/core/built/admin/assets/posts/{post-analytics-context-B1r3HBrp.mjs → post-analytics-context-Be7THgnd.mjs} +7 -7
- package/core/built/admin/assets/posts/{post-analytics-header-DTWSO0Aq.mjs → post-analytics-header-iDFy7Bao.mjs} +2762 -2764
- package/core/built/admin/assets/posts/{post-share-modal-ohHwk0Yt.mjs → post-share-modal-akP16idN.mjs} +50 -48
- package/core/built/admin/assets/posts/posts.js +2 -1
- package/core/built/admin/assets/posts/{settings-CZYL6Jhr.mjs → settings-BdQhfHxY.mjs} +2 -2
- package/core/built/admin/assets/posts/{sheet-D7YesQJE.mjs → sheet-3dBlKeXf.mjs} +14 -13
- package/core/built/admin/assets/posts/{skeleton-S1k58YKa.mjs → skeleton-DMgSvYqr.mjs} +11 -11
- package/core/built/admin/assets/posts/{source-icon-DnpCy5N4.mjs → source-icon-Bp5Qxq1E.mjs} +4 -4
- package/core/built/admin/assets/posts/{stats-CbOpowXb.mjs → stats-DJx9qc_u.mjs} +4 -4
- package/core/built/admin/assets/posts/{table-Cw3Wxf1C.mjs → table-BpVwuzeH.mjs} +2 -2
- package/core/built/admin/assets/posts/{data-list-BnZARP88.mjs → tabs-CbZCpxfI.mjs} +4885 -5284
- package/core/built/admin/assets/posts/{tags-BoMt6Ce3.mjs → tags-CNtvDS6f.mjs} +33 -33
- package/core/built/admin/assets/posts/{tags-CNdsvTtZ.mjs → tags-DFOoFZ_3.mjs} +2 -2
- package/core/built/admin/assets/posts/{tooltip-CTcyINxz.mjs → tooltip-Cc_09RDU.mjs} +106 -117
- package/core/built/admin/assets/posts/value-CwGM7M-1.mjs +410 -0
- package/core/built/admin/assets/posts/{virtual-list-window-Bs88yrut.mjs → virtual-list-window-D6nRoL0y.mjs} +5 -5
- package/core/built/admin/assets/posts/{web-Bsd8eTP6.mjs → web-yfPssEt8.mjs} +1042 -1040
- package/core/built/admin/assets/posts/x-DrUGcpfp.mjs +9 -0
- package/core/built/admin/assets/posts/zap-DAN0ur-o.mjs +24 -0
- package/core/built/admin/assets/posts-D-jyPPE6.js +1 -0
- package/core/built/admin/assets/power-Cc5ncYVl.js +1 -0
- package/core/built/admin/assets/{referrers-kcBT5smx.js → referrers-qnXsKsuv.js} +1 -1
- package/core/built/admin/assets/{repeat-Cn2sKd-L.js → repeat-BGIBynZH.js} +1 -1
- package/core/built/admin/assets/{reply-D7E-ZMd5.js → reply-DOKdAsL_.js} +1 -1
- package/core/built/admin/assets/{rocket-DnH0XAGW.js → rocket-CAN2NB82.js} +1 -1
- package/core/built/admin/assets/{select-f0MMMQWu.js → select-8S9kYGSM.js} +1 -1
- package/core/built/admin/assets/{settings-BAQNP3gU.js → settings-DeD-bmZc.js} +4 -4
- package/core/built/admin/assets/{settings-ORttG1jT.js → settings-e4IB9sx6.js} +1 -1
- package/core/built/admin/assets/{share-modal-CXU4tGY8.js → share-modal-CBavk20I.js} +1 -1
- package/core/built/admin/assets/{sort-button-BXMrChFu.js → sort-button-DlSKBzUq.js} +1 -1
- package/core/built/admin/assets/{source-icon-cjy79jGT.js → source-icon-27VIJSXr.js} +1 -1
- package/core/built/admin/assets/{sprout-DZ4tZ78G.js → sprout-D66nqc79.js} +1 -1
- package/core/built/admin/assets/{square-vOzqJf7x.js → square-DccaXQWe.js} +1 -1
- package/core/built/admin/assets/stats/{audience-5NeJICtP.mjs → audience-B84ClTDM.mjs} +3 -3
- package/core/built/admin/assets/stats/{content-helpers-DFSTl3uB.mjs → content-helpers-CBAddUg7.mjs} +4 -4
- package/core/built/admin/assets/stats/{index-C4T7u699.mjs → index-Bz5oVpED.mjs} +5 -5
- package/core/built/admin/assets/stats/{index-B8bp6qe2.mjs → index-Cs8i1uR4.mjs} +74 -75
- package/core/built/admin/assets/stats/{index-wvi17m79.mjs → index-DEsP_Tbd.mjs} +8 -8
- package/core/built/admin/assets/stats/{index-CFfAYHdD.mjs → index-Ds-pM47x.mjs} +5 -5
- package/core/built/admin/assets/stats/{index-Chqk7VlR.mjs → index-Wzk1IcAz.mjs} +6 -6
- package/core/built/admin/assets/stats/{kpi-tabs-Df9yOI2P.mjs → kpi-tabs-Cs4l8B_M.mjs} +7 -7
- package/core/built/admin/assets/stats/{sort-button-Doi8OgMU.mjs → sort-button-rR-GA9Hu.mjs} +3 -3
- package/core/built/admin/assets/stats/{stats-FXeCefyd.mjs → stats-RFpEJpEq.mjs} +34 -34
- package/core/built/admin/assets/stats/stats.js +1 -1
- package/core/built/admin/assets/stats/{use-growth-stats-93C_3EZk.mjs → use-growth-stats-DwgFzbq9.mjs} +3 -3
- package/core/built/admin/assets/{stats-CIGz2Win.js → stats-B4OybenW.js} +1 -1
- package/core/built/admin/assets/{stats-view-BTKRxYTQ.js → stats-view-BuExwFXa.js} +1 -1
- package/core/built/admin/assets/{step-1-BSMHbT-q.js → step-1-D_5LOQrC.js} +1 -1
- package/core/built/admin/assets/{step-2-_YC0LCZv.js → step-2-BZLBj8cj.js} +1 -1
- package/core/built/admin/assets/{step-3-DWMqP2bO.js → step-3-s5uNbcOH.js} +1 -1
- package/core/built/admin/assets/{table-mHKjXQba.js → table-pGS7EeJe.js} +1 -1
- package/core/built/admin/assets/{tabs-B93IVRBz.js → tabs-CjdKssUe.js} +1 -1
- package/core/built/admin/assets/{tags-DCb-x2ZB.js → tags-B0vRk0A6.js} +1 -1
- package/core/built/admin/assets/{tags-kUHJ1MZo.js → tags-Dso4-FA_.js} +1 -1
- package/core/built/admin/assets/{textarea-DswCtM7_.js → textarea-DzFnun0_.js} +1 -1
- package/core/built/admin/assets/{tiers-DueraQKd.js → tiers-4Vkr_D-L.js} +1 -1
- package/core/built/admin/assets/{toggle-group-ChTo8MPD.js → toggle-group-hDsr47IB.js} +1 -1
- package/core/built/admin/assets/{topic-filter-DrW3fYWo.js → topic-filter-C7TpPe1G.js} +1 -1
- package/core/built/admin/assets/{trash-B2ESGftj.js → trash-DCVuk7ZX.js} +1 -1
- package/core/built/admin/assets/{underline-vo-bPRac.js → underline-CjK8la2G.js} +1 -1
- package/core/built/admin/assets/{upload-DXQRkBBq.js → upload-CPYhmtWZ.js} +1 -1
- package/core/built/admin/assets/{use-growth-stats-CoB8-lsd.js → use-growth-stats-CaFSgp_R.js} +1 -1
- package/core/built/admin/assets/{use-simple-pagination-Y_Wv2N6g.js → use-simple-pagination-D0vJBMvF.js} +1 -1
- package/core/built/admin/assets/{user-round-check-Dp9a2x-e.js → user-round-check-CzQQeFd8.js} +1 -1
- package/core/built/admin/assets/{user-round-x-DwCTGlxc.js → user-round-x-BDFm8yQj.js} +1 -1
- package/core/built/admin/assets/value-BGBiTkF3.js +1 -0
- package/core/built/admin/assets/{vendor-17f3b979ba6f3898d1a8c5249cc22ff1.js → vendor-ce3fc901ccbcf37f36eb791b4e454138.js} +29 -31
- package/core/built/admin/assets/{virtual-list-window-BF7IvGMC.js → virtual-list-window-EKtWZ5cP.js} +1 -1
- package/core/built/admin/assets/{wallet-cards-IEmcxJvG.js → wallet-cards-C1NOBbWE.js} +1 -1
- package/core/built/admin/assets/web-DCA7HR1S.js +1 -0
- package/core/built/admin/index.html +8 -8
- package/core/frontend/helpers/social_accounts.js +91 -0
- package/core/frontend/services/sitemap/handler.js +14 -2
- package/core/frontend/services/sitemap/site-map-manager.js +133 -9
- package/core/frontend/web/middleware/handle-image-sizes.js +4 -0
- package/core/server/api/endpoints/automations.js +38 -45
- package/core/server/models/member.js +30 -25
- package/core/server/services/automations/automations-api.js +58 -0
- package/core/server/services/automations/automations-api.ts +71 -0
- package/core/server/services/automations/automations-repository.js +2 -0
- package/core/server/services/automations/automations-repository.ts +63 -0
- package/core/server/services/automations/fake-database-automations-repository.js +210 -0
- package/core/server/services/automations/fake-database-automations-repository.ts +273 -0
- package/core/server/services/automations/temporary-fake-database.js +5 -4
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.hbs +10 -32
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.js +6 -8
- package/core/server/services/gifts/email-templates/gift-purchase-confirmation.ts +6 -8
- package/core/server/services/gifts/email-templates/gift-reminder.hbs +12 -37
- package/core/server/services/gifts/email-templates/gift-reminder.js +5 -9
- package/core/server/services/gifts/email-templates/gift-reminder.ts +5 -9
- package/core/server/services/gifts/gift-service.js +1 -1
- package/core/server/services/gifts/gift-service.ts +2 -2
- package/core/server/services/member-welcome-emails/service.js +0 -1
- package/core/server/services/members/members-api/controllers/router-controller.js +52 -7
- package/core/server/services/members/members-api/services/member-bread-service.js +0 -8
- package/core/server/services/route-settings/route-settings.js +18 -2
- package/core/server/services/staff/email-templates/gift.hbs +1 -1
- package/core/server/services/staff/email-templates/gift.txt.js +1 -1
- package/core/server/services/staff/email-templates/new-gift-subscription.hbs +1 -1
- package/core/server/services/staff/email-templates/new-gift-subscription.txt.js +1 -1
- package/core/server/services/staff/staff-service-emails.js +1 -1
- package/core/server/services/url/index.js +16 -1
- package/core/server/services/url/lazy-find-resource.js +49 -0
- package/core/server/services/url/lazy-find-resource.ts +62 -0
- package/core/server/services/url/lazy-url-service.js +220 -0
- package/core/server/services/url/lazy-url-service.ts +265 -0
- package/core/server/web/api/endpoints/admin/routes.js +1 -0
- package/core/server/web/gift-preview/Inter.ttf +0 -0
- package/core/server/web/gift-preview/controller.js +56 -6
- package/core/server/web/gift-preview/gift-card-noise.png +0 -0
- package/core/server/web/gift-preview/gift-card-orb.png +0 -0
- package/core/server/web/gift-preview/image.js +170 -43
- package/core/shared/config/defaults.json +1 -0
- package/core/shared/one-at-a-time.js +28 -33
- package/core/shared/one-at-a-time.ts +58 -0
- package/package.json +18 -16
- package/pnpm-lock.yaml +989 -356
- package/core/built/admin/assets/automations-D1uMjd06.js +0 -1
- package/core/built/admin/assets/chart-_EXssQtm.js +0 -67
- package/core/built/admin/assets/comments-E00h1SbR.js +0 -1
- package/core/built/admin/assets/filters-CC7XfjOS.js +0 -1
- package/core/built/admin/assets/gh-chart-B9_ywrJJ.js +0 -1
- package/core/built/admin/assets/index-AsV-aY0z.css +0 -1
- package/core/built/admin/assets/index-B0y_ynZw.js +0 -1
- package/core/built/admin/assets/index-Bs6wWbLA.js +0 -2
- package/core/built/admin/assets/index-Cb4641XW.js +0 -1
- package/core/built/admin/assets/loader-circle-B5UPqSok.js +0 -1
- package/core/built/admin/assets/minus-DWx9VUtr.js +0 -1
- package/core/built/admin/assets/newsletter-fkQye9OK.js +0 -1
- package/core/built/admin/assets/overview-CZ-VrnXy.js +0 -1
- package/core/built/admin/assets/posts/createLucideIcon-DcUfTBt_.mjs +0 -454
- package/core/built/admin/assets/posts/get-site-timezone-DlXmHA3y.mjs +0 -93
- package/core/built/admin/assets/posts-Bx1RWrub.js +0 -1
- package/core/built/admin/assets/web-DSME3e8c.js +0 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// # Social Accounts Helper
|
|
2
|
+
// Usage:
|
|
3
|
+
// {{#social_accounts @site}}
|
|
4
|
+
// <a href="{{href}}" target="_blank" rel="noopener" aria-label="{{name}}">
|
|
5
|
+
// {{> (concat "icons/" type)}}
|
|
6
|
+
// </a>
|
|
7
|
+
// {{/social_accounts}}
|
|
8
|
+
//
|
|
9
|
+
// Iterates over the social accounts on the given source object in canonical
|
|
10
|
+
// order, yielding `{type, href, username, name}` for each platform with a
|
|
11
|
+
// username set.
|
|
12
|
+
//
|
|
13
|
+
// A source must be passed explicitly:
|
|
14
|
+
// {{#social_accounts @site}} - site-level accounts
|
|
15
|
+
// {{#social_accounts author}} - a specific author
|
|
16
|
+
// {{#social_accounts this}} - the current context (e.g. inside {{#foreach authors}})
|
|
17
|
+
const errors = require('@tryghost/errors');
|
|
18
|
+
const tpl = require('@tryghost/tpl');
|
|
19
|
+
const {socialUrls} = require('../services/proxy');
|
|
20
|
+
const {hbs} = require('../services/handlebars');
|
|
21
|
+
|
|
22
|
+
const createFrame = hbs.handlebars.createFrame;
|
|
23
|
+
|
|
24
|
+
const messages = {
|
|
25
|
+
sourceRequired: 'The {{#social_accounts}} helper requires a source argument, e.g. {{#social_accounts @site}}...{{/social_accounts}}.',
|
|
26
|
+
blockRequired: 'The {{#social_accounts}} helper must be used as a block helper, e.g. {{#social_accounts @site}}...{{/social_accounts}}.'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Canonical order mirrors the admin settings UI (SOCIAL_PLATFORM_KEYS).
|
|
30
|
+
const SOCIAL_PLATFORMS = [
|
|
31
|
+
{type: 'twitter', name: 'X'},
|
|
32
|
+
{type: 'facebook', name: 'Facebook'},
|
|
33
|
+
{type: 'linkedin', name: 'LinkedIn'},
|
|
34
|
+
{type: 'bluesky', name: 'Bluesky'},
|
|
35
|
+
{type: 'threads', name: 'Threads'},
|
|
36
|
+
{type: 'mastodon', name: 'Mastodon'},
|
|
37
|
+
{type: 'tiktok', name: 'TikTok'},
|
|
38
|
+
{type: 'youtube', name: 'YouTube'},
|
|
39
|
+
{type: 'instagram', name: 'Instagram'}
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
module.exports = function social_accounts(source, options) { // eslint-disable-line camelcase
|
|
43
|
+
// {{#social_accounts}} with no positional arg: handlebars passes only options.
|
|
44
|
+
if (arguments.length < 2) {
|
|
45
|
+
throw new errors.IncorrectUsageError({
|
|
46
|
+
level: 'normal',
|
|
47
|
+
message: tpl(messages.sourceRequired),
|
|
48
|
+
help: 'https://ghost.org/docs/themes/helpers/social-accounts/'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
options = options || {};
|
|
53
|
+
options.data = options.data || {};
|
|
54
|
+
|
|
55
|
+
const {fn, inverse, data} = options;
|
|
56
|
+
|
|
57
|
+
if (typeof fn !== 'function') {
|
|
58
|
+
throw new errors.IncorrectUsageError({
|
|
59
|
+
level: 'normal',
|
|
60
|
+
message: tpl(messages.blockRequired),
|
|
61
|
+
help: 'https://ghost.org/docs/themes/helpers/social-accounts/'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const accounts = SOCIAL_PLATFORMS.reduce((acc, {type, name}) => {
|
|
66
|
+
const username = source && source[type];
|
|
67
|
+
if (username && typeof socialUrls[type] === 'function') {
|
|
68
|
+
acc.push({type, name, username, href: socialUrls[type](username)});
|
|
69
|
+
}
|
|
70
|
+
return acc;
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
if (!accounts.length) {
|
|
74
|
+
return inverse ? inverse(this) : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const frame = createFrame(data);
|
|
78
|
+
let output = '';
|
|
79
|
+
|
|
80
|
+
accounts.forEach((account, index) => {
|
|
81
|
+
frame.index = index;
|
|
82
|
+
frame.number = index + 1;
|
|
83
|
+
frame.first = index === 0;
|
|
84
|
+
frame.last = index === accounts.length - 1;
|
|
85
|
+
frame.even = index % 2 === 1;
|
|
86
|
+
frame.odd = !frame.even;
|
|
87
|
+
output += fn(account, {data: frame});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return output;
|
|
91
|
+
};
|
|
@@ -13,7 +13,13 @@ module.exports = function handler(siteApp) {
|
|
|
13
13
|
next();
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
siteApp.get('/sitemap.xml', function sitemapXML(req, res) {
|
|
16
|
+
siteApp.get('/sitemap.xml', async function sitemapXML(req, res, next) {
|
|
17
|
+
try {
|
|
18
|
+
await manager.ensurePopulatedFromDatabase();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return next(err);
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
res.set({
|
|
18
24
|
'Cache-Control': 'public, max-age=' + config.get('caching:sitemap:maxAge'),
|
|
19
25
|
'Content-Type': 'text/xml'
|
|
@@ -22,11 +28,17 @@ module.exports = function handler(siteApp) {
|
|
|
22
28
|
res.send(manager.getIndexXml());
|
|
23
29
|
});
|
|
24
30
|
|
|
25
|
-
siteApp.get('/sitemap-:resource.xml', verifyResourceType, function sitemapResourceXML(req, res) {
|
|
31
|
+
siteApp.get('/sitemap-:resource.xml', verifyResourceType, async function sitemapResourceXML(req, res, next) {
|
|
26
32
|
const type = req.params.resource.replace(/-\d+$/, '');
|
|
27
33
|
const pageParam = (req.params.resource.match(/-(\d+)$/) || [null, null])[1];
|
|
28
34
|
const page = pageParam ? parseInt(pageParam, 10) : 1;
|
|
29
35
|
|
|
36
|
+
try {
|
|
37
|
+
await manager.ensurePopulatedFromDatabase();
|
|
38
|
+
} catch (err) {
|
|
39
|
+
return next(err);
|
|
40
|
+
}
|
|
41
|
+
|
|
30
42
|
const content = manager.getSiteMapXml(type, page);
|
|
31
43
|
// Prevent x-1.xml as it is a duplicate of x.xml and empty sitemaps
|
|
32
44
|
// (except for the first page so that at least one sitemap exists per type)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const DomainEvents = require('@tryghost/domain-events');
|
|
2
|
+
const config = require('../../../shared/config');
|
|
2
3
|
const {URLResourceUpdatedEvent} = require('../../../shared/events');
|
|
4
|
+
const toPlain = require('../../../server/lib/common/to-plain');
|
|
3
5
|
const IndexMapGenerator = require('./site-map-index-generator');
|
|
4
6
|
const PagesMapGenerator = require('./page-map-generator');
|
|
5
7
|
const PostsMapGenerator = require('./post-map-generator');
|
|
@@ -21,6 +23,20 @@ class SiteMapManager {
|
|
|
21
23
|
this.tags = options.tags || this.createTagsGenerator(options);
|
|
22
24
|
this.index = options.index || this.createIndexGenerator(options);
|
|
23
25
|
|
|
26
|
+
// The lazy URL service does not fire url.added / url.removed /
|
|
27
|
+
// URLResourceUpdatedEvent. When that mode is active the sitemap
|
|
28
|
+
// populates itself from the database on first request instead.
|
|
29
|
+
this._lazyRouting = options.lazyRouting === undefined
|
|
30
|
+
? config.get('lazyRouting')
|
|
31
|
+
: options.lazyRouting;
|
|
32
|
+
this._populated = false;
|
|
33
|
+
this._populating = null;
|
|
34
|
+
// Each routers.reset bumps this generation. An in-flight populate
|
|
35
|
+
// captures the generation it started in; if that generation is no
|
|
36
|
+
// longer current when the populate settles, its result is discarded
|
|
37
|
+
// so we don't mark a stale (potentially mid-reset) lookup as ready.
|
|
38
|
+
this._populationGeneration = 0;
|
|
39
|
+
|
|
24
40
|
events.on('router.created', (router) => {
|
|
25
41
|
if (router.name === 'StaticRoutesRouter') {
|
|
26
42
|
this.pages.addUrl(router.getRoute({absolute: true}), {id: router.identifier, staticRoute: true});
|
|
@@ -31,23 +47,31 @@ class SiteMapManager {
|
|
|
31
47
|
}
|
|
32
48
|
});
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
if (!this._lazyRouting) {
|
|
51
|
+
DomainEvents.subscribe(URLResourceUpdatedEvent, (event) => {
|
|
52
|
+
this[event.data.resourceType].updateURL(event.data);
|
|
53
|
+
});
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
events.on('url.added', (obj) => {
|
|
56
|
+
this[obj.resource.config.type].addUrl(obj.url.absolute, obj.resource.data);
|
|
57
|
+
});
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
events.on('url.removed', (obj) => {
|
|
60
|
+
this[obj.resource.config.type].removeUrl(obj.url.absolute, obj.resource.data);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
45
63
|
|
|
46
64
|
events.on('routers.reset', () => {
|
|
47
65
|
this.pages && this.pages.reset();
|
|
48
66
|
this.posts && this.posts.reset();
|
|
49
67
|
this.users && this.users.reset();
|
|
50
68
|
this.tags && this.tags.reset();
|
|
69
|
+
// Force the next sitemap request to repopulate from the DB.
|
|
70
|
+
// Bumping the generation invalidates any populate currently in
|
|
71
|
+
// flight (its `.then` will see a stale generation and bail).
|
|
72
|
+
this._populated = false;
|
|
73
|
+
this._populating = null;
|
|
74
|
+
this._populationGeneration += 1;
|
|
51
75
|
});
|
|
52
76
|
}
|
|
53
77
|
|
|
@@ -86,6 +110,106 @@ class SiteMapManager {
|
|
|
86
110
|
getSiteMapXml(type, page) {
|
|
87
111
|
return this[type].getXml(page);
|
|
88
112
|
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Populate the sitemap from the database. Used when the lazy URL service
|
|
116
|
+
* is active and the URL-added/-removed events are not firing. Idempotent;
|
|
117
|
+
* a second call is a no-op while the first is in flight or has finished.
|
|
118
|
+
*/
|
|
119
|
+
async ensurePopulatedFromDatabase() {
|
|
120
|
+
if (!this._lazyRouting || this._populated) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (this._populating) {
|
|
124
|
+
return this._populating;
|
|
125
|
+
}
|
|
126
|
+
const generation = this._populationGeneration;
|
|
127
|
+
const populating = this._populateFromDatabase().then(
|
|
128
|
+
() => {
|
|
129
|
+
// If a routers.reset happened while we were running, the
|
|
130
|
+
// generators have been wiped. Don't flip _populated; let the
|
|
131
|
+
// next request kick off a fresh populate.
|
|
132
|
+
if (this._populationGeneration === generation) {
|
|
133
|
+
this._populated = true;
|
|
134
|
+
}
|
|
135
|
+
// Only clear our own handle. routers.reset already nulled
|
|
136
|
+
// _populating and a successor populate may have replaced it
|
|
137
|
+
// — clobbering would orphan the successor.
|
|
138
|
+
if (this._populating === populating) {
|
|
139
|
+
this._populating = null;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
(err) => {
|
|
143
|
+
if (this._populating === populating) {
|
|
144
|
+
this._populating = null;
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
this._populating = populating;
|
|
150
|
+
return this._populating;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async _populateFromDatabase() {
|
|
154
|
+
const models = require('../../../server/models');
|
|
155
|
+
const urlService = require('../../../server/services/url');
|
|
156
|
+
const facade = urlService.facade;
|
|
157
|
+
|
|
158
|
+
// Use TagPublic/Author for the shouldHavePosts gate (so tags/users
|
|
159
|
+
// with no published posts are excluded). The visibility filter is
|
|
160
|
+
// applied in TYPE_BROWSE_OPTIONS below; together these mirror what
|
|
161
|
+
// the eager URL service does in services/url/config.js.
|
|
162
|
+
await Promise.all([
|
|
163
|
+
this._loadType(models.Post, 'posts', this.posts, facade),
|
|
164
|
+
this._loadType(models.Post, 'pages', this.pages, facade),
|
|
165
|
+
this._loadType(models.TagPublic, 'tags', this.tags, facade),
|
|
166
|
+
this._loadType(models.Author, 'authors', this.users, facade)
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async _loadType(Model, type, generator, facade) {
|
|
171
|
+
const baseOptions = browseOptionsFor(type);
|
|
172
|
+
let page = 1;
|
|
173
|
+
// eslint-disable-next-line no-constant-condition
|
|
174
|
+
while (true) {
|
|
175
|
+
const result = await Model.findPage({...baseOptions, page});
|
|
176
|
+
for (const model of result.data) {
|
|
177
|
+
const datum = toPlain(model);
|
|
178
|
+
const url = facade.getUrlForResource({...datum, type}, {absolute: true});
|
|
179
|
+
if (url && !url.match(/\/404\//)) {
|
|
180
|
+
generator.addUrl(url, datum);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const totalPages = result?.meta?.pagination?.pages;
|
|
184
|
+
// Guard against unexpected response shapes (missing meta,
|
|
185
|
+
// empty page) so we don't loop forever.
|
|
186
|
+
if (!totalPages || page >= totalPages || result.data.length === 0) {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
page += 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Per-router-type browse options for the lazy populate path. Mirrors the
|
|
195
|
+
// eager URL service's resource queries (services/url/config.js): posts/pages
|
|
196
|
+
// filter by published+type, tags/authors by visibility:public.
|
|
197
|
+
//
|
|
198
|
+
// `withRelated: ['tags', 'authors']` for posts: needed so the Bookshelf
|
|
199
|
+
// model's toJSON output includes `primary_tag` and `primary_author`, which
|
|
200
|
+
// custom permalink templates like `/:primary_tag/:slug/` evaluate against.
|
|
201
|
+
// Without this preload the lazy sitemap would emit `/undefined/.../` for
|
|
202
|
+
// any site using a primary_tag/primary_author permalink. Pages exclude
|
|
203
|
+
// these in the eager config so we mirror that.
|
|
204
|
+
const TYPE_BROWSE_OPTIONS = {
|
|
205
|
+
posts: {limit: 200, status: 'published', filter: 'type:post', withRelated: ['tags', 'authors']},
|
|
206
|
+
pages: {limit: 200, status: 'published', filter: 'type:page'},
|
|
207
|
+
tags: {limit: 200, filter: 'visibility:public'},
|
|
208
|
+
authors: {limit: 200, filter: 'visibility:public'}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
function browseOptionsFor(type) {
|
|
212
|
+
return TYPE_BROWSE_OPTIONS[type] || {limit: 200};
|
|
89
213
|
}
|
|
90
214
|
|
|
91
215
|
module.exports = SiteMapManager;
|
|
@@ -48,6 +48,10 @@ module.exports = function handleImageSizes(req, res, next) {
|
|
|
48
48
|
if (format) {
|
|
49
49
|
url = url.replace(`/format/${format}`, '');
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
// Strip multiple leading slashes to prevent protocol-relative redirects
|
|
53
|
+
url = url.replace(/^\/+/, '/');
|
|
54
|
+
|
|
51
55
|
return res.redirect(url);
|
|
52
56
|
};
|
|
53
57
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
const errors = require('@tryghost/errors');
|
|
2
|
+
const automationsApi = require('../../services/automations/automations-api');
|
|
3
|
+
|
|
4
|
+
const VALID_AUTOMATION_STATUSES = ['active', 'inactive'];
|
|
5
|
+
|
|
6
|
+
const messages = {
|
|
7
|
+
invalidAutomationStatus: 'Automation status must be one of: active, inactive.',
|
|
8
|
+
invalidAutomationStatusHelp: 'Use "active" or "inactive" for automation status.'
|
|
9
|
+
};
|
|
4
10
|
|
|
5
11
|
/** @type {import('@tryghost/api-framework').Controller} */
|
|
6
12
|
const controller = {
|
|
@@ -12,15 +18,7 @@ const controller = {
|
|
|
12
18
|
},
|
|
13
19
|
permissions: true,
|
|
14
20
|
async query() {
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
data: automations.map(automation => ({
|
|
18
|
-
id: automation.get('id'),
|
|
19
|
-
name: automation.get('name'),
|
|
20
|
-
slug: automation.get('slug'),
|
|
21
|
-
status: automation.get('status')
|
|
22
|
-
}))
|
|
23
|
-
};
|
|
21
|
+
return await automationsApi.browse();
|
|
24
22
|
}
|
|
25
23
|
},
|
|
26
24
|
|
|
@@ -32,38 +30,33 @@ const controller = {
|
|
|
32
30
|
'id'
|
|
33
31
|
],
|
|
34
32
|
permissions: true,
|
|
35
|
-
query(frame) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
edges: [{
|
|
63
|
-
source_action_id: '67f3f3f3f3f3f3f3f3f3f3f4',
|
|
64
|
-
target_action_id: '67f3f3f3f3f3f3f3f3f3f3f5'
|
|
65
|
-
}]
|
|
66
|
-
};
|
|
33
|
+
async query(frame) {
|
|
34
|
+
return await automationsApi.read(frame.data.id);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
edit: {
|
|
39
|
+
headers: {
|
|
40
|
+
cacheInvalidate: false
|
|
41
|
+
},
|
|
42
|
+
options: [
|
|
43
|
+
'id'
|
|
44
|
+
],
|
|
45
|
+
validation(frame) {
|
|
46
|
+
const status = frame.data?.automations?.[0]?.status;
|
|
47
|
+
|
|
48
|
+
if (!VALID_AUTOMATION_STATUSES.includes(status)) {
|
|
49
|
+
throw new errors.ValidationError({
|
|
50
|
+
message: messages.invalidAutomationStatus,
|
|
51
|
+
context: status === undefined ? undefined : `Received status "${status}".`,
|
|
52
|
+
help: messages.invalidAutomationStatusHelp,
|
|
53
|
+
property: 'status'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
permissions: true,
|
|
58
|
+
async query(frame) {
|
|
59
|
+
return await automationsApi.edit(frame.options.id, frame.data.automations[0]);
|
|
67
60
|
}
|
|
68
61
|
},
|
|
69
62
|
|
|
@@ -77,7 +70,7 @@ const controller = {
|
|
|
77
70
|
method: 'poll'
|
|
78
71
|
},
|
|
79
72
|
query() {
|
|
80
|
-
|
|
73
|
+
automationsApi.requestPoll();
|
|
81
74
|
}
|
|
82
75
|
}
|
|
83
76
|
};
|
|
@@ -312,32 +312,29 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
312
312
|
},
|
|
313
313
|
|
|
314
314
|
onSaving: function onSaving(model, attr, options) {
|
|
315
|
-
|
|
315
|
+
const rawLabels = this.get('labels');
|
|
316
316
|
|
|
317
|
-
if (_.isUndefined(
|
|
317
|
+
if (_.isUndefined(rawLabels)) {
|
|
318
318
|
this.unset('labels');
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
// CASE:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
this.set('labels', labelsToSave);
|
|
339
|
-
}
|
|
322
|
+
// CASE: trim, drop nameless, and dedupe by case-insensitive name (first wins)
|
|
323
|
+
const seen = new Set();
|
|
324
|
+
const labelsToSave = (rawLabels || []).filter((item) => {
|
|
325
|
+
item.name = item.name && item.name.trim();
|
|
326
|
+
if (!item.name) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
const key = item.name.toLowerCase();
|
|
330
|
+
if (seen.has(key)) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
seen.add(key);
|
|
334
|
+
return true;
|
|
335
|
+
});
|
|
340
336
|
|
|
337
|
+
this.set('labels', labelsToSave);
|
|
341
338
|
this.handleAttachedModels(model);
|
|
342
339
|
|
|
343
340
|
// CASE: Detect existing labels with same case-insensitive name and replace
|
|
@@ -346,12 +343,20 @@ const Member = ghostBookshelf.Model.extend({
|
|
|
346
343
|
columns: ['id', 'name']
|
|
347
344
|
}, _.pick(options, 'transacting')))
|
|
348
345
|
.then((labels) => {
|
|
346
|
+
const existingByName = new Map();
|
|
347
|
+
for (const lab of labels.models) {
|
|
348
|
+
const name = lab.get('name');
|
|
349
|
+
if (name) {
|
|
350
|
+
existingByName.set(name.toLowerCase(), lab);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
349
354
|
labelsToSave.forEach((label) => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
+
const match = existingByName.get(label.name.toLowerCase());
|
|
356
|
+
if (match) {
|
|
357
|
+
label.name = match.get('name');
|
|
358
|
+
label.id = match.id;
|
|
359
|
+
}
|
|
355
360
|
});
|
|
356
361
|
|
|
357
362
|
model.set('labels', labelsToSave);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
7
|
+
const errors_1 = __importDefault(require("@tryghost/errors"));
|
|
8
|
+
const tpl_1 = __importDefault(require("@tryghost/tpl"));
|
|
9
|
+
const fake_database_automations_repository_1 = require("./fake-database-automations-repository");
|
|
10
|
+
const domainEvents = require('@tryghost/domain-events');
|
|
11
|
+
const StartAutomationsPollEvent = require('./events/start-automations-poll-event');
|
|
12
|
+
const temporaryFakeAutomationsDatabase = require('./temporary-fake-database');
|
|
13
|
+
const messages = {
|
|
14
|
+
automationNotFound: 'Automation not found.'
|
|
15
|
+
};
|
|
16
|
+
let testDatabase = null;
|
|
17
|
+
const repository = (0, fake_database_automations_repository_1.createFakeDatabaseAutomationsRepository)({
|
|
18
|
+
getDatabase: () => {
|
|
19
|
+
if (process.env.NODE_ENV?.startsWith('testing')) {
|
|
20
|
+
testDatabase ??= temporaryFakeAutomationsDatabase.createTemporaryFakeAutomationsDatabase();
|
|
21
|
+
return testDatabase;
|
|
22
|
+
}
|
|
23
|
+
return temporaryFakeAutomationsDatabase.getTemporaryFakeAutomationsDatabase();
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
async function browse() {
|
|
27
|
+
return await repository.browse();
|
|
28
|
+
}
|
|
29
|
+
async function read(automationId) {
|
|
30
|
+
const automation = await repository.getById(automationId);
|
|
31
|
+
if (!automation) {
|
|
32
|
+
throw new errors_1.default.NotFoundError({
|
|
33
|
+
message: (0, tpl_1.default)(messages.automationNotFound)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return automation;
|
|
37
|
+
}
|
|
38
|
+
async function edit(automationId, data) {
|
|
39
|
+
// TODO (NY-1229): Allow updating other fields and actions/edges.
|
|
40
|
+
const automation = await repository.edit(automationId, {
|
|
41
|
+
status: data.status
|
|
42
|
+
});
|
|
43
|
+
if (!automation) {
|
|
44
|
+
throw new errors_1.default.NotFoundError({
|
|
45
|
+
message: (0, tpl_1.default)(messages.automationNotFound)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return automation;
|
|
49
|
+
}
|
|
50
|
+
function requestPoll() {
|
|
51
|
+
domainEvents.dispatch(StartAutomationsPollEvent.create());
|
|
52
|
+
}
|
|
53
|
+
module.exports = {
|
|
54
|
+
browse,
|
|
55
|
+
edit,
|
|
56
|
+
read,
|
|
57
|
+
requestPoll
|
|
58
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
2
|
+
import errors from '@tryghost/errors';
|
|
3
|
+
import tpl from '@tryghost/tpl';
|
|
4
|
+
import type {DatabaseSync} from 'node:sqlite';
|
|
5
|
+
import {createFakeDatabaseAutomationsRepository} from './fake-database-automations-repository';
|
|
6
|
+
|
|
7
|
+
const domainEvents = require('@tryghost/domain-events');
|
|
8
|
+
const StartAutomationsPollEvent = require('./events/start-automations-poll-event');
|
|
9
|
+
const temporaryFakeAutomationsDatabase = require('./temporary-fake-database');
|
|
10
|
+
|
|
11
|
+
const messages = {
|
|
12
|
+
automationNotFound: 'Automation not found.'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
interface EditAutomationData {
|
|
16
|
+
status: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let testDatabase: DatabaseSync | null = null;
|
|
20
|
+
|
|
21
|
+
const repository = createFakeDatabaseAutomationsRepository({
|
|
22
|
+
getDatabase: () => {
|
|
23
|
+
if (process.env.NODE_ENV?.startsWith('testing')) {
|
|
24
|
+
testDatabase ??= temporaryFakeAutomationsDatabase.createTemporaryFakeAutomationsDatabase();
|
|
25
|
+
return testDatabase;
|
|
26
|
+
}
|
|
27
|
+
return temporaryFakeAutomationsDatabase.getTemporaryFakeAutomationsDatabase();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function browse() {
|
|
32
|
+
return await repository.browse();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function read(automationId: string) {
|
|
36
|
+
const automation = await repository.getById(automationId);
|
|
37
|
+
|
|
38
|
+
if (!automation) {
|
|
39
|
+
throw new errors.NotFoundError({
|
|
40
|
+
message: tpl(messages.automationNotFound)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return automation;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function edit(automationId: string, data: EditAutomationData) {
|
|
48
|
+
// TODO (NY-1229): Allow updating other fields and actions/edges.
|
|
49
|
+
const automation = await repository.edit(automationId, {
|
|
50
|
+
status: data.status
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!automation) {
|
|
54
|
+
throw new errors.NotFoundError({
|
|
55
|
+
message: tpl(messages.automationNotFound)
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return automation;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function requestPoll() {
|
|
63
|
+
domainEvents.dispatch(StartAutomationsPollEvent.create());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
browse,
|
|
68
|
+
edit,
|
|
69
|
+
read,
|
|
70
|
+
requestPoll
|
|
71
|
+
};
|