ghost 5.6.0 → 5.8.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.
Files changed (450) hide show
  1. package/components/tryghost-custom-theme-settings-service-0.0.0.tgz +0 -0
  2. package/components/tryghost-email-analytics-service-0.0.0.tgz +0 -0
  3. package/components/tryghost-html-to-plaintext-0.0.0.tgz +0 -0
  4. package/components/tryghost-job-manager-0.0.0.tgz +0 -0
  5. package/components/tryghost-magic-link-0.0.0.tgz +0 -0
  6. package/components/tryghost-members-api-0.0.0.tgz +0 -0
  7. package/components/tryghost-members-offers-0.0.0.tgz +0 -0
  8. package/components/tryghost-members-ssr-0.0.0.tgz +0 -0
  9. package/components/tryghost-members-stripe-service-0.0.0.tgz +0 -0
  10. package/components/tryghost-minifier-0.0.0.tgz +0 -0
  11. package/components/tryghost-security-0.0.0.tgz +0 -0
  12. package/content/themes/casper/assets/built/screen.css +1 -1
  13. package/content/themes/casper/assets/built/screen.css.map +1 -1
  14. package/content/themes/casper/assets/css/screen.css +21 -0
  15. package/content/themes/casper/package.json +1 -1
  16. package/core/built/admin/assets/chunk.143.85b848fc430f289d64b8.js +41 -0
  17. package/core/built/admin/assets/chunk.174.eec7f6398cef4c3e2485.js +239 -0
  18. package/core/built/admin/assets/chunk.178.11b2d387c027bafd815d.js +11 -0
  19. package/core/built/admin/assets/chunk.351.cbc224ca65c14ef5322d.js +9272 -0
  20. package/core/built/admin/assets/chunk.351.cbc224ca65c14ef5322d.js.LICENSE.txt +138 -0
  21. package/core/built/{assets/codemirror/codemirror-30201ab2cff61db847193fc9f5ed922d.js → admin/assets/codemirror/codemirror-1b460af155bb0ecaab9591678ac8c942.js} +1 -1
  22. package/core/built/{assets → admin/assets}/fonts/Inter.ttf +0 -0
  23. package/core/built/{assets/ghost.min-b0576e0e36343533e70992f3e5bb02bb.css → admin/assets/ghost-365d87dc3d1d5ee26cba6d952dc52d5b.css} +1 -1
  24. package/core/built/{assets/ghost.min-31bd482d1bcfe706448bc6f401481a28.js → admin/assets/ghost-50d38cd0f3736f23fc59ca91bb176d54.js} +368 -340
  25. package/core/built/admin/assets/ghost-dark-92b1e3544fb91e071fe3db40f0acae31.css +1 -0
  26. package/core/built/{assets → admin/assets}/icons/account-group.svg +0 -0
  27. package/core/built/{assets → admin/assets}/icons/activity-placeholder.svg +0 -0
  28. package/core/built/{assets → admin/assets}/icons/add-stroke.svg +0 -0
  29. package/core/built/{assets → admin/assets}/icons/add-view.svg +0 -0
  30. package/core/built/{assets → admin/assets}/icons/add.svg +0 -0
  31. package/core/built/{assets → admin/assets}/icons/align-center.svg +0 -0
  32. package/core/built/{assets → admin/assets}/icons/align-left.svg +0 -0
  33. package/core/built/{assets → admin/assets}/icons/ambulance.svg +0 -0
  34. package/core/built/{assets → admin/assets}/icons/analytics.svg +0 -0
  35. package/core/built/{assets → admin/assets}/icons/arrow-down-small.svg +0 -0
  36. package/core/built/{assets → admin/assets}/icons/arrow-down-stroke.svg +0 -0
  37. package/core/built/{assets → admin/assets}/icons/arrow-down.svg +0 -0
  38. package/core/built/{assets → admin/assets}/icons/arrow-left-small.svg +0 -0
  39. package/core/built/{assets → admin/assets}/icons/arrow-left-stroke.svg +0 -0
  40. package/core/built/{assets → admin/assets}/icons/arrow-left-tail.svg +0 -0
  41. package/core/built/{assets → admin/assets}/icons/arrow-left.svg +0 -0
  42. package/core/built/{assets → admin/assets}/icons/arrow-right-small.svg +0 -0
  43. package/core/built/{assets → admin/assets}/icons/arrow-right-stroke.svg +0 -0
  44. package/core/built/{assets → admin/assets}/icons/arrow-right-tail.svg +0 -0
  45. package/core/built/{assets → admin/assets}/icons/arrow-right.svg +0 -0
  46. package/core/built/{assets → admin/assets}/icons/arrow-up-small.svg +0 -0
  47. package/core/built/{assets → admin/assets}/icons/arrow-up-stroke.svg +0 -0
  48. package/core/built/{assets → admin/assets}/icons/arrow-up.svg +0 -0
  49. package/core/built/{assets → admin/assets}/icons/arrow2-down.svg +0 -0
  50. package/core/built/{assets → admin/assets}/icons/arrow2-right.svg +0 -0
  51. package/core/built/{assets → admin/assets}/icons/audio-file.svg +0 -0
  52. package/core/built/{assets → admin/assets}/icons/audio-upload.svg +0 -0
  53. package/core/built/{assets → admin/assets}/icons/book-open.svg +0 -0
  54. package/core/built/{assets → admin/assets}/icons/bookmark-article.svg +0 -0
  55. package/core/built/{assets → admin/assets}/icons/box-hands.svg +0 -0
  56. package/core/built/{assets → admin/assets}/icons/box.svg +0 -0
  57. package/core/built/{assets → admin/assets}/icons/boxes.svg +0 -0
  58. package/core/built/{assets → admin/assets}/icons/brackets.svg +0 -0
  59. package/core/built/{assets → admin/assets}/icons/button.svg +0 -0
  60. package/core/built/{assets → admin/assets}/icons/calendar.svg +0 -0
  61. package/core/built/{assets → admin/assets}/icons/cash.svg +0 -0
  62. package/core/built/{assets → admin/assets}/icons/chat-double-bubble.svg +0 -0
  63. package/core/built/{assets → admin/assets}/icons/check-2.svg +0 -0
  64. package/core/built/{assets → admin/assets}/icons/check-circle-stroke.svg +0 -0
  65. package/core/built/{assets → admin/assets}/icons/check-circle.svg +0 -0
  66. package/core/built/{assets → admin/assets}/icons/check.svg +0 -0
  67. package/core/built/{assets → admin/assets}/icons/cheeseburger.svg +0 -0
  68. package/core/built/{assets → admin/assets}/icons/circle-ellipsis.svg +0 -0
  69. package/core/built/{assets → admin/assets}/icons/clock.svg +0 -0
  70. package/core/built/{assets → admin/assets}/icons/clockface.svg +0 -0
  71. package/core/built/{assets → admin/assets}/icons/close-stroke.svg +0 -0
  72. package/core/built/{assets → admin/assets}/icons/close.svg +0 -0
  73. package/core/built/{assets → admin/assets}/icons/cloud.svg +0 -0
  74. package/core/built/{assets → admin/assets}/icons/compass-2.svg +0 -0
  75. package/core/built/{assets → admin/assets}/icons/compass.svg +0 -0
  76. package/core/built/{assets → admin/assets}/icons/computer.svg +0 -0
  77. package/core/built/{assets → admin/assets}/icons/confetti.svg +0 -0
  78. package/core/built/{assets → admin/assets}/icons/content-bold.svg +0 -0
  79. package/core/built/{assets → admin/assets}/icons/content.svg +0 -0
  80. package/core/built/{assets → admin/assets}/icons/copy.svg +0 -0
  81. package/core/built/{assets → admin/assets}/icons/credit-card.svg +0 -0
  82. package/core/built/{assets → admin/assets}/icons/cross-circle.svg +0 -0
  83. package/core/built/{assets → admin/assets}/icons/cycle.svg +0 -0
  84. package/core/built/{assets → admin/assets}/icons/default-favicon.svg +0 -0
  85. package/core/built/{assets → admin/assets}/icons/desert.svg +0 -0
  86. package/core/built/{assets → admin/assets}/icons/desktop.svg +0 -0
  87. package/core/built/{assets → admin/assets}/icons/diamond.svg +0 -0
  88. package/core/built/{assets → admin/assets}/icons/discount-bubble.svg +0 -0
  89. package/core/built/{assets → admin/assets}/icons/dividers.svg +0 -0
  90. package/core/built/{assets → admin/assets}/icons/dotdotdot.svg +0 -0
  91. package/core/built/{assets → admin/assets}/icons/download-circle.svg +0 -0
  92. package/core/built/{assets → admin/assets}/icons/download.svg +0 -0
  93. package/core/built/{assets → admin/assets}/icons/eco-globe.svg +0 -0
  94. package/core/built/{assets → admin/assets}/icons/eco-lightbulb.svg +0 -0
  95. package/core/built/{assets → admin/assets}/icons/edit-view.svg +0 -0
  96. package/core/built/{assets → admin/assets}/icons/ellipsis.svg +0 -0
  97. package/core/built/{assets → admin/assets}/icons/email-at.svg +0 -0
  98. package/core/built/{assets → admin/assets}/icons/email-body.svg +0 -0
  99. package/core/built/{assets → admin/assets}/icons/email-footer.svg +0 -0
  100. package/core/built/{assets → admin/assets}/icons/email-header.svg +0 -0
  101. package/core/built/{assets → admin/assets}/icons/email-love-letter.svg +0 -0
  102. package/core/built/{assets → admin/assets}/icons/email-member.svg +0 -0
  103. package/core/built/{assets → admin/assets}/icons/email-name.svg +0 -0
  104. package/core/built/{assets → admin/assets}/icons/email-send.svg +0 -0
  105. package/core/built/{assets → admin/assets}/icons/email-stroke.svg +0 -0
  106. package/core/built/{assets → admin/assets}/icons/email-unread.svg +0 -0
  107. package/core/built/{assets → admin/assets}/icons/email.svg +0 -0
  108. package/core/built/{assets → admin/assets}/icons/event-canceled-subscription.svg +0 -0
  109. package/core/built/{assets → admin/assets}/icons/event-comment.svg +0 -0
  110. package/core/built/{assets → admin/assets}/icons/event-email-delivery-failed.svg +0 -0
  111. package/core/built/{assets → admin/assets}/icons/event-logged-in.svg +0 -0
  112. package/core/built/{assets → admin/assets}/icons/event-made-a-payment.svg +0 -0
  113. package/core/built/{assets → admin/assets}/icons/event-opened-email.svg +0 -0
  114. package/core/built/{assets → admin/assets}/icons/event-received-email.svg +0 -0
  115. package/core/built/{assets → admin/assets}/icons/event-signed-up.svg +0 -0
  116. package/core/built/{assets → admin/assets}/icons/event-subscribed-to-email.svg +0 -0
  117. package/core/built/{assets → admin/assets}/icons/event-subscriptions.svg +0 -0
  118. package/core/built/{assets → admin/assets}/icons/event-unsubscribed-from-email.svg +0 -0
  119. package/core/built/{assets → admin/assets}/icons/expand.svg +0 -0
  120. package/core/built/{assets → admin/assets}/icons/external.svg +0 -0
  121. package/core/built/{assets → admin/assets}/icons/eye.svg +0 -0
  122. package/core/built/{assets → admin/assets}/icons/facebook-heart.svg +0 -0
  123. package/core/built/{assets → admin/assets}/icons/facebook-like.svg +0 -0
  124. package/core/built/{assets → admin/assets}/icons/facebook.svg +0 -0
  125. package/core/built/{assets → admin/assets}/icons/feature-image.svg +0 -0
  126. package/core/built/{assets → admin/assets}/icons/file-download.svg +0 -0
  127. package/core/built/{assets → admin/assets}/icons/file-tabular-data.svg +0 -0
  128. package/core/built/{assets → admin/assets}/icons/file-text-document.svg +0 -0
  129. package/core/built/{assets → admin/assets}/icons/file-upload.svg +0 -0
  130. package/core/built/{assets → admin/assets}/icons/film-camera.svg +0 -0
  131. package/core/built/{assets → admin/assets}/icons/filter.svg +0 -0
  132. package/core/built/{assets → admin/assets}/icons/firstpromoter-ce8b5219e5b5ebda463bd60396c6ab81.png +0 -0
  133. package/core/built/{assets → admin/assets}/icons/folder.svg +0 -0
  134. package/core/built/{assets → admin/assets}/icons/gallery-placeholder.svg +0 -0
  135. package/core/built/{assets → admin/assets}/icons/get-started-import.svg +0 -0
  136. package/core/built/{assets → admin/assets}/icons/get-started-members.svg +0 -0
  137. package/core/built/{assets → admin/assets}/icons/get-started-migrations.svg +0 -0
  138. package/core/built/{assets → admin/assets}/icons/get-started.svg +0 -0
  139. package/core/built/{assets → admin/assets}/icons/ghost-logo-orb.svg +0 -0
  140. package/core/built/{assets → admin/assets}/icons/ghost-logo.svg +0 -0
  141. package/core/built/{assets → admin/assets}/icons/ghost-orb.svg +0 -0
  142. package/core/built/{assets → admin/assets}/icons/ghost-squircle.svg +0 -0
  143. package/core/built/{assets → admin/assets}/icons/gift.svg +0 -0
  144. package/core/built/{assets → admin/assets}/icons/github-outline.svg +0 -0
  145. package/core/built/{assets → admin/assets}/icons/github-star.svg +0 -0
  146. package/core/built/{assets → admin/assets}/icons/github.svg +0 -0
  147. package/core/built/{assets → admin/assets}/icons/globe.svg +0 -0
  148. package/core/built/{assets → admin/assets}/icons/google-favicon.svg +0 -0
  149. package/core/built/{assets → admin/assets}/icons/google-search.svg +0 -0
  150. package/core/built/{assets → admin/assets}/icons/google.svg +0 -0
  151. package/core/built/{assets → admin/assets}/icons/grab.svg +0 -0
  152. package/core/built/{assets → admin/assets}/icons/graph-line.svg +0 -0
  153. package/core/built/{assets → admin/assets}/icons/heart-beat.svg +0 -0
  154. package/core/built/{assets → admin/assets}/icons/heart-circle.svg +0 -0
  155. package/core/built/{assets → admin/assets}/icons/heart.svg +0 -0
  156. package/core/built/{assets → admin/assets}/icons/help.svg +0 -0
  157. package/core/built/{assets → admin/assets}/icons/hotspot.svg +0 -0
  158. package/core/built/{assets → admin/assets}/icons/house-bold.svg +0 -0
  159. package/core/built/{assets → admin/assets}/icons/house.svg +0 -0
  160. package/core/built/{assets → admin/assets}/icons/icon.svg +0 -0
  161. package/core/built/{assets → admin/assets}/icons/id-card.svg +0 -0
  162. package/core/built/{assets → admin/assets}/icons/idea.svg +0 -0
  163. package/core/built/{assets → admin/assets}/icons/info.svg +0 -0
  164. package/core/built/{assets → admin/assets}/icons/instagram.svg +0 -0
  165. package/core/built/{assets → admin/assets}/icons/integration.svg +0 -0
  166. package/core/built/{assets → admin/assets}/icons/labs.svg +0 -0
  167. package/core/built/{assets → admin/assets}/icons/laptop.svg +0 -0
  168. package/core/built/{assets → admin/assets}/icons/line.svg +0 -0
  169. package/core/built/{assets → admin/assets}/icons/link.svg +0 -0
  170. package/core/built/{assets → admin/assets}/icons/list-bullet.svg +0 -0
  171. package/core/built/{assets → admin/assets}/icons/list-number.svg +0 -0
  172. package/core/built/{assets → admin/assets}/icons/lock.svg +0 -0
  173. package/core/built/{assets → admin/assets}/icons/lock2.svg +0 -0
  174. package/core/built/{assets → admin/assets}/icons/loop-infinite.svg +0 -0
  175. package/core/built/{assets → admin/assets}/icons/lotus.svg +0 -0
  176. package/core/built/{assets → admin/assets}/icons/markdown.svg +0 -0
  177. package/core/built/{assets → admin/assets}/icons/member-add.svg +0 -0
  178. package/core/built/{assets → admin/assets}/icons/member.svg +0 -0
  179. package/core/built/{assets → admin/assets}/icons/members-all.svg +0 -0
  180. package/core/built/{assets → admin/assets}/icons/members-bold.svg +0 -0
  181. package/core/built/{assets → admin/assets}/icons/members-outline.svg +0 -0
  182. package/core/built/{assets → admin/assets}/icons/members-paid.svg +0 -0
  183. package/core/built/{assets → admin/assets}/icons/members-placeholder.svg +0 -0
  184. package/core/built/{assets → admin/assets}/icons/members-post.svg +0 -0
  185. package/core/built/{assets → admin/assets}/icons/members-segment.svg +0 -0
  186. package/core/built/{assets → admin/assets}/icons/members.svg +0 -0
  187. package/core/built/{assets → admin/assets}/icons/mobile-phone-heart.svg +0 -0
  188. package/core/built/{assets → admin/assets}/icons/mobile-phone.svg +0 -0
  189. package/core/built/{assets → admin/assets}/icons/module.svg +0 -0
  190. package/core/built/{assets → admin/assets}/icons/modules.svg +0 -0
  191. package/core/built/{assets → admin/assets}/icons/monitor-labs.svg +0 -0
  192. package/core/built/{assets → admin/assets}/icons/moon.svg +0 -0
  193. package/core/built/{assets → admin/assets}/icons/mountains.svg +0 -0
  194. package/core/built/{assets → admin/assets}/icons/mute.svg +0 -0
  195. package/core/built/{assets → admin/assets}/icons/navigation.svg +0 -0
  196. package/core/built/{assets → admin/assets}/icons/network.svg +0 -0
  197. package/core/built/{assets → admin/assets}/icons/news-article.svg +0 -0
  198. package/core/built/{assets → admin/assets}/icons/nightshift.svg +0 -0
  199. package/core/built/{assets → admin/assets}/icons/no-data-line-chart.svg +0 -0
  200. package/core/built/{assets → admin/assets}/icons/no-data-list.svg +0 -0
  201. package/core/built/{assets → admin/assets}/icons/no-data-subscription.svg +0 -0
  202. package/core/built/{assets → admin/assets}/icons/no-email.svg +0 -0
  203. package/core/built/{assets → admin/assets}/icons/no-members.svg +0 -0
  204. package/core/built/{assets → admin/assets}/icons/offer.svg +0 -0
  205. package/core/built/{assets → admin/assets}/icons/page-bold.svg +0 -0
  206. package/core/built/{assets → admin/assets}/icons/page.svg +0 -0
  207. package/core/built/{assets → admin/assets}/icons/pages-placeholder.svg +0 -0
  208. package/core/built/{assets → admin/assets}/icons/paint-palette.svg +0 -0
  209. package/core/built/{assets → admin/assets}/icons/paintbrush.svg +0 -0
  210. package/core/built/{assets → admin/assets}/icons/pause.svg +0 -0
  211. package/core/built/{assets → admin/assets}/icons/pen.svg +0 -0
  212. package/core/built/{assets → admin/assets}/icons/pencil-circle.svg +0 -0
  213. package/core/built/{assets → admin/assets}/icons/pencil.svg +0 -0
  214. package/core/built/{assets → admin/assets}/icons/percentage.svg +0 -0
  215. package/core/built/{assets → admin/assets}/icons/photos-people.svg +0 -0
  216. package/core/built/{assets → admin/assets}/icons/photos.svg +0 -0
  217. package/core/built/{assets → admin/assets}/icons/piggy-bank.svg +0 -0
  218. package/core/built/{assets → admin/assets}/icons/pin.svg +0 -0
  219. package/core/built/{assets → admin/assets}/icons/plane.svg +0 -0
  220. package/core/built/{assets → admin/assets}/icons/play.svg +0 -0
  221. package/core/built/{assets → admin/assets}/icons/plus.svg +0 -0
  222. package/core/built/{assets → admin/assets}/icons/portal-icon-1.svg +0 -0
  223. package/core/built/{assets → admin/assets}/icons/portal-icon-2.svg +0 -0
  224. package/core/built/{assets → admin/assets}/icons/portal-icon-3.svg +0 -0
  225. package/core/built/{assets → admin/assets}/icons/portal-icon-4.svg +0 -0
  226. package/core/built/{assets → admin/assets}/icons/portal-icon-5.svg +0 -0
  227. package/core/built/{assets → admin/assets}/icons/portal-logo-stroke.svg +0 -0
  228. package/core/built/{assets → admin/assets}/icons/portal-logo.svg +0 -0
  229. package/core/built/{assets → admin/assets}/icons/post.svg +0 -0
  230. package/core/built/{assets → admin/assets}/icons/posts-placeholder.svg +0 -0
  231. package/core/built/{assets → admin/assets}/icons/posts.svg +0 -0
  232. package/core/built/{assets → admin/assets}/icons/powered-by-stripe.svg +0 -0
  233. package/core/built/{assets → admin/assets}/icons/powered-by-tenor.svg +0 -0
  234. package/core/built/{assets → admin/assets}/icons/presentation-code.svg +0 -0
  235. package/core/built/{assets → admin/assets}/icons/published-post.svg +0 -0
  236. package/core/built/{assets → admin/assets}/icons/recycle.svg +0 -0
  237. package/core/built/{assets → admin/assets}/icons/reload.svg +0 -0
  238. package/core/built/{assets → admin/assets}/icons/repo.svg +0 -0
  239. package/core/built/{assets → admin/assets}/icons/retry.svg +0 -0
  240. package/core/built/{assets → admin/assets}/icons/satellite.svg +0 -0
  241. package/core/built/{assets → admin/assets}/icons/search.svg +0 -0
  242. package/core/built/{assets → admin/assets}/icons/send-email.svg +0 -0
  243. package/core/built/{assets → admin/assets}/icons/server.svg +0 -0
  244. package/core/built/{assets → admin/assets}/icons/settings.svg +0 -0
  245. package/core/built/{assets → admin/assets}/icons/shield-lock.svg +0 -0
  246. package/core/built/{assets → admin/assets}/icons/shield.svg +0 -0
  247. package/core/built/{assets → admin/assets}/icons/sidemenu-open.svg +0 -0
  248. package/core/built/{assets → admin/assets}/icons/sidemenu.svg +0 -0
  249. package/core/built/{assets → admin/assets}/icons/signal-tower.svg +0 -0
  250. package/core/built/{assets → admin/assets}/icons/signout.svg +0 -0
  251. package/core/built/{assets → admin/assets}/icons/smiley.svg +0 -0
  252. package/core/built/{assets → admin/assets}/icons/social-facebook.svg +0 -0
  253. package/core/built/{assets → admin/assets}/icons/social-share.svg +0 -0
  254. package/core/built/{assets → admin/assets}/icons/social-twitter.svg +0 -0
  255. package/core/built/{assets → admin/assets}/icons/spinner.svg +0 -0
  256. package/core/built/{assets → admin/assets}/icons/staff.svg +0 -0
  257. package/core/built/{assets → admin/assets}/icons/star-filled.svg +0 -0
  258. package/core/built/{assets → admin/assets}/icons/store.svg +0 -0
  259. package/core/built/{assets → admin/assets}/icons/stripe-verified-partner-badge.svg +0 -0
  260. package/core/built/{assets → admin/assets}/icons/summer.svg +0 -0
  261. package/core/built/{assets → admin/assets}/icons/sun.svg +0 -0
  262. package/core/built/{assets → admin/assets}/icons/support.svg +0 -0
  263. package/core/built/{assets → admin/assets}/icons/sync.svg +0 -0
  264. package/core/built/{assets → admin/assets}/icons/tag.svg +0 -0
  265. package/core/built/{assets → admin/assets}/icons/tags-placeholder.svg +0 -0
  266. package/core/built/{assets → admin/assets}/icons/tenor.svg +0 -0
  267. package/core/built/{assets → admin/assets}/icons/terminal.svg +0 -0
  268. package/core/built/{assets → admin/assets}/icons/text-vector.svg +0 -0
  269. package/core/built/{assets → admin/assets}/icons/text.svg +0 -0
  270. package/core/built/{assets → admin/assets}/icons/theme.svg +0 -0
  271. package/core/built/{assets → admin/assets}/icons/ticket.svg +0 -0
  272. package/core/built/{assets → admin/assets}/icons/trash.svg +0 -0
  273. package/core/built/{assets → admin/assets}/icons/trophy.svg +0 -0
  274. package/core/built/{assets → admin/assets}/icons/tumbleweed.svg +0 -0
  275. package/core/built/{assets → admin/assets}/icons/twitter-comment.svg +0 -0
  276. package/core/built/{assets → admin/assets}/icons/twitter-like.svg +0 -0
  277. package/core/built/{assets → admin/assets}/icons/twitter-link.svg +0 -0
  278. package/core/built/{assets → admin/assets}/icons/twitter-logo.svg +0 -0
  279. package/core/built/{assets → admin/assets}/icons/twitter-retweet.svg +0 -0
  280. package/core/built/{assets → admin/assets}/icons/twitter-share.svg +0 -0
  281. package/core/built/{assets → admin/assets}/icons/twitter.svg +0 -0
  282. package/core/built/{assets → admin/assets}/icons/ufo-attack.svg +0 -0
  283. package/core/built/{assets → admin/assets}/icons/unmute.svg +0 -0
  284. package/core/built/{assets → admin/assets}/icons/unsplash-heart.svg +0 -0
  285. package/core/built/{assets → admin/assets}/icons/unsplash.svg +0 -0
  286. package/core/built/{assets → admin/assets}/icons/unsubscribed.svg +0 -0
  287. package/core/built/{assets → admin/assets}/icons/upload-fill.svg +0 -0
  288. package/core/built/{assets → admin/assets}/icons/upload.svg +0 -0
  289. package/core/built/{assets → admin/assets}/icons/user-circle.svg +0 -0
  290. package/core/built/{assets → admin/assets}/icons/user-group.svg +0 -0
  291. package/core/built/{assets → admin/assets}/icons/user-group2.svg +0 -0
  292. package/core/built/{assets → admin/assets}/icons/v-ellipsis.svg +0 -0
  293. package/core/built/{assets → admin/assets}/icons/view-site.svg +0 -0
  294. package/core/built/{assets → admin/assets}/icons/warning-stroke.svg +0 -0
  295. package/core/built/{assets → admin/assets}/icons/warning.svg +0 -0
  296. package/core/built/{assets → admin/assets}/icons/window-app.svg +0 -0
  297. package/core/built/{assets → admin/assets}/icons/window-pulse.svg +0 -0
  298. package/core/built/{assets → admin/assets}/icons/wrench-double.svg +0 -0
  299. package/core/built/{assets → admin/assets}/icons/zap.svg +0 -0
  300. package/core/built/{assets → admin/assets}/img/404-ghost-e112dbfadde79cf30dbca8e8cfe7a21a.png +0 -0
  301. package/core/built/{assets → admin/assets}/img/404-ghost@2x-34c1119ed15cc3ea71e4406f80c8e810.png +0 -0
  302. package/core/built/{assets → admin/assets}/img/abstract-2-2937e2902b64360d0cbe4cec8bd8479b.jpg +0 -0
  303. package/core/built/{assets → admin/assets}/img/abstract-c52b2f4208e7fd2e7b8abd8b1eec4f7b.jpg +0 -0
  304. package/core/built/{assets → admin/assets}/img/amp.svg +0 -0
  305. package/core/built/{assets → admin/assets}/img/apple-touch-icon-74680e326a7e87b159d366c7d4fb3d4b.png +0 -0
  306. package/core/built/{assets → admin/assets}/img/buffer-db31f6b5dc27a298050e16312619c586.png +0 -0
  307. package/core/built/{assets → admin/assets}/img/community-background-3f501ff1d764d0cb81f7c2cbacfc6503.jpg +0 -0
  308. package/core/built/{assets → admin/assets}/img/community-be8c1dcecfb157f2bfba5cababc8e686.jpg +0 -0
  309. package/core/built/{assets → admin/assets}/img/dashboard/bp1-03a13253453b372d0bbd5e6a19b20fca.jpg +0 -0
  310. package/core/built/{assets → admin/assets}/img/dashboard/bp2-ed14f9f48c56d5d6fe997dc637a8b1ad.jpg +0 -0
  311. package/core/built/{assets → admin/assets}/img/dashboard/join-community-bb5478f7dca6c04a2d0baf2b02f880fb.jpg +0 -0
  312. package/core/built/{assets → admin/assets}/img/dashboard-feature-image-8949197550decb94a8f1dea57ad47819.jpeg +0 -0
  313. package/core/built/{assets → admin/assets}/img/disqus.svg +0 -0
  314. package/core/built/{assets → admin/assets}/img/favicon.ico +0 -0
  315. package/core/built/{assets → admin/assets}/img/firstpromoter-ce8b5219e5b5ebda463bd60396c6ab81.png +0 -0
  316. package/core/built/{assets → admin/assets}/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
  317. package/core/built/{assets → admin/assets}/img/get-started-995c36b9ede90bb3777c83751e798ef7.jpg +0 -0
  318. package/core/built/{assets → admin/assets}/img/github.svg +0 -0
  319. package/core/built/{assets → admin/assets}/img/google-analytics-e05fc1327377939663407200d4978eba.png +0 -0
  320. package/core/built/{assets → admin/assets}/img/google-docs.svg +0 -0
  321. package/core/built/{assets → admin/assets}/img/install-welcome-ff3912d18bf8949df89c83b1c3b8bb66.png +0 -0
  322. package/core/built/{assets → admin/assets}/img/invite-placeholder-4b3731d47c6c8f6cb3aaa6bc4d870c9f.png +0 -0
  323. package/core/built/{assets → admin/assets}/img/large-ac90af7c93a4b47e8d956fa9fef31d9d.png +0 -0
  324. package/core/built/{assets → admin/assets}/img/launch-wizard-bg-13b995b1f02bce34b269db3190897384.png +0 -0
  325. package/core/built/{assets → admin/assets}/img/loadingcat-5f501b7a8c7bd54b253463e261c99634.gif +0 -0
  326. package/core/built/{assets → admin/assets}/img/logos/ghost-logo-black-1-fb561a374422b405ec90fa586b05ebdf.png +0 -0
  327. package/core/built/{assets → admin/assets}/img/logos/orb-black-1-32b72766be2164fdea6ab2565fb1c137.png +0 -0
  328. package/core/built/{assets → admin/assets}/img/logos/orb-black-2-4b0baef1ec34816af6f3fbede7ab191d.png +0 -0
  329. package/core/built/{assets → admin/assets}/img/logos/orb-black-3-9780c5e9991b69ed2303b526aa3e3252.png +0 -0
  330. package/core/built/{assets → admin/assets}/img/logos/orb-black-4-d9c3caacb8d2ba98a52517ff2971433c.png +0 -0
  331. package/core/built/{assets → admin/assets}/img/logos/orb-black-5-063a200f749e09c587b9443ffd2c4cfa.png +0 -0
  332. package/core/built/{assets → admin/assets}/img/mailchimp.svg +0 -0
  333. package/core/built/{assets → admin/assets}/img/marketing/members-1-8b89a1f48fe7b336754e91a429531f65.jpg +0 -0
  334. package/core/built/{assets → admin/assets}/img/marketing/members-2-791205c82d5cf221f8c99a74f9ee1739.jpg +0 -0
  335. package/core/built/{assets → admin/assets}/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
  336. package/core/built/{assets → admin/assets}/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
  337. package/core/built/{assets → admin/assets}/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
  338. package/core/built/{assets → admin/assets}/img/medium-fef07013cffd5c45a655a250912a0ad7.png +0 -0
  339. package/core/built/{assets → admin/assets}/img/more-fe5480ab76c7be7aec28bcfe844f41c1.png +0 -0
  340. package/core/built/{assets → admin/assets}/img/newsletter-1-197ae8063dfb2e22278d355198029c9e.jpg +0 -0
  341. package/core/built/{assets → admin/assets}/img/newsletter-2-5a2c7693ea9380d4282061302c01267a.jpg +0 -0
  342. package/core/built/{assets → admin/assets}/img/orb-squircle-645fd62e92a80557905c0cb60874b699.png +0 -0
  343. package/core/built/{assets → admin/assets}/img/patreon.svg +0 -0
  344. package/core/built/{assets → admin/assets}/img/paypal.svg +0 -0
  345. package/core/built/{assets → admin/assets}/img/plausible-1b754a95e19a6d935e0bc8bf6790ba56.png +0 -0
  346. package/core/built/{assets → admin/assets}/img/resource-1-722f202795856e4a5596c8a3b7bedc43.jpg +0 -0
  347. package/core/built/{assets → admin/assets}/img/slackicon-406aadea8994ca2ddee9c1d7157208db.png +0 -0
  348. package/core/built/{assets → admin/assets}/img/small-b90396925485f17b2ca82c31be42de5f.png +0 -0
  349. package/core/built/{assets → admin/assets}/img/themes/Alto-a5172a42994782321d8c9f268928d987.jpg +0 -0
  350. package/core/built/{assets → admin/assets}/img/themes/Alto-cut-c667d9d6457c5e981aa7bdc8946b215a.jpg +0 -0
  351. package/core/built/{assets → admin/assets}/img/themes/Alto-f4db5af43ca9771c7ac1f754de3ddf2f.png +0 -0
  352. package/core/built/{assets → admin/assets}/img/themes/Bulletin-57d45b992ff0e26e0acdce7ed4cccd67.png +0 -0
  353. package/core/built/{assets → admin/assets}/img/themes/Casper-19b7267aac5acc6abfaaed7e41eddae8.jpg +0 -0
  354. package/core/built/{assets → admin/assets}/img/themes/Dawn-be81aa8c8caae8fcfb5d5fbec823fdcc.png +0 -0
  355. package/core/built/{assets → admin/assets}/img/themes/Digest-d3467ac22a290e1ad3a543014758286e.png +0 -0
  356. package/core/built/{assets → admin/assets}/img/themes/Dope-6f8e0bbc199ce4af9a60859e9e6a74ad.png +0 -0
  357. package/core/built/{assets → admin/assets}/img/themes/Ease-7cfb8f06f9b66b1aa04b92493a471fef.jpg +0 -0
  358. package/core/built/{assets → admin/assets}/img/themes/Ease-9c279ea6cec3c0f1823f81c9dd24b116.png +0 -0
  359. package/core/built/{assets → admin/assets}/img/themes/Ease-cut-78d5aa00682bee6a4cf22155f09e49e8.jpg +0 -0
  360. package/core/built/{assets → admin/assets}/img/themes/Edge-0258906309e11fd075a1d9880aa09b20.png +0 -0
  361. package/core/built/{assets → admin/assets}/img/themes/Edition-b94372eb5c88fce0f4befba39148ff28.jpg +0 -0
  362. package/core/built/{assets → admin/assets}/img/themes/Edition-cut-e6545bab2768aafa1996100774f310ff.jpg +0 -0
  363. package/core/built/{assets → admin/assets}/img/themes/Edition-d8f508e93bc24bdf2716ae6f8b3d44f8.png +0 -0
  364. package/core/built/{assets → admin/assets}/img/themes/Headline-c5070cf549e797a6a72b87237caa1617.jpg +0 -0
  365. package/core/built/{assets → admin/assets}/img/themes/Journal-accf0031bbae0919900a049061e65a04.png +0 -0
  366. package/core/built/{assets → admin/assets}/img/themes/London-3f07efcee9e5bfb9a33827064eb77e70.jpg +0 -0
  367. package/core/built/{assets → admin/assets}/img/themes/London-cut-599cba221f81a15ad37fe395cf4e572c.jpg +0 -0
  368. package/core/built/{assets → admin/assets}/img/themes/Ruby-11a53c62015612f4b3aca8f503121225.png +0 -0
  369. package/core/built/{assets → admin/assets}/img/themes/Wave-86e8044c2d76cb57a9030e4c24ac9520.png +0 -0
  370. package/core/built/{assets → admin/assets}/img/touch-icon-ipad-2e78629d62ad05746f980f14623dfadb.png +0 -0
  371. package/core/built/{assets → admin/assets}/img/touch-icon-iphone-93ed4382d391be9180093fd77ce8f410.png +0 -0
  372. package/core/built/{assets → admin/assets}/img/twitter.svg +0 -0
  373. package/core/built/{assets → admin/assets}/img/typeform.svg +0 -0
  374. package/core/built/{assets → admin/assets}/img/ulysses-3c66b59fb45c285bf336b4b613dc86e4.png +0 -0
  375. package/core/built/{assets → admin/assets}/img/unsplash-404-71ab3705c9920a9706bb822c3d7e34d1.png +0 -0
  376. package/core/built/{assets → admin/assets}/img/user-cover-e8f42b12b5fcba292a8b5dfa81e13dd2.png +0 -0
  377. package/core/built/{assets → admin/assets}/img/user-image-639a88b784fb5f10964be8b975ca9fdf.png +0 -0
  378. package/core/built/{assets → admin/assets}/img/users-33427addfe0f11c78b42cccaf83be073.png +0 -0
  379. package/core/built/{assets → admin/assets}/img/whats-new-header-bg.svg +0 -0
  380. package/core/built/{assets → admin/assets}/img/zapier-logo.svg +0 -0
  381. package/core/built/{assets → admin/assets}/img/zapier.svg +0 -0
  382. package/core/built/{assets → admin/assets}/img/zero-bounce-ee799eddb1f88e33ab8c462858cbfed9.png +0 -0
  383. package/core/built/{assets/simplemde/simplemde-fb4527da6e489b34dc24b28ea870b286.js → admin/assets/simplemde/simplemde-0cd04dbb388ed0cf571a6808464ea57e.js} +1 -1
  384. package/core/built/admin/assets/vendor-46baf13852f545c6c89756c8e0ccbff2.js +13821 -0
  385. package/core/built/{assets/vendor.min-4a6661c574707ceca220aa2e76558995.css → admin/assets/vendor-bc9d2c9e5c8a33f0c92e81189d48e04c.css} +17 -9
  386. package/core/{server/web/admin/views/default.html → built/admin/index.html} +13 -15
  387. package/core/frontend/helpers/comment_count.js +15 -9
  388. package/core/frontend/helpers/img_url.js +67 -6
  389. package/core/frontend/helpers/title.js +1 -1
  390. package/core/frontend/public/ghost.min.css +1 -1
  391. package/core/frontend/services/admin-auth-assets/service.js +19 -3
  392. package/core/frontend/src/admin-auth/message-handler.js +7 -3
  393. package/core/frontend/src/comment-counts/js/comment-counts.js +17 -2
  394. package/core/frontend/web/middleware/handle-image-sizes.js +7 -11
  395. package/core/server/adapters/cache/Base.js +12 -0
  396. package/core/server/adapters/cache/Memory.js +36 -0
  397. package/core/server/adapters/cache/index.js +17 -0
  398. package/core/server/api/endpoints/comments-members.js +2 -19
  399. package/core/server/api/endpoints/integrations.js +12 -8
  400. package/core/server/api/endpoints/utils/serializers/input/tiers.js +5 -0
  401. package/core/server/api/endpoints/utils/serializers/output/mappers/comments.js +1 -1
  402. package/core/server/api/endpoints/utils/serializers/output/tiers.js +5 -0
  403. package/core/server/api/endpoints/utils/serializers/output/utils/post-gating.js +1 -1
  404. package/core/server/data/migrations/versions/4.0/23-regenerate-posts-html.js +1 -1
  405. package/core/server/data/migrations/versions/4.9/05-fix-missed-mobiledoc-url-transforms.js +1 -1
  406. package/core/server/data/migrations/versions/5.0/2022-05-21-00-00-regenerate-posts-html.js +1 -1
  407. package/core/server/data/migrations/versions/5.8/2022-08-02-06-09-add-trial-period-days-column-to-tiers.js +8 -0
  408. package/core/server/data/migrations/versions/5.8/2022-08-03-15-28-add-trial-start-column-to-stripe-subscriptions.js +6 -0
  409. package/core/server/data/migrations/versions/5.8/2022-08-03-15-29-add-trial-end-column-to-stripe-subscriptions.js +6 -0
  410. package/core/server/data/schema/schema.js +3 -0
  411. package/core/server/ghost-server.js +7 -0
  412. package/core/server/models/base/index.js +1 -1
  413. package/core/server/models/base/plugins/filtered-collection.js +3 -3
  414. package/core/server/models/comment.js +39 -14
  415. package/core/server/models/post.js +1 -1
  416. package/core/server/models/product.js +2 -1
  417. package/core/server/models/tag.js +1 -1
  418. package/core/server/models/user.js +2 -2
  419. package/core/server/services/adapter-manager/index.js +2 -1
  420. package/core/server/services/comments/controller.js +11 -1
  421. package/core/server/services/comments/email-templates/new-comment-reply.hbs +9 -11
  422. package/core/server/services/comments/email-templates/new-comment-reply.txt.js +1 -1
  423. package/core/server/services/comments/email-templates/new-comment.hbs +8 -10
  424. package/core/server/services/comments/email-templates/new-comment.txt.js +1 -1
  425. package/core/server/services/comments/email-templates/report.hbs +7 -9
  426. package/core/server/services/comments/email-templates/report.txt.js +1 -1
  427. package/core/server/services/comments/emails.js +4 -4
  428. package/core/server/services/comments/index.js +5 -1
  429. package/core/server/services/comments/stats.js +44 -0
  430. package/core/server/services/mega/mega.js +1 -1
  431. package/core/server/services/mega/post-email-serializer.js +3 -3
  432. package/core/server/services/members/middleware.js +1 -1
  433. package/core/server/services/members/utils.js +1 -0
  434. package/core/server/services/settings/settings-service.js +3 -1
  435. package/core/server/services/url/Resources.js +1 -1
  436. package/core/server/web/admin/app.js +2 -1
  437. package/core/server/web/admin/controller.js +1 -2
  438. package/core/shared/config/defaults.json +7 -4
  439. package/core/shared/config/overrides.json +1 -2
  440. package/core/shared/labs.js +4 -3
  441. package/core/shared/settings-cache/cache.js +124 -87
  442. package/core/shared/settings-cache/index.js +6 -9
  443. package/loggingrc.js +1 -1
  444. package/package.json +30 -33
  445. package/yarn.lock +13987 -3229
  446. package/core/built/assets/chunk.3.33097bb5eb150719bdd2.js +0 -197
  447. package/core/built/assets/ghost-dark-1bdd57aba1fa4a23388121740454dab2.css +0 -1
  448. package/core/built/assets/vendor.min-3dd40d3052381526f38fd290d13baa47.js +0 -22339
  449. package/core/server/web/admin/views/default-prod.html +0 -63
  450. package/core/shared/html-to-plaintext.js +0 -80
