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
@@ -29,27 +29,25 @@ class LocalFileStore extends StorageBase {
29
29
 
30
30
  /**
31
31
  * Saves a buffer in the targetPath
32
- * - buffer is an instance of Buffer
33
- * - returns a Promise which returns the full URL to retrieve the data
32
+ * @param {Buffer} buffer is an instance of Buffer
33
+ * @param {String} targetPath path to which the buffer should be written
34
+ * @returns {Promise<String>} a URL to retrieve the data
34
35
  */
35
- saveRaw(buffer, targetPath) {
36
+ async saveRaw(buffer, targetPath) {
36
37
  const storagePath = path.join(this.storagePath, targetPath);
37
38
  const targetDir = path.dirname(storagePath);
38
39
 
39
- return fs.mkdirs(targetDir)
40
- .then(() => {
41
- return fs.writeFile(storagePath, buffer);
42
- })
43
- .then(() => {
44
- // For local file system storage can use relative path so add a slash
45
- const fullUrl = (
46
- urlUtils.urlJoin('/', urlUtils.getSubdir(),
47
- urlUtils.STATIC_IMAGE_URL_PREFIX,
48
- targetPath)
49
- ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
50
-
51
- return fullUrl;
52
- });
40
+ await fs.mkdirs(targetDir);
41
+ await fs.writeFile(storagePath, buffer);
42
+
43
+ // For local file system storage can use relative path so add a slash
44
+ const fullUrl = (
45
+ urlUtils.urlJoin('/', urlUtils.getSubdir(),
46
+ urlUtils.STATIC_IMAGE_URL_PREFIX,
47
+ targetPath)
48
+ ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
49
+
50
+ return fullUrl;
53
51
  }
54
52
 
55
53
  /**
@@ -57,34 +55,32 @@ class LocalFileStore extends StorageBase {
57
55
  * - image is the express image object
58
56
  * - returns a promise which ultimately returns the full url to the uploaded image
59
57
  *
60
- * @param image
61
- * @param targetDir
62
- * @returns {*}
58
+ * @param {StorageBase.Image} image
59
+ * @param {String} targetDir
60
+ * @returns {Promise<String>}
63
61
  */
64
- save(image, targetDir) {
62
+ async save(image, targetDir) {
65
63
  let targetFilename;
66
64
 
67
65
  // NOTE: the base implementation of `getTargetDir` returns the format this.storagePath/YYYY/MM
68
66
  targetDir = targetDir || this.getTargetDir(this.storagePath);
69
67
 
70
- return this.getUniqueFileName(image, targetDir).then((filename) => {
71
- targetFilename = filename;
72
- return fs.mkdirs(targetDir);
73
- }).then(() => {
74
- return fs.copy(image.path, targetFilename);
75
- }).then(() => {
76
- // The src for the image must be in URI format, not a file system path, which in Windows uses \
77
- // For local file system storage can use relative path so add a slash
78
- const fullUrl = (
79
- urlUtils.urlJoin('/', urlUtils.getSubdir(),
80
- urlUtils.STATIC_IMAGE_URL_PREFIX,
81
- path.relative(this.storagePath, targetFilename))
82
- ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
83
-
84
- return fullUrl;
85
- }).catch((e) => {
86
- return Promise.reject(e);
87
- });
68
+ const filename = await this.getUniqueFileName(image, targetDir);
69
+
70
+ targetFilename = filename;
71
+ await fs.mkdirs(targetDir);
72
+
73
+ await fs.copy(image.path, targetFilename);
74
+
75
+ // The src for the image must be in URI format, not a file system path, which in Windows uses \
76
+ // For local file system storage can use relative path so add a slash
77
+ const fullUrl = (
78
+ urlUtils.urlJoin('/', urlUtils.getSubdir(),
79
+ urlUtils.STATIC_IMAGE_URL_PREFIX,
80
+ path.relative(this.storagePath, targetFilename))
81
+ ).replace(new RegExp(`\\${path.sep}`, 'g'), '/');
82
+
83
+ return fullUrl;
88
84
  }
89
85
 
90
86
  exists(fileName, targetDir) {
@@ -1,7 +1,17 @@
1
1
  const adapterManager = require('../../services/adapter-manager');
2
2
 
3
- function getStorage() {
4
- return adapterManager.getAdapter('storage');
3
+ /**
4
+ * @param {'images'|'videos'|'audios'} [feature] - name for the "feature" to enable through adapter, e.g.: images or videos storage
5
+ * @returns {Object} adapter instance
6
+ */
7
+ function getStorage(feature) {
8
+ let adapterName = 'storage';
9
+
10
+ if (feature) {
11
+ adapterName += `:${feature}`;
12
+ }
13
+
14
+ return adapterManager.getAdapter(adapterName);
5
15
  }
6
16
 
7
17
  module.exports.getStorage = getStorage;
@@ -6,7 +6,7 @@ module.exports = {
6
6
  browse: {
7
7
  permissions: true,
8
8
  query() {
9
- return customThemeSettingsService.listSettings();
9
+ return customThemeSettingsService.api.listSettings();
10
10
  }
11
11
  },
12
12
 
@@ -16,7 +16,7 @@ module.exports = {
16
16
  },
17
17
  permissions: true,
18
18
  query(frame) {
19
- return customThemeSettingsService.updateSettings(frame.data.custom_theme_settings);
19
+ return customThemeSettingsService.api.updateSettings(frame.data.custom_theme_settings);
20
20
  }
21
21
  }
22
22
  };
@@ -7,7 +7,7 @@ module.exports = {
7
7
  statusCode: 201,
8
8
  permissions: false,
9
9
  query(frame) {
10
- const store = storage.getStorage();
10
+ const store = storage.getStorage('images');
11
11
 
12
12
  if (frame.files) {
13
13
  return Promise
@@ -1,8 +1,8 @@
1
1
  const config = require('../../../shared/config');
2
2
  const externalRequest = require('../../lib/request-external');
3
- const tpl = require('@tryghost/tpl');
3
+
4
4
  const OEmbed = require('../../services/oembed');
5
- const oembed = new OEmbed({config, externalRequest, tpl});
5
+ const oembed = new OEmbed({config, externalRequest});
6
6
 
7
7
  module.exports = {
8
8
  docName: 'oembed',
@@ -1,12 +1,21 @@
1
+ const tpl = require('@tryghost/tpl');
2
+ const errors = require('@tryghost/errors');
1
3
  const offersService = require('../../services/offers');
2
4
 
5
+ const messages = {
6
+ offerNotFound: 'Offer not found.'
7
+ };
8
+
3
9
  module.exports = {
4
10
  docName: 'offers',
5
11
 
6
12
  browse: {
13
+ options: [
14
+ 'filter'
15
+ ],
7
16
  permissions: true,
8
17
  async query(frame) {
9
- const offers = await offersService.api.listOffers();
18
+ const offers = await offersService.api.listOffers(frame.options);
10
19
  frame.response = {
11
20
  offers
12
21
  };
@@ -18,6 +27,12 @@ module.exports = {
18
27
  permissions: true,
19
28
  async query(frame) {
20
29
  const offer = await offersService.api.getOffer(frame.data);
30
+ if (!offer) {
31
+ throw new errors.NotFoundError({
32
+ message: tpl(messages.offerNotFound)
33
+ });
34
+ }
35
+
21
36
  frame.response = {
22
37
  offers: [offer]
23
38
  };
@@ -27,11 +42,21 @@ module.exports = {
27
42
  edit: {
28
43
  options: ['id'],
29
44
  permissions: true,
45
+ headers: {
46
+ cacheInvalidate: true
47
+ },
30
48
  async query(frame) {
31
49
  const offer = await offersService.api.updateOffer({
32
50
  ...frame.data.offers[0],
33
51
  id: frame.options.id
34
52
  });
53
+
54
+ if (!offer) {
55
+ throw new errors.NotFoundError({
56
+ message: tpl(messages.offerNotFound)
57
+ });
58
+ }
59
+
35
60
  frame.response = {
36
61
  offers: [offer]
37
62
  };
@@ -40,6 +65,9 @@ module.exports = {
40
65
 
41
66
  add: {
42
67
  permissions: true,
68
+ headers: {
69
+ cacheInvalidate: true
70
+ },
43
71
  async query(frame) {
44
72
  const offer = await offersService.api.createOffer(frame.data.offers[0]);
45
73
  frame.response = {
@@ -1,8 +1,12 @@
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
  const allowedIncludes = ['tags', 'authors'];
5
5
 
6
+ const messages = {
7
+ postNotFound: 'Post not found.'
8
+ };
9
+
6
10
  module.exports = {
7
11
  docName: 'posts',
8
12
 
@@ -63,7 +67,7 @@ module.exports = {
63
67
  .then((model) => {
64
68
  if (!model) {
65
69
  throw new errors.NotFoundError({
66
- message: i18n.t('errors.api.posts.postNotFound')
70
+ message: tpl(messages.postNotFound)
67
71
  });
68
72
  }
69
73
 
@@ -3,10 +3,14 @@
3
3
  const errors = require('@tryghost/errors');
4
4
  const membersService = require('../../services/members');
5
5
 
6
- const i18n = require('../../../shared/i18n');
6
+ const tpl = require('@tryghost/tpl');
7
7
 
8
8
  const allowedIncludes = ['stripe_prices', 'monthly_price', 'yearly_price', 'benefits'];
9
9
 
10
+ const messages = {
11
+ productNotFound: 'Product not found.'
12
+ };
13
+
10
14
  module.exports = {
11
15
  docName: 'products',
12
16
 
@@ -56,7 +60,7 @@ module.exports = {
56
60
 
57
61
  if (!model) {
58
62
  throw new errors.NotFoundError({
59
- message: i18n.t('errors.api.products.productNotFound')
63
+ message: tpl(messages.productNotFound)
60
64
  });
61
65
  }
62
66
 
@@ -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
 
6
6
  const ALLOWED_INCLUDES = ['count.posts'];
7
7
 
8
+ const messages = {
9
+ tagNotFound: 'Tag not found.'
10
+ };
11
+
8
12
  module.exports = {
9
13
  docName: 'tags',
10
14
 
@@ -56,7 +60,7 @@ module.exports = {
56
60
  .then((model) => {
57
61
  if (!model) {
58
62
  return Promise.reject(new errors.NotFoundError({
59
- message: i18n.t('errors.api.tags.tagNotFound')
63
+ message: tpl(messages.tagNotFound)
60
64
  }));
61
65
  }
62
66
 
@@ -1,5 +1,5 @@
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 permissionsService = require('../../services/permissions');
@@ -12,11 +12,16 @@ const userService = new UsersService({dbBackup, models, auth, apiMail, apiSettin
12
12
  const ALLOWED_INCLUDES = ['count.posts', 'permissions', 'roles', 'roles.permissions'];
13
13
  const UNSAFE_ATTRS = ['status', 'roles'];
14
14
 
15
+ const messages = {
16
+ noPermissionToAction: 'You do not have permission to perform this action',
17
+ userNotFound: 'User not found.'
18
+ };
19
+
15
20
  function permissionOnlySelf(frame) {
16
21
  const targetId = getTargetId(frame);
17
22
  const userId = frame.user.id;
18
23
  if (targetId !== userId) {
19
- return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.permissions.noPermissionToAction')}));
24
+ return Promise.reject(new errors.NoPermissionError({message: tpl(messages.noPermissionToAction)}));
20
25
  }
21
26
  return Promise.resolve();
22
27
  }
@@ -88,7 +93,7 @@ module.exports = {
88
93
  .then((model) => {
89
94
  if (!model) {
90
95
  return Promise.reject(new errors.NotFoundError({
91
- message: i18n.t('errors.api.users.userNotFound')
96
+ message: tpl(messages.userNotFound)
92
97
  }));
93
98
  }
94
99
 
@@ -121,7 +126,7 @@ module.exports = {
121
126
  .then((model) => {
122
127
  if (!model) {
123
128
  return Promise.reject(new errors.NotFoundError({
124
- message: i18n.t('errors.api.users.userNotFound')
129
+ message: tpl(messages.userNotFound)
125
130
  }));
126
131
  }
127
132
 
@@ -1,13 +1,13 @@
1
1
  module.exports = {
2
2
  browse(models, apiConfig, frame) {
3
3
  frame.response = {
4
- customThemeSettings: models
4
+ custom_theme_settings: models
5
5
  };
6
6
  },
7
7
 
8
8
  edit(models, apiConfig, frame) {
9
9
  frame.response = {
10
- customThemeSettings: models
10
+ custom_theme_settings: models
11
11
  };
12
12
  }
13
13
  };
@@ -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,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,7 +1,10 @@
1
1
  const Promise = require('bluebird');
2
2
  const tpl = require('@tryghost/tpl');
3
3
  const errors = require('@tryghost/errors');
4
- const messages = {noUrlProvided: 'No url provided.'};
4
+
5
+ const messages = {
6
+ noUrlProvided: 'No url provided.'
7
+ };
5
8
 
6
9
  module.exports = {
7
10
  read(apiConfig, frame) {
@@ -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:canary: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:canary: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:canary: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
  }
@@ -7,7 +7,7 @@ module.exports = {
7
7
  statusCode: 201,
8
8
  permissions: false,
9
9
  query(frame) {
10
- const store = storage.getStorage();
10
+ const store = storage.getStorage('images');
11
11
 
12
12
  if (frame.files) {
13
13
  return Promise
@@ -1,6 +1,11 @@
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:v2:utils:serializers:output:authentication');
4
+ const messages = {
5
+ checkEmailForInstructions: 'Check your email for further instructions.',
6
+ passwordChanged: 'Password changed successfully.',
7
+ invitationAccepted: 'Invitation accepted.'
8
+ };
4
9
 
5
10
  module.exports = {
6
11
  setup(user, apiConfig, frame) {
@@ -28,7 +33,7 @@ module.exports = {
28
33
  generateResetToken(data, apiConfig, frame) {
29
34
  frame.response = {
30
35
  passwordreset: [{
31
- message: i18n.t('common.api.authentication.mail.checkEmailForInstructions')
36
+ message: tpl(messages.checkEmailForInstructions)
32
37
  }]
33
38
  };
34
39
  },
@@ -36,7 +41,7 @@ module.exports = {
36
41
  resetPassword(data, apiConfig, frame) {
37
42
  frame.response = {
38
43
  passwordreset: [{
39
- message: i18n.t('common.api.authentication.mail.passwordChanged')
44
+ message: tpl(messages.passwordChanged)
40
45
  }]
41
46
  };
42
47
  },
@@ -46,7 +51,7 @@ module.exports = {
46
51
 
47
52
  frame.response = {
48
53
  invitation: [
49
- {message: i18n.t('common.api.authentication.mail.invitationAccepted')}
54
+ {message: tpl(messages.invitationAccepted)}
50
55
  ]
51
56
  };
52
57
  },
@@ -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 = {
@@ -1,7 +1,9 @@
1
1
  const debug = require('@tryghost/debug')('api:v2: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
+ const messages = {
5
+ pwdChangedSuccessfully: 'Password changed successfully.'
6
+ };
5
7
  module.exports = {
6
8
  browse(models, apiConfig, frame) {
7
9
  debug('browse');
@@ -29,7 +31,7 @@ module.exports = {
29
31
  debug('changePassword');
30
32
 
31
33
  frame.response = {
32
- password: [{message: i18n.t('notices.api.users.pwdChangedSuccessfully')}]
34
+ password: [{message: tpl(messages.pwdChangedSuccessfully)}]
33
35
  };
34
36
  },
35
37
 
@@ -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