ghost 4.18.0 → 4.20.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 (237) hide show
  1. package/.eslintrc.js +9 -8
  2. package/Gruntfile.js +1 -1
  3. package/PRIVACY.md +3 -0
  4. package/content/adapters/README.md +2 -2
  5. package/core/boot.js +17 -12
  6. package/core/bridge.js +9 -1
  7. package/core/built/assets/{chunk.3.b80d3e1e6b8556aaff3c.js → chunk.3.777d43e2ce954ba8b2f5.js} +25 -25
  8. package/core/built/assets/codemirror/{codemirror-21a09582262987037db73b152fb35f7c.js → codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js} +14 -14
  9. package/core/built/assets/ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css +1 -0
  10. package/core/built/assets/{ghost.min-88d647a008a5b1dd678a89ae1e55c038.js → ghost.min-26e427944e719b616b8dc7fbb3bbd2f9.js} +709 -422
  11. package/core/built/assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css +1 -0
  12. package/core/built/assets/icons/arrow-left-small.svg +0 -4
  13. package/core/built/assets/icons/paintbrush.svg +10 -1
  14. package/core/built/assets/icons/post.svg +3 -1
  15. package/core/built/assets/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
  16. package/core/built/assets/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
  17. package/core/built/assets/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
  18. package/core/built/assets/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
  19. package/core/built/assets/img/themes/Alto-f4db5af43ca9771c7ac1f754de3ddf2f.png +0 -0
  20. package/core/built/assets/img/themes/Bulletin-57d45b992ff0e26e0acdce7ed4cccd67.png +0 -0
  21. package/core/built/assets/img/themes/Casper-c7e784d7188cc5d7f097d9b6c97b0263.jpg +0 -0
  22. package/core/built/assets/img/themes/Dawn-be81aa8c8caae8fcfb5d5fbec823fdcc.png +0 -0
  23. package/core/built/assets/img/themes/Digest-d3467ac22a290e1ad3a543014758286e.png +0 -0
  24. package/core/built/assets/img/themes/Dope-6f8e0bbc199ce4af9a60859e9e6a74ad.png +0 -0
  25. package/core/built/assets/img/themes/Ease-9c279ea6cec3c0f1823f81c9dd24b116.png +0 -0
  26. package/core/built/assets/img/themes/Edge-0258906309e11fd075a1d9880aa09b20.png +0 -0
  27. package/core/built/assets/img/themes/Edition-d8f508e93bc24bdf2716ae6f8b3d44f8.png +0 -0
  28. package/core/built/assets/img/themes/Editorial-a25a4a34c04dedd858bd5e05ef388b1c.jpg +0 -0
  29. package/core/built/assets/img/themes/Journal-accf0031bbae0919900a049061e65a04.png +0 -0
  30. package/core/built/assets/img/themes/London-3f07efcee9e5bfb9a33827064eb77e70.jpg +0 -0
  31. package/core/built/assets/img/themes/Massively-06edf00108429f7fb8e65f190fba34fe.jpg +0 -0
  32. package/core/built/assets/img/themes/Ruby-11a53c62015612f4b3aca8f503121225.png +0 -0
  33. package/core/built/assets/img/themes/Wave-86e8044c2d76cb57a9030e4c24ac9520.png +0 -0
  34. package/core/built/assets/simplemde/{simplemde-232f69d126310434489071a1891e6d8b.js → simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js} +14 -14
  35. package/core/built/assets/{vendor.min-7dc7cf9c92175ebfb9cea95c120ee8a7.js → vendor.min-af502ac4142871500fc424f6a5a254ec.js} +2206 -1859
  36. package/core/frontend/apps/amp/lib/router.js +1 -1
  37. package/core/frontend/helpers/match.js +17 -23
  38. package/core/frontend/meta/author-url.js +1 -1
  39. package/core/frontend/meta/url.js +1 -1
  40. package/core/{server → frontend}/public/favicon.ico +0 -0
  41. package/core/{server → frontend}/public/ghost.css +0 -0
  42. package/core/{server → frontend}/public/ghost.min.css +0 -0
  43. package/core/{server → frontend}/public/robots.txt +0 -0
  44. package/core/{server → frontend}/public/sitemap.xsl +0 -0
  45. package/core/frontend/services/proxy.js +1 -1
  46. package/core/frontend/services/rendering.js +1 -1
  47. package/core/frontend/services/routing/CollectionRouter.js +3 -49
  48. package/core/frontend/services/routing/ParentRouter.js +1 -4
  49. package/core/frontend/services/routing/StaticPagesRouter.js +3 -5
  50. package/core/frontend/services/routing/StaticRoutesRouter.js +4 -6
  51. package/core/frontend/services/routing/TaxonomyRouter.js +4 -5
  52. package/core/frontend/services/routing/controllers/collection.js +2 -2
  53. package/core/frontend/services/routing/controllers/email-post.js +2 -2
  54. package/core/frontend/services/routing/controllers/entry.js +2 -2
  55. package/core/frontend/services/routing/controllers/preview.js +2 -2
  56. package/core/frontend/services/routing/index.js +6 -12
  57. package/core/frontend/services/routing/registry.js +13 -0
  58. package/core/frontend/services/routing/router-manager.js +185 -0
  59. package/core/frontend/services/rss/generate-feed.js +2 -2
  60. package/core/frontend/services/theme-engine/i18n/i18n.js +267 -28
  61. package/core/frontend/services/theme-engine/i18n/index.js +1 -1
  62. package/core/frontend/services/theme-engine/i18n/theme-i18n.js +73 -0
  63. package/core/frontend/web/index.js +1 -0
  64. package/core/{server/web/site → frontend/web}/middleware/handle-image-sizes.js +4 -4
  65. package/core/{server/web/site → frontend/web}/middleware/index.js +0 -0
  66. package/core/{server/web/site → frontend/web}/middleware/redirect-ghost-to-admin.js +3 -3
  67. package/core/{server/web/site → frontend/web}/middleware/serve-favicon.js +6 -6
  68. package/core/{server/web/site → frontend/web}/middleware/serve-public-file.js +2 -2
  69. package/core/{server/web/site → frontend/web}/middleware/static-theme.js +3 -3
  70. package/core/frontend/web/routes.js +13 -0
  71. package/core/{server/web/site/app.js → frontend/web/site.js} +12 -16
  72. package/core/server/adapters/storage/LocalFileStorage.js +35 -39
  73. package/core/server/adapters/storage/index.js +12 -2
  74. package/core/server/api/canary/custom-theme-settings.js +2 -2
  75. package/core/server/api/canary/images.js +1 -1
  76. package/core/server/api/canary/oembed.js +2 -2
  77. package/core/server/api/canary/offers.js +29 -1
  78. package/core/server/api/canary/posts-public.js +6 -2
  79. package/core/server/api/canary/products.js +6 -2
  80. package/core/server/api/canary/tags-public.js +6 -2
  81. package/core/server/api/canary/users.js +9 -4
  82. package/core/server/api/canary/utils/serializers/output/custom-theme-settings.js +2 -2
  83. package/core/server/api/canary/utils/serializers/output/notifications.js +1 -0
  84. package/core/server/api/canary/utils/serializers/output/settings.js +2 -3
  85. package/core/server/api/canary/utils/serializers/output/utils/url.js +1 -1
  86. package/core/server/api/canary/utils/validators/input/oembed.js +4 -1
  87. package/core/server/api/canary/utils/validators/input/passwordreset.js +8 -3
  88. package/core/server/api/canary/utils/validators/input/settings.js +5 -4
  89. package/core/server/api/canary/utils/validators/input/setup.js +6 -2
  90. package/core/server/api/canary/utils/validators/input/users.js +6 -2
  91. package/core/server/api/canary/utils/validators/input/webhooks.js +8 -3
  92. package/core/server/api/v2/images.js +1 -1
  93. package/core/server/api/v2/utils/serializers/output/authentication.js +9 -4
  94. package/core/server/api/v2/utils/serializers/output/notifications.js +1 -0
  95. package/core/server/api/v2/utils/serializers/output/users.js +5 -3
  96. package/core/server/api/v2/utils/serializers/output/utils/url.js +1 -1
  97. package/core/server/api/v2/utils/validators/input/images.js +11 -6
  98. package/core/server/api/v2/utils/validators/input/invitations.js +14 -6
  99. package/core/server/api/v2/utils/validators/input/invites.js +6 -2
  100. package/core/server/api/v2/utils/validators/input/oembed.js +6 -2
  101. package/core/server/api/v2/utils/validators/input/passwordreset.js +8 -3
  102. package/core/server/api/v2/utils/validators/input/settings.js +10 -4
  103. package/core/server/api/v2/utils/validators/input/setup.js +6 -2
  104. package/core/server/api/v2/utils/validators/input/users.js +5 -2
  105. package/core/server/api/v3/authentication.js +6 -2
  106. package/core/server/api/v3/authors-public.js +6 -2
  107. package/core/server/api/v3/email.js +9 -4
  108. package/core/server/api/v3/images.js +1 -1
  109. package/core/server/api/v3/integrations.js +7 -3
  110. package/core/server/api/v3/invites.js +6 -3
  111. package/core/server/api/v3/labels.js +10 -5
  112. package/core/server/api/v3/memberSigninUrls.js +5 -2
  113. package/core/server/api/v3/oembed.js +2 -2
  114. package/core/server/api/v3/pages-public.js +5 -2
  115. package/core/server/api/v3/pages.js +6 -3
  116. package/core/server/api/v3/posts-public.js +5 -3
  117. package/core/server/api/v3/posts.js +7 -3
  118. package/core/server/api/v3/preview.js +5 -3
  119. package/core/server/api/v3/session.js +7 -3
  120. package/core/server/api/v3/settings.js +8 -3
  121. package/core/server/api/v3/slugs.js +5 -4
  122. package/core/server/api/v3/utils/serializers/output/authentication.js +10 -4
  123. package/core/server/api/v3/utils/serializers/output/notifications.js +1 -0
  124. package/core/server/api/v3/utils/serializers/output/settings.js +2 -3
  125. package/core/server/api/v3/utils/serializers/output/users.js +6 -2
  126. package/core/server/api/v3/utils/serializers/output/utils/url.js +1 -1
  127. package/core/server/api/v3/utils/validators/input/images.js +12 -7
  128. package/core/server/api/v3/utils/validators/input/invitations.js +14 -6
  129. package/core/server/api/v3/utils/validators/input/invites.js +6 -2
  130. package/core/server/api/v3/utils/validators/input/oembed.js +6 -2
  131. package/core/server/api/v3/utils/validators/input/passwordreset.js +8 -3
  132. package/core/server/api/v3/utils/validators/input/settings.js +5 -4
  133. package/core/server/api/v3/utils/validators/input/setup.js +6 -2
  134. package/core/server/api/v3/utils/validators/input/users.js +6 -2
  135. package/core/server/api/v3/utils/validators/input/webhooks.js +8 -3
  136. package/core/server/data/exporter/table-lists.js +2 -1
  137. package/core/server/data/importer/handlers/image.js +1 -1
  138. package/core/server/data/importer/importers/image.js +1 -1
  139. package/core/server/data/migrations/init/1-create-tables.js +7 -8
  140. package/core/server/data/migrations/init/2-create-fixtures.js +8 -8
  141. package/core/server/data/migrations/versions/4.19/01-add-active-column-to-offers.js +7 -0
  142. package/core/server/data/migrations/versions/4.19/02-add-offer-redemptions-table.js +8 -0
  143. package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +19 -0
  144. package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +30 -0
  145. package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +21 -0
  146. package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +9 -0
  147. package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +41 -0
  148. package/core/server/data/schema/fixtures/utils.js +150 -143
  149. package/core/server/data/schema/schema.js +15 -3
  150. package/core/server/frontend/ghost.min.css +1 -0
  151. package/core/server/lib/image/blog-icon.js +10 -10
  152. package/core/server/lib/image/image-size.js +5 -5
  153. package/core/server/lib/image/image-utils.js +4 -4
  154. package/core/server/lib/image/index.js +1 -2
  155. package/core/server/lib/mobiledoc.js +3 -2
  156. package/core/server/models/action.js +7 -4
  157. package/core/server/models/base/plugins/overrides.js +19 -6
  158. package/core/server/models/custom-theme-setting.js +56 -1
  159. package/core/server/models/index.js +4 -45
  160. package/core/server/models/member.js +5 -0
  161. package/core/server/models/offer-redemption.js +10 -0
  162. package/core/server/models/user.js +2 -1
  163. package/core/server/overrides.js +6 -2
  164. package/core/server/run-update-check.js +0 -3
  165. package/core/server/services/adapter-manager/config.js +1 -0
  166. package/core/server/services/adapter-manager/index.js +9 -5
  167. package/core/server/services/adapter-manager/options-resolver.js +18 -0
  168. package/core/server/services/bulk-email/bulk-email-processor.js +6 -2
  169. package/core/server/services/bulk-email/mailgun.js +1 -1
  170. package/core/server/services/custom-theme-settings.js +10 -4
  171. package/core/server/services/invites/index.js +0 -2
  172. package/core/server/services/invites/invites.js +5 -5
  173. package/core/server/services/mail/GhostMailer.js +18 -10
  174. package/core/server/services/mega/mega.js +3 -3
  175. package/core/server/services/mega/post-email-serializer.js +2 -2
  176. package/core/server/services/members/api.js +3 -4
  177. package/core/server/services/members/emails/signin.js +1 -1
  178. package/core/server/services/members/emails/signup.js +1 -1
  179. package/core/server/services/members/emails/subscribe.js +1 -1
  180. package/core/server/services/members/middleware.js +10 -0
  181. package/core/server/services/members/service.js +2 -1
  182. package/core/server/services/notifications/index.js +1 -1
  183. package/core/server/services/notifications/notifications.js +40 -35
  184. package/core/server/services/oembed.js +4 -9
  185. package/core/server/services/offers/service.js +16 -6
  186. package/core/server/services/permissions/public.js +6 -2
  187. package/core/server/services/route-settings/route-settings.js +1 -1
  188. package/core/server/services/settings/index.js +3 -1
  189. package/core/server/services/settings/settings-bread-service.js +42 -20
  190. package/core/server/services/slack.js +1 -1
  191. package/core/server/services/themes/activate.js +2 -2
  192. package/core/server/services/themes/activation-bridge.js +6 -6
  193. package/core/server/services/themes/storage.js +1 -1
  194. package/core/{frontend → server}/services/url/Queue.js +0 -0
  195. package/core/{frontend → server}/services/url/Resource.js +0 -0
  196. package/core/{frontend → server}/services/url/Resources.js +2 -2
  197. package/core/{frontend → server}/services/url/UrlGenerator.js +14 -14
  198. package/core/{frontend → server}/services/url/UrlService.js +12 -15
  199. package/core/{frontend → server}/services/url/Urls.js +1 -1
  200. package/core/{frontend → server}/services/url/configs/canary.js +0 -0
  201. package/core/{frontend → server}/services/url/configs/v2.js +0 -0
  202. package/core/{frontend → server}/services/url/configs/v3.js +0 -0
  203. package/core/{frontend → server}/services/url/configs/v4.js +0 -0
  204. package/core/{frontend → server}/services/url/index.js +0 -0
  205. package/core/server/services/xmlrpc.js +1 -1
  206. package/core/server/update-check.js +3 -3
  207. package/core/server/web/admin/controller.js +11 -0
  208. package/core/server/web/admin/views/default-prod.html +4 -4
  209. package/core/server/web/admin/views/default.html +4 -4
  210. package/core/server/web/api/app.js +8 -9
  211. package/core/server/web/members/app.js +1 -0
  212. package/core/server/web/oauth/app.js +4 -2
  213. package/core/server/web/parent/backend.js +3 -3
  214. package/core/server/web/parent/frontend.js +2 -2
  215. package/core/server/web/shared/middlewares/api/spam-prevention.js +0 -2
  216. package/core/server/web/shared/middlewares/custom-redirects.js +0 -8
  217. package/core/server/web/shared/middlewares/maintenance.js +1 -1
  218. package/core/server/web/well-known.js +10 -10
  219. package/core/shared/config/defaults.json +2 -2
  220. package/core/shared/config/overrides.json +1 -1
  221. package/core/shared/express.js +10 -0
  222. package/core/shared/html-to-plaintext.js +2 -2
  223. package/core/shared/labs.js +14 -6
  224. package/loggingrc.js +10 -0
  225. package/package.json +60 -57
  226. package/yarn.lock +1317 -914
  227. package/core/built/assets/ghost-dark-13627f10941a7dbb2b12e1d41dc51c34.css +0 -1
  228. package/core/built/assets/ghost.min-d9cbfb4eb2db8915fcd2bf2416218616.css +0 -1
  229. package/core/built/assets/img/themes/London-68501c8ab797de7f2851cf9ea0a28e26.jpg +0 -0
  230. package/core/frontend/services/routing/bootstrap.js +0 -114
  231. package/core/server/public/404-ghost.png +0 -0
  232. package/core/server/public/404-ghost@2x.png +0 -0
  233. package/core/server/web/site/index.js +0 -1
  234. package/core/server/web/site/routes.js +0 -9
  235. package/core/shared/i18n/i18n.js +0 -312
  236. package/core/shared/i18n/index.js +0 -6
  237. package/core/shared/i18n/translations/en.json +0 -675
