ghost 6.44.0 → 6.44.1

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.
Files changed (267) hide show
  1. package/core/boot.js +9 -13
  2. package/core/bridge.js +6 -10
  3. package/core/built/admin/assets/{PolarAngleAxis-eFuSrR7S.js → PolarAngleAxis-DWP6qq-Z.js} +2 -2
  4. package/core/built/admin/assets/{_baseAssignValue-XVHfKew7.js → _baseAssignValue-N9f4o_XK.js} +1 -1
  5. package/core/built/admin/assets/{a-large-small-DOzPDzKp.js → a-large-small-BOAtcFiE.js} +1 -1
  6. package/core/built/admin/assets/{account-migration-Cx45M5qm.js → account-migration-CtZx_5ib.js} +1 -1
  7. package/core/built/admin/assets/admin-x-settings/admin-x-settings.js +1 -1
  8. package/core/built/admin/assets/admin-x-settings/{code-editor-view-Bi8Orhyj.mjs → code-editor-view-BMovCjer.mjs} +2 -2
  9. package/core/built/admin/assets/admin-x-settings/{index-1_V9g9Pt.mjs → index-BT1n-g2b.mjs} +3 -3
  10. package/core/built/admin/assets/admin-x-settings/{index-e9saYQJa.mjs → index-BnowNBGd.mjs} +5 -5
  11. package/core/built/admin/assets/admin-x-settings/{index-CnvQi01U.mjs → index-CRpK6gF8.mjs} +3 -3
  12. package/core/built/admin/assets/admin-x-settings/{index-BmU6BOfy.mjs → index-CfXS3AZ8.mjs} +3 -3
  13. package/core/built/admin/assets/admin-x-settings/{index-EuMiBt6I.mjs → index-Cs8zML6u.mjs} +3 -3
  14. package/core/built/admin/assets/admin-x-settings/{index-B43MPc5u.mjs → index-DmpwO1w3.mjs} +2 -2
  15. package/core/built/admin/assets/admin-x-settings/{index-DOgG0XZa.mjs → index-DwFIdhcR.mjs} +3 -3
  16. package/core/built/admin/assets/admin-x-settings/{index-CWdxMIcZ.mjs → index-sNyPiIJA.mjs} +2 -2
  17. package/core/built/admin/assets/admin-x-settings/{index-Ojg_pBvq.mjs → index-xTILut54.mjs} +1259 -1259
  18. package/core/built/admin/assets/admin-x-settings/{modals-Ckbx9UQF.mjs → modals-Cm5t13os.mjs} +9 -9
  19. package/core/built/admin/assets/{arrow-right-DckEQtp9.js → arrow-right-BxdHN_En.js} +1 -1
  20. package/core/built/admin/assets/{at-sign-BChf68aS.js → at-sign-Cajf-dOf.js} +1 -1
  21. package/core/built/admin/assets/{audience-BYU5Bd7D.js → audience-CHNb9mCT.js} +1 -1
  22. package/core/built/admin/assets/{automated-email-design-Z26Qit0_.js → automated-email-design-B9C9mKUS.js} +2 -2
  23. package/core/built/admin/assets/{automated-emails-aBicreLv.js → automated-emails-DhLhkq8L.js} +1 -1
  24. package/core/built/admin/assets/{automations-vpH-d72N.js → automations-6-cgZovk.js} +1 -1
  25. package/core/built/admin/assets/{automations-Ddo22yPk.js → automations-CxJ-N_YK.js} +1 -1
  26. package/core/built/admin/assets/{avatar-flipboard-CoI3LQ-o.js → avatar-flipboard-f5nSMmek.js} +1 -1
  27. package/core/built/admin/assets/{bluesky-sharing-iVDkCMKN.js → bluesky-sharing-Dm2k8qZH.js} +1 -1
  28. package/core/built/admin/assets/{chart-CjM1VQqH.js → chart-BbJnjWey.js} +23 -23
  29. package/core/built/admin/assets/{checkbox-CC5kd_Kf.js → checkbox-CT7DNFAI.js} +1 -1
  30. package/core/built/admin/assets/{chevron-up-D__wzyXT.js → chevron-up-BxijmTT9.js} +1 -1
  31. package/core/built/admin/assets/{chunk.696.79505fa556d329d6e824.js → chunk.167.37bb0d7f6ac67fefa679.js} +3184 -3523
  32. package/core/built/admin/assets/{chunk.696.79505fa556d329d6e824.js.LICENSE.txt → chunk.167.37bb0d7f6ac67fefa679.js.LICENSE.txt} +1 -1
  33. package/core/built/admin/assets/{chunk.524.1ff649a6cc62dc38f538.js → chunk.524.ed812b62217210a913d4.js} +5 -5
  34. package/core/built/admin/assets/{chunk.582.408a40e0095e479e3d11.js → chunk.582.7db5c9f17bca5a484049.js} +6 -6
  35. package/core/built/admin/assets/{chunk.945.835812fcb16029abd7f9.js → chunk.834.f9db7c62b9457db93e8c.js} +3 -3
  36. package/core/built/admin/assets/{code-editor-view-C735d5EA.js → code-editor-view-D8slE0IA.js} +1 -1
  37. package/core/built/admin/assets/{comments-DgFPax3Y.js → comments-BvfR-fA8.js} +1 -1
  38. package/core/built/admin/assets/{content-helpers-DjugSfvm.js → content-helpers-DUWGLvgL.js} +1 -1
  39. package/core/built/admin/assets/{copy-D9JONGcK.js → copy-ht18t5gu.js} +1 -1
  40. package/core/built/admin/assets/{data-list-QWLEHG4y.js → data-list-Daha2dIs.js} +1 -1
  41. package/core/built/admin/assets/{deleted-feed-item-CwSCflTW.js → deleted-feed-item-ChP5bwgi.js} +1 -1
  42. package/core/built/admin/assets/{dropzone-lK_4vpWS.js → dropzone-h_qyk514.js} +1 -1
  43. package/core/built/admin/assets/{edit-profile-BmioJZHv.js → edit-profile-BT8RQpAG.js} +1 -1
  44. package/core/built/admin/assets/editor-s1mqd9_o.js +8 -0
  45. package/core/built/admin/assets/{empty-indicator-Dg64lT_g.js → empty-indicator-BIUvv6JE.js} +1 -1
  46. package/core/built/admin/assets/{en-DCwBNJ98.js → en-BEcDl42w.js} +1 -1
  47. package/core/built/admin/assets/{eye-psLxWnjO.js → eye-TPnIdamu.js} +1 -1
  48. package/core/built/admin/assets/{feed-TUcCSZHL.js → feed-BK3qvLWi.js} +1 -1
  49. package/core/built/admin/assets/{field-Uk8NOvtc.js → field-CSFsrvVy.js} +1 -1
  50. package/core/built/admin/assets/{filter-query-core-CbH_VKcG.js → filter-query-core-R8cQCO2w.js} +1 -1
  51. package/core/built/admin/assets/{filters-DmawZ5Uu.js → filters-Cp1voB1e.js} +1 -1
  52. package/core/built/admin/assets/gh-chart-BFsyOJD_.js +1 -0
  53. package/core/built/admin/assets/{ghost-2baa7ddc3630015f41c0efd13bd586b8.js → ghost-1881362e8a92dae1e3333a0484c173a3.js} +7 -6
  54. package/core/built/admin/assets/{growth-BAl-TyMy.js → growth-DIigOxF1.js} +1 -1
  55. package/core/built/admin/assets/{hash-Q1ou0OMZ.js → hash-B3iFVeCl.js} +1 -1
  56. package/core/built/admin/assets/{inbox-BAOtR6tN.js → inbox-2bPMLikt.js} +1 -1
  57. package/core/built/admin/assets/{index-BnJp51QE.js → index-52qUC8yv.js} +5 -5
  58. package/core/built/admin/assets/{index-BDIwushG.js → index-9j01Aj_X.js} +1 -1
  59. package/core/built/admin/assets/index-B9UVpmey.js +1 -0
  60. package/core/built/admin/assets/{index-DTeOW3Se.js → index-BXc5OF6x.js} +1 -1
  61. package/core/built/admin/assets/{index-Duc21aJ7.js → index-BaGL3IFe.js} +1 -1
  62. package/core/built/admin/assets/{index-DKC8uXHk.js → index-BdozZbt2.js} +2 -2
  63. package/core/built/admin/assets/{index-z951kI4K.js → index-Bgpz7M7i.js} +1 -1
  64. package/core/built/admin/assets/{index-BfSvX9is.js → index-Bp4O7TVf.js} +1 -1
  65. package/core/built/admin/assets/{index-C5S08C6P.js → index-BuqUmpe9.js} +1 -1
  66. package/core/built/admin/assets/{index-D7OtVnbo.js → index-CEF_Ofn0.js} +1 -1
  67. package/core/built/admin/assets/{index-Bs6rUYFS.js → index-CgSVv5Up.js} +58 -58
  68. package/core/built/admin/assets/{index-Ro9qXetv.js → index-Cr6x2czK.js} +1 -1
  69. package/core/built/admin/assets/{index-DJbgQjV6.js → index-CrbZj3Ju.js} +1 -1
  70. package/core/built/admin/assets/{index-BTBcWw_z.js → index-CrlAPDdD.js} +2 -2
  71. package/core/built/admin/assets/{index-B6SD2Qs-.js → index-CtrmMGWT.js} +1 -1
  72. package/core/built/admin/assets/{index-Bw0oyOFd.js → index-D-V_DAdt.js} +1 -1
  73. package/core/built/admin/assets/{index-DAJduTBl.js → index-D0poOSjw.js} +4 -4
  74. package/core/built/admin/assets/{index-CGT3W8fi.js → index-DWVGCPEC.js} +3 -3
  75. package/core/built/admin/assets/index-Dozbr_4s.css +1 -0
  76. package/core/built/admin/assets/{index-FyplpnYQ.js → index-S3Ai3lDy.js} +1 -1
  77. package/core/built/admin/assets/{index-DLr8D_Ks.js → index-VDhA5kgU.js} +1 -1
  78. package/core/built/admin/assets/{index-DRA1fORQ.js → index-fnIFqFMt.js} +1 -1
  79. package/core/built/admin/assets/{index-BhWQZObu.js → index-kA3UcdW7.js} +1 -1
  80. package/core/built/admin/assets/{input-group-DH32syTz.js → input-group-Fdl0S6pw.js} +1 -1
  81. package/core/built/admin/assets/{koenig-lexical-CxVcbgsH.js → koenig-lexical-REiYtzGD.js} +1 -1
  82. package/core/built/admin/assets/{kpi-card-DBWHDi3l.js → kpi-card-BS8vEj4c.js} +1 -1
  83. package/core/built/admin/assets/{kpi-card-BYhzWV5F.js → kpi-card-D3HEfOCP.js} +1 -1
  84. package/core/built/admin/assets/{kpi-tabs-Kxc-utyg.js → kpi-tabs-8vaI7yWq.js} +1 -1
  85. package/core/built/admin/assets/kpis-CkD0Xteg.js +1 -0
  86. package/core/built/admin/assets/label-DT38Gd2T.js +1 -0
  87. package/core/built/admin/assets/{links-Cg8O8ZGP.js → links-BK-azjCL.js} +1 -1
  88. package/core/built/admin/assets/{list-page-D2KCBsiy.js → list-page-DVpac3lc.js} +1 -1
  89. package/core/built/admin/assets/{loader-circle-nmLqhMZL.js → loader-circle-CIq5Cv-9.js} +1 -1
  90. package/core/built/admin/assets/{mail-D7cxYMd5.js → mail-gdeIU06V.js} +1 -1
  91. package/core/built/admin/assets/{main-layout-BXdUkeCo.js → main-layout-B2t-RiNE.js} +1 -1
  92. package/core/built/admin/assets/members-7z-tESWd.js +10 -0
  93. package/core/built/admin/assets/minus-BbGfVkll.js +1 -0
  94. package/core/built/admin/assets/{modals-VRvX8mK0.js → modals-DVIfijEz.js} +3 -3
  95. package/core/built/admin/assets/{moderation-DkT8qVJs.js → moderation-DwzDOhih.js} +1 -1
  96. package/core/built/admin/assets/{newsletter-sUT4ciRJ.js → newsletter-D1N8bUfp.js} +1 -1
  97. package/core/built/admin/assets/{newsletters-BNvAF2Oi.js → newsletters-7GSeQdQe.js} +1 -1
  98. package/core/built/admin/assets/{note-B0JhJv7s.js → note-Bn1-ZWDE.js} +1 -1
  99. package/core/built/admin/assets/offers-BsuLKYy9.js +1 -0
  100. package/core/built/admin/assets/{onboarding-route-IrO_SCb3.js → onboarding-route-C73r9kyO.js} +1 -1
  101. package/core/built/admin/assets/{overview-C2nk_B6i.js → overview-6U5p38zx.js} +1 -1
  102. package/core/built/admin/assets/{pagemenu-BGhty1iq.js → pagemenu-DrueoEX9.js} +1 -1
  103. package/core/built/admin/assets/{pencil-DiA3J4m5.js → pencil-Csnv8udS.js} +1 -1
  104. package/core/built/admin/assets/{pin-off-HWos3YHh.js → pin-off-BDeHGsSQ.js} +1 -1
  105. package/core/built/admin/assets/{post-analytics-context-BJwP-XAB.js → post-analytics-context-jKeUh2Ka.js} +1 -1
  106. package/core/built/admin/assets/{post-analytics-header-CZF5QmAD.js → post-analytics-header-C5QLn6Lm.js} +1 -1
  107. package/core/built/admin/assets/{post-analytics-DBUHbt1b.js → post-analytics-ngMqqbt9.js} +1 -1
  108. package/core/built/admin/assets/{post-share-modal-Bb8R05dH.js → post-share-modal-04B7JT4R.js} +1 -1
  109. package/core/built/admin/assets/posts/{alert-dialog-mmQvJUHQ.mjs → alert-dialog-CYvHtOQ5.mjs} +4 -4
  110. package/core/built/admin/assets/posts/{app-utils-CD0bhOLs.mjs → app-utils-Cs-8qje1.mjs} +3 -3
  111. package/core/built/admin/assets/posts/{automations-BL0WaXHA.mjs → automations-DE2rwx-Z.mjs} +25 -25
  112. package/core/built/admin/assets/posts/{automations-BmGKngyG.mjs → automations-Dq0_cprS.mjs} +4 -4
  113. package/core/built/admin/assets/posts/{avatar-CeJ0Zq_A.mjs → avatar-Dt_bGEgl.mjs} +7 -7
  114. package/core/built/admin/assets/posts/{button-CQCNdxY6.mjs → button-DO7JrPsL.mjs} +2 -2
  115. package/core/built/admin/assets/posts/{chevron-up-DaikhWC2.mjs → chevron-up-B8WyJjCr.mjs} +3 -3
  116. package/core/built/admin/assets/posts/{comments-DUSJ25vf.mjs → comments-B3-RpqJU.mjs} +1933 -1905
  117. package/core/built/admin/assets/posts/{data-list-BiiYXQes.mjs → data-list-BiZFP6Co.mjs} +5 -5
  118. package/core/built/admin/assets/posts/{dialog-DRN6olky.mjs → dialog-Bvl2PD8s.mjs} +3 -3
  119. package/core/built/admin/assets/posts/{editor-DN_Zz0m2.mjs → editor-BCtdAD74.mjs} +3230 -3130
  120. package/core/built/admin/assets/posts/{ellipsis-BIs4PL5-.mjs → ellipsis-B5H7Hudx.mjs} +2 -2
  121. package/core/built/admin/assets/posts/{empty-indicator-Wiukrx4T.mjs → empty-indicator-BRoAf6Jv.mjs} +2 -2
  122. package/core/built/admin/assets/posts/{filters-CJl5ebXd.mjs → filters-CffYQY_8.mjs} +12 -12
  123. package/core/built/admin/assets/posts/{get-site-timezone-JepxGwVr.mjs → get-site-timezone-CSrPVUfX.mjs} +2 -2
  124. package/core/built/admin/assets/posts/{growth-cNlqbz-x.mjs → growth--LVGRPtS.mjs} +13 -13
  125. package/core/built/admin/assets/posts/{heading-C_V6Ckxx.mjs → heading-BRQEP7IA.mjs} +2 -2
  126. package/core/built/admin/assets/posts/{heart-BMBWsLs2.mjs → heart-CC2f1qWb.mjs} +2 -2
  127. package/core/built/admin/assets/posts/{hooks-D4pJ_p_P.mjs → hooks-J2WpDj1d.mjs} +2 -2
  128. package/core/built/admin/assets/posts/{index-DzWTmJMy.mjs → index-D-vhKzUJ.mjs} +3 -3
  129. package/core/built/admin/assets/posts/{index-CDwlVw9E.mjs → index-LnPyGojr.mjs} +2 -2
  130. package/core/built/admin/assets/posts/{index-BuIZkUhD.mjs → index-nxfIbREV.mjs} +11 -11
  131. package/core/built/admin/assets/posts/{input-DUvSHOFz.mjs → input-DTtfruk_.mjs} +3 -3
  132. package/core/built/admin/assets/posts/{input-surface-CO8-j6V2.mjs → input-surface-DXTGCuo2.mjs} +2 -2
  133. package/core/built/admin/assets/posts/{koenig-lexical-cRm7bY-6.mjs → koenig-lexical-CNcifuT7.mjs} +2 -2
  134. package/core/built/admin/assets/posts/{kpis-BXukzbg6.mjs → kpis-n8L4BlMi.mjs} +5843 -5854
  135. package/core/built/admin/assets/posts/{label-TTt0QyDb.mjs → label-yl8B8ADj.mjs} +6 -6
  136. package/core/built/admin/assets/posts/{links-BAXELE82.mjs → links-DDApMJfk.mjs} +4 -4
  137. package/core/built/admin/assets/posts/{list-page-DQIiFvFP.mjs → list-page-BJJ9amzJ.mjs} +4 -4
  138. package/core/built/admin/assets/posts/{loading-indicator-BsY7eGY6.mjs → loading-indicator-CQBDNvdo.mjs} +3 -3
  139. package/core/built/admin/assets/posts/{mail-Bi1gt3MU.mjs → mail-BHg0SvNu.mjs} +2 -2
  140. package/core/built/admin/assets/posts/{main-layout-DejRuxP8.mjs → main-layout-DXveCJ5W.mjs} +2 -2
  141. package/core/built/admin/assets/posts/{newsletter-PCUtHW6Z.mjs → newsletter-DUpVmnKN.mjs} +19 -19
  142. package/core/built/admin/assets/posts/{overview-f4eDWazr.mjs → overview-sv6i-2h9.mjs} +18 -18
  143. package/core/built/admin/assets/posts/{pencil-DDnfXQqn.mjs → pencil-DsGDLJp6.mjs} +2 -2
  144. package/core/built/admin/assets/posts/{plus-DW3bG6ui.mjs → plus-DjZ_REvJ.mjs} +2 -2
  145. package/core/built/admin/assets/posts/{popover-Du2NcggM.mjs → popover-2PA9zdiD.mjs} +4 -4
  146. package/core/built/admin/assets/posts/{post-analytics-BmnHyWJq.mjs → post-analytics-CKHoiJFY.mjs} +6 -6
  147. package/core/built/admin/assets/posts/{post-analytics-context-0fieM91Y.mjs → post-analytics-context-D17CB7nm.mjs} +6 -6
  148. package/core/built/admin/assets/posts/{post-analytics-header-Bxi56AWV.mjs → post-analytics-header-aQLnrdWi.mjs} +12 -12
  149. package/core/built/admin/assets/posts/{post-share-modal-DiuT4GFO.mjs → post-share-modal-C8-8CZw8.mjs} +6 -6
  150. package/core/built/admin/assets/posts/{posts-CYjfxmgX.mjs → posts-BA2vu_T2.mjs} +2 -2
  151. package/core/built/admin/assets/posts/posts.js +1 -1
  152. package/core/built/admin/assets/posts/{search-Dz7F6ifc.mjs → search-Bx6BvCRk.mjs} +2 -2
  153. package/core/built/admin/assets/posts/{select-FX6QYAFU.mjs → select-QOJ2OvL0.mjs} +7 -7
  154. package/core/built/admin/assets/posts/{separator-C7QPCSjM.mjs → separator-CHtBa4OX.mjs} +3 -3
  155. package/core/built/admin/assets/posts/{settings-DJqx19W1.mjs → settings-CxhTCxXg.mjs} +2 -2
  156. package/core/built/admin/assets/posts/{sheet-wg_8-w45.mjs → sheet-DO5nhiru.mjs} +4 -4
  157. package/core/built/admin/assets/posts/{site-Bxv-L5Dn.mjs → site-6g4pKVnR.mjs} +2 -2
  158. package/core/built/admin/assets/posts/{skeleton-DrFcKwP3.mjs → skeleton-DgWx-52p.mjs} +3 -3
  159. package/core/built/admin/assets/posts/{source-icon-DD_bmIqg.mjs → source-icon-_zLbYeab.mjs} +4 -4
  160. package/core/built/admin/assets/posts/{stats-DkOg80Vt.mjs → stats-BGJFxx5f.mjs} +4 -4
  161. package/core/built/admin/assets/posts/{switch-DZFGAF_Y.mjs → switch-Djipx7gc.mjs} +5 -5
  162. package/core/built/admin/assets/posts/{table-CZCVviNy.mjs → table-DpDCE80J.mjs} +2 -2
  163. package/core/built/admin/assets/posts/{tabs-1fkhM4p8.mjs → tabs-CLWGiyQ4.mjs} +4 -4
  164. package/core/built/admin/assets/posts/{tags-CkyGqwqi.mjs → tags-B4KIn7er.mjs} +2 -2
  165. package/core/built/admin/assets/posts/{tags-BAmugIR1.mjs → tags-Dn1L_Z_4.mjs} +16 -16
  166. package/core/built/admin/assets/posts/{toggle-group-B_ubMZvo.mjs → toggle-group-DMMjXGB3.mjs} +4 -4
  167. package/core/built/admin/assets/posts/{tooltip-D1X0uVas.mjs → tooltip-DlCjZwmt.mjs} +5 -5
  168. package/core/built/admin/assets/posts/{underline-CV8OJ7qc.mjs → underline-WN3ZSFyI.mjs} +2 -2
  169. package/core/built/admin/assets/posts/{value-DhEK7_uT.mjs → value-BWiEmo45.mjs} +2 -2
  170. package/core/built/admin/assets/posts/{virtual-list-window-DqbQUkIo.mjs → virtual-list-window-F3Tvi9H5.mjs} +3 -3
  171. package/core/built/admin/assets/posts/{web-5jcYGeBz.mjs → web-C8PxkHvH.mjs} +18 -18
  172. package/core/built/admin/assets/posts/{x-Cl72IwQm.mjs → x-BdlZ3sG3.mjs} +2 -2
  173. package/core/built/admin/assets/posts/{zap-BPDyHF1u.mjs → zap-BuAk2C7n.mjs} +33 -21
  174. package/core/built/admin/assets/{posts-D6K0FKB9.js → posts-bRma2Aji.js} +1 -1
  175. package/core/built/admin/assets/power-B4VCYMPn.js +1 -0
  176. package/core/built/admin/assets/{referrers-C9UK_aF_.js → referrers-BgXy4ZDF.js} +1 -1
  177. package/core/built/admin/assets/{repeat-BwgUDBQn.js → repeat-BElPLZnh.js} +1 -1
  178. package/core/built/admin/assets/{reply-DbknkQhJ.js → reply-qlUFZ_G8.js} +1 -1
  179. package/core/built/admin/assets/{rocket-1EqU2MzS.js → rocket-CXYckFbm.js} +1 -1
  180. package/core/built/admin/assets/{select-BGJXWb8i.js → select-MGAxyhXP.js} +1 -1
  181. package/core/built/admin/assets/{send-_CqvCQBY.js → send-DcU5LcPI.js} +1 -1
  182. package/core/built/admin/assets/{settings-Bnep890F.js → settings-DGSAz4oa.js} +4 -4
  183. package/core/built/admin/assets/{settings-Ct244PHl.js → settings-DI95fXs5.js} +1 -1
  184. package/core/built/admin/assets/{share-modal-B8pN_3tk.js → share-modal-CsNAIPeN.js} +1 -1
  185. package/core/built/admin/assets/{sort-button-EXsVVSdw.js → sort-button-wV9PBd2P.js} +1 -1
  186. package/core/built/admin/assets/{source-icon-XMs0toB0.js → source-icon-CYt4X1-M.js} +1 -1
  187. package/core/built/admin/assets/{sprout-uA_pVmDr.js → sprout-5QqpAoO8.js} +1 -1
  188. package/core/built/admin/assets/{square-atKnWQyw.js → square-YHeYyKG9.js} +1 -1
  189. package/core/built/admin/assets/{stats-CPNnZGD9.js → stats-PD64opSw.js} +1 -1
  190. package/core/built/admin/assets/{stats-view-BP8LU5rf.js → stats-view-C4fnD3uG.js} +1 -1
  191. package/core/built/admin/assets/{step-1-FHeGC4GW.js → step-1-BqeM_LxC.js} +1 -1
  192. package/core/built/admin/assets/{step-2-BFhgD2N8.js → step-2-C7AcokE9.js} +1 -1
  193. package/core/built/admin/assets/{step-3-BZMKjY5n.js → step-3-9Z0qkfpM.js} +2 -2
  194. package/core/built/admin/assets/{table-kGA62bBZ.js → table-C8Mxw8fs.js} +1 -1
  195. package/core/built/admin/assets/{tabs-Dl3BXr4Z.js → tabs-2KXMNRG-.js} +1 -1
  196. package/core/built/admin/assets/{tags-CLhASxwZ.js → tags-Cx-Jx8Yb.js} +1 -1
  197. package/core/built/admin/assets/{tags-D-W0kJMe.js → tags-cn7wgGit.js} +1 -1
  198. package/core/built/admin/assets/{textarea-DMub6CsU.js → textarea-dKwIcAK5.js} +1 -1
  199. package/core/built/admin/assets/{thumbs-up-DZu4SX5N.js → thumbs-up-gzGza05L.js} +1 -1
  200. package/core/built/admin/assets/{tiers-2bg8BCFj.js → tiers-D--LgD9v.js} +1 -1
  201. package/core/built/admin/assets/{toggle-group-ByS0KZ3G.js → toggle-group-C8KbiBQS.js} +1 -1
  202. package/core/built/admin/assets/{topic-filter-oRqwlePZ.js → topic-filter--VQn4A7D.js} +1 -1
  203. package/core/built/admin/assets/{trash-BHoSD8mS.js → trash-CnBwlhjN.js} +1 -1
  204. package/core/built/admin/assets/{underline-1YSgOpPm.js → underline-B2FjHOE0.js} +1 -1
  205. package/core/built/admin/assets/{undo-2-BwYMlB8G.js → undo-2-fUPuO2Zt.js} +1 -1
  206. package/core/built/admin/assets/{upload-BDz0_o9b.js → upload-BPOrFdtp.js} +1 -1
  207. package/core/built/admin/assets/{use-growth-stats-PRVcCUT7.js → use-growth-stats-By0ZDqFi.js} +1 -1
  208. package/core/built/admin/assets/{use-pintura-config-CROsgSEd.js → use-pintura-config-0ykvCF7U.js} +1 -1
  209. package/core/built/admin/assets/{use-simple-pagination-CBjKBNNI.js → use-simple-pagination-TruuCHV6.js} +1 -1
  210. package/core/built/admin/assets/{user-round-check-DKooQiV7.js → user-round-check-C1Vahz0b.js} +1 -1
  211. package/core/built/admin/assets/{user-round-x-B94nAYJb.js → user-round-x-CzKFo5GY.js} +1 -1
  212. package/core/built/admin/assets/{virtual-list-window-Cf_vCQFb.js → virtual-list-window-BWEknk_A.js} +1 -1
  213. package/core/built/admin/assets/{wallet-cards-BorRQari.js → wallet-cards-CRHrrjiy.js} +1 -1
  214. package/core/built/admin/assets/{web-BVHy-80k.js → web-C2H0oAut.js} +1 -1
  215. package/core/built/admin/index.html +7 -7
  216. package/core/frontend/services/llms/service.js +37 -10
  217. package/core/frontend/services/routing/config.js +5 -3
  218. package/core/frontend/services/routing/config.ts +59 -0
  219. package/core/frontend/services/sitemap/handler.js +2 -14
  220. package/core/frontend/services/sitemap/site-map-manager.js +9 -214
  221. package/core/server/api/endpoints/comment-replies.js +1 -2
  222. package/core/server/api/endpoints/members.js +4 -1
  223. package/core/server/api/endpoints/search-index-public.js +12 -1
  224. package/core/server/api/endpoints/search-index.js +13 -2
  225. package/core/server/lib/bootstrap-socket.js +31 -36
  226. package/core/server/lib/bootstrap-socket.ts +114 -0
  227. package/core/server/lib/common/to-plain.js +9 -21
  228. package/core/server/lib/common/to-plain.ts +25 -0
  229. package/core/server/models/role-utils.js +5 -7
  230. package/core/server/models/role-utils.ts +56 -0
  231. package/core/server/services/adapter-manager/adapter-manager.js +5 -1
  232. package/core/server/services/audience-feedback/audience-feedback-service.js +1 -1
  233. package/core/server/services/automations/automations-api.js +37 -2
  234. package/core/server/services/automations/automations-api.ts +43 -2
  235. package/core/server/services/comments/comments-controller.js +87 -55
  236. package/core/server/services/comments/comments-service-emails.js +1 -1
  237. package/core/server/services/comments/comments-service.js +249 -79
  238. package/core/server/services/mail/templates/notification.html +157 -0
  239. package/core/server/services/member-attribution/url-translator.js +1 -1
  240. package/core/server/services/members/members-api/services/token-service.js +21 -2
  241. package/core/server/services/notifications/notification-email.js +38 -0
  242. package/core/server/services/notifications/notification-email.ts +66 -0
  243. package/core/server/services/notifications/sanitize-email-html.js +37 -0
  244. package/core/server/services/notifications/sanitize-email-html.ts +35 -0
  245. package/core/server/services/route-settings/route-settings.js +2 -18
  246. package/core/server/services/route-settings/validate.js +2 -1
  247. package/core/server/services/update-check/index.js +10 -3
  248. package/core/server/services/update-check/update-check-service.js +18 -25
  249. package/core/server/services/url/index.js +1 -16
  250. package/core/shared/config/defaults.json +0 -1
  251. package/package.json +2 -1
  252. package/pnpm-lock.yaml +11 -1
  253. package/pnpm-workspace.yaml +2 -0
  254. package/core/built/admin/assets/editor-G3BxjP48.js +0 -8
  255. package/core/built/admin/assets/gh-chart-vpcpdiS9.js +0 -1
  256. package/core/built/admin/assets/index-BP5ial3p.js +0 -1
  257. package/core/built/admin/assets/index-Bd9VZ28k.css +0 -1
  258. package/core/built/admin/assets/kpis-B8pv3wne.js +0 -1
  259. package/core/built/admin/assets/label-BjHICZC1.js +0 -1
  260. package/core/built/admin/assets/members-DcuuSs_v.js +0 -10
  261. package/core/built/admin/assets/minus-Dv51QEND.js +0 -1
  262. package/core/built/admin/assets/offers-DKZ0qsmb.js +0 -1
  263. package/core/built/admin/assets/power-D5QO6MjY.js +0 -1
  264. package/core/server/services/url/lazy-find-resource.js +0 -49
  265. package/core/server/services/url/lazy-find-resource.ts +0 -62
  266. package/core/server/services/url/lazy-url-service.js +0 -262
  267. package/core/server/services/url/lazy-url-service.ts +0 -308
