ghost 5.46.1 → 5.47.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 (274) hide show
  1. package/MigratorConfig.js +1 -0
  2. package/components/tryghost-adapter-cache-memory-ttl-5.47.1.tgz +0 -0
  3. package/components/tryghost-adapter-cache-redis-5.47.1.tgz +0 -0
  4. package/components/{tryghost-adapter-manager-5.46.1.tgz → tryghost-adapter-manager-5.47.1.tgz} +0 -0
  5. package/components/{tryghost-announcement-bar-settings-5.46.1.tgz → tryghost-announcement-bar-settings-5.47.1.tgz} +0 -0
  6. package/components/tryghost-api-framework-5.47.1.tgz +0 -0
  7. package/components/tryghost-api-version-compatibility-service-5.47.1.tgz +0 -0
  8. package/components/{tryghost-audience-feedback-5.46.1.tgz → tryghost-audience-feedback-5.47.1.tgz} +0 -0
  9. package/components/tryghost-bootstrap-socket-5.47.1.tgz +0 -0
  10. package/components/tryghost-constants-5.47.1.tgz +0 -0
  11. package/components/tryghost-custom-theme-settings-service-5.47.1.tgz +0 -0
  12. package/components/tryghost-data-generator-5.47.1.tgz +0 -0
  13. package/components/{tryghost-domain-events-5.46.1.tgz → tryghost-domain-events-5.47.1.tgz} +0 -0
  14. package/components/tryghost-dynamic-routing-events-5.47.1.tgz +0 -0
  15. package/components/tryghost-email-analytics-provider-mailgun-5.47.1.tgz +0 -0
  16. package/components/tryghost-email-analytics-service-5.47.1.tgz +0 -0
  17. package/components/tryghost-email-content-generator-5.47.1.tgz +0 -0
  18. package/components/tryghost-email-events-5.47.1.tgz +0 -0
  19. package/components/tryghost-email-service-5.47.1.tgz +0 -0
  20. package/components/{tryghost-email-suppression-list-5.46.1.tgz → tryghost-email-suppression-list-5.47.1.tgz} +0 -0
  21. package/components/tryghost-event-aware-cache-wrapper-5.47.1.tgz +0 -0
  22. package/components/tryghost-express-dynamic-redirects-5.47.1.tgz +0 -0
  23. package/components/tryghost-external-media-inliner-5.47.1.tgz +0 -0
  24. package/components/tryghost-extract-api-key-5.47.1.tgz +0 -0
  25. package/components/{tryghost-html-to-plaintext-5.46.1.tgz → tryghost-html-to-plaintext-5.47.1.tgz} +0 -0
  26. package/components/tryghost-i18n-5.47.1.tgz +0 -0
  27. package/components/{tryghost-importer-handler-content-files-5.46.1.tgz → tryghost-importer-handler-content-files-5.47.1.tgz} +0 -0
  28. package/components/tryghost-importer-revue-5.47.1.tgz +0 -0
  29. package/components/tryghost-in-memory-repository-5.47.1.tgz +0 -0
  30. package/components/tryghost-job-manager-5.47.1.tgz +0 -0
  31. package/components/tryghost-link-redirects-5.47.1.tgz +0 -0
  32. package/components/tryghost-link-replacer-5.47.1.tgz +0 -0
  33. package/components/tryghost-link-tracking-5.47.1.tgz +0 -0
  34. package/components/{tryghost-magic-link-5.46.1.tgz → tryghost-magic-link-5.47.1.tgz} +0 -0
  35. package/components/tryghost-mailgun-client-5.47.1.tgz +0 -0
  36. package/components/tryghost-member-attribution-5.47.1.tgz +0 -0
  37. package/components/tryghost-member-events-5.47.1.tgz +0 -0
  38. package/components/tryghost-members-api-5.47.1.tgz +0 -0
  39. package/components/tryghost-members-csv-5.47.1.tgz +0 -0
  40. package/components/tryghost-members-events-service-5.47.1.tgz +0 -0
  41. package/components/tryghost-members-importer-5.47.1.tgz +0 -0
  42. package/components/tryghost-members-offers-5.47.1.tgz +0 -0
  43. package/components/tryghost-members-payments-5.47.1.tgz +0 -0
  44. package/components/tryghost-members-ssr-5.47.1.tgz +0 -0
  45. package/components/tryghost-members-stripe-service-5.47.1.tgz +0 -0
  46. package/components/{tryghost-mentions-email-report-5.46.1.tgz → tryghost-mentions-email-report-5.47.1.tgz} +0 -0
  47. package/components/tryghost-milestones-5.47.1.tgz +0 -0
  48. package/components/tryghost-minifier-5.47.1.tgz +0 -0
  49. package/components/tryghost-mw-api-version-mismatch-5.47.1.tgz +0 -0
  50. package/components/tryghost-mw-cache-control-5.47.1.tgz +0 -0
  51. package/components/tryghost-mw-error-handler-5.47.1.tgz +0 -0
  52. package/components/tryghost-mw-session-from-token-5.47.1.tgz +0 -0
  53. package/components/{tryghost-mw-update-user-last-seen-5.46.1.tgz → tryghost-mw-update-user-last-seen-5.47.1.tgz} +0 -0
  54. package/components/tryghost-mw-version-match-5.47.1.tgz +0 -0
  55. package/components/tryghost-mw-vhost-5.47.1.tgz +0 -0
  56. package/components/tryghost-oembed-service-5.47.1.tgz +0 -0
  57. package/components/tryghost-package-json-5.47.1.tgz +0 -0
  58. package/components/tryghost-post-revisions-5.47.1.tgz +0 -0
  59. package/components/tryghost-posts-service-5.47.1.tgz +0 -0
  60. package/components/tryghost-referrers-5.47.1.tgz +0 -0
  61. package/components/{tryghost-security-5.46.1.tgz → tryghost-security-5.47.1.tgz} +0 -0
  62. package/components/tryghost-session-service-5.47.1.tgz +0 -0
  63. package/components/tryghost-settings-path-manager-5.47.1.tgz +0 -0
  64. package/components/{tryghost-slack-notifications-5.46.1.tgz → tryghost-slack-notifications-5.47.1.tgz} +0 -0
  65. package/components/tryghost-staff-service-5.47.1.tgz +0 -0
  66. package/components/tryghost-stats-service-5.47.1.tgz +0 -0
  67. package/components/tryghost-tiers-5.47.1.tgz +0 -0
  68. package/components/tryghost-update-check-service-5.47.1.tgz +0 -0
  69. package/components/tryghost-verification-trigger-5.47.1.tgz +0 -0
  70. package/components/tryghost-version-notifications-data-service-5.47.1.tgz +0 -0
  71. package/components/{tryghost-webmentions-5.46.1.tgz → tryghost-webmentions-5.47.1.tgz} +0 -0
  72. package/content/themes/casper/assets/built/global.css +1 -1
  73. package/content/themes/casper/assets/built/global.css.map +1 -1
  74. package/content/themes/casper/assets/built/screen.css +1 -1
  75. package/content/themes/casper/assets/built/screen.css.map +1 -1
  76. package/content/themes/casper/assets/css/screen.css +18 -2
  77. package/content/themes/casper/package.json +1 -1
  78. package/core/boot.js +2 -2
  79. package/core/built/admin/assets/{chunk.604.e25172fcbbcc8ddcbdec.js → chunk.11.a4f9c30c0891933434bf.js} +4136 -4448
  80. package/core/built/admin/assets/{chunk.143.0ab429a53bdda025f009.js → chunk.143.70ebfe98c846ee93f854.js} +5 -5
  81. package/core/built/admin/assets/{chunk.178.3ec92986406c285cdd68.js → chunk.178.e3546b84ecb79aae1772.js} +4 -4
  82. package/core/built/admin/assets/{ghost-2e3d55ca4212a50f87aba88c26b55945.js → ghost-48e9d3e33a5bb943cd479b941dbfe62e.js} +444 -478
  83. package/core/built/admin/assets/ghost-a2e163a59a7ec0c8ceb97289113a9994.css +1 -0
  84. package/core/built/admin/assets/ghost-dark-f2d9fd8b4f7b41e98642a27afdc9b35a.css +1 -0
  85. package/core/built/admin/assets/{vendor-e3de790ee48087482d42c1686d5fefd9.js → vendor-39356907c39237f12c305e1d3ca46c85.js} +11 -11
  86. package/core/built/admin/index.html +6 -6
  87. package/core/frontend/public/ghost.min.css +1 -1
  88. package/core/frontend/services/admin-auth-assets/index.js +1 -1
  89. package/core/frontend/services/card-assets/{service.js → CardAssetService.js} +2 -2
  90. package/core/frontend/services/card-assets/index.js +1 -1
  91. package/core/frontend/services/comment-counts-assets/index.js +1 -1
  92. package/core/frontend/services/member-attribution-assets/index.js +1 -1
  93. package/core/frontend/services/routing/index.js +1 -1
  94. package/core/frontend/services/sitemap/{page-generator.js → PageMapGenerator.js} +1 -1
  95. package/core/frontend/services/sitemap/{post-generator.js → PostMapGenerator.js} +1 -1
  96. package/core/frontend/services/sitemap/{manager.js → SiteMapManager.js} +5 -5
  97. package/core/frontend/services/sitemap/{tag-generator.js → TagsMapGenerator.js} +1 -1
  98. package/core/frontend/services/sitemap/{user-generator.js → UserMapGenerator.js} +1 -1
  99. package/core/frontend/services/sitemap/handler.js +1 -1
  100. package/core/frontend/services/theme-engine/i18n/{theme-i18n.js → ThemeI18n.js} +1 -1
  101. package/core/frontend/services/theme-engine/i18n/index.js +1 -1
  102. package/core/frontend/web/routes.js +1 -1
  103. package/core/frontend/web/site.js +2 -2
  104. package/core/server/adapters/cache/Redis.js +1 -0
  105. package/core/server/adapters/scheduling/post-scheduling/index.js +1 -1
  106. package/core/server/adapters/scheduling/{SchedulingDefault.js → scheduling-default.js} +1 -1
  107. package/core/server/adapters/sso/{Default.js → DefaultSSOAdapter.js} +1 -1
  108. package/core/server/adapters/storage/LocalMediaStorage.js +2 -2
  109. package/core/server/api/endpoints/authentication.js +7 -3
  110. package/core/server/api/endpoints/invites.js +2 -2
  111. package/core/server/api/endpoints/users.js +1 -1
  112. package/core/server/api/endpoints/utils/serializers/output/members.js +1 -4
  113. package/core/server/api/endpoints/utils/validators/input/password_reset.js +1 -0
  114. package/core/server/data/importer/importers/data/{custom-theme-settings.js → CustomThemeSettingsImporter.js} +1 -1
  115. package/core/server/data/importer/importers/data/{newsletters.js → NewslettersImporter.js} +1 -1
  116. package/core/server/data/importer/importers/data/{posts.js → PostsImporter.js} +1 -1
  117. package/core/server/data/importer/importers/data/{products.js → ProductsImporter.js} +1 -1
  118. package/core/server/data/importer/importers/data/{revue-subscriber.js → RevueSubscriberImporter.js} +1 -1
  119. package/core/server/data/importer/importers/data/{roles.js → RolesImporter.js} +1 -1
  120. package/core/server/data/importer/importers/data/{settings.js → SettingsImporter.js} +1 -1
  121. package/core/server/data/importer/importers/data/{stripe-prices.js → StripePricesImporter.js} +1 -1
  122. package/core/server/data/importer/importers/data/{stripe-products.js → StripeProductsImporter.js} +1 -1
  123. package/core/server/data/importer/importers/data/{tags.js → TagsImporter.js} +1 -1
  124. package/core/server/data/importer/importers/data/{users.js → UsersImporter.js} +1 -1
  125. package/core/server/data/importer/importers/data/data-importer.js +11 -11
  126. package/core/server/data/migrations/hooks/migrate/afterEach.js +1 -0
  127. package/core/server/data/migrations/hooks/migrate/beforeEach.js +1 -0
  128. package/core/server/data/migrations/versions/4.4/02-migrate-members-signup-access.js +1 -1
  129. package/core/server/data/schema/fixtures/index.js +1 -1
  130. package/core/server/lib/image/{image-utils.js → ImageUtils.js} +4 -4
  131. package/core/server/lib/image/index.js +1 -1
  132. package/core/server/models/base/plugins/actions.js +104 -29
  133. package/core/server/models/base/plugins/bulk-operations.js +28 -7
  134. package/core/server/models/invite.js +10 -6
  135. package/core/server/models/post.js +21 -7
  136. package/core/server/services/{users.js → Users.js} +1 -1
  137. package/core/server/services/adapter-manager/index.js +2 -2
  138. package/core/server/services/auth/session/express-session.js +1 -1
  139. package/core/server/services/comments/{controller.js → CommentsController.js} +2 -2
  140. package/core/server/services/comments/{service.js → CommentsService.js} +1 -1
  141. package/core/server/services/comments/index.js +3 -3
  142. package/core/server/services/custom-redirects/index.js +1 -1
  143. package/core/server/services/email-analytics/index.js +1 -1
  144. package/core/server/services/email-service/{wrapper.js → EmailServiceWrapper.js} +2 -2
  145. package/core/server/services/email-service/index.js +1 -1
  146. package/core/server/services/explore/index.js +1 -1
  147. package/core/server/services/frontend-data-service/index.js +1 -1
  148. package/core/server/services/invites/{invites.js → Invites.js} +25 -10
  149. package/core/server/services/invites/index.js +3 -1
  150. package/core/server/services/mail/templates/invite-user-by-api-key.html +160 -0
  151. package/core/server/services/members/api.js +1 -1
  152. package/core/server/services/members/emails/signin.js +8 -8
  153. package/core/server/services/members/emails/signup-paid.js +7 -7
  154. package/core/server/services/members/emails/signup.js +9 -9
  155. package/core/server/services/members/emails/subscribe.js +9 -9
  156. package/core/server/services/members/emails/{updateEmail.js → update-email.js} +7 -7
  157. package/core/server/services/members/service.js +2 -2
  158. package/core/server/services/members/utils.js +1 -3
  159. package/core/server/services/newsletters/index.js +1 -1
  160. package/core/server/services/notifications/index.js +1 -1
  161. package/core/server/services/oembed/{twitter-embed.js → TwitterOEmbedProvider.js} +2 -1
  162. package/core/server/services/oembed/service.js +2 -2
  163. package/core/server/services/posts/posts-service.js +1 -1
  164. package/core/server/services/route-settings/index.js +3 -3
  165. package/core/server/services/settings/settings-service.js +1 -1
  166. package/core/server/services/settings-helpers/index.js +1 -1
  167. package/core/server/services/webhooks/listen.js +1 -1
  168. package/core/server/web/api/endpoints/admin/middleware.js +2 -0
  169. package/core/server/web/members/app.js +1 -1
  170. package/core/server/web/parent/frontend.js +1 -1
  171. package/core/shared/config/defaults.json +5 -5
  172. package/core/shared/labs.js +3 -4
  173. package/core/shared/settings-cache/index.js +1 -1
  174. package/package.json +156 -153
  175. package/yarn.lock +1033 -809
  176. package/components/tryghost-adapter-cache-memory-ttl-5.46.1.tgz +0 -0
  177. package/components/tryghost-adapter-cache-redis-5.46.1.tgz +0 -0
  178. package/components/tryghost-api-framework-5.46.1.tgz +0 -0
  179. package/components/tryghost-api-version-compatibility-service-5.46.1.tgz +0 -0
  180. package/components/tryghost-bootstrap-socket-5.46.1.tgz +0 -0
  181. package/components/tryghost-constants-5.46.1.tgz +0 -0
  182. package/components/tryghost-custom-theme-settings-service-5.46.1.tgz +0 -0
  183. package/components/tryghost-data-generator-5.46.1.tgz +0 -0
  184. package/components/tryghost-dynamic-routing-events-5.46.1.tgz +0 -0
  185. package/components/tryghost-email-analytics-provider-mailgun-5.46.1.tgz +0 -0
  186. package/components/tryghost-email-analytics-service-5.46.1.tgz +0 -0
  187. package/components/tryghost-email-content-generator-5.46.1.tgz +0 -0
  188. package/components/tryghost-email-events-5.46.1.tgz +0 -0
  189. package/components/tryghost-email-service-5.46.1.tgz +0 -0
  190. package/components/tryghost-event-aware-cache-wrapper-5.46.1.tgz +0 -0
  191. package/components/tryghost-express-dynamic-redirects-5.46.1.tgz +0 -0
  192. package/components/tryghost-external-media-inliner-5.46.1.tgz +0 -0
  193. package/components/tryghost-extract-api-key-5.46.1.tgz +0 -0
  194. package/components/tryghost-i18n-5.46.1.tgz +0 -0
  195. package/components/tryghost-importer-revue-5.46.1.tgz +0 -0
  196. package/components/tryghost-job-manager-5.46.1.tgz +0 -0
  197. package/components/tryghost-link-redirects-5.46.1.tgz +0 -0
  198. package/components/tryghost-link-replacer-5.46.1.tgz +0 -0
  199. package/components/tryghost-link-tracking-5.46.1.tgz +0 -0
  200. package/components/tryghost-mailgun-client-5.46.1.tgz +0 -0
  201. package/components/tryghost-member-attribution-5.46.1.tgz +0 -0
  202. package/components/tryghost-member-events-5.46.1.tgz +0 -0
  203. package/components/tryghost-members-api-5.46.1.tgz +0 -0
  204. package/components/tryghost-members-csv-5.46.1.tgz +0 -0
  205. package/components/tryghost-members-events-service-5.46.1.tgz +0 -0
  206. package/components/tryghost-members-importer-5.46.1.tgz +0 -0
  207. package/components/tryghost-members-offers-5.46.1.tgz +0 -0
  208. package/components/tryghost-members-payments-5.46.1.tgz +0 -0
  209. package/components/tryghost-members-ssr-5.46.1.tgz +0 -0
  210. package/components/tryghost-members-stripe-service-5.46.1.tgz +0 -0
  211. package/components/tryghost-milestones-5.46.1.tgz +0 -0
  212. package/components/tryghost-minifier-5.46.1.tgz +0 -0
  213. package/components/tryghost-mw-api-version-mismatch-5.46.1.tgz +0 -0
  214. package/components/tryghost-mw-cache-control-5.46.1.tgz +0 -0
  215. package/components/tryghost-mw-error-handler-5.46.1.tgz +0 -0
  216. package/components/tryghost-mw-session-from-token-5.46.1.tgz +0 -0
  217. package/components/tryghost-mw-version-match-5.46.1.tgz +0 -0
  218. package/components/tryghost-mw-vhost-5.46.1.tgz +0 -0
  219. package/components/tryghost-oembed-service-5.46.1.tgz +0 -0
  220. package/components/tryghost-package-json-5.46.1.tgz +0 -0
  221. package/components/tryghost-post-revisions-5.46.1.tgz +0 -0
  222. package/components/tryghost-posts-service-5.46.1.tgz +0 -0
  223. package/components/tryghost-referrers-5.46.1.tgz +0 -0
  224. package/components/tryghost-session-service-5.46.1.tgz +0 -0
  225. package/components/tryghost-settings-path-manager-5.46.1.tgz +0 -0
  226. package/components/tryghost-staff-service-5.46.1.tgz +0 -0
  227. package/components/tryghost-stats-service-5.46.1.tgz +0 -0
  228. package/components/tryghost-tiers-5.46.1.tgz +0 -0
  229. package/components/tryghost-update-check-service-5.46.1.tgz +0 -0
  230. package/components/tryghost-verification-trigger-5.46.1.tgz +0 -0
  231. package/components/tryghost-version-notifications-data-service-5.46.1.tgz +0 -0
  232. package/core/built/admin/assets/ghost-c3187d2bb18193c111d376a110c8c80d.css +0 -1
  233. package/core/built/admin/assets/ghost-dark-4948d897bf814756a3870ef564ba6a6d.css +0 -1
  234. /package/core/built/admin/assets/{chunk.604.e25172fcbbcc8ddcbdec.js.LICENSE.txt → chunk.11.a4f9c30c0891933434bf.js.LICENSE.txt} +0 -0
  235. /package/core/frontend/services/admin-auth-assets/{service.js → AdminAuthAssetsService.js} +0 -0
  236. /package/core/frontend/services/comment-counts-assets/{service.js → CommentCountsAssetsService.js} +0 -0
  237. /package/core/frontend/services/member-attribution-assets/{service.js → MemberAttributionAssetsService.js} +0 -0
  238. /package/core/frontend/services/routing/{router-manager.js → RouterManager.js} +0 -0
  239. /package/core/frontend/services/sitemap/{base-generator.js → BaseSiteMapGenerator.js} +0 -0
  240. /package/core/frontend/services/sitemap/{index-generator.js → SiteMapIndexGenerator.js} +0 -0
  241. /package/core/frontend/services/theme-engine/i18n/{i18n.js → I18n.js} +0 -0
  242. /package/core/server/{ghost-server.js → GhostServer.js} +0 -0
  243. /package/core/server/adapters/cache/{Memory.js → MemoryCache.js} +0 -0
  244. /package/core/server/adapters/cache/{MemoryTTL.js → memory-ttl.js} +0 -0
  245. /package/core/server/adapters/scheduling/post-scheduling/{post-scheduler.js → PostScheduler.js} +0 -0
  246. /package/core/server/adapters/scheduling/{SchedulingBase.js → scheduling-base.js} +0 -0
  247. /package/core/server/adapters/sso/{Base.js → SSOBase.js} +0 -0
  248. /package/core/server/data/db/{state-manager.js → DatabaseStateManager.js} +0 -0
  249. /package/core/server/data/importer/importers/data/{base.js → Base.js} +0 -0
  250. /package/core/server/data/schema/fixtures/{fixture-manager.js → FixtureManager.js} +0 -0
  251. /package/core/server/lib/image/{blog-icon.js → BlogIcon.js} +0 -0
  252. /package/core/server/lib/image/{cached-image-size-from-url.js → CachedImageSizeFromUrl.js} +0 -0
  253. /package/core/server/lib/image/{gravatar.js → Gravatar.js} +0 -0
  254. /package/core/server/lib/image/{image-size.js → ImageSize.js} +0 -0
  255. /package/core/server/services/auth/session/{store.js → SessionStore.js} +0 -0
  256. /package/core/server/services/comments/{emails.js → CommentsServiceEmails.js} +0 -0
  257. /package/core/server/services/comments/{stats.js → CommentsStatsService.js} +0 -0
  258. /package/core/server/services/custom-redirects/{api.js → CustomRedirectsAPI.js} +0 -0
  259. /package/core/server/services/email-analytics/{wrapper.js → EmailAnalyticsServiceWrapper.js} +0 -0
  260. /package/core/server/services/explore/{service.js → ExploreService.js} +0 -0
  261. /package/core/server/services/frontend-data-service/{frontend-data-service.js → FrontendDataService.js} +0 -0
  262. /package/core/server/services/members/{config.js → MembersConfigProvider.js} +0 -0
  263. /package/core/server/services/members/stats/{members-stats.js → MembersStats.js} +0 -0
  264. /package/core/server/services/newsletters/{service.js → NewslettersService.js} +0 -0
  265. /package/core/server/services/notifications/{notifications.js → Notifications.js} +0 -0
  266. /package/core/server/services/oembed/{nft-oembed.js → NFTOEmbedProvider.js} +0 -0
  267. /package/core/server/services/posts/stats/{post-stats.js → PostStats.js} +0 -0
  268. /package/core/server/services/route-settings/{default-settings-manager.js → DefaultSettingsManager.js} +0 -0
  269. /package/core/server/services/route-settings/{route-settings.js → RouteSettings.js} +0 -0
  270. /package/core/server/services/route-settings/{settings-loader.js → SettingsLoader.js} +0 -0
  271. /package/core/server/services/settings/{settings-bread-service.js → SettingsBREADService.js} +0 -0
  272. /package/core/server/services/settings-helpers/{settings-helpers.js → SettingsHelpers.js} +0 -0
  273. /package/core/server/services/webhooks/{trigger.js → WebhookTrigger.js} +0 -0
  274. /package/core/shared/settings-cache/{cache.js → CacheManager.js} +0 -0
