ghost 4.41.1 → 4.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/boot.js +24 -6
- package/core/built/assets/ghost-dark-97613c037232aba4490489431ce170ca.css +1 -0
- package/core/built/assets/{ghost.min-8e2e6c7a01fde044c566c1650a36bfc2.js → ghost.min-20096eef632760c3a2906e243adbd24b.js} +1035 -841
- package/core/built/assets/ghost.min-c08ce1872f0e09edb63eb13c43606d18.css +1 -0
- package/core/built/assets/{vendor.min-9094db77ba3190cb10876f8e42e1d90d.js → vendor.min-21f79c68a284acb1b70039f3f63e5507.js} +68 -68
- package/core/built/assets/{vendor.min-2c8ad32b7960bb605ebc20097fee5ebd.css → vendor.min-ba66b98f7c24fa40e061c7ffc94f4e23.css} +214 -0
- package/core/frontend/helpers/price.js +1 -0
- package/core/frontend/web/middleware/error-handler.js +5 -3
- package/core/server/api/canary/email-preview.js +2 -1
- package/core/server/api/canary/{email.js → emails.js} +0 -0
- package/core/server/api/canary/index.js +11 -3
- package/core/server/api/canary/{memberSigninUrls.js → member-signin-urls.js} +0 -1
- package/core/server/api/canary/{membersStripeConnect.js → members-stripe-connect.js} +0 -0
- package/core/server/api/canary/members.js +0 -45
- package/core/server/api/canary/newsletters.js +45 -0
- package/core/server/api/canary/stats.js +14 -0
- package/core/server/api/canary/utils/serializers/output/authentication.js +4 -0
- package/core/server/api/canary/utils/serializers/output/default.js +35 -0
- package/core/server/api/canary/utils/serializers/output/email-previews.js +7 -0
- package/core/server/api/canary/utils/serializers/output/index.js +18 -42
- package/core/server/api/canary/utils/serializers/output/mappers/authors.js +1 -0
- package/core/server/api/canary/utils/serializers/output/mappers/index.js +2 -1
- package/core/server/api/canary/utils/serializers/output/mappers/integrations.js +1 -1
- package/core/server/api/canary/utils/serializers/output/mappers/snippets.js +36 -0
- package/core/server/api/canary/utils/serializers/output/members-stripe-connect.js +6 -0
- package/core/server/api/canary/utils/serializers/output/members.js +2 -2
- package/core/server/api/canary/utils/serializers/output/oembed.js +2 -2
- package/core/server/api/canary/utils/serializers/output/offers.js +8 -0
- package/core/server/api/canary/utils/serializers/output/redirects.js +2 -2
- package/core/server/api/canary/utils/serializers/output/schedules.js +2 -2
- package/core/server/api/canary/utils/serializers/output/session.js +9 -0
- package/core/server/api/canary/utils/serializers/output/settings.js +64 -37
- package/core/server/api/canary/utils/serializers/output/slack.js +9 -0
- package/core/server/api/canary/utils/serializers/output/themes.js +3 -24
- package/core/server/api/canary/utils/serializers/output/users.js +0 -23
- package/core/server/api/shared/serializers/handle.js +25 -11
- package/core/server/data/exporter/table-lists.js +1 -0
- package/core/server/data/migrations/utils.js +1 -1
- package/core/server/data/migrations/versions/4.42/2022-03-21-17-17-add.js +20 -0
- package/core/server/data/migrations/versions/4.42/2022-03-30-15-44-add-newsletter-permissions.js +28 -0
- package/core/server/data/schema/commands.js +13 -13
- package/core/server/data/schema/schema.js +18 -0
- package/core/server/models/newsletter.js +9 -0
- package/core/server/services/mega/template.js +25 -13
- package/core/server/services/members/service.js +2 -1
- package/core/server/services/offers/service.js +11 -8
- package/core/server/services/stats/index.js +1 -0
- package/core/server/services/stats/lib/members-stats-service.js +165 -0
- package/core/server/services/stats/service.js +6 -0
- package/core/server/services/themes/validate.js +4 -3
- package/core/server/services/webhooks/webhooks-service.js +3 -1
- package/core/server/web/admin/app.js +8 -0
- package/core/server/web/admin/views/default-prod.html +5 -5
- package/core/server/web/admin/views/default.html +5 -5
- package/core/server/web/api/canary/admin/routes.js +8 -2
- package/core/shared/config/defaults.json +2 -2
- package/core/shared/config/env/config.development.json +26 -0
- package/core/shared/config/env/config.production.json +21 -0
- package/core/shared/config/env/config.testing-mysql.json +59 -0
- package/core/shared/config/env/config.testing.json +58 -0
- package/core/shared/labs.js +3 -1
- package/core/shared/settings-cache/cache.js +1 -1
- package/package.json +66 -66
- package/yarn.lock +1609 -1781
- package/.c8rc.json +0 -34
- package/.eslintrc.js +0 -118
- package/core/built/assets/ghost-dark-6fbe502f2bb2cde92e15b2f1a9da57a0.css +0 -1
- package/core/built/assets/ghost.min-09301e5bd933cf6d24368e98a4d898a9.css +0 -1
- package/core/server/api/canary/utils/serializers/output/actions.js +0 -13
- package/core/server/api/canary/utils/serializers/output/authors.js +0 -21
- package/core/server/api/canary/utils/serializers/output/email-preview.js +0 -7
- package/core/server/api/canary/utils/serializers/output/emails.js +0 -22
- package/core/server/api/canary/utils/serializers/output/identities.js +0 -7
- package/core/server/api/canary/utils/serializers/output/integrations.js +0 -34
- package/core/server/api/canary/utils/serializers/output/invites.js +0 -24
- package/core/server/api/canary/utils/serializers/output/labels.js +0 -25
- package/core/server/api/canary/utils/serializers/output/mappers/labels.js +0 -4
- package/core/server/api/canary/utils/serializers/output/member-signin_urls.js +0 -7
- package/core/server/api/canary/utils/serializers/output/snippets.js +0 -97
- package/core/server/api/canary/utils/serializers/output/tags.js +0 -25
- package/core/server/api/canary/utils/serializers/output/webhooks.js +0 -15
- package/jsconfig.json +0 -13
|
@@ -9,6 +9,8 @@ module.exports = {
|
|
|
9
9
|
read: createSerializer('read', singleMember),
|
|
10
10
|
edit: createSerializer('edit', singleMember),
|
|
11
11
|
add: createSerializer('add', singleMember),
|
|
12
|
+
destroy: createSerializer('destroy', passthrough),
|
|
13
|
+
|
|
12
14
|
editSubscription: createSerializer('editSubscription', singleMember),
|
|
13
15
|
createSubscription: createSerializer('createSubscription', singleMember),
|
|
14
16
|
bulkDestroy: createSerializer('bulkDestroy', passthrough),
|
|
@@ -18,8 +20,6 @@ module.exports = {
|
|
|
18
20
|
importCSV: createSerializer('importCSV', passthrough),
|
|
19
21
|
memberStats: createSerializer('memberStats', passthrough),
|
|
20
22
|
mrrStats: createSerializer('mrrStats', passthrough),
|
|
21
|
-
subscriberStats: createSerializer('subscriberStats', passthrough),
|
|
22
|
-
grossVolumeStats: createSerializer('grossVolumeStats', passthrough),
|
|
23
23
|
activityFeed: createSerializer('activityFeed', passthrough)
|
|
24
24
|
};
|
|
25
25
|
|
|
@@ -1,65 +1,92 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const utils = require('../../index');
|
|
3
3
|
const mappers = require('./mappers');
|
|
4
|
-
const _private = {};
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
* ### Settings Filter
|
|
8
6
|
* Filters an object based on a given filter object
|
|
9
7
|
* @private
|
|
10
8
|
* @param {Object} settings
|
|
11
9
|
* @param {String} filter
|
|
12
10
|
* @returns {*}
|
|
13
11
|
*/
|
|
14
|
-
|
|
15
|
-
let filteredGroups = filter ? filter.split(',') :
|
|
12
|
+
function settingsFilter(settings, filter) {
|
|
13
|
+
let filteredGroups = filter ? filter.split(',') : [];
|
|
16
14
|
return _.filter(settings, (setting) => {
|
|
17
|
-
if (filteredGroups) {
|
|
15
|
+
if (filteredGroups.length > 0) {
|
|
18
16
|
return _.includes(filteredGroups, setting.group);
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
return true;
|
|
22
20
|
});
|
|
23
|
-
}
|
|
21
|
+
}
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Serialies a settings object into the desired API repsonse format
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} models
|
|
27
|
+
* @param {Object} apiConfig
|
|
28
|
+
* @param {Object} frame
|
|
29
|
+
*/
|
|
30
|
+
function serializeSettings(models, apiConfig, frame) {
|
|
31
|
+
let filteredSettings;
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// If this is public, we already have the right data, we just need to add an Array wrapper
|
|
34
|
+
if (utils.isContentAPI(frame)) {
|
|
35
|
+
filteredSettings = models;
|
|
36
|
+
} else {
|
|
37
|
+
filteredSettings = _.values(settingsFilter(models, frame.options.group));
|
|
38
|
+
}
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
frame.response = {
|
|
41
|
+
settings: mappers.settings(filteredSettings, frame),
|
|
42
|
+
meta: {}
|
|
43
|
+
};
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
if (frame.options.type || frame.options.group) {
|
|
46
|
+
frame.response.meta.filters = {};
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if (frame.options.type) {
|
|
49
|
+
frame.response.meta.filters.type = frame.options.type;
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
52
|
+
if (frame.options.group) {
|
|
53
|
+
frame.response.meta.filters.group = frame.options.group;
|
|
51
54
|
}
|
|
52
|
-
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
53
57
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
/**
|
|
59
|
+
* This noop results in there being no response body
|
|
60
|
+
*
|
|
61
|
+
* @template Data
|
|
62
|
+
* @param {Data} data
|
|
63
|
+
* @returns Data
|
|
64
|
+
*/
|
|
65
|
+
function passthrough(data) {
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns the data as-is without any further modiications
|
|
71
|
+
*
|
|
72
|
+
* @template Data
|
|
73
|
+
* @param {Data} data
|
|
74
|
+
* @param {Object} apiConfig
|
|
75
|
+
* @param {Object} frame
|
|
76
|
+
*/
|
|
77
|
+
function serializeData(data, apiConfig, frame) {
|
|
78
|
+
frame.response = data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = {
|
|
82
|
+
browse: serializeSettings,
|
|
83
|
+
read: serializeSettings,
|
|
84
|
+
edit: serializeSettings,
|
|
57
85
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
86
|
+
download: serializeData,
|
|
87
|
+
upload: serializeData,
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
updateMembersEmail: passthrough,
|
|
90
|
+
validateMembersEmailUpdate: passthrough,
|
|
91
|
+
disconnectStripeConnectIntegration: passthrough
|
|
65
92
|
};
|
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:themes');
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
|
-
|
|
5
|
-
debug('
|
|
4
|
+
all(data, apiConfig, frame) {
|
|
5
|
+
debug('all');
|
|
6
6
|
|
|
7
|
-
frame.response =
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
upload() {
|
|
11
|
-
debug('upload');
|
|
12
|
-
this.browse(...arguments);
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
install() {
|
|
16
|
-
debug('install');
|
|
17
|
-
this.browse(...arguments);
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
activate() {
|
|
21
|
-
debug('activate');
|
|
22
|
-
this.browse(...arguments);
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
download(fn, apiConfig, frame) {
|
|
26
|
-
debug('download');
|
|
27
|
-
|
|
28
|
-
frame.response = fn;
|
|
7
|
+
frame.response = data;
|
|
29
8
|
}
|
|
30
9
|
};
|
|
@@ -1,34 +1,11 @@
|
|
|
1
1
|
const debug = require('@tryghost/debug')('api:canary:utils:serializers:output:users');
|
|
2
2
|
const tpl = require('@tryghost/tpl');
|
|
3
|
-
const mappers = require('./mappers');
|
|
4
3
|
|
|
5
4
|
const messages = {
|
|
6
5
|
pwdChangedSuccessfully: 'Password changed successfully.'
|
|
7
6
|
};
|
|
8
7
|
|
|
9
8
|
module.exports = {
|
|
10
|
-
browse(models, apiConfig, frame) {
|
|
11
|
-
debug('browse');
|
|
12
|
-
|
|
13
|
-
frame.response = {
|
|
14
|
-
users: models.data.map(model => mappers.users(model, frame)),
|
|
15
|
-
meta: models.meta
|
|
16
|
-
};
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
read(model, apiConfig, frame) {
|
|
20
|
-
debug('read');
|
|
21
|
-
|
|
22
|
-
frame.response = {
|
|
23
|
-
users: [mappers.users(model, frame)]
|
|
24
|
-
};
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
edit() {
|
|
28
|
-
debug('edit');
|
|
29
|
-
this.read(...arguments);
|
|
30
|
-
},
|
|
31
|
-
|
|
32
9
|
destroy(filename, apiConfig, frame) {
|
|
33
10
|
debug('destroy');
|
|
34
11
|
|
|
@@ -67,6 +67,19 @@ module.exports.input = (apiConfig, apiSerializers, frame) => {
|
|
|
67
67
|
return sequence(tasks);
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
const getBestMatchSerializer = function (apiSerializers, docName, method) {
|
|
71
|
+
if (apiSerializers[docName] && apiSerializers[docName][method]) {
|
|
72
|
+
debug(`Calling ${docName}.${method}`);
|
|
73
|
+
return apiSerializers[docName][method].bind(apiSerializers[docName]);
|
|
74
|
+
} else if (apiSerializers[docName] && apiSerializers[docName].all) {
|
|
75
|
+
debug(`Calling ${docName}.all`);
|
|
76
|
+
return apiSerializers[docName].all.bind(apiSerializers[docName]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
debug(`Returning as-is`);
|
|
80
|
+
return false;
|
|
81
|
+
};
|
|
82
|
+
|
|
70
83
|
/**
|
|
71
84
|
* @description Shared output serialization handler.
|
|
72
85
|
*
|
|
@@ -101,18 +114,19 @@ module.exports.output = (response = {}, apiConfig, apiSerializers, frame) => {
|
|
|
101
114
|
});
|
|
102
115
|
}
|
|
103
116
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
tasks.push(function serializeOptionsShared() {
|
|
107
|
-
return apiSerializers[apiConfig.docName].all(response, apiConfig, frame);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
117
|
+
const customSerializer = getBestMatchSerializer(apiSerializers, apiConfig.docName, apiConfig.method);
|
|
118
|
+
const defaultSerializer = getBestMatchSerializer(apiSerializers, 'default', apiConfig.method);
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
120
|
+
if (customSerializer) {
|
|
121
|
+
// CASE: custom serializer exists
|
|
122
|
+
tasks.push(function doCustomSerializer() {
|
|
123
|
+
return customSerializer(response, apiConfig, frame);
|
|
124
|
+
});
|
|
125
|
+
} else if (defaultSerializer) {
|
|
126
|
+
// CASE: Fall back to default serializer
|
|
127
|
+
tasks.push(function doDefaultSerializer() {
|
|
128
|
+
return defaultSerializer(response, apiConfig, frame);
|
|
129
|
+
});
|
|
116
130
|
}
|
|
117
131
|
|
|
118
132
|
if (apiSerializers.all && apiSerializers.all.after) {
|
|
@@ -178,7 +178,7 @@ function addPermissionToRole(config) {
|
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
logging.
|
|
181
|
+
logging.info(`Adding permission(${config.permission}) to role(${config.role})`);
|
|
182
182
|
await connection('permissions_roles').insert({
|
|
183
183
|
id: ObjectId().toHexString(),
|
|
184
184
|
permission_id: permission.id,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const {addTable} = require('../../utils');
|
|
2
|
+
|
|
3
|
+
module.exports = addTable('newsletters', {
|
|
4
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
5
|
+
name: {type: 'string', maxlength: 191, nullable: false},
|
|
6
|
+
description: {type: 'string', maxlength: 2000, nullable: true},
|
|
7
|
+
sender_name: {type: 'string', maxlength: 191, nullable: false},
|
|
8
|
+
sender_email: {type: 'string', maxlength: 191, nullable: false, validations: {isEmail: true}},
|
|
9
|
+
sender_reply_to: {type: 'string', maxlength: 191, nullable: false, validations: {isEmail: true}},
|
|
10
|
+
default: {type: 'bool', nullable: false, defaultTo: false},
|
|
11
|
+
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'active'},
|
|
12
|
+
recipient_filter: {
|
|
13
|
+
type: 'text',
|
|
14
|
+
maxlength: 1000000000,
|
|
15
|
+
nullable: false,
|
|
16
|
+
defaultTo: ''
|
|
17
|
+
},
|
|
18
|
+
subscribe_on_signup: {type: 'bool', nullable: false, defaultTo: false},
|
|
19
|
+
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
|
|
20
|
+
});
|
package/core/server/data/migrations/versions/4.42/2022-03-30-15-44-add-newsletter-permissions.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const {
|
|
2
|
+
addPermissionWithRoles,
|
|
3
|
+
combineTransactionalMigrations
|
|
4
|
+
} = require('../../utils');
|
|
5
|
+
|
|
6
|
+
module.exports = combineTransactionalMigrations(
|
|
7
|
+
addPermissionWithRoles({
|
|
8
|
+
name: 'Browse newsletters',
|
|
9
|
+
action: 'browse',
|
|
10
|
+
object: 'newsletter'
|
|
11
|
+
}, [
|
|
12
|
+
'Administrator'
|
|
13
|
+
]),
|
|
14
|
+
addPermissionWithRoles({
|
|
15
|
+
name: 'Add newsletters',
|
|
16
|
+
action: 'add',
|
|
17
|
+
object: 'newsletter'
|
|
18
|
+
}, [
|
|
19
|
+
'Administrator'
|
|
20
|
+
]),
|
|
21
|
+
addPermissionWithRoles({
|
|
22
|
+
name: 'Edit newsletters',
|
|
23
|
+
action: 'edit',
|
|
24
|
+
object: 'newsletter'
|
|
25
|
+
}, [
|
|
26
|
+
'Administrator'
|
|
27
|
+
])
|
|
28
|
+
);
|
|
@@ -80,18 +80,18 @@ function dropColumn(tableName, column, transaction = db.knex) {
|
|
|
80
80
|
*/
|
|
81
81
|
async function addUnique(tableName, columns, transaction = db.knex) {
|
|
82
82
|
try {
|
|
83
|
-
logging.info(`Adding unique constraint for
|
|
83
|
+
logging.info(`Adding unique constraint for '${columns}' in table '${tableName}'`);
|
|
84
84
|
|
|
85
85
|
return await transaction.schema.table(tableName, function (table) {
|
|
86
86
|
table.unique(columns);
|
|
87
87
|
});
|
|
88
88
|
} catch (err) {
|
|
89
89
|
if (err.code === 'SQLITE_ERROR') {
|
|
90
|
-
logging.warn(`Constraint for
|
|
90
|
+
logging.warn(`Constraint for '${columns}' already exists for table '${tableName}'`);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
if (err.code === 'ER_DUP_KEYNAME') {
|
|
94
|
-
logging.warn(`Constraint for
|
|
94
|
+
logging.warn(`Constraint for '${columns}' already exists for table '${tableName}'`);
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
throw err;
|
|
@@ -107,18 +107,18 @@ async function addUnique(tableName, columns, transaction = db.knex) {
|
|
|
107
107
|
*/
|
|
108
108
|
async function dropUnique(tableName, columns, transaction = db.knex) {
|
|
109
109
|
try {
|
|
110
|
-
logging.info(`Dropping unique constraint for
|
|
110
|
+
logging.info(`Dropping unique constraint for '${columns}' in table '${tableName}'`);
|
|
111
111
|
|
|
112
112
|
return await transaction.schema.table(tableName, function (table) {
|
|
113
113
|
table.dropUnique(columns);
|
|
114
114
|
});
|
|
115
115
|
} catch (err) {
|
|
116
116
|
if (err.code === 'SQLITE_ERROR') {
|
|
117
|
-
logging.warn(`Constraint for
|
|
117
|
+
logging.warn(`Constraint for '${columns}' does not exist for table '${tableName}'`);
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
if (err.code === 'ER_CANT_DROP_FIELD_OR_KEY') {
|
|
121
|
-
logging.warn(`Constraint for
|
|
121
|
+
logging.warn(`Constraint for '${columns}' does not exist for table '${tableName}'`);
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
throw err;
|
|
@@ -164,7 +164,7 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
|
|
|
164
164
|
if (DatabaseInfo.isSQLite(transaction)) {
|
|
165
165
|
const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
|
|
166
166
|
if (foreignKeyExists) {
|
|
167
|
-
logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} -
|
|
167
|
+
logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - already exists`);
|
|
168
168
|
return;
|
|
169
169
|
}
|
|
170
170
|
}
|
|
@@ -195,7 +195,7 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
|
|
|
195
195
|
}
|
|
196
196
|
} catch (err) {
|
|
197
197
|
if (err.code === 'ER_DUP_KEY' || err.code === 'ER_FK_DUP_KEY' || err.code === 'ER_FK_DUP_NAME') {
|
|
198
|
-
logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} -
|
|
198
|
+
logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - already exists`);
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
201
|
throw err;
|
|
@@ -216,7 +216,7 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
|
|
|
216
216
|
if (DatabaseInfo.isSQLite(transaction)) {
|
|
217
217
|
const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
|
|
218
218
|
if (!foreignKeyExists) {
|
|
219
|
-
logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} -
|
|
219
|
+
logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - does not exist`);
|
|
220
220
|
return;
|
|
221
221
|
}
|
|
222
222
|
}
|
|
@@ -243,7 +243,7 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
|
|
|
243
243
|
}
|
|
244
244
|
} catch (err) {
|
|
245
245
|
if (err.code === 'ER_CANT_DROP_FIELD_OR_KEY') {
|
|
246
|
-
logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} -
|
|
246
|
+
logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - does not exist`);
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
249
|
throw err;
|
|
@@ -280,18 +280,18 @@ async function addPrimaryKey(tableName, columns, transaction = db.knex) {
|
|
|
280
280
|
if (DatabaseInfo.isSQLite(transaction)) {
|
|
281
281
|
const primaryKeyExists = await hasPrimaryKeySQLite(tableName, transaction);
|
|
282
282
|
if (primaryKeyExists) {
|
|
283
|
-
logging.warn(`Primary key constraint for
|
|
283
|
+
logging.warn(`Primary key constraint for '${columns}' already exists for table '${tableName}'`);
|
|
284
284
|
return;
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
try {
|
|
288
|
-
logging.info(`Adding primary key constraint for
|
|
288
|
+
logging.info(`Adding primary key constraint for '${columns}' in table '${tableName}'`);
|
|
289
289
|
return await transaction.schema.table(tableName, function (table) {
|
|
290
290
|
table.primary(columns);
|
|
291
291
|
});
|
|
292
292
|
} catch (err) {
|
|
293
293
|
if (err.code === 'ER_MULTIPLE_PRI_KEY') {
|
|
294
|
-
logging.warn(`Primary key constraint for
|
|
294
|
+
logging.warn(`Primary key constraint for '${columns}' already exists for table '${tableName}'`);
|
|
295
295
|
return;
|
|
296
296
|
}
|
|
297
297
|
throw err;
|
|
@@ -711,5 +711,23 @@ module.exports = {
|
|
|
711
711
|
}
|
|
712
712
|
},
|
|
713
713
|
value: {type: 'text', maxlength: 65535, nullable: true}
|
|
714
|
+
},
|
|
715
|
+
newsletters: {
|
|
716
|
+
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
|
717
|
+
name: {type: 'string', maxlength: 191, nullable: false},
|
|
718
|
+
description: {type: 'string', maxlength: 2000, nullable: true},
|
|
719
|
+
sender_name: {type: 'string', maxlength: 191, nullable: false},
|
|
720
|
+
sender_email: {type: 'string', maxlength: 191, nullable: false, validations: {isEmail: true}},
|
|
721
|
+
sender_reply_to: {type: 'string', maxlength: 191, nullable: false, validations: {isEmail: true}},
|
|
722
|
+
default: {type: 'bool', nullable: false, defaultTo: false},
|
|
723
|
+
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'active'},
|
|
724
|
+
recipient_filter: {
|
|
725
|
+
type: 'text',
|
|
726
|
+
maxlength: 1000000000,
|
|
727
|
+
nullable: false,
|
|
728
|
+
defaultTo: ''
|
|
729
|
+
},
|
|
730
|
+
subscribe_on_signup: {type: 'bool', nullable: false, defaultTo: false},
|
|
731
|
+
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
|
|
714
732
|
}
|
|
715
733
|
};
|
|
@@ -198,48 +198,55 @@ h5,
|
|
|
198
198
|
h6 {
|
|
199
199
|
margin-top: 0;
|
|
200
200
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
201
|
-
line-height: 1.
|
|
202
|
-
font-weight:
|
|
201
|
+
line-height: 1.11em;
|
|
202
|
+
font-weight: 700;
|
|
203
203
|
text-rendering: optimizeLegibility;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
h1 {
|
|
207
207
|
margin: 1.5em 0 0.5em 0;
|
|
208
208
|
font-size: 42px;
|
|
209
|
-
font-weight:
|
|
209
|
+
font-weight: 700;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
h2 {
|
|
213
213
|
margin: 1.5em 0 0.5em 0;
|
|
214
214
|
font-size: 32px;
|
|
215
|
-
line-height: 1.22em;
|
|
216
215
|
}
|
|
217
216
|
|
|
218
217
|
h3 {
|
|
219
218
|
margin: 1.5em 0 0.5em 0;
|
|
220
219
|
font-size: 26px;
|
|
221
|
-
line-height: 1.25em;
|
|
222
220
|
}
|
|
223
221
|
|
|
224
222
|
h4 {
|
|
225
223
|
margin: 1.8em 0 0.5em 0;
|
|
226
224
|
font-size: 21px;
|
|
227
|
-
line-height: 1.
|
|
225
|
+
line-height: 1.2em;
|
|
228
226
|
}
|
|
229
227
|
|
|
230
228
|
h5 {
|
|
231
229
|
margin: 2em 0 0.5em 0;
|
|
232
230
|
font-size: 19px;
|
|
233
|
-
line-height: 1.
|
|
231
|
+
line-height: 1.3em;
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
h6 {
|
|
237
235
|
margin: 2em 0 0.5em 0;
|
|
238
236
|
font-size: 19px;
|
|
239
|
-
line-height: 1.
|
|
237
|
+
line-height: 1.3em;
|
|
240
238
|
font-weight: 700;
|
|
241
239
|
}
|
|
242
240
|
|
|
241
|
+
h1 strong,
|
|
242
|
+
h2 strong,
|
|
243
|
+
h3 strong,
|
|
244
|
+
h4 strong,
|
|
245
|
+
h5 strong,
|
|
246
|
+
h6 strong {
|
|
247
|
+
font-weight: 800;
|
|
248
|
+
}
|
|
249
|
+
|
|
243
250
|
strong {
|
|
244
251
|
font-weight: 700;
|
|
245
252
|
}
|
|
@@ -301,6 +308,10 @@ figure blockquote p {
|
|
|
301
308
|
|
|
302
309
|
.site-info {
|
|
303
310
|
padding-top: 50px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.site-info-bordered {
|
|
314
|
+
padding-top: 50px;
|
|
304
315
|
border-bottom: 1px solid #e5eff5;
|
|
305
316
|
}
|
|
306
317
|
|
|
@@ -322,11 +333,12 @@ figure blockquote p {
|
|
|
322
333
|
padding-bottom: 10px;
|
|
323
334
|
font-size: 42px;
|
|
324
335
|
line-height: 1.1em;
|
|
325
|
-
font-weight:
|
|
336
|
+
font-weight: 700;
|
|
326
337
|
text-align: center;
|
|
327
338
|
}
|
|
328
339
|
.post-title-serif {
|
|
329
340
|
font-family: Georgia, serif;
|
|
341
|
+
letter-spacing: -0.01em;
|
|
330
342
|
}
|
|
331
343
|
.post-title-left {
|
|
332
344
|
text-align: left;
|
|
@@ -388,7 +400,7 @@ figure blockquote p {
|
|
|
388
400
|
font-family: Georgia, serif;
|
|
389
401
|
font-size: 18px;
|
|
390
402
|
line-height: 1.5em;
|
|
391
|
-
color: #
|
|
403
|
+
color: #15212A;
|
|
392
404
|
padding-bottom: 20px;
|
|
393
405
|
border-bottom: 1px solid #e5eff5;
|
|
394
406
|
}
|
|
@@ -397,7 +409,7 @@ figure blockquote p {
|
|
|
397
409
|
max-width: 600px !important;
|
|
398
410
|
font-size: 17px;
|
|
399
411
|
line-height: 1.5em;
|
|
400
|
-
color: #
|
|
412
|
+
color: #15212A;
|
|
401
413
|
padding-bottom: 20px;
|
|
402
414
|
border-bottom: 1px solid #e5eff5;
|
|
403
415
|
}
|
|
@@ -711,7 +723,7 @@ a[data-flickr-embed] img {
|
|
|
711
723
|
}
|
|
712
724
|
|
|
713
725
|
.kg-header-card h3 strong {
|
|
714
|
-
font-weight:
|
|
726
|
+
font-weight: 700;
|
|
715
727
|
}
|
|
716
728
|
|
|
717
729
|
.kg-header-card.kg-size-large h3 {
|
|
@@ -1148,7 +1160,7 @@ ${ templateSettings.showBadge ? `
|
|
|
1148
1160
|
|
|
1149
1161
|
${ templateSettings.showHeaderIcon || templateSettings.showHeaderTitle ? `
|
|
1150
1162
|
<tr>
|
|
1151
|
-
<td class="site-info" width="100%" align="center">
|
|
1163
|
+
<td class="${templateSettings.showHeaderTitle ? `site-info-bordered` : `site-info`}" width="100%" align="center">
|
|
1152
1164
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
1153
1165
|
${ templateSettings.showHeaderIcon && site.iconUrl ? `
|
|
1154
1166
|
<tr>
|