@@ -23,13 +23,55 @@ const messages = {
23
23
 
24
24
  const COMMENT_LIKE_SCORE = 1;
25
25
  const COMMENT_DISLIKE_SCORE = -1;
26
+ const COMMENT_STATUS_PUBLISHED = 'published';
27
+ const COMMENT_STATUS_HIDDEN = 'hidden';
28
+ const COMMENT_STATUS_DELETED = 'deleted';
29
+ const COMMENT_STATUSES_READABLE = [COMMENT_STATUS_PUBLISHED, COMMENT_STATUS_HIDDEN];
30
+ const COMMENT_STATUSES_REPLY_PARENT = [COMMENT_STATUS_PUBLISHED, COMMENT_STATUS_HIDDEN, COMMENT_STATUS_DELETED];
31
+ const COMMENT_STATUSES_IN_REPLY_TO = [COMMENT_STATUS_PUBLISHED, COMMENT_STATUS_HIDDEN];
26
32
 
27
- function withPinnedSelect(options = {}) {
28
- if (!options.columns?.includes('pinned')) {
29
- return options;
33
+ // Columns each action reads from the comment row beyond `id`/`status`, which the
34
+ // lookup helpers always select. Keeping these lists together makes the coupling
35
+ // between "fields read after the fetch" and "columns requested" explicit, so a
36
+ // new `comment.get(...)` call has one obvious place to register its column.
37
+ const REPLY_PARENT_REQUIRED_COLUMNS = ['parent_id', 'post_id'];
38
+ const IN_REPLY_TO_REQUIRED_COLUMNS = ['parent_id'];
39
+ const OWNERSHIP_REQUIRED_COLUMNS = ['member_id'];
40
+ const REPORT_REQUIRED_COLUMNS = ['post_id', 'member_id', 'html', 'created_at'];
41
+
42
+ function getColumnList(columns) {
43
+ if (!columns) {
44
+ return null;
45
+ }
46
+
47
+ if (Array.isArray(columns)) {
48
+ return columns;
49
+ }
50
+
51
+ return columns.split(',').map(column => column.trim()).filter(Boolean);
52
+ }
53
+
54
+ function withRequiredColumns(options = {}, requiredColumns = []) {
55
+ const columns = getColumnList(options.columns);
56
+
57
+ if (!columns) {
58
+ return {...options};
59
+ }
60
+
61
+ return {
62
+ ...options,
63
+ columns: Array.from(new Set(['id', ...columns, ...requiredColumns]))
64
+ };
65
+ }
66
+
67
+ function withPinnedSelect(options = {}, {includeHidden = false} = {}) {
68
+ const columns = getColumnList(options.columns);
69
+
70
+ if (!columns?.includes('pinned')) {
71
+ return {...options};
30
72
  }
31
73
 
32
- const statusClause = options.isAdmin ? '' : ' AND comments.status = \'published\'';
74
+ const statusClause = includeHidden ? '' : ' AND comments.status = \'published\'';
33
75
  const pinnedSelect = `CASE WHEN comments.parent_id IS NULL AND comments.pinned_at IS NOT NULL${statusClause} THEN 1 ELSE 0 END AS pinned`;
34
76
 
35
77
  return {
@@ -38,6 +80,19 @@ function withPinnedSelect(options = {}) {
38
80
  };
39
81
  }
40
82
 
83
+ function getSafeFetchOptions(options = {}, columns = []) {
84
+ return {
85
+ ...(options.transacting ? {transacting: options.transacting} : {}),
86
+ columns: Array.from(new Set(['id', ...columns]))
87
+ };
88
+ }
89
+
90
+ function getSafeWriteOptions(options = {}) {
91
+ return {
92
+ ...(options.transacting ? {transacting: options.transacting} : {})
93
+ };
94
+ }
95
+
41
96
  class CommentsService {
42
97
  constructor({config, logging, models, mailer, settingsCache, settingsHelpers, urlService, urlUtils, contentGating, labs}) {
43
98
  /** @private */
@@ -119,14 +174,148 @@ class CommentsService {
119
174
  });
120
175
  }
121
176
 
177
+ /**
178
+ * @private
179
+ * Primary comment lookup: a single keyed fetch with the allowed statuses
180
+ * applied in the query (`WHERE id = ? AND status IN (...)`), optionally locked
181
+ * with `FOR UPDATE` inside a transaction. It deliberately does not load the
182
+ * member-facing relation graph; the one read that needs those relations
183
+ * (#getReadableCommentByID, backing getCommentByID) uses `findOne` instead.
184
+ */
185
+ async #fetchCommentByID(id, options = {}, {requiredColumns = [], statuses = [], forUpdate = false} = {}) {
186
+ const model = this.models.Comment.forge();
187
+ model.query((qb) => {
188
+ qb.where('comments.id', id);
189
+
190
+ if (statuses.length === 1) {
191
+ qb.where('comments.status', statuses[0]);
192
+ } else if (statuses.length > 1) {
193
+ qb.whereIn('comments.status', statuses);
194
+ }
195
+
196
+ if (forUpdate && options.transacting) {
197
+ qb.forUpdate();
198
+ }
199
+ });
200
+
201
+ return await model.fetch(getSafeFetchOptions(options, requiredColumns));
202
+ }
203
+
122
204
  /** @private */
123
- async #getMemberCommentVotes(commentId, memberId, options) {
124
- const votes = await this.models.CommentLike.findAll({
125
- ...options,
126
- filter: `comment_id:'${commentId}'+member_id:'${memberId}'`,
127
- order: 'created_at asc'
205
+ async #getPublishedCommentForAction(id, options = {}, requiredColumns = [], {forUpdate = false} = {}) {
206
+ const model = await this.#fetchCommentByID(id, options, {
207
+ requiredColumns,
208
+ statuses: [COMMENT_STATUS_PUBLISHED],
209
+ forUpdate
210
+ });
211
+
212
+ if (!model) {
213
+ throw new errors.NotFoundError({
214
+ message: tpl(messages.commentNotFound)
215
+ });
216
+ }
217
+
218
+ return model;
219
+ }
220
+
221
+ /**
222
+ * @private
223
+ * Member-facing read: goes through `findOne` (not the lean primitive) so the
224
+ * serializer's default relation graph (member, counts, in_reply_to, replies)
225
+ * is loaded. Readable statuses are constrained in the query via an NQL filter,
226
+ * so a non-readable comment is never fetched and its relations never loaded — a
227
+ * missing or non-readable id is a 404. `status` stays selected for the
228
+ * serializer's hidden/deleted redaction.
229
+ */
230
+ async #getReadableCommentByID(id, options = {}, requiredColumns = []) {
231
+ const readableFilter = `status:[${COMMENT_STATUSES_READABLE.join(',')}]`;
232
+ const model = await this.models.Comment.findOne(
233
+ {id},
234
+ withRequiredColumns(
235
+ {
236
+ ...options,
237
+ filter: options.filter ? `(${options.filter})+${readableFilter}` : readableFilter
238
+ },
239
+ ['status', ...requiredColumns]
240
+ )
241
+ );
242
+
243
+ if (!model) {
244
+ throw new errors.NotFoundError({
245
+ message: tpl(messages.commentNotFound)
246
+ });
247
+ }
248
+
249
+ return model;
250
+ }
251
+
252
+ /** @private */
253
+ async #getReplyParentCommentByID(id, options = {}) {
254
+ // Deleted parent comments intentionally remain valid thread anchors: a reply
255
+ // can still be posted under a top-level comment whose root was removed.
256
+ const model = await this.#fetchCommentByID(id, options, {
257
+ requiredColumns: REPLY_PARENT_REQUIRED_COLUMNS,
258
+ statuses: COMMENT_STATUSES_REPLY_PARENT
259
+ });
260
+
261
+ if (!model) {
262
+ throw new errors.NotFoundError({
263
+ message: tpl(messages.commentNotFound)
264
+ });
265
+ }
266
+
267
+ return model;
268
+ }
269
+
270
+ /** @private */
271
+ async #getInReplyToCommentByID(id, parent, options = {}) {
272
+ const model = await this.#fetchCommentByID(id, options, {
273
+ requiredColumns: IN_REPLY_TO_REQUIRED_COLUMNS,
274
+ statuses: COMMENT_STATUSES_IN_REPLY_TO
275
+ });
276
+
277
+ // A target that is missing, deleted, or not a readable sibling is simply not
278
+ // a valid direct-reply anchor: drop the reference and let the reply post
279
+ // anyway (you can reply within the thread, just not "to" a dead comment). The
280
+ // query already excluded disallowed statuses, so only the cross-thread
281
+ // relationship check remains here. Hidden-target snippets are redacted
282
+ // downstream.
283
+ if (!model || model.get('parent_id') !== parent) {
284
+ return null;
285
+ }
286
+
287
+ return model;
288
+ }
289
+
290
+ /** @private */
291
+ async #assertCommentExists(commentId, options = {}) {
292
+ const model = await this.#fetchCommentByID(commentId, options, {
293
+ requiredColumns: ['id']
294
+ });
295
+
296
+ if (!model) {
297
+ throw new errors.NotFoundError({
298
+ message: tpl(messages.commentNotFound)
299
+ });
300
+ }
301
+ }
302
+
303
+ /** @private */
304
+ async #getMemberCommentVotes({commentId, memberId, score}, options) {
305
+ const collection = this.models.CommentLike.forge();
306
+ collection.query((qb) => {
307
+ qb.where('comment_likes.comment_id', commentId)
308
+ .where('comment_likes.member_id', memberId);
309
+
310
+ if (score !== undefined) {
311
+ qb.where('comment_likes.score', score);
312
+ }
313
+
314
+ qb.orderBy('comment_likes.created_at', 'asc');
128
315
  });