@@ -5,17 +5,17 @@ const {IncorrectUsageError} = require('@tryghost/errors');
5
5
  const debug = require('@tryghost/debug')('importer:data');
6
6
  const {sequence} = require('@tryghost/promise');
7
7
  const models = require('../../../../models');
8
- const PostsImporter = require('./posts');
9
- const TagsImporter = require('./tags');
10
- const SettingsImporter = require('./settings');
11
- const UsersImporter = require('./users');
12
- const NewslettersImporter = require('./newsletters');
13
- const ProductsImporter = require('./products');
14
- const StripeProductsImporter = require('./stripe-products');
15
- const StripePricesImporter = require('./stripe-prices');
16
- const CustomThemeSettingsImporter = require('./custom-theme-settings');
17
- const RevueSubscriberImporter = require('./revue-subscriber');
18
- const RolesImporter = require('./roles');
8
+ const PostsImporter = require('./PostsImporter');
9
+ const TagsImporter = require('./TagsImporter');
10
+ const SettingsImporter = require('./SettingsImporter');
11
+ const UsersImporter = require('./UsersImporter');
12
+ const NewslettersImporter = require('./NewslettersImporter');
13
+ const ProductsImporter = require('./ProductsImporter');
14
+ const StripeProductsImporter = require('./StripeProductsImporter');
15
+ const StripePricesImporter = require('./StripePricesImporter');
16
+ const CustomThemeSettingsImporter = require('./CustomThemeSettingsImporter');
17
+ const RevueSubscriberImporter = require('./RevueSubscriberImporter');
18
+ const RolesImporter = require('./RolesImporter');
19
19
  const {slugify} = require('@tryghost/string/lib');
