diff --git a/ghost/admin/app/components/posts/analytics.hbs b/ghost/admin/app/components/posts/analytics.hbs index 8157a95de0..82040680b8 100644 --- a/ghost/admin/app/components/posts/analytics.hbs +++ b/ghost/admin/app/components/posts/analytics.hbs @@ -85,23 +85,23 @@ /> {{/if}} - {{/if}} - {{#if this.post.showAudienceFeedback }} - -

{{svg-jar "analytics-tab-feedback-large"}}{{format-number this.post.count.positive_feedback}}

-

{{svg-jar "analytics-tab-feedback"}}Feedback — {{this.post.sentiment}}%

-
+ {{#if this.post.isFeedbackEnabledForEmail }} + +

{{svg-jar "analytics-tab-feedback-large"}}{{format-number this.post.count.positive_feedback}}

+

{{svg-jar "analytics-tab-feedback"}}Feedback — {{this.post.sentiment}}%

+
- - - + + + + {{/if}} {{/if}} {{#if this.post.showAttributionAnalytics }} diff --git a/ghost/admin/app/models/email.js b/ghost/admin/app/models/email.js index d4c7c7ccb3..4c51d6c870 100644 --- a/ghost/admin/app/models/email.js +++ b/ghost/admin/app/models/email.js @@ -21,6 +21,8 @@ export default Model.extend({ trackOpens: attr('boolean'), trackClicks: attr('boolean'), + feedbackEnabled: attr('boolean'), + createdAtUTC: attr('moment-utc'), createdBy: attr('string'), updatedAtUTC: attr('moment-utc'), diff --git a/ghost/admin/app/models/post.js b/ghost/admin/app/models/post.js index c8b0eacb42..ac63caa992 100644 --- a/ghost/admin/app/models/post.js +++ b/ghost/admin/app/models/post.js @@ -251,6 +251,8 @@ export default Model.extend(Comparable, ValidationEngine, { return this.get('ghostPaths.url').join(blogUrl, previewKeyword, uuid); }), + isFeedbackEnabledForEmail: computed.reads('email.feedbackEnabled'), + isPublic: computed('visibility', function () { return this.visibility === 'public' ? true : false; }), diff --git a/ghost/core/core/server/data/migrations/versions/5.21/2022-10-26-09-32-add-feedback-enabled-column-to-emails.js b/ghost/core/core/server/data/migrations/versions/5.21/2022-10-26-09-32-add-feedback-enabled-column-to-emails.js new file mode 100644 index 0000000000..46bcfda420 --- /dev/null +++ b/ghost/core/core/server/data/migrations/versions/5.21/2022-10-26-09-32-add-feedback-enabled-column-to-emails.js @@ -0,0 +1,7 @@ +const {createAddColumnMigration} = require('../../utils'); + +module.exports = createAddColumnMigration('emails', 'feedback_enabled', { + type: 'boolean', + nullable: false, + defaultTo: false +}); diff --git a/ghost/core/core/server/data/schema/schema.js b/ghost/core/core/server/data/schema/schema.js index 567b9228cc..09614af37d 100644 --- a/ghost/core/core/server/data/schema/schema.js +++ b/ghost/core/core/server/data/schema/schema.js @@ -783,6 +783,7 @@ module.exports = { plaintext: {type: 'text', maxlength: 1000000000, fieldtype: 'long', nullable: true}, track_opens: {type: 'boolean', nullable: false, defaultTo: false}, track_clicks: {type: 'boolean', nullable: false, defaultTo: false}, + feedback_enabled: {type: 'boolean', nullable: false, defaultTo: false}, submitted_at: {type: 'dateTime', nullable: false}, newsletter_id: {type: 'string', maxlength: 24, nullable: true, references: 'newsletters.id'}, created_at: {type: 'dateTime', nullable: false}, diff --git a/ghost/core/core/server/models/email.js b/ghost/core/core/server/models/email.js index a0788b8fe6..2f2632b7a0 100644 --- a/ghost/core/core/server/models/email.js +++ b/ghost/core/core/server/models/email.js @@ -11,6 +11,7 @@ const Email = ghostBookshelf.Model.extend({ recipient_filter: 'status:-free', track_opens: false, track_clicks: false, + feedback_enabled: false, delivered_count: 0, opened_count: 0, failed_count: 0 diff --git a/ghost/core/core/server/services/mega/mega.js b/ghost/core/core/server/services/mega/mega.js index 641e19b0b7..921d3a82f2 100644 --- a/ghost/core/core/server/services/mega/mega.js +++ b/ghost/core/core/server/services/mega/mega.js @@ -240,6 +240,7 @@ const addEmail = async (postModel, options) => { submitted_at: moment().toDate(), track_opens: !!settingsCache.get('email_track_opens'), track_clicks: !!settingsCache.get('email_track_clicks'), + feedback_enabled: !!newsletter.get('feedback_enabled'), recipient_filter: emailRecipientFilter, newsletter_id: newsletter.id }, knexOptions); diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap index 7d1bf8e8ca..207254b08f 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snap @@ -81,7 +81,7 @@ exports[`Activity Feed API Can filter events by post id 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "23031", + "content-length": "23206", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", @@ -401,7 +401,7 @@ exports[`Activity Feed API Returns email delivered events in activity feed 2: [h Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1246", + "content-length": "1271", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", @@ -434,7 +434,7 @@ exports[`Activity Feed API Returns email opened events in activity feed 2: [head Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1243", + "content-length": "1268", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", @@ -483,7 +483,7 @@ exports[`Activity Feed API Returns email sent events in activity feed 2: [header Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "5774", + "content-length": "5899", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/emails.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/emails.test.js.snap index 74466f3e17..c37abf11d5 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/emails.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/emails.test.js.snap @@ -10,6 +10,7 @@ Object { "error": null, "error_data": null, "failed_count": 0, + "feedback_enabled": false, "from": null, "html": "

Look! I'm an email

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, @@ -34,6 +35,7 @@ Object { "error": "Everything went south", "error_data": null, "failed_count": 0, + "feedback_enabled": false, "from": null, "html": "

What's that? Another email!

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, @@ -69,7 +71,7 @@ exports[`Emails API Can browse emails 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "1285", + "content-length": "1335", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", @@ -87,6 +89,7 @@ Object { "error": null, "error_data": null, "failed_count": 0, + "feedback_enabled": false, "from": null, "html": "

Look! I'm an email

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, @@ -112,7 +115,7 @@ exports[`Emails API Can read an email 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "582", + "content-length": "607", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", @@ -130,6 +133,7 @@ Object { "error": "Everything went south", "error_data": null, "failed_count": 0, + "feedback_enabled": false, "from": null, "html": "

What's that? Another email!

", "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, @@ -155,7 +159,7 @@ exports[`Emails API Can retry a failed email 2: [headers] 1`] = ` Object { "access-control-allow-origin": "http://127.0.0.1:2369", "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", - "content-length": "628", + "content-length": "653", "content-type": "application/json; charset=utf-8", "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, "vary": "Accept-Version, Origin, Accept-Encoding", diff --git a/ghost/core/test/unit/server/data/schema/integrity.test.js b/ghost/core/test/unit/server/data/schema/integrity.test.js index a438be33ee..4f08100757 100644 --- a/ghost/core/test/unit/server/data/schema/integrity.test.js +++ b/ghost/core/test/unit/server/data/schema/integrity.test.js @@ -35,7 +35,7 @@ const validateRouteSettings = require('../../../../../core/server/services/route */ describe('DB version integrity', function () { // Only these variables should need updating - const currentSchemaHash = 'bb9d35d276f407c4a353e22e1e5b5538'; + const currentSchemaHash = 'f94be1265fce0bfbe5c98cb02610f9de'; const currentFixturesHash = 'dcb7ba7c66b4b98d6c26a722985e756a'; const currentSettingsHash = '2978a5684a2d5fcf089f61f5d368a0c0'; const currentRoutesHash = '3d180d52c663d173a6be791ef411ed01';