129
316
 
317
+ const votes = await collection.fetchAll(options);
318
+
130
319
  return votes.models || [];
131
320
  }
132
321
 
@@ -148,7 +337,12 @@ class CommentsService {
148
337
  this.checkCommentAccess(memberModel);
149
338
 
150
339
  return await this.#withTransaction(options, async (transactionOptions) => {
151
- const votes = await this.#getMemberCommentVotes(commentId, memberModel.id, transactionOptions);
340
+ await this.#getPublishedCommentForAction(commentId, transactionOptions, [], {forUpdate: true});
341
+
342
+ const votes = await this.#getMemberCommentVotes({
343
+ commentId,
344
+ memberId: memberModel.id
345
+ }, transactionOptions);
152
346
  const alreadyHasSingleTargetVote = votes.length === 1 && Number(votes[0].get('score')) === targetScore;
153
347
 
154
348
  if (alreadyHasSingleTargetVote) {
@@ -170,8 +364,13 @@ class CommentsService {
170
364
  /** @private */
171
365
  async #clearCommentVote(commentId, member, targetScore, notFoundMessage, options = {}) {
172
366
  await this.#withTransaction(options, async (transactionOptions) => {
173
- const votes = await this.#getMemberCommentVotes(commentId, member.id, transactionOptions);
174
- const votesToRemove = votes.filter(vote => Number(vote.get('score')) === targetScore);
367
+ await this.#getPublishedCommentForAction(commentId, transactionOptions, [], {forUpdate: true});
368
+
369
+ const votesToRemove = await this.#getMemberCommentVotes({
370
+ commentId,
371
+ memberId: member.id,
372
+ score: targetScore
373
+ }, transactionOptions);
175
374
 