20
20
 
21
21
  let importers = {};
@@ -1,3 +1,4 @@
1
+ /* eslint-disable ghost/filenames/match-regex */
1
2
  const Promise = require('bluebird');
2
3
 
3
4
  module.exports = function afterEach() {
@@ -1,3 +1,4 @@
1
+ /* eslint-disable ghost/filenames/match-regex */
1
2
  const Promise = require('bluebird');
2
3
 
3
4
  module.exports = function beforeEach() {
@@ -1,6 +1,6 @@
1
1
  const logging = require('@tryghost/logging');
2
2
  const {createTransactionalMigration} = require('../../utils');
3
- const MembersConfigProvider = require('../../../../services/members/config');
3
+ const MembersConfigProvider = require('../../../../services/members/MembersConfigProvider');
4
4
  const settingsCache = require('../../../../../shared/settings-cache');
5
5
  const config = require('../../../../../shared/config');
6
6
 
@@ -1,4 +1,4 @@
1
- const FixtureManager = require('./fixture-manager');
1
+ const FixtureManager = require('./FixtureManager');
2
2
  const config = require('../../../../shared/config');
3
3
 
4
4
  const fixturePath = config.get('paths').fixtures;
@@ -1,7 +1,7 @@
1
- const BlogIcon = require('./blog-icon');
2
- const CachedImageSizeFromUrl = require('./cached-image-size-from-url');
3
- const Gravatar = require('./gravatar');
4
- const ImageSize = require('./image-size');
1
+ const BlogIcon = require('./BlogIcon');
2
+ const CachedImageSizeFromUrl = require('./CachedImageSizeFromUrl');
3
+ const Gravatar = require('./Gravatar');
4
+ const ImageSize = require('./ImageSize');
5
5
 
6
6
  class ImageUtils {
7
7
  constructor({config, urlUtils, settingsCache, storageUtils, storage, validator, request, cacheStore}) {
@@ -5,7 +5,7 @@ const storageUtils = require('../../adapters/storage/utils');
5
5
  const validator = require('@tryghost/validator');
6
6
  const config = require('../../../shared/config');
7
7
  const settingsCache = require('../../../shared/settings-cache');
8
- const ImageUtils = require('./image-utils');
8
+ const ImageUtils = require('./ImageUtils');
9
9
 
10
10
  const adapterManager = require('../../services/adapter-manager');
11
11
 
@@ -6,6 +6,54 @@ const logging = require('@tryghost/logging');
6
6
  * @param {import('bookshelf')} Bookshelf
7
7
  */
8
8
  module.exports = function (Bookshelf) {
9
+ const insertAction = (data, options) => {
10
+ // CASE: model does not support action for target event
11
+ if (!data) {
12
+ return;
13
+ }
14
+
15
+ const insert = (action) => {
16
+ Bookshelf.model('Action')
17
+ .add(action, {autoRefresh: false})
18
+ .catch((err) => {
19
+ if (_.isArray(err)) {
20
+ err = err[0];
21
+ }
22
+
23
+ logging.error(new errors.InternalServerError({
24
+ err
25
+ }));
26
+ });
27
+ };
28
+
29
+ if (options.transacting) {
30
+ options.transacting.once('committed', (committed) => {
31
+ if (!committed) {
32
+ return;
33
+ }
34
+
35
+ insert(data);
36
+ });
37
+ } else {
38
+ insert(data);
39
+ }
40
+ };
41
+
42
+ // We need this addAction accessible from the static model and instances
43
+ const addAction = (model, event, options) => {
44
+ if (!model.wasChanged()) {
45
+ return;
46
+ }
47
+
48
+ // CASE: model does not support actions at all
49
+ if (!model.getAction) {
50
+ return;
51
+ }
52
+
53
+ const data = model.getAction(event, options);
54
+ insertAction(data, options);
55
+ };
56
+
9
57
  Bookshelf.Model = Bookshelf.Model.extend({
10
58
  /**
11
59
  * Constructs data to be stored in the database with info
@@ -33,7 +81,9 @@ module.exports = function (Bookshelf) {
33
81
  return;
34
82
  }
35
83
 
36
- let context = {};
84
+ let context = {
85
+ action_name: options.actionName
86
+ };
37
87
 
38
88
  if (this.actionsExtraContext && Array.isArray(this.actionsExtraContext)) {
39
89
  for (const c of this.actionsExtraContext) {
@@ -74,48 +124,73 @@ module.exports = function (Bookshelf) {
74
124
  *
75
125
  * We could embed adding actions more nicely in the future e.g. plugin.
76
126
  */
77
- addAction: (model, event, options) => {
78
- if (!model.wasChanged()) {
127
+ addAction
128
+ }, {
129
+ addAction,
130
+ async addActions(event, ids, options) {
131
+ if (ids.length === 1) {
132
+ // We want to store an event for a single model in the actions table
133
+ // This is so we can include the name
134
+ const model = await this.findOne({[options.column ?? 'id']: ids[0]}, {require: true, transacting: options.transacting, context: {internal: true}});
135
+ this.addAction(model, event, options);
79
136
  return;
80
137
  }
81
138
 
82
- // CASE: model does not support actions at all
83
- if (!model.getAction) {
139
+ const existingAction = this.getBulkAction(event, ids.length, options);
140
+ insertAction(existingAction, options);
141
+ },
142
+
143
+ /**
144
+ * Constructs data to be stored in the database with info
145
+ * on particular actions
146
+ */
147
+ getBulkAction(event, count, options) {
148
+ const actor = this.prototype.getActor(options);
149
+
150
+ // @NOTE: we ignore internal updates (`options.context.internal`) for now
151
+ if (!actor) {
152
+ return;
153
+ }
154
+
155
+ if (!this.prototype.actionsCollectCRUD) {
84
156
  return;
85
157
  }
86
158
 
87
- const existingAction = model.getAction(event, options);
159
+ let resourceType = this.prototype.actionsResourceType;
160
+
161
+ if (typeof resourceType === 'function') {
162
+ resourceType = resourceType.bind(this)();
163
+ }
88
164
 
89
- // CASE: model does not support action for target event
90
- if (!existingAction) {
165
+ if (!resourceType) {
91
166
  return;
92
167
  }
93
168
 
94
- const insert = (action) => {
95
- Bookshelf.model('Action')
96
- .add(action, {autoRefresh: false})
97
- .catch((err) => {
98
- if (_.isArray(err)) {
99
- err = err[0];
100
- }
101
-
102
- logging.error(new errors.InternalServerError({
103
- err
104
- }));
105
- });
169
+ let context = {
170
+ count,
171
+ action_name: options.actionName
106
172
  };
107
173
 
108
- if (options.transacting) {
109
- options.transacting.once('committed', (committed) => {
110
- if (!committed) {
111
- return;
112
- }
174
+ if (this.getBulkActionExtraContext && typeof this.getBulkActionExtraContext === 'function') {
175
+ context = {
176
+ ...context,
177
+ ...this.getBulkActionExtraContext.bind(this)(options)
178
+ };
179
+ }
113
180
 
114
- insert(existingAction);
115
- });
116
- } else {
117
- insert(existingAction);
181
+ const data = {
182
+ event,
183
+ resource_id: null,
184
+ resource_type: resourceType,
185
+ actor_id: actor.id,
186
+ actor_type: actor.type
187
+ };
188
+
189
+ if (context && Object.keys(context).length) {
190
+ data.context = context;
118
191
  }
192
+
193
+ return data;
119
194
  }
120
195
  });
121
196
  };
@@ -60,7 +60,7 @@ async function editSingle(knex, table, id, options) {
60
60
  if (options.transacting) {
61
61
  k = k.transacting(options.transacting);
62
62
  }
63
- await k.where('id', id).update(options.data);
63
+ await k.where(options.column ?? 'id', id).update(options.data);
64
64
  }
65
65
 
66
66
  async function editMultiple(knex, table, chunk, options) {
@@ -68,7 +68,7 @@ async function editMultiple(knex, table, chunk, options) {
68
68
  if (options.transacting) {
69
69
  k = k.transacting(options.transacting);
70
70
  }
71
- await k.whereIn('id', chunk).update(options.data);
71
+ await k.whereIn(options.column ?? 'id', chunk).update(options.data);
72
72
  }
73
73
 
74
74
  async function delSingle(knex, table, id, options) {
@@ -112,23 +112,44 @@ module.exports = function (Bookshelf) {
112
112
  return insert(Bookshelf.knex, tableName, data, options);
113
113
  },
114
114
 
115
- bulkEdit: async function bulkEdit(data, tableName, options = {}) {
115
+ /**
116
+ *
117
+ * @param {*} ids
118
+ * @param {*} tableName
119
+ * @param {object} options
120
+ * @param {object} [options.data] Data change you want to apply to the rows
121
+ * @param {string} [options.column] Update the rows where this column equals the ids (defaults to 'id')
122
+ * @returns
123
+ */
124
+ bulkEdit: async function bulkEdit(ids, tableName, options = {}) {
116
125
  tableName = tableName || this.prototype.tableName;
117
126
 
118
- return await edit(Bookshelf.knex, tableName, data, options);
127
+ const result = await edit(Bookshelf.knex, tableName, ids, options);
128
+
129
+ if (result.successful > 0 && tableName === this.prototype.tableName) {
130
+ await this.addActions('edited', ids, options);
131
+ }
132
+
133
+ return result;
119
134
  },
120
135
 
121
136
  /**
122
137
  *
123
- * @param {string[]} data List of ids to delete
138
+ * @param {string[]} ids List of ids to delete
124
139
  * @param {*} tableName
125
140
  * @param {Object} [options]
126
141
  * @param {string} [options.column] Delete the rows where this column equals the ids in `data` (defaults to 'id')
127
142
  * @returns
128
143
  */
129
- bulkDestroy: async function bulkDestroy(data, tableName, options = {}) {
144
+ bulkDestroy: async function bulkDestroy(ids, tableName, options = {}) {
130
145
  tableName = tableName || this.prototype.tableName;
131
- return await del(Bookshelf.knex, tableName, data, options);
146
+
147
+ if (tableName === this.prototype.tableName) {
148
+ // Needs to happen before, otherwise we cannot fetch the names of the deleted items
149
+ await this.addActions('deleted', ids, options);
150
+ }
151
+
152
+ return await del(Bookshelf.knex, tableName, ids, options);
132
153
  }
133
154
  });
134
155
  };
@@ -87,12 +87,16 @@ Invite = ghostBookshelf.Model.extend({
87
87
  }
88
88
 
89
89
  let allowed = [];
90
-
91
- if (_.some(loadedPermissions.user.roles, {name: 'Owner'}) ||
92
- _.some(loadedPermissions.user.roles, {name: 'Administrator'})) {
93
- allowed = ['Administrator', 'Editor', 'Author', 'Contributor'];
94
- } else if (_.some(loadedPermissions.user.roles, {name: 'Editor'})) {
95
- allowed = ['Author', 'Contributor'];
90
+ if (loadedPermissions.user) {
91
+ if (_.some(loadedPermissions.user.roles, {name: 'Owner'}) ||
92
+ _.some(loadedPermissions.user.roles, {name: 'Administrator'})) {
93
+ allowed = ['Administrator', 'Editor', 'Author', 'Contributor'];
94
+ } else if (_.some(loadedPermissions.user.roles, {name: 'Editor'})) {
95
+ allowed = ['Author', 'Contributor'];
96
+ }
97
+ }
98
+ if (loadedPermissions.apiKey) {
99
+ allowed = ['Editor', 'Author', 'Contributor'];
96
100
  }
97
101
 
98
102
  if (allowed.indexOf(roleToInvite.get('name')) === -1) {
@@ -19,7 +19,7 @@ const urlUtils = require('../../shared/url-utils');
19
19
  const {Tag} = require('./tag');
20
20
  const {Newsletter} = require('./newsletter');
21
21
  const {BadRequestError} = require('@tryghost/errors');
22
- const PostRevisions = require('@tryghost/post-revisions');
22
+ const {PostRevisions} = require('@tryghost/post-revisions');
23
23
  const labs = require('../../shared/labs');
24
24
 
25
25
  const messages = {
@@ -36,7 +36,7 @@ const messages = {
36
36
  };
37
37
 
38
38
  const MOBILEDOC_REVISIONS_COUNT = 10;
39
- const POST_REVISIONS_COUNT = 10;
39
+ const POST_REVISIONS_COUNT = 25;
40
40
  const POST_REVISIONS_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
41
41
  const ALL_STATUSES = ['published', 'draft', 'scheduled', 'sent'];
42
42
 
@@ -915,7 +915,7 @@ Post = ghostBookshelf.Model.extend({
915
915
  const revisionModels = await ghostBookshelf.model('PostRevision')
916
916
  .findAll(Object.assign({
917
917
  filter: `post_id:${model.id}`,
918
- columns: ['id', 'lexical', 'created_at', 'author_id', 'title', 'reason', 'post_status', 'created_at_ts']
918
+ columns: ['id', 'lexical', 'created_at', 'author_id', 'title', 'reason', 'post_status', 'created_at_ts', 'feature_image']
919
919
  }, _.pick(options, 'transacting')));
920
920
 
921
921
  const revisions = revisionModels.toJSON();
@@ -935,7 +935,9 @@ Post = ghostBookshelf.Model.extend({
935
935
  // This can be refactored once we have the status stored in each revision
936
936
  const revisionOptions = {
937
937
  forceRevision: options.save_revision,
938
- isPublished: newStatus === 'published'
938
+ isPublished: newStatus === 'published',
939
+ newStatus,
940
+ olderStatus
939
941
  };
940
942
  const newRevisions = await postRevisions.getRevisions(current, revisions, revisionOptions);
941
943
  model.set('post_revisions', newRevisions);
@@ -1127,6 +1129,16 @@ Post = ghostBookshelf.Model.extend({
1127
1129
  return filter;
1128
1130
  }
1129
1131
  }, {
1132
+ getBulkActionExtraContext: function (options) {
1133
+ if (options && options.filter && options.filter.includes('type:page')) {
1134
+ return {
1135
+ type: 'page'
1136
+ };
1137
+ }
1138
+ return {
1139
+ type: 'post'
1140
+ };
1141
+ },
1130
1142
  allowedFormats: ['mobiledoc', 'lexical', 'html', 'plaintext'],
1131
1143
 
1132
1144
  orderDefaultOptions: function orderDefaultOptions() {
@@ -1234,9 +1246,11 @@ Post = ghostBookshelf.Model.extend({
1234
1246
  * **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
1235
1247
  */
1236
1248
  findOne: function findOne(data = {}, options = {}) {
1237
- // @TODO: remove when we drop v0.1
1238
- if (!options.filter && !data.status) {
1239
- data.status = 'published';
1249
+ if (!options.context || !options.context.internal) {
1250
+ // @TODO: remove when we drop v0.1
1251
+ if (!options.filter && !data.status) {
1252
+ data.status = 'published';
1253
+ }
1240
1254
  }
1241
1255
 
1242
1256
  if (data.status === 'all') {
@@ -4,7 +4,7 @@ const path = require('path');
4
4
  /**
5
5
  * @TODO: pass these in as dependencies
6
6
  */
7
- const PostRevisions = require('@tryghost/post-revisions');
7
+ const {PostRevisions} = require('@tryghost/post-revisions');
8
8
  const labs = require('../../shared/labs');
9
9
 
10
10
  /**
@@ -13,8 +13,8 @@ const adapterManager = new AdapterManager({
13
13
  });
14
14
 
15
15
  adapterManager.registerAdapter('storage', require('ghost-storage-base'));
16
- adapterManager.registerAdapter('scheduling', require('../../adapters/scheduling/SchedulingBase'));
17
- adapterManager.registerAdapter('sso', require('../../adapters/sso/Base'));
16
+ adapterManager.registerAdapter('scheduling', require('../../adapters/scheduling/scheduling-base'));
17
+ adapterManager.registerAdapter('sso', require('../../adapters/sso/SSOBase'));
18
18
  adapterManager.registerAdapter('cache', require('@tryghost/adapter-base-cache'));
19
19
 
20
20
  module.exports = {
@@ -6,7 +6,7 @@ const settingsCache = require('../../../../shared/settings-cache');
6
6
  const models = require('../../../models');
7
7
  const urlUtils = require('../../../../shared/url-utils');
8
8
 
9
- const SessionStore = require('./store');
9
+ const SessionStore = require('./SessionStore');
10
10
  const sessionStore = new SessionStore(models.Session);
11
11
 
12
12
  let unoExpressSessionMiddleware;
@@ -15,8 +15,8 @@ const messages = {
15
15
 
16
16
  module.exports = class CommentsController {
17
17
  /**
18
- * @param {import('./service')} service
19
- * @param {import('./stats')} stats
18
+ * @param {import('./CommentsService')} service
19
+ * @param {import('./CommentsStatsService')} stats
20
20
  */
21
21
  constructor(service, stats) {
22
22
  this.service = service;
@@ -25,7 +25,7 @@ class CommentsService {
25
25
  /** @private */
26
26
  this.contentGating = contentGating;
27
27
 
28
- const Emails = require('./emails');
28
+ const Emails = require('./CommentsServiceEmails');
29
29
  /** @private */
30
30
  this.emails = new Emails({
31
31
  config,
@@ -1,8 +1,8 @@
1
1
  class CommentsServiceWrapper {
2
2
  init() {
3
- const CommentsService = require('./service');
4
- const CommentsController = require('./controller');
5
- const CommentsStats = require('./stats');
3
+ const CommentsService = require('./CommentsService');
4
+ const CommentsController = require('./CommentsController');
5
+ const CommentsStats = require('./CommentsStatsService');
6
6
 
7
7
  const config = require('../../../shared/config');
8
8
  const logging = require('@tryghost/logging');
@@ -2,7 +2,7 @@ const config = require('../../../shared/config');
2
2
  const urlUtils = require('../../../shared/url-utils');
3
3
 
4
4
  const DynamicRedirectManager = require('@tryghost/express-dynamic-redirects');
5
- const CustomRedirectsAPI = require('./api');
5
+ const CustomRedirectsAPI = require('./CustomRedirectsAPI');
6
6
  const validation = require('./validation');
7
7
  const {getBackupRedirectsFilePath} = require('./utils');
8
8
 
@@ -1,3 +1,3 @@
1
- const EmailAnalyticsServiceWrapper = require('./wrapper');
1
+ const EmailAnalyticsServiceWrapper = require('./EmailAnalyticsServiceWrapper');
2
2
 
3
3
  module.exports = new EmailAnalyticsServiceWrapper();
@@ -1,5 +1,5 @@
1
1
  const logging = require('@tryghost/logging');
2
- const url = require('../../../server/api/endpoints/utils/serializers/output/utils/url');
2
+ const url = require('../../api/endpoints/utils/serializers/output/utils/url');
3
3
 
4
4
  class EmailServiceWrapper {
5
5
  getPostUrl(post) {
@@ -18,7 +18,7 @@ class EmailServiceWrapper {
18
18
  const MailgunClient = require('@tryghost/mailgun-client');
19
19
  const configService = require('../../../shared/config');
20
20
  const settingsCache = require('../../../shared/settings-cache');
21
- const settingsHelpers = require('../../services/settings-helpers');
21
+ const settingsHelpers = require('../settings-helpers');
22
22
  const jobsService = require('../jobs');
23
23
  const membersService = require('../members');
24
24
  const db = require('../../data/db');
@@ -1,3 +1,3 @@
1
- const EmailServiceWrapper = require('./wrapper');
1
+ const EmailServiceWrapper = require('./EmailServiceWrapper');
2
2
 
3
3
  module.exports = new EmailServiceWrapper();
@@ -1,4 +1,4 @@
1
- const ExploreService = require('./service');
1
+ const ExploreService = require('./ExploreService');
2
2
 
3
3
  const MembersService = require('../members');
4
4
  const PostsService = require('../posts/posts-service')();
@@ -1,5 +1,5 @@
1
1
  const models = require('../../models');
2
- const FrontendDataService = require('./frontend-data-service');
2
+ const FrontendDataService = require('./FrontendDataService');
3
3
 
4
4
  module.exports.init = () => {
5
5
  return new FrontendDataService({IntegrationModel: models.Integration});
@@ -4,6 +4,7 @@ const logging = require('@tryghost/logging');
4
4
 
5
5
  const messages = {
6
6
  invitedByName: '{invitedByName} has invited you to join {blogName}',
7
+ invitedByNoName: 'You have been invited to join {blogName}',
7
8
  errorSendingEmail: {
8
9
  error: 'Error sending email: {message}',
9
10
  help: 'Please check your email settings and resend the invitation.'
@@ -11,8 +12,9 @@ const messages = {
11
12
  };
12
13
 
13
14
  class Invites {
14
- constructor({settingsCache, mailService, urlUtils}) {
15
+ constructor({settingsCache, settingsHelpers, mailService, urlUtils}) {
15
16
  this.settingsCache = settingsCache;
17
+ this.settingsHelpers = settingsHelpers;
16
18
  this.mailService = mailService;
17
19
  this.urlUtils = urlUtils;
18
20
  }
@@ -37,15 +39,26 @@ class Invites {
37
39
 
38
40
  const adminUrl = this.urlUtils.urlFor('admin', true);
39
41
 
40
- emailData = {
41
- blogName: this.settingsCache.get('title'),
42
- invitedByName: user.name,
43
- invitedByEmail: user.email,
44
- resetLink: this.urlUtils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/'),
45
- recipientEmail: invite.get('email')
46
- };
42
+ if (user.name || user.email) {
43
+ emailData = {
44
+ blogName: this.settingsCache.get('title'),
45
+ invitedByName: user.name,
46
+ invitedByEmail: user.email,
47
+ resetLink: this.urlUtils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/'),
48
+ recipientEmail: invite.get('email')
49
+ };
50
+
51
+ return this.mailService.utils.generateContent({data: emailData, template: 'invite-user'});
52
+ } else {
53
+ emailData = {
54
+ blogName: this.settingsCache.get('title'),
55
+ invitedByEmail: `ghost@${this.settingsHelpers.getDefaultEmailDomain()}`,
56
+ resetLink: this.urlUtils.urlJoin(adminUrl, 'signup', security.url.encodeBase64(invite.get('token')), '/'),
57
+ recipientEmail: invite.get('email')
58
+ };
47
59
 
48
- return this.mailService.utils.generateContent({data: emailData, template: 'invite-user'});
60
+ return this.mailService.utils.generateContent({data: emailData, template: 'invite-user-by-api-key'});
61
+ }
49
62
  })
50
63
  .then((emailContent) => {
51
64
  const payload = {
@@ -53,9 +66,11 @@ class Invites {
53
66
  message: {
54
67
  to: invite.get('email'),
55
68
  replyTo: emailData.invitedByEmail,
56
- subject: tpl(messages.invitedByName, {
69
+ subject: emailData.invitedByName ? tpl(messages.invitedByName, {
57
70
  invitedByName: emailData.invitedByName,
58
71
  blogName: emailData.blogName
72
+ }) : tpl(messages.invitedByNoName, {
73
+ blogName: emailData.blogName
59
74
  }),
60
75
  html: emailContent.html,
61
76
  text: emailContent.text
@@ -1,10 +1,12 @@
1
1
  const settingsCache = require('../../../shared/settings-cache');
2
+ const settingsHelpers = require('../settings-helpers');
2
3
  const mailService = require('../../services/mail');
3
4
  const urlUtils = require('../../../shared/url-utils');
4
- const Invites = require('./invites');
5
+ const Invites = require('./Invites');
5
6
 
6
7
  module.exports = new Invites({
7
8
  settingsCache,
9
+ settingsHelpers,
8
10
  mailService,
9
11
  urlUtils
10
12
  });