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.
- package/.eslintrc.js +9 -8
- package/Gruntfile.js +1 -1
- package/PRIVACY.md +3 -0
- package/content/adapters/README.md +2 -2
- package/core/boot.js +17 -12
- package/core/bridge.js +9 -1
- package/core/built/assets/{chunk.3.b80d3e1e6b8556aaff3c.js → chunk.3.777d43e2ce954ba8b2f5.js} +25 -25
- package/core/built/assets/codemirror/{codemirror-21a09582262987037db73b152fb35f7c.js → codemirror-d25c379b87ec8b33d54ac7149bc0b6ae.js} +14 -14
- package/core/built/assets/ghost-dark-20e2892d4f30d0d1183c9ac725ea37d0.css +1 -0
- package/core/built/assets/{ghost.min-88d647a008a5b1dd678a89ae1e55c038.js → ghost.min-26e427944e719b616b8dc7fbb3bbd2f9.js} +709 -422
- package/core/built/assets/ghost.min-57e46fd3b1145ecf2cbd185a13611f3b.css +1 -0
- package/core/built/assets/icons/arrow-left-small.svg +0 -4
- package/core/built/assets/icons/paintbrush.svg +10 -1
- package/core/built/assets/icons/post.svg +3 -1
- package/core/built/assets/img/footer-marketplace-bg-572b6c6486a7e26316954d599eaa9f30.png +0 -0
- package/core/built/assets/img/marketing/offers-1-f2e1b653c4d5bb90eea9d7a2862530f9.jpg +0 -0
- package/core/built/assets/img/marketing/offers-2-28a225d34cc39d133748431536961d00.jpg +0 -0
- package/core/built/assets/img/marketing/offers-3-2094c91ab21a16c37fbe6ec16c140160.jpg +0 -0
- package/core/built/assets/img/themes/Alto-f4db5af43ca9771c7ac1f754de3ddf2f.png +0 -0
- package/core/built/assets/img/themes/Bulletin-57d45b992ff0e26e0acdce7ed4cccd67.png +0 -0
- package/core/built/assets/img/themes/Casper-c7e784d7188cc5d7f097d9b6c97b0263.jpg +0 -0
- package/core/built/assets/img/themes/Dawn-be81aa8c8caae8fcfb5d5fbec823fdcc.png +0 -0
- package/core/built/assets/img/themes/Digest-d3467ac22a290e1ad3a543014758286e.png +0 -0
- package/core/built/assets/img/themes/Dope-6f8e0bbc199ce4af9a60859e9e6a74ad.png +0 -0
- package/core/built/assets/img/themes/Ease-9c279ea6cec3c0f1823f81c9dd24b116.png +0 -0
- package/core/built/assets/img/themes/Edge-0258906309e11fd075a1d9880aa09b20.png +0 -0
- package/core/built/assets/img/themes/Edition-d8f508e93bc24bdf2716ae6f8b3d44f8.png +0 -0
- package/core/built/assets/img/themes/Editorial-a25a4a34c04dedd858bd5e05ef388b1c.jpg +0 -0
- package/core/built/assets/img/themes/Journal-accf0031bbae0919900a049061e65a04.png +0 -0
- package/core/built/assets/img/themes/London-3f07efcee9e5bfb9a33827064eb77e70.jpg +0 -0
- package/core/built/assets/img/themes/Massively-06edf00108429f7fb8e65f190fba34fe.jpg +0 -0
- package/core/built/assets/img/themes/Ruby-11a53c62015612f4b3aca8f503121225.png +0 -0
- package/core/built/assets/img/themes/Wave-86e8044c2d76cb57a9030e4c24ac9520.png +0 -0
- package/core/built/assets/simplemde/{simplemde-232f69d126310434489071a1891e6d8b.js → simplemde-3ffc0ec9e9fecf29b9a499db678c9e65.js} +14 -14
- package/core/built/assets/{vendor.min-7dc7cf9c92175ebfb9cea95c120ee8a7.js → vendor.min-af502ac4142871500fc424f6a5a254ec.js} +2206 -1859
- package/core/frontend/apps/amp/lib/router.js +1 -1
- package/core/frontend/helpers/match.js +17 -23
- package/core/frontend/meta/author-url.js +1 -1
- package/core/frontend/meta/url.js +1 -1
- package/core/{server → frontend}/public/favicon.ico +0 -0
- package/core/{server → frontend}/public/ghost.css +0 -0
- package/core/{server → frontend}/public/ghost.min.css +0 -0
- package/core/{server → frontend}/public/robots.txt +0 -0
- package/core/{server → frontend}/public/sitemap.xsl +0 -0
- package/core/frontend/services/proxy.js +1 -1
- package/core/frontend/services/rendering.js +1 -1
- package/core/frontend/services/routing/CollectionRouter.js +3 -49
- package/core/frontend/services/routing/ParentRouter.js +1 -4
- package/core/frontend/services/routing/StaticPagesRouter.js +3 -5
- package/core/frontend/services/routing/StaticRoutesRouter.js +4 -6
- package/core/frontend/services/routing/TaxonomyRouter.js +4 -5
- package/core/frontend/services/routing/controllers/collection.js +2 -2
- package/core/frontend/services/routing/controllers/email-post.js +2 -2
- package/core/frontend/services/routing/controllers/entry.js +2 -2
- package/core/frontend/services/routing/controllers/preview.js +2 -2
- package/core/frontend/services/routing/index.js +6 -12
- package/core/frontend/services/routing/registry.js +13 -0
- package/core/frontend/services/routing/router-manager.js +185 -0
- package/core/frontend/services/rss/generate-feed.js +2 -2
- package/core/frontend/services/theme-engine/i18n/i18n.js +267 -28
- package/core/frontend/services/theme-engine/i18n/index.js +1 -1
- package/core/frontend/services/theme-engine/i18n/theme-i18n.js +73 -0
- package/core/frontend/web/index.js +1 -0
- package/core/{server/web/site → frontend/web}/middleware/handle-image-sizes.js +4 -4
- package/core/{server/web/site → frontend/web}/middleware/index.js +0 -0
- package/core/{server/web/site → frontend/web}/middleware/redirect-ghost-to-admin.js +3 -3
- package/core/{server/web/site → frontend/web}/middleware/serve-favicon.js +6 -6
- package/core/{server/web/site → frontend/web}/middleware/serve-public-file.js +2 -2
- package/core/{server/web/site → frontend/web}/middleware/static-theme.js +3 -3
- package/core/frontend/web/routes.js +13 -0
- package/core/{server/web/site/app.js → frontend/web/site.js} +12 -16
- package/core/server/adapters/storage/LocalFileStorage.js +35 -39
- package/core/server/adapters/storage/index.js +12 -2
- package/core/server/api/canary/custom-theme-settings.js +2 -2
- package/core/server/api/canary/images.js +1 -1
- package/core/server/api/canary/oembed.js +2 -2
- package/core/server/api/canary/offers.js +29 -1
- package/core/server/api/canary/posts-public.js +6 -2
- package/core/server/api/canary/products.js +6 -2
- package/core/server/api/canary/tags-public.js +6 -2
- package/core/server/api/canary/users.js +9 -4
- package/core/server/api/canary/utils/serializers/output/custom-theme-settings.js +2 -2
- package/core/server/api/canary/utils/serializers/output/notifications.js +1 -0
- package/core/server/api/canary/utils/serializers/output/settings.js +2 -3
- package/core/server/api/canary/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/canary/utils/validators/input/oembed.js +4 -1
- package/core/server/api/canary/utils/validators/input/passwordreset.js +8 -3
- package/core/server/api/canary/utils/validators/input/settings.js +5 -4
- package/core/server/api/canary/utils/validators/input/setup.js +6 -2
- package/core/server/api/canary/utils/validators/input/users.js +6 -2
- package/core/server/api/canary/utils/validators/input/webhooks.js +8 -3
- package/core/server/api/v2/images.js +1 -1
- package/core/server/api/v2/utils/serializers/output/authentication.js +9 -4
- package/core/server/api/v2/utils/serializers/output/notifications.js +1 -0
- package/core/server/api/v2/utils/serializers/output/users.js +5 -3
- package/core/server/api/v2/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v2/utils/validators/input/images.js +11 -6
- package/core/server/api/v2/utils/validators/input/invitations.js +14 -6
- package/core/server/api/v2/utils/validators/input/invites.js +6 -2
- package/core/server/api/v2/utils/validators/input/oembed.js +6 -2
- package/core/server/api/v2/utils/validators/input/passwordreset.js +8 -3
- package/core/server/api/v2/utils/validators/input/settings.js +10 -4
- package/core/server/api/v2/utils/validators/input/setup.js +6 -2
- package/core/server/api/v2/utils/validators/input/users.js +5 -2
- package/core/server/api/v3/authentication.js +6 -2
- package/core/server/api/v3/authors-public.js +6 -2
- package/core/server/api/v3/email.js +9 -4
- package/core/server/api/v3/images.js +1 -1
- package/core/server/api/v3/integrations.js +7 -3
- package/core/server/api/v3/invites.js +6 -3
- package/core/server/api/v3/labels.js +10 -5
- package/core/server/api/v3/memberSigninUrls.js +5 -2
- package/core/server/api/v3/oembed.js +2 -2
- package/core/server/api/v3/pages-public.js +5 -2
- package/core/server/api/v3/pages.js +6 -3
- package/core/server/api/v3/posts-public.js +5 -3
- package/core/server/api/v3/posts.js +7 -3
- package/core/server/api/v3/preview.js +5 -3
- package/core/server/api/v3/session.js +7 -3
- package/core/server/api/v3/settings.js +8 -3
- package/core/server/api/v3/slugs.js +5 -4
- package/core/server/api/v3/utils/serializers/output/authentication.js +10 -4
- package/core/server/api/v3/utils/serializers/output/notifications.js +1 -0
- package/core/server/api/v3/utils/serializers/output/settings.js +2 -3
- package/core/server/api/v3/utils/serializers/output/users.js +6 -2
- package/core/server/api/v3/utils/serializers/output/utils/url.js +1 -1
- package/core/server/api/v3/utils/validators/input/images.js +12 -7
- package/core/server/api/v3/utils/validators/input/invitations.js +14 -6
- package/core/server/api/v3/utils/validators/input/invites.js +6 -2
- package/core/server/api/v3/utils/validators/input/oembed.js +6 -2
- package/core/server/api/v3/utils/validators/input/passwordreset.js +8 -3
- package/core/server/api/v3/utils/validators/input/settings.js +5 -4
- package/core/server/api/v3/utils/validators/input/setup.js +6 -2
- package/core/server/api/v3/utils/validators/input/users.js +6 -2
- package/core/server/api/v3/utils/validators/input/webhooks.js +8 -3
- package/core/server/data/exporter/table-lists.js +2 -1
- package/core/server/data/importer/handlers/image.js +1 -1
- package/core/server/data/importer/importers/image.js +1 -1
- package/core/server/data/migrations/init/1-create-tables.js +7 -8
- package/core/server/data/migrations/init/2-create-fixtures.js +8 -8
- package/core/server/data/migrations/versions/4.19/01-add-active-column-to-offers.js +7 -0
- package/core/server/data/migrations/versions/4.19/02-add-offer-redemptions-table.js +8 -0
- package/core/server/data/migrations/versions/4.20/01-remove-offer-redemptions-table.js +19 -0
- package/core/server/data/migrations/versions/4.20/02-remove-offers-table.js +30 -0
- package/core/server/data/migrations/versions/4.20/03-add-offers-table.js +21 -0
- package/core/server/data/migrations/versions/4.20/04-add-offer-redemptions-table.js +9 -0
- package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js +41 -0
- package/core/server/data/schema/fixtures/utils.js +150 -143
- package/core/server/data/schema/schema.js +15 -3
- package/core/server/frontend/ghost.min.css +1 -0
- package/core/server/lib/image/blog-icon.js +10 -10
- package/core/server/lib/image/image-size.js +5 -5
- package/core/server/lib/image/image-utils.js +4 -4
- package/core/server/lib/image/index.js +1 -2
- package/core/server/lib/mobiledoc.js +3 -2
- package/core/server/models/action.js +7 -4
- package/core/server/models/base/plugins/overrides.js +19 -6
- package/core/server/models/custom-theme-setting.js +56 -1
- package/core/server/models/index.js +4 -45
- package/core/server/models/member.js +5 -0
- package/core/server/models/offer-redemption.js +10 -0
- package/core/server/models/user.js +2 -1
- package/core/server/overrides.js +6 -2
- package/core/server/run-update-check.js +0 -3
- package/core/server/services/adapter-manager/config.js +1 -0
- package/core/server/services/adapter-manager/index.js +9 -5
- package/core/server/services/adapter-manager/options-resolver.js +18 -0
- package/core/server/services/bulk-email/bulk-email-processor.js +6 -2
- package/core/server/services/bulk-email/mailgun.js +1 -1
- package/core/server/services/custom-theme-settings.js +10 -4
- package/core/server/services/invites/index.js +0 -2
- package/core/server/services/invites/invites.js +5 -5
- package/core/server/services/mail/GhostMailer.js +18 -10
- package/core/server/services/mega/mega.js +3 -3
- package/core/server/services/mega/post-email-serializer.js +2 -2
- package/core/server/services/members/api.js +3 -4
- package/core/server/services/members/emails/signin.js +1 -1
- package/core/server/services/members/emails/signup.js +1 -1
- package/core/server/services/members/emails/subscribe.js +1 -1
- package/core/server/services/members/middleware.js +10 -0
- package/core/server/services/members/service.js +2 -1
- package/core/server/services/notifications/index.js +1 -1
- package/core/server/services/notifications/notifications.js +40 -35
- package/core/server/services/oembed.js +4 -9
- package/core/server/services/offers/service.js +16 -6
- package/core/server/services/permissions/public.js +6 -2
- package/core/server/services/route-settings/route-settings.js +1 -1
- package/core/server/services/settings/index.js +3 -1
- package/core/server/services/settings/settings-bread-service.js +42 -20
- package/core/server/services/slack.js +1 -1
- package/core/server/services/themes/activate.js +2 -2
- package/core/server/services/themes/activation-bridge.js +6 -6
- package/core/server/services/themes/storage.js +1 -1
- package/core/{frontend → server}/services/url/Queue.js +0 -0
- package/core/{frontend → server}/services/url/Resource.js +0 -0
- package/core/{frontend → server}/services/url/Resources.js +2 -2
- package/core/{frontend → server}/services/url/UrlGenerator.js +14 -14
- package/core/{frontend → server}/services/url/UrlService.js +12 -15
- package/core/{frontend → server}/services/url/Urls.js +1 -1
- package/core/{frontend → server}/services/url/configs/canary.js +0 -0
- package/core/{frontend → server}/services/url/configs/v2.js +0 -0
- package/core/{frontend → server}/services/url/configs/v3.js +0 -0
- package/core/{frontend → server}/services/url/configs/v4.js +0 -0
- package/core/{frontend → server}/services/url/index.js +0 -0
- package/core/server/services/xmlrpc.js +1 -1
- package/core/server/update-check.js +3 -3
- package/core/server/web/admin/controller.js +11 -0
- package/core/server/web/admin/views/default-prod.html +4 -4
- package/core/server/web/admin/views/default.html +4 -4
- package/core/server/web/api/app.js +8 -9
- package/core/server/web/members/app.js +1 -0
- package/core/server/web/oauth/app.js +4 -2
- package/core/server/web/parent/backend.js +3 -3
- package/core/server/web/parent/frontend.js +2 -2
- package/core/server/web/shared/middlewares/api/spam-prevention.js +0 -2
- package/core/server/web/shared/middlewares/custom-redirects.js +0 -8
- package/core/server/web/shared/middlewares/maintenance.js +1 -1
- package/core/server/web/well-known.js +10 -10
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/overrides.json +1 -1
- package/core/shared/express.js +10 -0
- package/core/shared/html-to-plaintext.js +2 -2
- package/core/shared/labs.js +14 -6
- package/loggingrc.js +10 -0
- package/package.json +60 -57
- package/yarn.lock +1317 -914
- package/core/built/assets/ghost-dark-13627f10941a7dbb2b12e1d41dc51c34.css +0 -1
- package/core/built/assets/ghost.min-d9cbfb4eb2db8915fcd2bf2416218616.css +0 -1
- package/core/built/assets/img/themes/London-68501c8ab797de7f2851cf9ea0a28e26.jpg +0 -0
- package/core/frontend/services/routing/bootstrap.js +0 -114
- package/core/server/public/404-ghost.png +0 -0
- package/core/server/public/404-ghost@2x.png +0 -0
- package/core/server/web/site/index.js +0 -1
- package/core/server/web/site/routes.js +0 -9
- package/core/shared/i18n/i18n.js +0 -312
- package/core/shared/i18n/index.js +0 -6
- package/core/shared/i18n/translations/en.json +0 -675
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const utils = require('../../utils');
|
|
2
|
+
|
|
3
|
+
const migration = utils.addTable('offers', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
6
|
+
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
7
|
+
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
8
|
+
product_id: {type: 'string', maxlength: 24, nullable: false, references: 'products.id'},
|
|
9
|
+
stripe_coupon_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
|
|
10
|
+
interval: {type: 'string', maxlength: 50, nullable: false},
|
|
11
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
12
|
+
discount_type: {type: 'string', maxlength: 50, nullable: false},
|
|
13
|
+
discount_amount: {type: 'integer', nullable: false},
|
|
14
|
+
duration: {type: 'string', maxlength: 50, nullable: false},
|
|
15
|
+
duration_in_months: {type: 'integer', nullable: true},
|
|
16
|
+
portal_title: {type: 'string', maxlength: 191, nullable: false},
|
|
17
|
+
portal_description: {type: 'string', maxlength: 2000, nullable: true},
|
|
18
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
19
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// This reverses an "addTable" migration so that we
|
|
23
|
+
// drop the table going forwards and re-add it going back
|
|
24
|
+
const up = migration.down;
|
|
25
|
+
const down = migration.up;
|
|
26
|
+
|
|
27
|
+
migration.up = up;
|
|
28
|
+
migration.down = down;
|
|
29
|
+
|
|
30
|
+
module.exports = migration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const utils = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = utils.addTable('offers', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
6
|
+
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
7
|
+
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
8
|
+
product_id: {type: 'string', maxlength: 24, nullable: false, references: 'products.id'},
|
|
9
|
+
stripe_coupon_id: {type: 'string', maxlength: 255, nullable: true, unique: true},
|
|
10
|
+
interval: {type: 'string', maxlength: 50, nullable: false},
|
|
11
|
+
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
12
|
+
discount_type: {type: 'string', maxlength: 50, nullable: false},
|
|
13
|
+
discount_amount: {type: 'integer', nullable: false},
|
|
14
|
+
duration: {type: 'string', maxlength: 50, nullable: false},
|
|
15
|
+
duration_in_months: {type: 'integer', nullable: true},
|
|
16
|
+
portal_title: {type: 'string', maxlength: 191, nullable: false},
|
|
17
|
+
portal_description: {type: 'string', maxlength: 2000, nullable: true},
|
|
18
|
+
created_at: {type: 'dateTime', nullable: false},
|
|
19
|
+
updated_at: {type: 'dateTime', nullable: true}
|
|
20
|
+
});
|
|
21
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
9
|
+
});
|
package/core/server/data/migrations/versions/4.20/05-remove-not-null-constraint-from-portal-title.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const logging = require('@tryghost/logging');
|
|
2
|
+
const {createNonTransactionalMigration} = require('../../utils');
|
|
3
|
+
const {addUnique} = require('../../../schema/commands');
|
|
4
|
+
|
|
5
|
+
module.exports = createNonTransactionalMigration(
|
|
6
|
+
async function up(knex) {
|
|
7
|
+
logging.info('Dropping NOT NULL constraint for: portal_title in table: offers');
|
|
8
|
+
|
|
9
|
+
await knex.schema.table('offers', function (table) {
|
|
10
|
+
table.dropColumn('portal_title');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await knex.schema.table('offers', function (table) {
|
|
14
|
+
table.string('portal_title', 191).nullable();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (knex.client.config.client === 'sqlite3') {
|
|
18
|
+
for (const column of ['name', 'code', 'stripe_coupon_id']) {
|
|
19
|
+
await addUnique('offers', column, knex);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
async function down(knex) {
|
|
24
|
+
logging.info('Adding NOT NULL constraint for: portal_title in table: offers');
|
|
25
|
+
|
|
26
|
+
await knex.schema.table('offers', function (table) {
|
|
27
|
+
table.dropColumn('portal_title');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await knex.schema.table('offers', function (table) {
|
|
31
|
+
table.string('portal_title', 191).notNullable();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (knex.client.config.client === 'sqlite3') {
|
|
35
|
+
for (const column of ['name', 'code', 'stripe_coupon_id']) {
|
|
36
|
+
await addUnique('offers', column, knex);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
@@ -47,7 +47,7 @@ const matchObj = function matchObj(match, item) {
|
|
|
47
47
|
const matchedObj = {};
|
|
48
48
|
|
|
49
49
|
if (_.isArray(match)) {
|
|
50
|
-
_.each(match,
|
|
50
|
+
_.each(match, (matchProp) => {
|
|
51
51
|
matchedObj[matchProp] = item.get(matchProp);
|
|
52
52
|
});
|
|
53
53
|
} else {
|
|
@@ -76,115 +76,9 @@ const fetchRelationData = function fetchRelationData(relation, options) {
|
|
|
76
76
|
return Promise.props(props);
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* Takes a model fixture, with a name and some entries and processes these
|
|
82
|
-
* into a sequence of promises to get each fixture added.
|
|
83
|
-
*
|
|
84
|
-
* @param {{name, entries}} modelFixture
|
|
85
|
-
* @returns {Promise.<*>}
|
|
79
|
+
/*
|
|
80
|
+
* Find methods - use the local fixtures
|
|
86
81
|
*/
|
|
87
|
-
const addFixturesForModel = function addFixturesForModel(modelFixture, options = {}) {
|
|
88
|
-
// Clone the fixtures as they get changed in this function.
|
|
89
|
-
// The initial blog posts will be added a `published_at` property, which
|
|
90
|
-
// would change the fixturesHash.
|
|
91
|
-
modelFixture = _.cloneDeep(modelFixture);
|
|
92
|
-
// The Post model fixtures need a `published_at` date, where at least the seconds
|
|
93
|
-
// are different, otherwise `prev_post` and `next_post` helpers won't workd with
|
|
94
|
-
// them.
|
|
95
|
-
if (modelFixture.name === 'Post') {
|
|
96
|
-
_.forEach(modelFixture.entries, function (post, index) {
|
|
97
|
-
if (!post.published_at) {
|
|
98
|
-
post.published_at = moment().add(index, 'seconds');
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return Promise.mapSeries(modelFixture.entries, function (entry) {
|
|
104
|
-
let data = {};
|
|
105
|
-
|
|
106
|
-
// CASE: if id is specified, only query by id
|
|
107
|
-
if (entry.id) {
|
|
108
|
-
data.id = entry.id;
|
|
109
|
-
} else if (entry.slug) {
|
|
110
|
-
data.slug = entry.slug;
|
|
111
|
-
} else {
|
|
112
|
-
data = _.cloneDeep(entry);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (modelFixture.name === 'Post') {
|
|
116
|
-
data.status = 'all';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return models[modelFixture.name].findOne(data, options).then(function (found) {
|
|
120
|
-
if (!found) {
|
|
121
|
-
return models[modelFixture.name].add(entry, options);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
}).then(function (results) {
|
|
125
|
-
return {expected: modelFixture.entries.length, done: _.compact(results).length};
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* ## Add Fixtures for Relation
|
|
131
|
-
* Takes a relation fixtures object, with a from, to and some entries and processes these
|
|
132
|
-
* into a sequence of promises, to get each fixture added.
|
|
133
|
-
*
|
|
134
|
-
* @param {{from, to, entries}} relationFixture
|
|
135
|
-
* @returns {Promise.<*>}
|
|
136
|
-
*/
|
|
137
|
-
const addFixturesForRelation = function addFixturesForRelation(relationFixture, options) {
|
|
138
|
-
const ops = [];
|
|
139
|
-
let max = 0;
|
|
140
|
-
|
|
141
|
-
return fetchRelationData(relationFixture, options).then(function getRelationOps(data) {
|
|
142
|
-
_.each(relationFixture.entries, function processEntries(entry, key) {
|
|
143
|
-
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
|
|
144
|
-
|
|
145
|
-
// CASE: You add new fixtures e.g. a new role in a new release.
|
|
146
|
-
// As soon as an **older** migration script wants to add permissions for any resource, it iterates over the
|
|
147
|
-
// permissions for each role. But if the role does not exist yet, it won't find the matching db entry and breaks.
|
|
148
|
-
if (!fromItem) {
|
|
149
|
-
logging.warn('Skip: Target database entry not found for key: ' + key);
|
|
150
|
-
return Promise.resolve();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
_.each(entry, function processEntryValues(value, entryKey) {
|
|
154
|
-
let toItems = data.to.filter(matchFunc(relationFixture.to.match, entryKey, value));
|
|
155
|
-
max += toItems.length;
|
|
156
|
-
|
|
157
|
-
// Remove any duplicates that already exist in the collection
|
|
158
|
-
toItems = _.reject(toItems, function (item) {
|
|
159
|
-
return fromItem
|
|
160
|
-
.related(relationFixture.from.relation)
|
|
161
|
-
.find((model) => {
|
|
162
|
-
const objectToMatch = matchObj(relationFixture.to.match, item);
|
|
163
|
-
return Object.keys(objectToMatch).every(function (keyToCheck) {
|
|
164
|
-
return model.get(keyToCheck) === objectToMatch[keyToCheck];
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
if (toItems && toItems.length > 0) {
|
|
170
|
-
ops.push(function addRelationItems() {
|
|
171
|
-
return baseUtils.attach(
|
|
172
|
-
models[relationFixture.from.Model || relationFixture.from.model],
|
|
173
|
-
fromItem.id,
|
|
174
|
-
relationFixture.from.relation,
|
|
175
|
-
toItems,
|
|
176
|
-
options
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return sequence(ops);
|
|
184
|
-
}).then(function (result) {
|
|
185
|
-
return {expected: max, done: _(result).map('length').sum()};
|
|
186
|
-
});
|
|
187
|
-
};
|
|
188
82
|
|
|
189
83
|
/**
|
|
190
84
|
* ### Find Model Fixture
|
|
@@ -194,7 +88,7 @@ const addFixturesForRelation = function addFixturesForRelation(relationFixture,
|
|
|
194
88
|
* @returns {Object} model fixture
|
|
195
89
|
*/
|
|
196
90
|
const findModelFixture = function findModelFixture(modelName) {
|
|
197
|
-
return _.find(fixtures.models,
|
|
91
|
+
return _.find(fixtures.models, (modelFixture) => {
|
|
198
92
|
return modelFixture.name === modelName;
|
|
199
93
|
});
|
|
200
94
|
};
|
|
@@ -232,7 +126,7 @@ const findModelFixtures = function findModelFixtures(modelName, matchExpr) {
|
|
|
232
126
|
* @returns {Object} relation fixture
|
|
233
127
|
*/
|
|
234
128
|
const findRelationFixture = function findRelationFixture(from, to) {
|
|
235
|
-
return _.find(fixtures.relations,
|
|
129
|
+
return _.find(fixtures.relations, (relation) => {
|
|
236
130
|
return relation.from.model === from && relation.to.model === to;
|
|
237
131
|
});
|
|
238
132
|
};
|
|
@@ -247,8 +141,8 @@ const findPermissionRelationsForObject = function findPermissionRelationsForObje
|
|
|
247
141
|
// Make a copy and delete any entries we don't want
|
|
248
142
|
const foundRelation = _.cloneDeep(findRelationFixture('Role', 'Permission'));
|
|
249
143
|
|
|
250
|
-
_.each(foundRelation.entries,
|
|
251
|
-
_.each(entry,
|
|
144
|
+
_.each(foundRelation.entries, (entry, key) => {
|
|
145
|
+
_.each(entry, (perm, obj) => {
|
|
252
146
|
if (obj !== objName) {
|
|
253
147
|
delete entry[obj];
|
|
254
148
|
}
|
|
@@ -262,53 +156,166 @@ const findPermissionRelationsForObject = function findPermissionRelationsForObje
|
|
|
262
156
|
return foundRelation;
|
|
263
157
|
};
|
|
264
158
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
159
|
+
/*
|
|
160
|
+
* Add and Remove Functions, require access to models
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* ### Add Fixtures for Model
|
|
165
|
+
* Takes a model fixture, with a name and some entries and processes these
|
|
166
|
+
* into a sequence of promises to get each fixture added.
|
|
167
|
+
*
|
|
168
|
+
* @param {{name, entries}} modelFixture
|
|
169
|
+
* @returns {Promise<any>}
|
|
170
|
+
*/
|
|
171
|
+
const addFixturesForModel = async function addFixturesForModel(modelFixture, options = {}) {
|
|
172
|
+
// Clone the fixtures as they get changed in this function.
|
|
173
|
+
// The initial blog posts will be added a `published_at` property, which
|
|
174
|
+
// would change the fixturesHash.
|
|
175
|
+
modelFixture = _.cloneDeep(modelFixture);
|
|
176
|
+
// The Post model fixtures need a `published_at` date, where at least the seconds
|
|
177
|
+
// are different, otherwise `prev_post` and `next_post` helpers won't workd with
|
|
178
|
+
// them.
|
|
179
|
+
if (modelFixture.name === 'Post') {
|
|
180
|
+
_.forEach(modelFixture.entries, (post, index) => {
|
|
181
|
+
if (!post.published_at) {
|
|
182
|
+
post.published_at = moment().add(index, 'seconds');
|
|
270
183
|
}
|
|
271
184
|
});
|
|
272
|
-
}
|
|
273
|
-
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const results = await Promise.mapSeries(modelFixture.entries, async (entry) => {
|
|
188
|
+
let data = {};
|
|
189
|
+
|
|
190
|
+
// CASE: if id is specified, only query by id
|
|
191
|
+
if (entry.id) {
|
|
192
|
+
data.id = entry.id;
|
|
193
|
+
} else if (entry.slug) {
|
|
194
|
+
data.slug = entry.slug;
|
|
195
|
+
} else {
|
|
196
|
+
data = _.cloneDeep(entry);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (modelFixture.name === 'Post') {
|
|
200
|
+
data.status = 'all';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const found = await models[modelFixture.name].findOne(data, options);
|
|
204
|
+
if (!found) {
|
|
205
|
+
return models[modelFixture.name].add(entry, options);
|
|
206
|
+
}
|
|
274
207
|
});
|
|
208
|
+
|
|
209
|
+
return {expected: modelFixture.entries.length, done: _.compact(results).length};
|
|
275
210
|
};
|
|
276
211
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
212
|
+
/**
|
|
213
|
+
* ## Add Fixtures for Relation
|
|
214
|
+
* Takes a relation fixtures object, with a from, to and some entries and processes these
|
|
215
|
+
* into a sequence of promises, to get each fixture added.
|
|
216
|
+
*
|
|
217
|
+
* @param {{from, to, entries}} relationFixture
|
|
218
|
+
* @returns {Promise<any>}
|
|
219
|
+
*/
|
|
220
|
+
const addFixturesForRelation = async function addFixturesForRelation(relationFixture, options) {
|
|
221
|
+
const ops = [];
|
|
222
|
+
let max = 0;
|
|
223
|
+
|
|
224
|
+
const data = await fetchRelationData(relationFixture, options);
|
|
225
|
+
|
|
226
|
+
_.each(relationFixture.entries, (entry, key) => {
|
|
227
|
+
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
|
|
228
|
+
|
|
229
|
+
// CASE: You add new fixtures e.g. a new role in a new release.
|
|
230
|
+
// As soon as an **older** migration script wants to add permissions for any resource, it iterates over the
|
|
231
|
+
// permissions for each role. But if the role does not exist yet, it won't find the matching db entry and breaks.
|
|
232
|
+
if (!fromItem) {
|
|
233
|
+
logging.warn('Skip: Target database entry not found for key: ' + key);
|
|
234
|
+
return Promise.resolve();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
_.each(entry, (value, entryKey) => {
|
|
238
|
+
let toItems = data.to.filter(matchFunc(relationFixture.to.match, entryKey, value));
|
|
239
|
+
max += toItems.length;
|
|
240
|
+
|
|
241
|
+
// Remove any duplicates that already exist in the collection
|
|
242
|
+
toItems = _.reject(toItems, (item) => {
|
|
243
|
+
return fromItem
|
|
244
|
+
.related(relationFixture.from.relation)
|
|
245
|
+
.find((model) => {
|
|
246
|
+
const objectToMatch = matchObj(relationFixture.to.match, item);
|
|
247
|
+
return Object.keys(objectToMatch).every((keyToCheck) => {
|
|
248
|
+
return model.get(keyToCheck) === objectToMatch[keyToCheck];
|
|
249
|
+
});
|
|
296
250
|
});
|
|
297
|
-
}
|
|
298
251
|
});
|
|
252
|
+
|
|
253
|
+
if (toItems && toItems.length > 0) {
|
|
254
|
+
ops.push(function addRelationItems() {
|
|
255
|
+
return baseUtils.attach(
|
|
256
|
+
models[relationFixture.from.Model || relationFixture.from.model],
|
|
257
|
+
fromItem.id,
|
|
258
|
+
relationFixture.from.relation,
|
|
259
|
+
toItems,
|
|
260
|
+
options
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
299
264
|
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const result = await sequence(ops);
|
|
268
|
+
return {expected: max, done: _(result).map('length').sum()};
|
|
269
|
+
};
|
|
300
270
|
|
|
301
|
-
|
|
271
|
+
const removeFixturesForModel = async function removeFixturesForModel(modelFixture, options) {
|
|
272
|
+
const results = await Promise.mapSeries(modelFixture.entries, async (entry) => {
|
|
273
|
+
const found = models[modelFixture.name].findOne(entry.id ? {id: entry.id} : entry, options);
|
|
274
|
+
if (found) {
|
|
275
|
+
return models[modelFixture.name].destroy(_.extend(options, {id: found.id}));
|
|
276
|
+
}
|
|
302
277
|
});
|
|
278
|
+
|
|
279
|
+
return {expected: modelFixture.entries.length, done: results.length};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const removeFixturesForRelation = async function removeFixturesForRelation(relationFixture, options) {
|
|
283
|
+
const data = await fetchRelationData(relationFixture, options);
|
|
284
|
+
const ops = [];
|
|
285
|
+
|
|
286
|
+
_.each(relationFixture.entries, (entry, key) => {
|
|
287
|
+
const fromItem = data.from.find(matchFunc(relationFixture.from.match, key));
|
|
288
|
+
|
|
289
|
+
_.each(entry, (value, entryKey) => {
|
|
290
|
+
const toItems = data.to.filter(matchFunc(relationFixture.to.match, entryKey, value));
|
|
291
|
+
|
|
292
|
+
if (toItems && toItems.length > 0) {
|
|
293
|
+
ops.push(function detachRelation() {
|
|
294
|
+
return baseUtils.detach(
|
|
295
|
+
models[relationFixture.from.Model || relationFixture.from.model],
|
|
296
|
+
fromItem.id,
|
|
297
|
+
relationFixture.from.relation,
|
|
298
|
+
toItems,
|
|
299
|
+
options
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return await sequence(ops);
|
|
303
307
|
};
|
|
304
308
|
|
|
305
309
|
module.exports = {
|
|
306
|
-
|
|
307
|
-
addFixturesForRelation: addFixturesForRelation,
|
|
310
|
+
// find
|
|
308
311
|
findModelFixtureEntry: findModelFixtureEntry,
|
|
309
312
|
findModelFixtures: findModelFixtures,
|
|
310
313
|
findRelationFixture: findRelationFixture,
|
|
311
314
|
findPermissionRelationsForObject: findPermissionRelationsForObject,
|
|
315
|
+
|
|
316
|
+
// add / remove
|
|
317
|
+
addFixturesForModel: addFixturesForModel,
|
|
318
|
+
addFixturesForRelation: addFixturesForRelation,
|
|
312
319
|
removeFixturesForModel: removeFixturesForModel,
|
|
313
320
|
removeFixturesForRelation: removeFixturesForRelation
|
|
314
321
|
};
|
|
@@ -386,17 +386,18 @@ module.exports = {
|
|
|
386
386
|
},
|
|
387
387
|
offers: {
|
|
388
388
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
389
|
+
active: {type: 'boolean', nullable: false, defaultTo: true},
|
|
389
390
|
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
390
391
|
code: {type: 'string', maxlength: 191, nullable: false, unique: true},
|
|
391
392
|
product_id: {type: 'string', maxlength: 24, nullable: false, references: 'products.id'},
|
|
392
|
-
stripe_coupon_id: {type: 'string', maxlength: 255, nullable:
|
|
393
|
+
stripe_coupon_id: {type: 'string', maxlength: 255, nullable: true, unique: true},
|
|
393
394
|
interval: {type: 'string', maxlength: 50, nullable: false, validations: {isIn: [['month', 'year']]}},
|
|
394
395
|
currency: {type: 'string', maxlength: 50, nullable: true},
|
|
395
396
|
discount_type: {type: 'string', maxlength: 50, nullable: false, validations: {isIn: [['percent', 'amount']]}},
|
|
396
397
|
discount_amount: {type: 'integer', nullable: false},
|
|
397
398
|
duration: {type: 'string', maxlength: 50, nullable: false},
|
|
398
399
|
duration_in_months: {type: 'integer', nullable: true},
|
|
399
|
-
portal_title: {type: 'string', maxlength: 191, nullable:
|
|
400
|
+
portal_title: {type: 'string', maxlength: 191, nullable: true},
|
|
400
401
|
portal_description: {type: 'string', maxlength: 2000, nullable: true},
|
|
401
402
|
created_at: {type: 'dateTime', nullable: false},
|
|
402
403
|
updated_at: {type: 'dateTime', nullable: true}
|
|
@@ -528,6 +529,13 @@ module.exports = {
|
|
|
528
529
|
plan_amount: {type: 'integer', nullable: false},
|
|
529
530
|
plan_currency: {type: 'string', maxLength: 3, nullable: false}
|
|
530
531
|
},
|
|
532
|
+
offer_redemptions: {
|
|
533
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
534
|
+
offer_id: {type: 'string', maxlength: 24, nullable: false, references: 'offers.id', cascadeDelete: true},
|
|
535
|
+
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
|
|
536
|
+
subscription_id: {type: 'string', maxlength: 24, nullable: false, references: 'members_stripe_customers_subscriptions.id', cascadeDelete: true},
|
|
537
|
+
created_at: {type: 'dateTime', nullable: false}
|
|
538
|
+
},
|
|
531
539
|
members_subscribe_events: {
|
|
532
540
|
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
533
541
|
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
|
|
@@ -671,7 +679,11 @@ module.exports = {
|
|
|
671
679
|
nullable: false,
|
|
672
680
|
validations: {
|
|
673
681
|
isIn: [[
|
|
674
|
-
'select'
|
|
682
|
+
'select',
|
|
683
|
+
'boolean',
|
|
684
|
+
'color',
|
|
685
|
+
'text',
|
|
686
|
+
'image'
|
|
675
687
|
]]
|
|
676
688
|
}
|
|
677
689
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-family:sans-serif}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.darkgrey{color:#343f44}.midgrey{color:#738a94}.lightgrey{color:#e5eff5}.blue{color:#3eb0ef}.red{color:#f05230}.orange{color:#fecd35}.green{color:#a4d037}.darkgrey-hover:hover{color:#343f44}.midgrey-hover:hover{color:#738a94}.lightgrey-hover:hover{color:#e5eff5}.blue-hover:hover{color:#3eb0ef}.red-hover:hover{color:#f05230}.orange-hover:hover{color:#fecd35}.green-hover:hover{color:#a4d037}*,:after,:before{box-sizing:border-box}html{-webkit-tap-highlight-color:rgba(0,0,0,0);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:62.5%;letter-spacing:.2px;line-height:1.65;overflow:hidden}body,html{height:100%;width:100%}body{color:#343f44;font-size:1.4rem;overflow:auto;overflow-x:hidden}.gh-view{-ms-flex-positive:1;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;flex-grow:1}h1,h2{text-rendering:optimizeLegibility;color:#343f44;font-size:2.9rem;line-height:1.15em;margin:0 0 .3em;text-indent:-1px}@media (max-width:500px){h1{font-size:2.4rem}}.gh-input{-webkit-appearance:none;border:1px solid #d6e3eb;border-radius:4px;color:#4b5b62;display:block;font-size:1.6rem;font-weight:300;height:40px;line-height:1em;padding:10px 12px;transition:border-color .15s linear;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;width:100%}.gh-input:focus{border-color:#b4cbda;outline:0}.gh-btn{fill:#829aa8;-webkit-font-smoothing:subpixel-antialiased;border:1px solid #d6e3eb;border-radius:5px;color:#829aa8;display:inline-block;outline:none;text-decoration:none!important;text-shadow:0 1px 0 #fff;transition:all .2s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.gh-btn span{border-radius:4px;display:block;font-size:1.3rem;font-weight:400;height:33px;letter-spacing:.2px;line-height:33px;padding:0 12px;text-align:center}.gh-btn:hover{border-color:#b4cbda}.gh-btn-hover-blue:hover{border-color:#3eb0ef;color:#3eb0ef}.gh-btn-blue{fill:#fff;background:linear-gradient(#3da1d6,#2288bf);border:0;box-shadow:0 1px 0 rgba(0,0,0,.12);color:#fff;padding:1px;text-shadow:0 -1px 0 rgba(0,0,0,.1);transition:none!important}.gh-btn-blue span{background:linear-gradient(#4ab6f0,#2fa5e4 60%,#2fa5e4 90%,#38a9e5);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.gh-btn-blue:active,.gh-btn-blue:focus{background:#1e78a9}.gh-btn-blue:active span,.gh-btn-blue:focus span{background:#29a0e0;box-shadow:none}.gh-btn-block{display:block;width:100%}.gh-input-icon{display:block;position:relative}.gh-input-icon svg{fill:color(var(--midgrey) l(15%));height:14px;left:10px;position:absolute;top:50%;transform:translateY(-7px);width:auto;z-index:2}.gh-input-icon input{padding-left:35px}.gh-app{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;height:100%;overflow:hidden}.gh-viewport{max-height:100%;overflow:hidden}.gh-main,.gh-viewport{-ms-flex-positive:1;display:-ms-flexbox;display:flex;flex-grow:1}.gh-main{background:#fff;overflow-y:auto;position:relative}.gh-flow{-ms-flex-positive:1;-ms-flex-direction:column;flex-direction:column;flex-grow:1;min-height:100%;overflow-y:auto}.gh-flow,.gh-flow-head{display:-ms-flexbox;display:flex}.gh-flow-head{-ms-flex-negative:0;-ms-flex-pack:justify;flex-shrink:0;justify-content:space-between;padding-bottom:20px;padding-top:4vh}.gh-flow-content-wrap{-ms-flex-positive:1;-ms-flex-negative:0;-ms-flex-pack:center;flex-grow:1;flex-shrink:0;justify-content:center;margin:0 5%;padding-bottom:8vh}.gh-flow-back,.gh-flow-content-wrap{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex}.gh-flow-back{border:1px solid transparent;border-radius:4px;color:#7d878a;font-weight:100;left:0;margin:0 0 0 3%;padding:2px 9px 2px 5px;position:absolute;top:0;transition:all .3s ease}.gh-flow-back svg{height:12px;line-height:14px;margin-right:4px}.gh-flow-back svg path{stroke:#7d878a;stroke-width:1.2px}.gh-flow-back:hover{border:1px solid #dae1e3}.gh-flow-back-plain{-ms-flex-align:center;align-items:center;color:#7d878a;display:-ms-flexbox;display:flex;font-weight:300;left:0;margin:0 0 0 3%;padding:2px 9px 2px 5px;position:absolute;text-decoration:none;top:0;transition:all .3s ease}.gh-flow-back-plain svg{height:12px;line-height:14px;margin-right:4px}.gh-flow-back-plain svg path{stroke:#7d878a;stroke-width:1.2px}.gh-flow-back-plain:hover{color:#15212a}.gh-flow-nav{-ms-flex:1;flex:1;position:relative}.gh-flow-content{color:#738a94;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;font-size:1.9rem;font-weight:100;line-height:1.5em;max-width:700px;text-align:center;width:100%}.gh-flow-content .gh-input-icon input{padding-left:35px}.gh-flow-content-unsubscribe{font-weight:300}@media (max-width:500px){.gh-flow-head-unsubscribe{padding-top:2.8vh}.gh-flow-content{font-size:4vw}.gh-flow-content-unsubscribe{font-size:1.8rem;line-height:1.6em}}.gh-flow-content header{margin:0 auto;max-width:520px}.gh-flow-content h1{font-size:4.2rem;font-weight:100}@media (max-width:600px){.gh-flow-content h1{font-size:7vw}}.gh-flow-content .gh-btn{display:block;margin:20px auto 0;max-width:400px}.gh-flow-content .form-group{margin-bottom:2.5rem}.gh-flow-content input{border:1px solid #dae1e3;font-size:1.6rem;font-weight:100;line-height:1.4em;padding:10px}.gh-flow-em{font-weight:500}.gh-signin{background:#f8fbfd;border:1px solid #dae1e3;border-radius:5px;margin:30px auto;max-width:400px;padding:40px;position:relative;text-align:left;width:100%}.gh-signin .form-group{margin-bottom:1.5rem}.gh-signin .gh-btn{margin:0}.error-content{flex-grow:1;justify-content:center;padding:8vw;user-select:text}.error-content,.error-details{align-items:center;display:flex}.error-details{margin-bottom:4rem}.error-ghost{height:115px;margin:15px}@media (max-width:630px){.error-ghost{display:none}}.error-code{color:#c5d2d9;font-size:10vw;font-weight:600;letter-spacing:-.4vw;line-height:.9em;margin:0}.error-description{border:none;color:#54666d;font-size:2.3rem;font-weight:300;line-height:1.3em;margin:0;padding:0}.error-message{align-items:center;display:flex;flex-direction:column;margin:15px}.error-message a{font-size:1.4rem;line-height:1;margin:8px 0}.error-link{background-color:transparent;color:#5ba4e5;text-decoration:none;transition:background .3s,color .3s}.error-stack{background-color:hsla(0,0%,100%,.3);margin:1rem auto;max-width:800px;padding:2rem}.error-stack-list{list-style-type:none;margin:0;padding:0}.error-stack-list li{display:block}.error-stack-list li:before{color:#bbb;content:"\21AA";display:inline-block;font-size:1.2rem;margin-right:.5rem}.error-stack-function{font-weight:700}
|
|
@@ -3,15 +3,15 @@ const Promise = require('bluebird');
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const errors = require('@tryghost/errors');
|
|
6
|
+
const tpl = require('@tryghost/tpl');
|
|
6
7
|
|
|
7
8
|
const messages = {
|
|
8
9
|
error: 'Could not fetch icon dimensions.'
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
class BlogIcon {
|
|
12
|
-
constructor({config,
|
|
13
|
+
constructor({config, urlUtils, settingsCache, storageUtils}) {
|
|
13
14
|
this.config = config;
|
|
14
|
-
this.tpl = tpl;
|
|
15
15
|
this.urlUtils = urlUtils;
|
|
16
16
|
this.settingsCache = settingsCache;
|
|
17
17
|
this.storageUtils = storageUtils;
|
|
@@ -27,10 +27,10 @@ class BlogIcon {
|
|
|
27
27
|
getIconDimensions(storagePath) {
|
|
28
28
|
return new Promise((resolve, reject) => {
|
|
29
29
|
let dimensions;
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
try {
|
|
32
32
|
dimensions = sizeOf(storagePath);
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
if (dimensions.images) {
|
|
35
35
|
dimensions.width = _.maxBy(dimensions.images, function (w) {
|
|
36
36
|
return w.width;
|
|
@@ -39,14 +39,14 @@ class BlogIcon {
|
|
|
39
39
|
return h.height;
|
|
40
40
|
}).height;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
return resolve({
|
|
44
44
|
width: dimensions.width,
|
|
45
45
|
height: dimensions.height
|
|
46
46
|
});
|
|
47
47
|
} catch (err) {
|
|
48
48
|
return reject(new errors.ValidationError({
|
|
49
|
-
message:
|
|
49
|
+
message: tpl(messages.error, {
|
|
50
50
|
file: storagePath,
|
|
51
51
|
error: err.message
|
|
52
52
|
})
|
|
@@ -64,7 +64,7 @@ class BlogIcon {
|
|
|
64
64
|
*/
|
|
65
65
|
isIcoImageType(icon) {
|
|
66
66
|
const blogIcon = icon || this.settingsCache.get('icon');
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
return blogIcon.match(/.ico$/i) ? true : false;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -77,7 +77,7 @@ class BlogIcon {
|
|
|
77
77
|
*/
|
|
78
78
|
getIconType(icon) {
|
|
79
79
|
const blogIcon = icon || this.settingsCache.get('icon');
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
return this.isIcoImageType(blogIcon) ? 'x-icon' : 'png';
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -90,7 +90,7 @@ class BlogIcon {
|
|
|
90
90
|
*/
|
|
91
91
|
getIconUrl(absolut) {
|
|
92
92
|
const blogIcon = this.settingsCache.get('icon');
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
if (absolut) {
|
|
95
95
|
if (blogIcon) {
|
|
96
96
|
return this.isIcoImageType(blogIcon) ? this.urlUtils.urlFor({relativeUrl: '/favicon.ico'}, true) : this.urlUtils.urlFor({relativeUrl: '/favicon.png'}, true);
|
|
@@ -115,7 +115,7 @@ class BlogIcon {
|
|
|
115
115
|
*/
|
|
116
116
|
getIconPath() {
|
|
117
117
|
const blogIcon = this.settingsCache.get('icon');
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
if (blogIcon) {
|
|
120
120
|
return this.storageUtils.getLocalFileStoragePath(blogIcon);
|
|
121
121
|
} else {
|
|
@@ -6,6 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const Promise = require('bluebird');
|
|
7
7
|
const _ = require('lodash');
|
|
8
8
|
const errors = require('@tryghost/errors');
|
|
9
|
+
const tpl = require('@tryghost/tpl');
|
|
9
10
|
|
|
10
11
|
const messages = {
|
|
11
12
|
invalidDimensions: 'Could not fetch image dimensions.'
|
|
@@ -17,9 +18,8 @@ const FETCH_ONLY_FORMATS = [
|
|
|
17
18
|
];
|
|
18
19
|
|
|
19
20
|
class ImageSize {
|
|
20
|
-
constructor({config,
|
|
21
|
+
constructor({config, storage, storageUtils, validator, urlUtils, request}) {
|
|
21
22
|
this.config = config;
|
|
22
|
-
this.tpl = tpl;
|
|
23
23
|
this.storage = storage;
|
|
24
24
|
this.storageUtils = storageUtils;
|
|
25
25
|
this.validator = validator;
|
|
@@ -216,7 +216,7 @@ class ImageSize {
|
|
|
216
216
|
// get the storage readable filePath
|
|
217
217
|
filePath = this.storageUtils.getLocalFileStoragePath(imagePath);
|
|
218
218
|
|
|
219
|
-
return this.storage.getStorage()
|
|
219
|
+
return this.storage.getStorage('images')
|
|
220
220
|
.read({path: filePath})
|
|
221
221
|
.then((buf) => {
|
|
222
222
|
debug('Image fetched (storage):', filePath);
|
|
@@ -267,7 +267,7 @@ class ImageSize {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
const originalImagePath = path.join(dir, `${imageName}_o${imageNumber || ''}${ext}`);
|
|
270
|
-
const originalImageExists = await this.storage.getStorage().exists(originalImagePath);
|
|
270
|
+
const originalImageExists = await this.storage.getStorage('images').exists(originalImagePath);
|
|
271
271
|
|
|
272
272
|
return this.getImageSizeFromStoragePath(originalImageExists ? originalImagePath : imagePath);
|
|
273
273
|
}
|
|
@@ -320,7 +320,7 @@ class ImageSize {
|
|
|
320
320
|
});
|
|
321
321
|
} catch (err) {
|
|
322
322
|
return reject(new errors.ValidationError({
|
|
323
|
-
message:
|
|
323
|
+
message: tpl(messages.invalidDimensions, {
|
|
324
324
|
file: imagePath,
|
|
325
325
|
error: err.message
|
|
326
326
|
})
|