176
375
  if (votesToRemove.length === 0) {
177
376
  throw new errors.NotFoundError({
@@ -219,15 +418,16 @@ class CommentsService {
219
418
  await this.#clearCommentVote(commentId, member, COMMENT_DISLIKE_SCORE, messages.dislikeNotFound, options);
220
419
  }
221
420
 
222
- async reportComment(commentId, reporter) {
421
+ async reportComment(commentId, reporter, options = {}) {
223
422
  this.checkEnabled();
224
- const comment = await this.models.Comment.findOne({id: commentId}, {require: true});
423
+ const comment = await this.#getPublishedCommentForAction(commentId, options, REPORT_REQUIRED_COLUMNS);
424
+ const writeOptions = getSafeWriteOptions(options);
225
425
 
226
426
  // Check if this reporter already reported this comment (then don't send an email)?
227
427
  const existing = await this.models.CommentReport.findOne({
228
428
  comment_id: comment.id,
229
429
  member_id: reporter.id
230
- });
430
+ }, writeOptions);
231
431
 
232
432
  if (existing) {
233
433
  // Ignore silently for now
@@ -238,7 +438,7 @@ class CommentsService {
238
438
  await this.models.CommentReport.add({
239
439
  comment_id: comment.id,
240
440
  member_id: reporter.id
241
- });
441
+ }, writeOptions);
242
442
 
243
443
  await this.emails.notifyReport(comment, reporter);
244
444
  }
@@ -295,7 +495,12 @@ class CommentsService {
295
495
  async getAdminComments(options) {
296
496
  this.checkEnabled();
297
497
  const pinnedFirst = this.labs?.isSet('commentsPinning');
298
- const page = await this.models.Comment.findPage(withPinnedSelect({...options, parentId: null, isAdmin: true, pinnedFirst}));
498
+ const page = await this.models.Comment.findPage(withPinnedSelect({
499
+ ...options,
500
+ parentId: null,
501
+ isAdmin: true,
502
+ pinnedFirst
503
+ }, {includeHidden: true}));
299
504
 
300
505
  return page;
301
506
  }
@@ -306,7 +511,7 @@ class CommentsService {
306
511
  if (Object.prototype.hasOwnProperty.call(data, 'status')) {
307
512
  editData.status = data.status;
308
513
 
309
- if (data.status === 'deleted') {
514
+ if (data.status === COMMENT_STATUS_DELETED) {
310
515
  editData.pinned_at = null;
311
516
  }
312
517
  }
@@ -334,7 +539,7 @@ class CommentsService {
334
539
  });
335
540
  }
336
541
 
337
- if (existingComment.get('status') === 'deleted' || data.status === 'deleted') {
542
+ if (existingComment.get('status') === COMMENT_STATUS_DELETED || data.status === COMMENT_STATUS_DELETED) {
338
543
  throw new errors.BadRequestError({
339
544
  message: tpl(messages.cannotPinDeletedComment)
340
545
  });
@@ -355,25 +560,27 @@ class CommentsService {
355
560
  * @param {string} id - The ID of the Comment to get replies from
356
561
  * @param {any} options
357
562
  */
358
- async getReplies(id, options) {
563
+ async getReplies(id, options, {includeHidden = false} = {}) {
359
564
  this.checkEnabled();
360
- const page = await this.models.Comment.findPage(withPinnedSelect({...options, parentId: id}));
565
+ const page = await this.models.Comment.findPage(withPinnedSelect({...options, parentId: id}, {includeHidden}));
361
566
 
362
567
  return page;
363
568
  }
364
569
 
570
+ async getAdminReplies(id, options) {
571
+ return await this.getReplies(id, {
572
+ ...options,
573
+ isAdmin: true
574
+ }, {includeHidden: true});
575
+ }
576
+
365
577
  /**
366
578
  * Get reporters for a comment (admin only)
367
579
  * @param {string} commentId - The ID of the Comment to get reporters for
368
580
  * @param {any} options - Query options (page, limit)
369
581
  */
370
582
  async getCommentReporters(commentId, options = {}) {
371
- const comment = await this.models.Comment.findOne({id: commentId});
372
- if (!comment) {
373
- throw new errors.NotFoundError({
374
- message: tpl(messages.commentNotFound)
375
- });
376
- }
583
+ await this.#assertCommentExists(commentId);
377
584
 
378
585
  const {page, limit} = options;
379
586
  const result = await this.models.CommentReport.findPage({
@@ -393,12 +600,7 @@ class CommentsService {
393
600
  * @param {any} options - Query options (page, limit)
394
601
  */
395
602
  async getCommentLikes(commentId, options = {}) {
396
- const comment = await this.models.Comment.findOne({id: commentId});
397
- if (!comment) {
398
- throw new errors.NotFoundError({
399
- message: tpl(messages.commentNotFound)
400
- });
401
- }
603
+ await this.#assertCommentExists(commentId);
402
604
 
403
605
  const {page, limit} = options;
404
606
  const result = await this.models.CommentLike.findPage({
@@ -418,12 +620,7 @@ class CommentsService {
418
620
  * @param {any} options - Query options (page, limit)
419
621
  */
420
622
  async getCommentDislikes(commentId, options = {}) {
421
- const comment = await this.models.Comment.findOne({id: commentId});
422
- if (!comment) {
423
- throw new errors.NotFoundError({
424
- message: tpl(messages.commentNotFound)
425
- });
426
- }
623
+ await this.#assertCommentExists(commentId);
427
624
 
428
625
  const {page, limit} = options;
429
626
  const result = await this.models.CommentLike.findPage({
@@ -437,21 +634,10 @@ class CommentsService {
437
634
  return result;
438
635
  }
439
636
 
440
- /**
441
- * @param {string} id - The ID of the Comment to get
442
- * @param {any} options
443
- */
444
- async getCommentByID(id, options) {
637
+ async getCommentByID(id, options = {}) {
445
638
  this.checkEnabled();
446
- const model = await this.models.Comment.findOne({id}, withPinnedSelect(options));
447
-
448
- if (!model) {
449
- throw new errors.NotFoundError({
450
- message: tpl(messages.commentNotFound)
451
- });
452
- }
453
639
 
454
- return model;
640
+ return await this.#getReadableCommentByID(id, options);
455
641
  }
456
642
 
457
643
  /**
@@ -488,7 +674,7 @@ class CommentsService {
488
674
  member_id: member,
489
675
  parent_id: null,
490
676
  html: comment,
491
- status: 'published'
677
+ status: COMMENT_STATUS_PUBLISHED
492
678
  };
493
679
 
494
680
  if (createdAt) {
@@ -531,12 +717,7 @@ class CommentsService {
531
717
 
532
718
  this.checkCommentAccess(memberModel);
533
719
 
534
- const parentComment = await this.getCommentByID(parent, options);
535
- if (!parentComment) {
536
- throw new errors.BadRequestError({
537
- message: tpl(messages.commentNotFound)
538
- });
539
- }
720
+ const parentComment = await this.#getReplyParentCommentByID(parent, options);
540
721
 
541
722
  if (parentComment.get('parent_id') !== null) {
542
723
  throw new errors.BadRequestError({
@@ -556,18 +737,7 @@ class CommentsService {
556
737
 
557
738
  let inReplyToComment;
558
739
  if (parent && inReplyTo) {
559
- inReplyToComment = await this.getCommentByID(inReplyTo, options);
560
-
561
- // we only allow references to published comments to avoid leaking
562
- // hidden data via the snippet included in API responses
563
- if (inReplyToComment && inReplyToComment.get('status') !== 'published') {
564
- inReplyToComment = null;
565
- }
566
-
567
- // we don't allow in_reply_to references across different parents
568
- if (inReplyToComment && inReplyToComment.get('parent_id') !== parent) {
569
- inReplyToComment = null;
570
- }
740
+ inReplyToComment = await this.#getInReplyToCommentByID(inReplyTo, parent, options);
571
741
  }
572
742
 
573
743
  const commentData = {
@@ -576,7 +746,7 @@ class CommentsService {
576
746
  parent_id: parentComment.id,
577
747
  in_reply_to_id: inReplyToComment && inReplyToComment.get('id'),
578
748
  html: comment,
579
- status: 'published'
749
+ status: COMMENT_STATUS_PUBLISHED
580
750
  };
581
751
 
582
752
  if (createdAt) {
@@ -606,7 +776,7 @@ class CommentsService {
606
776
  */
607
777
  async deleteComment(id, member, options) {
608
778
  this.checkEnabled();
609
- const existingComment = await this.getCommentByID(id, options);
779
+ const existingComment = await this.#getPublishedCommentForAction(id, options, OWNERSHIP_REQUIRED_COLUMNS);
610
780
 
611
781
  if (existingComment.get('member_id') !== member) {
612
782
  throw new errors.NoPermissionError({
@@ -616,7 +786,7 @@ class CommentsService {
616
786
  }
617
787
 
618
788
  const model = await this.models.Comment.edit({
619
- status: 'deleted',
789
+ status: COMMENT_STATUS_DELETED,
620
790
  pinned_at: null
621
791
  }, {
622
792
  id,
@@ -635,11 +805,7 @@ class CommentsService {
635
805
  */
636
806
  async editCommentContent(id, member, comment, options) {
637
807
  this.checkEnabled();
638
- const existingComment = await this.getCommentByID(id, options);
639
-
640
- if (!comment) {
641
- return existingComment;
642
- }
808
+ const existingComment = await this.#getPublishedCommentForAction(id, options, OWNERSHIP_REQUIRED_COLUMNS);
643
809
 
644
810
  if (existingComment.get('member_id') !== member) {
645
811
  throw new errors.NoPermissionError({
@@ -647,6 +813,10 @@ class CommentsService {
647
813
  });
648
814
  }
649
815
 
816
+ if (!comment) {
817
+ return existingComment;
818
+ }
819
+
650
820
  const model = await this.models.Comment.edit({
651
821
  html: comment,
652
822
  edited_at: new Date()
@@ -0,0 +1,157 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width">
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6
+ <title>Ghost notification</title>
7
+ <style>
8
+ @media only screen and (max-width: 620px) {
9
+ table[class=body] h1 {
10
+ font-size: 28px !important;
11
+ margin-bottom: 10px !important;
12
+ }
13
+ table[class=body] p,
14
+ table[class=body] ul,
15
+ table[class=body] ol,
16
+ table[class=body] td,
17
+ table[class=body] span,
18
+ table[class=body] a {
19
+ font-size: 16px !important;
20
+ }
21
+ table[class=body] .wrapper,
22
+ table[class=body] .article {
23
+ padding: 10px !important;
24
+ }
25
+ table[class=body] .content {
26
+ padding: 0 !important;
27
+ }
28
+ table[class=body] .container {
29
+ padding: 0 !important;
30
+ width: 100% !important;
31
+ }
32
+ table[class=body] .main {
33
+ border-left-width: 0 !important;
34
+ border-radius: 0 !important;
35
+ border-right-width: 0 !important;
36
+ }
37
+ table[class=body] .img-responsive {
38
+ height: auto !important;
39
+ max-width: 100% !important;
40
+ width: auto !important;
41
+ }
42
+ table[class=body] p[class=small],
43
+ table[class=body] a[class=small] {
44
+ font-size: 12px !important;
45
+ }
46
+ }
47
+ @media all {
48
+ .ExternalClass {
49
+ width: 100%;
50
+ }
51
+ .ExternalClass,
52
+ .ExternalClass p,
53
+ .ExternalClass span,
54
+ .ExternalClass font,
55
+ .ExternalClass td,
56
+ .ExternalClass div {
57
+ line-height: 100%;
58
+ }
59
+ .recipient-link a {
60
+ color: inherit !important;
61
+ font-family: inherit !important;
62
+ font-size: inherit !important;
63
+ font-weight: inherit !important;
64
+ line-height: inherit !important;
65
+ text-decoration: none !important;
66
+ }
67
+ #MessageViewBody a {
68
+ color: inherit;
69
+ text-decoration: none;
70
+ font-size: inherit;
71
+ font-family: inherit;
72
+ font-weight: inherit;
73
+ line-height: inherit;
74
+ }
75
+ }
76
+ .message {
77
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
78
+ font-size: 16px;
79
+ color: #3A464C;
80
+ line-height: 25px;
81
+ }
82
+ .message p {
83
+ margin: 0 0 20px 0;
84
+ }
85
+ .message a {
86
+ color: #15212A;
87
+ word-break: break-all;
88
+ }
89
+ .message hr {
90
+ border: 0;
91
+ border-top: 1px solid #EEF5F8;
92
+ margin: 28px 0;
93
+ }
94
+ .message ul,
95
+ .message ol {
96
+ margin: 0 0 20px 0;
97
+ padding-left: 20px;
98
+ }
99
+ .message blockquote {
100
+ margin: 0 0 20px 0;
101
+ padding-left: 16px;
102
+ border-left: 3px solid #EEF5F8;
103
+ color: #738A94;
104
+ }
105
+ .message code {
106
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
107
+ background: #F2F6F8;
108
+ padding: 1px 4px;
109
+ border-radius: 3px;
110
+ }
111
+ a {
112
+ color: #3A464C;
113
+ }
114
+ </style>
115
+ </head>
116
+ <body style="background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
117
+
118
+ <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
119
+ <tr>
120
+ <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;">&nbsp;</td>
121
+ <td class="container" 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; display: block; Margin: 0 auto; max-width: 540px; padding: 10px; width: 540px;">
122
+
123
+ <div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
124
+
125
+ <table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
126
+
127
+ <tr>
128
+ <td class="wrapper" 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; box-sizing: border-box; padding: 8px 8px 0;">
129
+ <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
130
+ <tr>
131
+ <td align="center" style="padding-top: 20px; padding-bottom: 12px;"><img src="https://static.ghost.org/v4.0.0/images/ghost-orb-2.png" width="60" height="60" style="width: 60px; height: 60px;" alt="Ghost" /></td>
132
+ </tr>
133
+ <tr>
134
+ <td class="message" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 16px; color: #3A464C; line-height: 25px; vertical-align: top; padding-top: 24px; padding-bottom: 10px;">
135
+ {{message}}
136
+ </td>
137
+ </tr>
138
+ </table>
139
+ </td>
140
+ </tr>
141
+ <tr>
142
+ <td style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; vertical-align: top; padding-top: 80px; padding-bottom: 10px;">
143
+ <div class="footer">
144
+ <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'; color: #738A94; font-weight: normal; margin: 0; line-height: 18px; margin-bottom: 0px; font-size: 11px;">This email was sent from <a href="{{siteUrl}}" style="color: #738A94;">{{siteUrl}}</a> to <a href="mailto:{{recipientEmail}}" style="color: #738A94;">{{recipientEmail}}</a></p>
145
+ </div>
146
+ </td>
147
+ </tr>
148
+
149
+ </table>
150
+
151
+ </div>
152
+ </td>
153
+ <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;">&nbsp;</td>
154
+ </tr>
155
+ </table>
156
+ </body>
157
+ </html>
@@ -6,7 +6,7 @@
6
6
  * }} facade
7
7
  */
8
8
 
9
- const toPlain = require('../../lib/common/to-plain');
9
+ const {toPlain} = require('../../lib/common/to-plain');
10
10
 
11
11
  const TYPE_TO_RESOURCE = {
12
12
  post: 'posts',