@@ -1,5 +1,5 @@
1
1
  const membersService = require('../../../../../../services/members');
2
- const htmlToPlaintext = require('../../../../../../../shared/html-to-plaintext');
2
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
3
3
 
4
4
  // @TODO: reconsider the location of this - it's part of members and adds a property to the API
5
5
  const forPost = (attrs, frame) => {
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createIrreversibleMigration} = require('../../utils');
3
3
  const mobiledocLib = require('../../../../lib/mobiledoc');
4
- const htmlToPlaintext = require('../../../../../shared/html-to-plaintext');
4
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
5
5
 
6
6
  module.exports = createIrreversibleMigration(async (knex) => {
7
7
  logging.info('Starting re-generation of posts html.');
@@ -1,6 +1,6 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const urlUtils = require('../../../../../shared/url-utils');
3
- const htmlToPlaintext = require('../../../../../shared/html-to-plaintext');
3
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
4
4
  const mobiledocLib = require('../../../../lib/mobiledoc');
5
5
  const {createTransactionalMigration} = require('../../utils');
6
6
 
@@ -1,7 +1,7 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createIrreversibleMigration} = require('../../utils');
3
3
  const mobiledocLib = require('../../../../lib/mobiledoc');
4
- const htmlToPlaintext = require('../../../../../shared/html-to-plaintext');
4
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
5
5
 
6
6
  module.exports = createIrreversibleMigration(async (knex) => {
7
7
  logging.info(`Starting regeneration of posts HTML`);
@@ -0,0 +1,8 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('products', 'trial_days', {
4
+ type: 'integer',
5
+ unsigned: true,
6
+ nullable: false,
7
+ defaultTo: 0
8
+ });
@@ -0,0 +1,6 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('members_stripe_customers_subscriptions', 'trial_start_at', {
4
+ type: 'dateTime',
5
+ nullable: true
6
+ });
@@ -0,0 +1,6 @@
1
+ const {createAddColumnMigration} = require('../../utils');
2
+
3
+ module.exports = createAddColumnMigration('members_stripe_customers_subscriptions', 'trial_end_at', {
4
+ type: 'dateTime',
5
+ nullable: true
6
+ });
@@ -422,6 +422,7 @@ module.exports = {
422
422
  defaultTo: 'none',
423
423
  validations: {isIn: [['public', 'none']]}
424
424
  },
425
+ trial_days: {type: 'integer', unsigned: true, nullable: false, defaultTo: 0},
425
426
  monthly_price_id: {type: 'string', maxlength: 24, nullable: true},
426
427
  yearly_price_id: {type: 'string', maxlength: 24, nullable: true},
427
428
  description: {type: 'string', maxlength: 191, nullable: true},
@@ -584,6 +585,8 @@ module.exports = {
584
585
  updated_by: {type: 'string', maxlength: 24, nullable: true},
585
586
  mrr: {type: 'integer', unsigned: true, nullable: false, defaultTo: 0},
586
587
  offer_id: {type: 'string', maxlength: 24, nullable: true, unique: false, references: 'offers.id'},
588
+ trial_start_at: {type: 'dateTime', nullable: true},
589
+ trial_end_at: {type: 'dateTime', nullable: true},
587
590
  /* Below fields are now redundant as we link prie_id to stripe_prices table */
588
591
  plan_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
589
592
  plan_nickname: {type: 'string', maxlength: 50, nullable: false},
@@ -137,7 +137,14 @@ class GhostServer {
137
137
  * Called on SIGINT or SIGTERM
138
138
  */
139
139
  async shutdown(code = 0) {
140
+ // Prevent this function being run multiple times by checking whether we're
141
+ // already shutting down
142
+ if (this.isShuttingDown) {
143
+ return;
144
+ }
145
+
140
146
  try {
147
+ this.isShuttingDown = true;
141
148
  logging.warn(tpl(messages.ghostIsShuttingDown));
142
149
  await this.stop();
143
150
  setTimeout(() => {
@@ -1,6 +1,6 @@
1
1
  // # Base Model
2
2
  // This is the model from which all other Ghost models extend. The model is based on Bookshelf.Model, and provides
3
- // several basic behaviours such as UUIDs, as well as a set of Data methods for accessing information from the database.
3
+ // several basic behaviors such as UUIDs, as well as a set of Data methods for accessing information from the database.
4
4
  //
5
5
  // The models are internal to Ghost, only the API and some internal functions such as migration and import/export
6
6
  // accesses the models directly.
@@ -6,13 +6,13 @@ module.exports = function (Bookshelf) {
6
6
  getFilteredCollection: function getFilteredCollection(options) {
7
7
  const filteredCollection = this.forge();
8
8
 
9
- // Apply model-specific query behaviour
9
+ // Apply model-specific query behavior
10
10
  filteredCollection.applyCustomQuery(options);
11
11
 
12
- // Add Filter behaviour
12
+ // Add Filter behavior
13
13
  filteredCollection.applyDefaultAndCustomFilters(options);
14
14
 
15
- // Apply model-specific search behaviour
15
+ // Apply model-specific search behavior
16
16
  filteredCollection.applySearchQuery(options);
17
17
 
18
18
  return filteredCollection;
@@ -2,13 +2,29 @@ const ghostBookshelf = require('./base');
2
2
  const _ = require('lodash');
3
3
  const errors = require('@tryghost/errors');
4
4
  const tpl = require('@tryghost/tpl');
5
+ const {ValidationError} = require('@tryghost/errors');
5
6
 
6
7
  const messages = {
8
+ emptyComment: 'The body of a comment cannot be empty',
7
9
  commentNotFound: 'Comment could not be found',
8
10
  notYourCommentToEdit: 'You may only edit your own comments',
9
11
  notYourCommentToDestroy: 'You may only delete your own comments'
10
12
  };
11
13
 
14
+ /**
15
+ * Remove empty paragraps from the start and end
16
+ * + remove duplicate empty paragrapsh (only one empty line allowed)
17
+ */
18
+ function trimParagraphs(str) {
19
+ const paragraph = '<p></p>';
20
+ const escapedParagraph = '<p>\\s*?</p>';
21
+
22
+ const startReg = new RegExp('^(' + escapedParagraph + ')+');
23
+ const endReg = new RegExp('(' + escapedParagraph + ')+$');
24
+ const duplicates = new RegExp('(' + escapedParagraph + ')+');
25
+ return str.replace(startReg, '').replace(endReg, '').replace(duplicates, paragraph);
26
+ }
27
+
12
28
  const Comment = ghostBookshelf.Model.extend({
13
29
  tableName: 'comments',
14
30
 
@@ -49,20 +65,29 @@ const Comment = ghostBookshelf.Model.extend({
49
65
  if (this.hasChanged('html')) {
50
66
  const sanitizeHtml = require('sanitize-html');
51
67
 
52
- this.set('html', sanitizeHtml(this.get('html'), {
53
- allowedTags: ['p', 'br', 'a', 'blockquote'],
54
- allowedAttributes: {
55
- a: ['href', 'target', 'rel']
56
- },
57
- selfClosing: ['br'],
58
- // Enforce _blank and safe URLs
59
- transformTags: {
60
- a: sanitizeHtml.simpleTransform('a', {
61
- target: '_blank',
62
- rel: 'ugc noopener noreferrer nofollow'
63
- })
64
- }
65
- }));
68
+ const html = trimParagraphs(
69
+ sanitizeHtml(this.get('html'), {
70
+ allowedTags: ['p', 'br', 'a', 'blockquote'],
71
+ allowedAttributes: {
72
+ a: ['href', 'target', 'rel']
73
+ },
74
+ selfClosing: ['br'],
75
+ // Enforce _blank and safe URLs
76
+ transformTags: {
77
+ a: sanitizeHtml.simpleTransform('a', {
78
+ target: '_blank',
79
+ rel: 'ugc noopener noreferrer nofollow'
80
+ })
81
+ }
82
+ })
83
+ ).trim();
84
+
85
+ if (html.length === 0) {
86
+ throw new ValidationError({
87
+ message: tpl(messages.emptyComment)
88
+ });
89
+ }
90
+ this.set('html', html);
66
91
  }
67
92
  },
68
93
 
@@ -7,7 +7,7 @@ const {sequence} = require('@tryghost/promise');
7
7
  const tpl = require('@tryghost/tpl');
8
8
  const errors = require('@tryghost/errors');
9
9
  const nql = require('@tryghost/nql');
10
- const htmlToPlaintext = require('../../shared/html-to-plaintext');
10
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
11
11
  const ghostBookshelf = require('./base');
12
12
  const config = require('../../shared/config');
13
13
  const settingsCache = require('../../shared/settings-cache');
@@ -6,7 +6,8 @@ const Product = ghostBookshelf.Model.extend({
6
6
 
7
7
  defaults: {
8
8
  active: true,
9
- visibility: 'none'
9
+ visibility: 'none',
10
+ trial_days: 0
10
11
  },
11
12
 
12
13
  relationships: ['benefits'],
@@ -103,7 +103,7 @@ Tag = ghostBookshelf.Model.extend({
103
103
  // Support tag creation with `posts: [{..., tags: [{slug: 'new'}]}]`
104
104
  // In that situation we have a slug but no name so validation will fail
105
105
  // unless we set one automatically. Re-using slug for name matches our
106
- // opposite name->slug behaviour.
106
+ // opposite name->slug behavior.
107
107
  if (!newTag.get('name') && newTag.get('slug')) {
108
108
  this.set('name', newTag.get('slug'));
109
109
  }
@@ -234,7 +234,7 @@ User = ghostBookshelf.Model.extend({
234
234
  * - we do some pre-validation checks, because onValidate is called AFTER onSaving
235
235
  * - when importing, we set the password to a random uid and don't validate, just hash it and lock the user
236
236
  * - when importing with `importPersistUser` we check if the password is a bcrypt hash already and fall back to
237
- * normal behaviour if not (set random password, lock user, and hash password)
237
+ * normal behavior if not (set random password, lock user, and hash password)
238
238
  * - no validations should run, when importing
239
239
  */
240
240
  if (self.hasChanged('password')) {
@@ -242,7 +242,7 @@ User = ghostBookshelf.Model.extend({
242
242
 
243
243
  // CASE: import with `importPersistUser` should always be an bcrypt password already,
244
244
  // and won't re-hash or overwrite it.
245
- // In case the password is not bcrypt hashed we fall back to the standard behaviour.
245
+ // In case the password is not bcrypt hashed we fall back to the standard behavior.
246
246
  if (options.importPersistUser && this.get('password').match(/^\$2[ayb]\$.{56}$/i)) {
247
247
  return;
248
248
  }
@@ -15,11 +15,12 @@ const adapterManager = new AdapterManager({
15
15
  adapterManager.registerAdapter('storage', require('ghost-storage-base'));
16
16
  adapterManager.registerAdapter('scheduling', require('../../adapters/scheduling/SchedulingBase'));
17
17
  adapterManager.registerAdapter('sso', require('../../adapters/sso/Base'));
18
+ adapterManager.registerAdapter('cache', require('../../adapters/cache/Base'));
18
19
 
19
20
  module.exports = {
20
21
  /**
21
22
  *
22
- * @param {String} name - one of 'storage', 'scheduling', 'sso' etc. Or can contain a "resource" extension like "storage:image"
23
+ * @param {String} name - one of 'storage', 'scheduling', 'sso', 'cache' etc. Or can contain a "resource" extension like "storage:image"
23
24
  * @returns {Object} instance of an adapter
24
25
  */
25
26
  getAdapter(name) {
@@ -12,9 +12,11 @@ const messages = {
12
12
  module.exports = class CommentsController {
13
13
  /**
14
14
  * @param {import('./service')} service
15
+ * @param {import('./stats')} stats
15
16
  */
16
- constructor(service) {
17
+ constructor(service, stats) {
17
18
  this.service = service;
19
+ this.stats = stats;
18
20
  }
19
21
 
20
22
  /**
@@ -79,4 +81,12 @@ module.exports = class CommentsController {
79
81
  message: tpl(messages.cannotDestroyComments)
80
82
  });
81
83
  }
84
+
85
+ async count(frame) {
86
+ if (!frame?.data?.ids) {
87
+ return await this.stats.getAllCounts();
88
+ }
89
+
90
+ return await this.stats.getCountsByPost(frame.data.ids);
91
+ }
82
92
  };
@@ -106,7 +106,7 @@
106
106
  <div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 600px; padding: 30px 20px;">
107
107
 
108
108
  <!-- START CENTERED CONTAINER -->
109
- <span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Someone left a comment on your post</span>
109
+ <span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Someone just replied to your comment</span>
110
110
  <table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 8px;">
111
111
 
112
112
  <!-- START MAIN CONTENT AREA -->
@@ -116,20 +116,18 @@
116
116
  <tr>
117
117
  <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;">
118
118
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Hey there,</p>
119
- <p 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; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">Someone just replied to your comment on <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>.</p>
119
+ <p 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; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">Someone just replied to your comment on <a href="{{postUrl}}" target="_blank" style="font-weight: bold; text-decoration: underline;">{{postTitle}}</a>.</p>
120
120
 
121
- <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
121
+ <table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
123
123
  <tr>
124
124
  <td align="left" style="padding: 16px;">
125
125
  <table border="0" cellpadding="0" cellspacing="0">
126
126
  <tr>
127
- <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: 16px; padding-right: 8px;">
128
- <table border="0" cellpadding="0" cellspacing="0" width="44" height="44" style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px;">
129
- <tr>
130
- <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: 18px; color: #FFFFFF; text-align: center; font-weight: 500; height: 44px; width: 44px;">{{memberInitials}}</td>
131
- </tr>
132
- </table>
127
+ <td style="padding-right: 8px;">
128
+ <div style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px; 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: #FFFFFF; text-align: center; vertical-align: center; font-weight: 500; line-height: 42px;">
129
+ {{memberInitials}}
130
+ </div>
133
131
  </td>
134
132
  <td style="padding-right: 8px;">
135
133
  <p 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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
@@ -154,7 +152,7 @@
154
152
  <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
155
153
  <tbody>
156
154
  <tr>
157
- <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments-root" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comments</a> </td>
155
+ <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comments</a> </td>
158
156
  </tr>
159
157
  </tbody>
160
158
  </table>
@@ -164,7 +162,7 @@
164
162
  </table>
165
163
  <hr/>
166
164
  <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
167
- <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments-root</p>
165
+ <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments</p>
168
166
  </td>
169
167
  </tr>
170
168
 
@@ -4,7 +4,7 @@ module.exports = function (data) {
4
4
 
5
5
  Someone just replied to your comment on "${data.postTitle}"
6
6
 
7
- ${data.postUrl}#ghost-comments-root
7
+ ${data.postUrl}#ghost-comments
8
8
 
9
9
  ---
10
10
 
@@ -116,20 +116,18 @@
116
116
  <tr>
117
117
  <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;">
118
118
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Hey there,</p>
119
- <p 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; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">Someone just left a comment on your post <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>.</p>
119
+ <p 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; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">Someone just left a comment on your post <a href="{{postUrl}}" target="_blank" style="font-weight: bold; text-decoration: underline;">{{postTitle}}</a>.</p>
120
120
 
121
- <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
121
+ <table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
123
123
  <tr>
124
124
  <td align="left" style="padding: 16px;">
125
125
  <table border="0" cellpadding="0" cellspacing="0">
126
126
  <tr>
127
- <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: 16px; padding-right: 8px;">
128
- <table border="0" cellpadding="0" cellspacing="0" width="44" height="44" style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px;">
129
- <tr>
130
- <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: 18px; color: #FFFFFF; text-align: center; font-weight: 500; height: 44px; width: 44px;">{{memberInitials}}</td>
131
- </tr>
132
- </table>
127
+ <td style="padding-right: 8px;">
128
+ <div style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px; 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: #FFFFFF; text-align: center; vertical-align: center; font-weight: 500; line-height: 42px;">
129
+ {{memberInitials}}
130
+ </div>
133
131
  </td>
134
132
  <td style="padding-right: 8px;">
135
133
  <p 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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
@@ -154,7 +152,7 @@
154
152
  <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
155
153
  <tbody>
156
154
  <tr>
157
- <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments-root" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comments</a> </td>
155
+ <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comments</a> </td>
158
156
  </tr>
159
157
  </tbody>
160
158
  </table>
@@ -164,7 +162,7 @@
164
162
  </table>
165
163
  <hr/>
166
164
  <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
167
- <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments-root</p>
165
+ <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments</p>
168
166
  </td>
169
167
  </tr>
170
168
 
@@ -5,7 +5,7 @@ Hey there,
5
5
 
6
6
  Someone just posted a comment on your post "${data.postTitle}"
7
7
 
8
- ${data.postUrl}#ghost-comments-root
8
+ ${data.postUrl}#ghost-comments
9
9
 
10
10
  ---
11
11
 
@@ -118,18 +118,16 @@
118
118
  <p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 20px; color: #15212A; font-weight: bold; line-height: 25px; margin: 0; margin-bottom: 15px;">Hey there,</p>
119
119
  <p 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; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 32px;">{{reporter}} has reported the comment below on <a href="{{postUrl}}" target="_blank">{{postTitle}}</a>. This comment will remain visible until you choose to remove it, which can be done directly on the post.</p>
120
120
 
121
- <table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
121
+ <table width="100" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed; width: 100%; min-width: 100%; box-sizing: border-box; background: #F9F9FA; border-radius: 7px;">
122
122
  <tbody>
123
123
  <tr>
124
124
  <td align="left" style="padding: 16px;">
125
125
  <table border="0" cellpadding="0" cellspacing="0">
126
126
  <tr>
127
- <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: 16px; padding-right: 8px;">
128
- <table border="0" cellpadding="0" cellspacing="0" width="44" height="44" style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px;">
129
- <tr>
130
- <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: 18px; color: #FFFFFF; text-align: center; font-weight: 500; height: 44px; width: 44px;">{{memberInitials}}</td>
131
- </tr>
132
- </table>
127
+ <td style="padding-right: 8px;">
128
+ <div style="width: 44px; height: 44px; background-color: #15171A; border-radius: 999px; 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: #FFFFFF; text-align: center; vertical-align: center; font-weight: 500; line-height: 42px;">
129
+ {{memberInitials}}
130
+ </div>
133
131
  </td>
134
132
  <td style="padding-right: 8px;">
135
133
  <p 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; padding-right: 8px; padding: 0; margin: 0; color: #15171A; font-weight: 600;">{{memberName}}</p>
@@ -154,7 +152,7 @@
154
152
  <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
155
153
  <tbody>
156
154
  <tr>
157
- <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments-root" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comment</a> </td>
155
+ <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: 16px; vertical-align: top; background-color: {{accentColor}}; border-radius: 5px; text-align: center;"> <a href="{{postUrl}}#ghost-comments" target="_blank" style="display: inline-block; color: #ffffff; background-color: {{accentColor}}; border: solid 1px {{accentColor}}; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 16px; font-weight: normal; margin: 0; padding: 9px 22px 10px; border-color: {{accentColor}};">View comment</a> </td>
158
156
  </tr>
159
157
  </tbody>
160
158
  </table>
@@ -164,7 +162,7 @@
164
162
  </table>
165
163
  <hr/>
166
164
  <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; color: #3A464C; font-weight: normal; margin: 0; line-height: 25px; margin-bottom: 5px;">You can also copy & paste this URL into your browser:</p>
167
- <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments-root</p>
165
+ <p style="word-break: break-all; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 25px; margin-top:0; color: #3A464C;">{{postUrl}}#ghost-comments</p>
168
166
  </td>
169
167
  </tr>
170
168
 
@@ -7,7 +7,7 @@ ${data.reporter} has reported the comment below on ${data.postTitle}. This comme
7
7
  ${data.memberName} (${data.memberEmail}):
8
8
  ${data.commentText}
9
9
 
10
- ${data.postUrl}#ghost-comments-root
10
+ ${data.postUrl}#ghost-comments
11
11
 
12
12
  ---
13
13
 
@@ -1,7 +1,7 @@
1
1
  const {promises: fs} = require('fs');
2
2
  const path = require('path');
3
3
  const moment = require('moment');
4
- const htmlToPlaintext = require('../../../shared/html-to-plaintext');
4
+ const htmlToPlaintext = require('@tryghost/html-to-plaintext');
5
5
 
6
6
  class CommentsServiceEmails {
7
7
  constructor({config, logging, models, mailer, settingsCache, urlService, urlUtils}) {
@@ -26,7 +26,7 @@ class CommentsServiceEmails {
26
26
  }
27
27
 
28
28
  const to = author.get('email');
29
- const subject = '💬 You have a new comment on one of your posts';
29
+ const subject = '💬 New comment on your post: ' + post.get('title');
30
30
 
31
31
  const memberName = member.get('name') || 'Anonymous';
32
32
 
@@ -72,7 +72,7 @@ class CommentsServiceEmails {
72
72
  }
73
73
 
74
74
  const to = parentMember.get('email');
75
- const subject = '💬 You have a new reply on one of your comments';
75
+ const subject = '↪️ New reply to your comment on ' + this.settingsCache.get('title');
76
76
 
77
77
  const post = await this.models.Post.findOne({id: reply.get('post_id')});
78
78
  const member = await this.models.Member.findOne({id: reply.get('member_id')});
@@ -129,7 +129,7 @@ class CommentsServiceEmails {
129
129
  postTitle: post.get('title'),
130
130
  postUrl: this.urlService.getUrlByResourceId(post.get('id'), {absolute: true}),
131
131
  commentHtml: comment.get('html'),
132
- commentText: htmlToPlaintext.email(comment.get('html')),
132
+ commentText: htmlToPlaintext.comment(comment.get('html')),
133
133
  commentDate: moment(comment.get('created_at')).tz(this.settingsCache.get('timezone')).format('D MMM YYYY'),
134
134
 
135
135
  reporterName: reporter.name,
@@ -2,6 +2,7 @@ class CommentsServiceWrapper {
2
2
  init() {
3
3
  const CommentsService = require('./service');
4
4
  const CommentsController = require('./controller');
5
+ const CommentsStats = require('./stats');
5
6
 
6
7
  const config = require('../../../shared/config');
7
8
  const logging = require('@tryghost/logging');
@@ -12,6 +13,7 @@ class CommentsServiceWrapper {
12
13
  const urlService = require('../url');
13
14
  const urlUtils = require('../../../shared/url-utils');
14
15
  const membersService = require('../members');
16
+ const db = require('../../data/db');
15
17
 
16
18
  this.api = new CommentsService({
17
19
  config,
@@ -24,7 +26,9 @@ class CommentsServiceWrapper {
24
26
  contentGating: membersService.contentGating
25
27
  });
26
28
 
27
- this.controller = new CommentsController(this.api);
29
+ const stats = new CommentsStats({db});
30
+
31
+ this.controller = new CommentsController(this.api, stats);
28
32
  }
29
33
  }
30
34
 
@@ -0,0 +1,44 @@
1
+ module.exports = class CommentsStatsService {
2
+ constructor(deps) {
3
+ this.db = deps.db;
4
+ }
5
+
6
+ /**
7
+ * @param {string[]} ids - List of post ids to fetch counts for
8
+ * @returns {Promise<Object<string, number>>}
9
+ */
10
+ async getCountsByPost(ids) {
11
+ const results = await this.db.knex('comments')
12
+ .select(this.db.knex.raw(`COUNT(*) AS count, post_id`))
13
+ .groupBy('post_id')
14
+ .whereIn('post_id', ids);
15
+
16
+ const counts = ids.reduce((memo, id) => {
17
+ const result = results.find(x => x.post_id === id);
18
+ return {
19
+ ...memo,
20
+ [id]: result?.count || 0
21
+ };
22
+ }, {});
23
+
24
+ return counts;
25
+ }
26
+
27
+ /**
28
+ * @returns {Promise<Object<string, number>>}
29
+ */
30
+ async getAllCounts() {
31
+ const results = await this.db.knex('comments')
32
+ .select(this.db.knex.raw(`COUNT(*) AS count, post_id`))
33
+ .groupBy('post_id');
34
+
35
+ /** @type Object<string, number> */
36
+ let counts = {};
37
+
38
+ for (const row of results) {
39
+ counts[row.post_id] = row.count;
40
+ }
41
+
42
+ return counts;
43
+ }
44
+ };
@@ -104,7 +104,7 @@ const sendTestEmail = async (postModel, toEmails, memberSegment) => {
104
104
  };
105
105
  }));
106
106
 
107
- // enable tracking for previews to match real-world behaviour
107
+ // enable tracking for previews to match real-world behavior
108
108
  emailData.track_opens = !!settingsCache.get('email_track_opens');
109
109
 
110
110
  const response = await bulkEmailService.send(emailData, recipients, memberSegment);