Skip to content

Commit 94f71dc

Browse files
committed
✨ Added publisher gift links
no issue Publishers can now share paywalled posts and pages with anyone via gift links, without comping a subscription or making the content public. Watch https://ghost.org/changelog for full details
1 parent 00fd975 commit 94f71dc

7 files changed

Lines changed: 25 additions & 24 deletions

File tree

apps/admin-x-settings/src/components/settings/advanced/labs/private-features.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ const features: Feature[] = [{
7171
title: 'Get helper deduplication',
7272
description: 'Deduplicate identical {{#get}} helper queries within a single request to avoid redundant database calls',
7373
flag: 'getHelperDeduplication'
74-
}, {
75-
title: 'Gift links',
76-
description: 'Tokenized links that let anyone read a single gated post or page in full, no account required',
77-
flag: 'giftLinks'
7874
}];
7975

8076
const AlphaFeatures: React.FC = () => {

ghost/admin/mirage/fixtures/settings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export default [
107107
// LABS
108108
setting('labs', 'labs', JSON.stringify({
109109
// Keep the GA flags that are not yet cleaned up in frontend code here
110+
giftLinks: true
110111
})),
111112

112113
// SLACK

ghost/admin/tests/acceptance/content-test.js

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -380,16 +380,17 @@ describe('Acceptance: Posts / Pages', function () {
380380
let buttons = contextMenu.querySelectorAll('button');
381381

382382
expect(contextMenu, 'context menu').to.exist;
383-
expect(buttons.length, 'context menu buttons').to.equal(6);
383+
expect(buttons.length, 'context menu buttons').to.equal(7);
384384
expect(buttons[0].innerText.trim(), 'context menu button 1').to.contain('Copy link to post');
385-
expect(buttons[1].innerText.trim(), 'context menu button 1').to.contain('Unpublish');
386-
expect(buttons[2].innerText.trim(), 'context menu button 2').to.contain('Feature'); // or Unfeature
387-
expect(buttons[3].innerText.trim(), 'context menu button 3').to.contain('Add a tag');
388-
expect(buttons[4].innerText.trim(), 'context menu button 4').to.contain('Duplicate');
389-
expect(buttons[5].innerText.trim(), 'context menu button 5').to.contain('Delete');
385+
expect(buttons[1].innerText.trim(), 'context menu button 2').to.contain('Share as a gift');
386+
expect(buttons[2].innerText.trim(), 'context menu button 3').to.contain('Unpublish');
387+
expect(buttons[3].innerText.trim(), 'context menu button 4').to.contain('Feature'); // or Unfeature
388+
expect(buttons[4].innerText.trim(), 'context menu button 5').to.contain('Add a tag');
389+
expect(buttons[5].innerText.trim(), 'context menu button 6').to.contain('Duplicate');
390+
expect(buttons[6].innerText.trim(), 'context menu button 7').to.contain('Delete');
390391

391392
// duplicate the post
392-
await click(buttons[4]);
393+
await click(buttons[5]);
393394

394395
const posts = findAll('[data-test-post-id]');
395396
expect(posts.length, 'all posts count').to.equal(5);
@@ -502,13 +503,14 @@ describe('Acceptance: Posts / Pages', function () {
502503
let buttons = contextMenu.querySelectorAll('button');
503504

504505
expect(contextMenu, 'context menu').to.exist;
505-
expect(buttons.length, 'context menu buttons').to.equal(6);
506+
expect(buttons.length, 'context menu buttons').to.equal(7);
506507
expect(buttons[0].innerText.trim(), 'context menu button 1').to.contain('Copy link to post');
507-
expect(buttons[1].innerText.trim(), 'context menu button 1').to.contain('Unpublish');
508-
expect(buttons[2].innerText.trim(), 'context menu button 2').to.contain('Feature'); // or Unfeature
509-
expect(buttons[3].innerText.trim(), 'context menu button 3').to.contain('Add a tag');
510-
expect(buttons[4].innerText.trim(), 'context menu button 4').to.contain('Duplicate');
511-
expect(buttons[5].innerText.trim(), 'context menu button 5').to.contain('Delete');
508+
expect(buttons[1].innerText.trim(), 'context menu button 2').to.contain('Share as a gift');
509+
expect(buttons[2].innerText.trim(), 'context menu button 3').to.contain('Unpublish');
510+
expect(buttons[3].innerText.trim(), 'context menu button 4').to.contain('Feature'); // or Unfeature
511+
expect(buttons[4].innerText.trim(), 'context menu button 5').to.contain('Add a tag');
512+
expect(buttons[5].innerText.trim(), 'context menu button 6').to.contain('Duplicate');
513+
expect(buttons[6].innerText.trim(), 'context menu button 7').to.contain('Delete');
512514

513515
// Copy the post link
514516
await click(buttons[0]);

ghost/core/core/shared/labs.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ const GA_FEATURES = [
2929
'featurebaseFeedback',
3030
'dangerZoneResetAuth',
3131
'indexnow',
32-
'llmsTxt'
32+
'llmsTxt',
33+
'giftLinks'
3334
];
3435

3536
// These features are considered publicly available and can be enabled/disabled by users
@@ -56,8 +57,7 @@ const PRIVATE_FEATURES = [
5657
'themeTranslation',
5758
'pictureImageFormats',
5859
'smarterCounts',
59-
'getHelperDeduplication',
60-
'giftLinks'
60+
'getHelperDeduplication'
6161
];
6262

6363
module.exports.GA_KEYS = [...GA_FEATURES];

ghost/core/test/e2e-api/admin/__snapshots__/settings.test.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2253,7 +2253,7 @@ exports[`Settings API Edit can edit Stripe settings when Stripe Connect limit is
22532253
Object {
22542254
"access-control-allow-origin": "http://127.0.0.1:2369",
22552255
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
2256-
"content-length": "5466",
2256+
"content-length": "5485",
22572257
"content-type": "application/json; charset=utf-8",
22582258
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
22592259
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,

ghost/core/test/unit/frontend/helpers/ghost-foot.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('{{ghost_foot}} helper', function () {
1818
it('outputs global injected code', function () {
1919
settingsCacheStub.withArgs('codeinjection_foot').returns('<script>var test = \'I am a variable!\'</script>');
2020

21-
const rendered = ghost_foot({data: {}});
21+
const rendered = ghost_foot({data: {root: {}}});
2222
assertExists(rendered);
2323
assert.match(rendered.string, /<script>var test = 'I am a variable!'<\/script>/);
2424
});
@@ -77,15 +77,15 @@ describe('{{ghost_foot}} helper', function () {
7777
it('handles global empty code injection', function () {
7878
settingsCacheStub.withArgs('codeinjection_foot').returns('');
7979

80-
const rendered = ghost_foot({data: {}});
80+
const rendered = ghost_foot({data: {root: {}}});
8181
assertExists(rendered);
8282
assert.equal(rendered.string, '');
8383
});
8484

8585
it('handles global undefined code injection', function () {
8686
settingsCacheStub.withArgs('codeinjection_foot').returns(undefined);
8787

88-
const rendered = ghost_foot({data: {}});
88+
const rendered = ghost_foot({data: {root: {}}});
8989
assertExists(rendered);
9090
assert.equal(rendered.string, '');
9191
});

ghost/core/test/unit/frontend/services/routing/controllers/entry.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const EDITOR_URL = `/#/editor/post/`;
1515
interface MockRequest {
1616
path: string;
1717
originalUrl: string;
18+
query: Record<string, unknown>;
1819
params: object;
1920
route: object;
2021
app: {get: sinon.SinonStub};
@@ -66,6 +67,7 @@ describe('Unit - services/routing/controllers/entry', function () {
6667
req = {
6768
path: '/',
6869
originalUrl: '/',
70+
query: {},
6971
params: {},
7072
route: {},
7173
app: {get: sinon.stub()},

0 commit comments

Comments
 (0)