@@ -1,10 +1,14 @@
1
1
  const Promise = require('bluebird');
2
- const i18n = require('../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const models = require('../../models');
5
5
  const auth = require('../../services/auth');
6
6
  const api = require('./index');
7
7
 
8
+ const messages = {
9
+ accessDenied: 'Access denied.'
10
+ };
11
+
8
12
  const session = {
9
13
  read(frame) {
10
14
  /*
@@ -20,7 +24,7 @@ const session = {
20
24
 
21
25
  if (!object || !object.username || !object.password) {
22
26
  return Promise.reject(new errors.UnauthorizedError({
23
- message: i18n.t('errors.middleware.auth.accessDenied')
27
+ message: tpl(messages.accessDenied)
24
28
  }));
25
29
  }
26
30
 
@@ -40,7 +44,7 @@ const session = {
40
44
  }).catch(async (err) => {
41
45
  if (!errors.utils.isIgnitionError(err)) {
42
46
  throw new errors.UnauthorizedError({
43
- message: i18n.t('errors.middleware.auth.accessDenied'),
47
+ message: tpl(messages.accessDenied),
44
48
  err
45
49
  });
46
50
  }
@@ -2,11 +2,16 @@ const Promise = require('bluebird');
2
2
  const _ = require('lodash');
3
3
  const models = require('../../models');
4
4
  const routeSettings = require('../../services/route-settings');
5
- const i18n = require('../../../shared/i18n');
5
+ const tpl = require('@tryghost/tpl');
6
6
  const {BadRequestError, NoPermissionError} = require('@tryghost/errors');
7
7
  const settingsService = require('../../services/settings');
8
8
  const membersService = require('../../services/members');
9
9
 
10
+ const messages = {
11
+ error: 'Failed to send email.',
12
+ accessCoreSettingFromExtReq: 'Attempted to access core setting from external request'
13
+ };
14
+
10
15
  const settingsBREADService = settingsService.getSettingsBREADServiceInstance();
11
16
 
12
17
  module.exports = {
@@ -109,7 +114,7 @@ module.exports = {
109
114
  } catch (err) {
110
115
  throw new BadRequestError({
111
116
  err,
112
- message: i18n.t('errors.mail.failedSendingEmail.error')
117
+ message: tpl(messages.error)
113
118
  });
114
119
  }
115
120
  }
@@ -162,7 +167,7 @@ module.exports = {
162
167
  const firstCoreSetting = frame.data.settings.find(setting => setting.group === 'core');
163
168
  if (firstCoreSetting) {
164
169
  throw new NoPermissionError({
165
- message: i18n.t('errors.api.settings.accessCoreSettingFromExtReq')
170
+ message: tpl(messages.accessCoreSettingFromExtReq)
166
171
  });
167
172
  }
168
173
  }
@@ -1,7 +1,10 @@
1
1
  const models = require('../../models');
2
- const i18n = require('../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
 
5
+ const messages = {
6
+ couldNotGenerateSlug: 'Could not generate slug.'
7
+ };
5
8
  const allowedTypes = {
6
9
  post: models.Post,
7
10
  tag: models.Tag,
@@ -36,9 +39,7 @@ module.exports = {
36
39
  return models.Base.Model.generateSlug(allowedTypes[frame.options.type], frame.data.name, {status: 'all'})
37
40
  .then((slug) => {
38
41
  if (!slug) {
39
- return Promise.reject(new errors.GhostError({
40
- message: i18n.t('errors.api.slugs.couldNotGenerateSlug')
41
- }));
42
+ return Promise.reject(new errors.GhostError({message: tpl(messages.couldNotGenerateSlug)}));
42
43
  }
43
44
  return slug;
44
45
  });
@@ -1,7 +1,13 @@
1
- const i18n = require('../../../../../../shared/i18n');
1
+ const tpl = require('@tryghost/tpl');
2
2
  const mapper = require('./utils/mapper');
3
3
  const debug = require('@tryghost/debug')('api:v3:utils:serializers:output:authentication');
4
4
 
5
+ const messages = {
6
+ checkEmailForInstructions: 'Check your email for further instructions.',
7
+ passwordChanged: 'Password changed successfully.',
8
+ invitationAccepted: 'Invitation accepted.'
9
+ };
10
+
5
11
  module.exports = {
6
12
  setup(user, apiConfig, frame) {
7
13
  frame.response = {
@@ -28,7 +34,7 @@ module.exports = {
28
34
  generateResetToken(data, apiConfig, frame) {
29
35
  frame.response = {
30
36
  passwordreset: [{
31
- message: i18n.t('common.api.authentication.mail.checkEmailForInstructions')
37
+ message: tpl(messages.checkEmailForInstructions)
32
38
  }]
33
39
  };
34
40
  },
@@ -36,7 +42,7 @@ module.exports = {
36
42
  resetPassword(data, apiConfig, frame) {
37
43
  frame.response = {
38
44
  passwordreset: [{
39
- message: i18n.t('common.api.authentication.mail.passwordChanged')
45
+ message: tpl(messages.passwordChanged)
40
46
  }]
41
47
  };
42
48
  },
@@ -46,7 +52,7 @@ module.exports = {
46
52
 
47
53
  frame.response = {
48
54
  invitation: [
49
- {message: i18n.t('common.api.authentication.mail.invitationAccepted')}
55
+ {message: tpl(messages.invitationAccepted)}
50
56
  ]
51
57
  };
52
58
  },
@@ -19,6 +19,7 @@ module.exports = {
19
19
  delete notification.seen;
20
20
  delete notification.seenBy;
21
21
  delete notification.addedAt;
22
+ delete notification.createdAtVersion;
22
23
  });
23
24
 
24
25
  frame.response = {
@@ -55,9 +55,8 @@ module.exports = {
55
55
  this.browse(...arguments);
56
56
  },
57
57
 
58
- edit(models, apiConfig, frame) {
59
- const settingsKeyedJSON = _.keyBy(_.invokeMap(models, 'toJSON'), 'key');
60
- this.browse(settingsKeyedJSON, apiConfig, frame);
58
+ edit() {
59
+ this.browse(...arguments);
61
60
  },
62
61
 
63
62
  download(bytes, apiConfig, frame) {
@@ -1,7 +1,11 @@
1
1
  const debug = require('@tryghost/debug')('api:v3:utils:serializers:output:users');
2
- const i18n = require('../../../../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const mapper = require('./utils/mapper');
4
4
 
5
+ const messages = {
6
+ pwdChangedSuccessfully: 'Password changed successfully.'
7
+ };
8
+
5
9
  module.exports = {
6
10
  browse(models, apiConfig, frame) {
7
11
  debug('browse');
@@ -39,7 +43,7 @@ module.exports = {
39
43
  debug('changePassword');
40
44
 
41
45
  frame.response = {
42
- password: [{message: i18n.t('notices.api.users.pwdChangedSuccessfully')}]
46
+ password: [{message: tpl(messages.pwdChangedSuccessfully)}]
43
47
  };
44
48
  },
45
49
 
@@ -1,4 +1,4 @@
1
- const urlService = require('../../../../../../../frontend/services/url');
1
+ const urlService = require('../../../../../../services/url');
2
2
  const urlUtils = require('../../../../../../../shared/url-utils');
3
3
  const localUtils = require('../../../index');
4
4
 
@@ -1,9 +1,14 @@
1
1
  const jsonSchema = require('../utils/json-schema');
2
2
  const config = require('../../../../../../shared/config');
3
- const i18n = require('../../../../../../shared/i18n');
3
+ const tpl = require('@tryghost/tpl');
4
4
  const errors = require('@tryghost/errors');
5
5
  const {imageSize, blogIcon} = require('../../../../../lib/image');
6
6
 
7
+ const messages = {
8
+ isNotSquare: 'Please select a valid image file with square dimensions.',
9
+ invalidFile: 'Icon must be a square .ico or .png file between 60px – 1,000px, under 100kb.'
10
+ };
11
+
7
12
  const profileImage = (frame) => {
8
13
  return imageSize.getImageSizeFromPath(frame.file.path).then((response) => {
9
14
  // save the image dimensions in new property for file
@@ -12,7 +17,7 @@ const profileImage = (frame) => {
12
17
  // CASE: file needs to be a square
13
18
  if (frame.file.dimensions.width !== frame.file.dimensions.height) {
14
19
  return Promise.reject(new errors.ValidationError({
15
- message: i18n.t('errors.api.images.isNotSquare')
20
+ message: tpl(messages.isNotSquare)
16
21
  }));
17
22
  }
18
23
  });
@@ -28,7 +33,7 @@ const icon = (frame) => {
28
33
  // CASE: file should not be larger than 100kb
29
34
  if (!validIconFileSize(frame.file.size)) {
30
35
  return Promise.reject(new errors.ValidationError({
31
- message: i18n.t('errors.api.icons.invalidFile', {extensions: iconExtensions})
36
+ message: tpl(messages.invalidFile, {extensions: iconExtensions})
32
37
  }));
33
38
  }
34
39
 
@@ -39,7 +44,7 @@ const icon = (frame) => {
39
44
  // CASE: file needs to be a square
40
45
  if (frame.file.dimensions.width !== frame.file.dimensions.height) {
41
46
  return Promise.reject(new errors.ValidationError({
42
- message: i18n.t('errors.api.icons.invalidFile', {extensions: iconExtensions})
47
+ message: tpl(messages.invalidFile, {extensions: iconExtensions})
43
48
  }));
44
49
  }
45
50
 
@@ -47,14 +52,14 @@ const icon = (frame) => {
47
52
  // .ico files can contain multiple sizes, we need at least a minimum of 60px (16px is ok, as long as 60px are present as well)
48
53
  if (frame.file.dimensions.width < 60) {
49
54
  return Promise.reject(new errors.ValidationError({
50
- message: i18n.t('errors.api.icons.invalidFile', {extensions: iconExtensions})
55
+ message: tpl(messages.invalidFile, {extensions: iconExtensions})
51
56
  }));
52
57
  }
53
58
 
54
59
  // CASE: icon needs to be smaller than or equal to 1000px
55
60
  if (frame.file.dimensions.width > 1000) {
56
61
  return Promise.reject(new errors.ValidationError({
57
- message: i18n.t('errors.api.icons.invalidFile', {extensions: iconExtensions})
62
+ message: tpl(messages.invalidFile, {extensions: iconExtensions})
58
63
  }));
59
64
  }
60
65
  });
@@ -77,4 +82,4 @@ module.exports = {
77
82
  }
78
83
  });
79
84
  }
80
- };
85
+ };
@@ -1,9 +1,17 @@
1
1
  const Promise = require('bluebird');
2
2
  const validator = require('@tryghost/validator');
3
3
  const debug = require('@tryghost/debug')('api:v3:utils:validators:input:invitation');
4
- const i18n = require('../../../../../../shared/i18n');
4
+ const tpl = require('@tryghost/tpl');
5
5
  const errors = require('@tryghost/errors');
6
6
 
7
+ const messages = {
8
+ noTokenProvided: 'No token provided.',
9
+ noEmailProvided: 'No email provided.',
10
+ noPasswordProvided: 'No password provided.',
11
+ noNameProvided: 'No name provided.',
12
+ invalidEmailReceived: 'The server did not receive a valid email'
13
+ };
14
+
7
15
  module.exports = {
8
16
  acceptInvitation(apiConfig, frame) {
9
17
  debug('acceptInvitation');
@@ -11,19 +19,19 @@ module.exports = {
11
19
  const data = frame.data.invitation[0];
12
20
 
13
21
  if (!data.token) {
14
- return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noTokenProvided')}));
22
+ return Promise.reject(new errors.ValidationError({message: tpl(messages.noTokenProvided)}));
15
23
  }
16
24
 
17
25
  if (!data.email) {
18
- return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noEmailProvided')}));
26
+ return Promise.reject(new errors.ValidationError({message: tpl(messages.noEmailProvided)}));
19
27
  }
20
28
 
21
29
  if (!data.password) {
22
- return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noPasswordProvided')}));
30
+ return Promise.reject(new errors.ValidationError({message: tpl(messages.noPasswordProvided)}));
23
31
  }
24
32
 
25
33
  if (!data.name) {
26
- return Promise.reject(new errors.ValidationError({message: i18n.t('errors.api.authentication.noNameProvided')}));
34
+ return Promise.reject(new errors.ValidationError({message: tpl(messages.noNameProvided)}));
27
35
  }
28
36
  },
29
37
 
@@ -34,7 +42,7 @@ module.exports = {
34
42
 
35
43
  if (typeof email !== 'string' || !validator.isEmail(email)) {
36
44
  throw new errors.BadRequestError({
37
- message: i18n.t('errors.api.authentication.invalidEmailReceived')
45
+ message: tpl(messages.invalidEmailReceived)
38
46
  });
39
47
  }
40
48
  }
@@ -1,15 +1,19 @@
1
1
  const Promise = require('bluebird');
2
- const i18n = require('../../../../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
  const models = require('../../../../../models');
5
5
 
6
+ const messages = {
7
+ userAlreadyRegistered: 'User is already registered.'
8
+ };
9
+
6
10
  module.exports = {
7
11
  add(apiConfig, frame) {
8
12
  return models.User.findOne({email: frame.data.invites[0].email}, frame.options)
9
13
  .then((user) => {
10
14
  if (user) {
11
15
  return Promise.reject(new errors.ValidationError({
12
- message: i18n.t('errors.api.users.userAlreadyRegistered')
16
+ message: tpl(messages.userAlreadyRegistered)
13
17
  }));
14
18
  }
15
19
  });
@@ -1,12 +1,16 @@
1
1
  const Promise = require('bluebird');
2
- const i18n = require('../../../../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
 
5
+ const messages = {
6
+ noUrlProvided: 'No url provided.'
7
+ };
8
+
5
9
  module.exports = {
6
10
  read(apiConfig, frame) {
7
11
  if (!frame.data.url || !frame.data.url.trim()) {
8
12
  return Promise.reject(new errors.BadRequestError({
9
- message: i18n.t('errors.api.oembed.noUrlProvided')
13
+ message: tpl(messages.noUrlProvided)
10
14
  }));
11
15
  }
12
16
  }
@@ -1,9 +1,14 @@
1
1
  const Promise = require('bluebird');
2
2
  const validator = require('@tryghost/validator');
3
3
  const debug = require('@tryghost/debug')('api:v3:utils:validators:input:passwordreset');
4
- const i18n = require('../../../../../../shared/i18n');
4
+ const tpl = require('@tryghost/tpl');
5
5
  const errors = require('@tryghost/errors');
6
6
 
7
+ const messages = {
8
+ newPasswordsDoNotMatch: 'Your new passwords do not match',
9
+ invalidEmailReceived: 'The server did not receive a valid email'
10
+ };
11
+
7
12
  module.exports = {
8
13
  resetPassword(apiConfig, frame) {
9
14
  debug('resetPassword');
@@ -12,7 +17,7 @@ module.exports = {
12
17
 
13
18
  if (data.newPassword !== data.ne2Password) {
14
19
  return Promise.reject(new errors.ValidationError({
15
- message: i18n.t('errors.models.user.newPasswordsDoNotMatch')
20
+ message: tpl(messages.newPasswordsDoNotMatch)
16
21
  }));
17
22
  }
18
23
  },
@@ -24,7 +29,7 @@ module.exports = {
24
29
 
25
30
  if (typeof email !== 'string' || !validator.isEmail(email)) {
26
31
  throw new errors.BadRequestError({
27
- message: i18n.t('errors.api.authentication.invalidEmailReceived')
32
+ message: tpl(messages.invalidEmailReceived)
28
33
  });
29
34
  }
30
35
  }
@@ -1,12 +1,13 @@
1
1
  const Promise = require('bluebird');
2
2
  const _ = require('lodash');
3
- const i18n = require('../../../../../../shared/i18n');
3
+ const tpl = require('@tryghost/tpl');
4
4
  const {NotFoundError, ValidationError, BadRequestError} = require('@tryghost/errors');
5
5
  const validator = require('@tryghost/validator');
6
6
 
7
7
  const messages = {
8
8
  invalidEmailReceived: 'Please send a valid email',
9
- invalidEmailTypeReceived: 'Invalid email type received'
9
+ invalidEmailTypeReceived: 'Invalid email type received',
10
+ problemFindingSetting: 'Problem finding setting: {key}'
10
11
  };
11
12
 
12
13
  module.exports = {
@@ -14,7 +15,7 @@ module.exports = {
14
15
  // @NOTE: was removed https://github.com/TryGhost/Ghost/issues/10373
15
16
  if (frame.options.key === 'ghost_head' || frame.options.key === 'ghost_foot') {
16
17
  return Promise.reject(new NotFoundError({
17
- message: i18n.t('errors.api.settings.problemFindingSetting', {
18
+ message: tpl(messages.problemFindingSetting, {
18
19
  key: frame.options.key
19
20
  })
20
21
  }));
@@ -28,7 +29,7 @@ module.exports = {
28
29
  if (setting.key === 'ghost_head' || setting.key === 'ghost_foot') {
29
30
  // @NOTE: was removed https://github.com/TryGhost/Ghost/issues/10373
30
31
  errors.push(new NotFoundError({
31
- message: i18n.t('errors.api.settings.problemFindingSetting', {
32
+ message: tpl(messages.problemFindingSetting, {
32
33
  key: setting.key
33
34
  })
34
35
  }));
@@ -1,13 +1,17 @@
1
1
  const debug = require('@tryghost/debug')('api:v3:utils:validators:input:updateSetup');
2
- const i18n = require('../../../../../../shared/i18n');
2
+ const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
4
 
5
+ const messages = {
6
+ notTheBlogOwner: 'You are not the site owner.'
7
+ };
8
+
5
9
  module.exports = {
6
10
  updateSetup(apiConfig, frame) {
7
11
  debug('resetPassword');
8
12
 
9
13
  if (!frame.options.context || !frame.options.context.user) {
10
- throw new errors.NoPermissionError({message: i18n.t('errors.api.authentication.notTheBlogOwner')});
14
+ throw new errors.NoPermissionError({message: tpl(messages.notTheBlogOwner)});
11
15
  }
12
16
  }
13
17
  };
@@ -1,8 +1,12 @@
1
1
  const Promise = require('bluebird');
2
2
  const debug = require('@tryghost/debug')('api:v3:utils:validators:input:users');
3
- const i18n = require('../../../../../../shared/i18n');
3
+ const tpl = require('@tryghost/tpl');
4
4
  const errors = require('@tryghost/errors');
5
5
 
6
+ const messages = {
7
+ newPasswordsDoNotMatch: 'Your new passwords do not match'
8
+ };
9
+
6
10
  module.exports = {
7
11
  changePassword(apiConfig, frame) {
8
12
  debug('changePassword');
@@ -11,7 +15,7 @@ module.exports = {
11
15
 
12
16
  if (data.newPassword !== data.ne2Password) {
13
17
  return Promise.reject(new errors.ValidationError({
14
- message: i18n.t('errors.models.user.newPasswordsDoNotMatch')
18
+ message: tpl(messages.newPasswordsDoNotMatch)
15
19
  }));
16
20
  }
17
21
  }
@@ -1,16 +1,21 @@
1
1
  const _ = require('lodash');
2
2
  const errors = require('@tryghost/errors');
3
- const i18n = require('../../../../../../shared/i18n');
3
+ const tpl = require('@tryghost/tpl');
4
4
  const jsonSchema = require('../utils/json-schema');
5
5
 
6
+ const messages = {
7
+ schemaValidationFailed: 'Validation failed for \'{key}\'.',
8
+ noIntegrationIdProvidedContext: 'You may only create webhooks with \'integration_id\' when using session authentication.'
9
+ };
10
+
6
11
  module.exports = {
7
12
  add(apiConfig, frame) {
8
13
  if (!_.get(frame, 'options.context.integration.id') && !_.get(frame.data, 'webhooks[0].integration_id')) {
9
14
  return Promise.reject(new errors.ValidationError({
10
- message: i18n.t('notices.data.validation.index.schemaValidationFailed', {
15
+ message: tpl(messages.schemaValidationFailed, {
11
16
  key: 'integration_id'
12
17
  }),
13
- context: i18n.t('errors.api.webhooks.noIntegrationIdProvided.context'),
18
+ context: tpl(messages.noIntegrationIdProvidedContext),
14
19
  property: 'integration_id'
15
20
  }));
16
21
  }
@@ -37,7 +37,8 @@ const BACKUP_TABLES = [
37
37
  'members_paid_subscription_events',
38
38
  'members_subscribe_events',
39
39
  'members_product_events',
40
- 'offers'
40
+ 'offers',
41
+ 'offer_redemptions'
41
42
  ];
42
43
 
43
44
  // NOTE: exposing only tables which are going to be included in a "default" export file
@@ -13,7 +13,7 @@ ImageHandler = {
13
13
  directories: ['images', 'content'],
14
14
 
15
15
  loadFile: function (files, baseDir) {
16
- const store = storage.getStorage();
16
+ const store = storage.getStorage('images');
17
17
  const baseDirRegex = baseDir ? new RegExp('^' + baseDir + '/') : new RegExp('');
18
18
 
19
19
  const imageFolderRegexes = _.map(urlUtils.STATIC_IMAGE_URL_PREFIX.split('/'), function (dir) {
@@ -64,7 +64,7 @@ ImageImporter = {
64
64
  return importData;
65
65
  },
66
66
  doImport: function (imageData) {
67
- const store = storage.getStorage();
67
+ const store = storage.getStorage('images');
68
68
 
69
69
  return Promise.map(imageData, function (image) {
70
70
  return store.save(image, image.targetDir).then(function (result) {
@@ -4,27 +4,26 @@ const schema = require('../../schema').tables;
4
4
  const logging = require('@tryghost/logging');
5
5
  const schemaTables = Object.keys(schema);
6
6
 
7
- module.exports.up = function createTables(options) {
7
+ module.exports.up = async (options) => {
8
8
  const connection = options.connection;
9
9
 
10
- return Promise.mapSeries(schemaTables, function createTable(table) {
10
+ await Promise.mapSeries(schemaTables, async (table) => {
11
11
  logging.info('Creating table: ' + table);
12
- return commands.createTable(table, connection);
12
+ await commands.createTable(table, connection);
13
13
  });
14
14
  };
15
15
 
16
16
  /**
17
- *
18
17
  @TODO: This works, but is very dangerous in the current state of the knex-migrator v3.
19
- @TODO: Enable if knex-migrator v3 is stable.
20
- module.exports.down = function dropTables(options) {
18
+ @TODO: Decide if we should enable or delete this
19
+ module.exports.down = async (options) => {
21
20
  var connection = options.connection;
22
21
 
23
22
  // Reference between tables!
24
23
  schemaTables.reverse();
25
- return Promise.mapSeries(schemaTables, function dropTable(table) {
24
+ await Promise.mapSeries(schemaTables, async (table) => {
26
25
  logging.info('Drop table: ' + table);
27
- return commands.deleteTable(table, connection);
26
+ await commands.deleteTable(table, connection);
28
27
  });
29
28
  };
30
29
  */
@@ -7,20 +7,20 @@ module.exports.config = {
7
7
  transaction: true
8
8
  };
9
9
 
10
- module.exports.up = function insertFixtures(options) {
10
+ module.exports.up = async (options) => {
11
11
  const localOptions = _.merge({
12
12
  context: {internal: true},
13
13
  migrating: true
14
14
  }, options);
15
15
 
16
- return Promise.mapSeries(fixtures.models, function (model) {
16
+ await Promise.mapSeries(fixtures.models, async (model) => {
17
17
  logging.info('Model: ' + model.name);
18
18
 
19
- return fixtures.utils.addFixturesForModel(model, localOptions);
20
- }).then(function () {
21
- return Promise.mapSeries(fixtures.relations, function (relation) {
22
- logging.info('Relation: ' + relation.from.model + ' to ' + relation.to.model);
23
- return fixtures.utils.addFixturesForRelation(relation, localOptions);
24
- });
19
+ await fixtures.utils.addFixturesForModel(model, localOptions);
20
+ });
21
+
22
+ await Promise.mapSeries(fixtures.relations, async (relation) => {
23
+ logging.info('Relation: ' + relation.from.model + ' to ' + relation.to.model);
24
+ await fixtures.utils.addFixturesForRelation(relation, localOptions);
25
25
  });
26
26
  };
@@ -0,0 +1,7 @@
1
+ const utils = require('../../utils');
2
+
3
+ module.exports = utils.createAddColumnMigration('offers', 'active', {
4
+ type: 'boolean',
5
+ nullable: false,
6
+ defaultTo: true
7
+ });
@@ -0,0 +1,8 @@
1
+ const {addTable} = require('../../utils');
2
+
3
+ module.exports = addTable('offer_redemptions', {
4
+ id: {type: 'string', maxlength: 24, nullable: false, primary: true},
5
+ offer_id: {type: 'string', maxlength: 24, nullable: false, references: 'offers.id', cascadeDelete: true},
6
+ member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
7
+ subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true}
8
+ });
@@ -0,0 +1,19 @@
1
+ const utils = require('../../utils');
2
+
3
+ const migration = utils.addTable('offer_redemptions', {
4
+ id: {type: 'string', maxlength: 24, nullable: false, primary: true},
5
+ offer_id: {type: 'string', maxlength: 24, nullable: false, references: 'offers.id', cascadeDelete: true},
6
+ member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
7
+ subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true}
8
+ });
9
+
10
+ // This reverses an "addTable" migration so that we
11
+ // drop the table going forwards and re-add it going back
12
+ const up = migration.down;
13
+ const down = migration.up;
14
+
15
+ migration.up = up;
16
+ migration.down = down;
17
+
18
+ module.exports = migration;
19
+