Merge branch 'main' into locales/hi
This commit is contained in:
commit
f295977bf0
5
.gitignore
vendored
5
.gitignore
vendored
@ -167,3 +167,8 @@ tsconfig.tsbuildinfo
|
|||||||
/apps/admin-x-settings/test-results/
|
/apps/admin-x-settings/test-results/
|
||||||
/apps/admin-x-settings/playwright-report/
|
/apps/admin-x-settings/playwright-report/
|
||||||
/apps/admin-x-settings/playwright/.cache/
|
/apps/admin-x-settings/playwright/.cache/
|
||||||
|
|
||||||
|
# Tinybird
|
||||||
|
.tinyb
|
||||||
|
.venv
|
||||||
|
.diff_tmp
|
||||||
|
@ -59,14 +59,6 @@ const features = [{
|
|||||||
title: 'Content Visibility',
|
title: 'Content Visibility',
|
||||||
description: 'Enables content visibility in Emails',
|
description: 'Enables content visibility in Emails',
|
||||||
flag: 'contentVisibility'
|
flag: 'contentVisibility'
|
||||||
},{
|
|
||||||
title: 'Publish Flow — End Screen',
|
|
||||||
description: 'Enables improved publish flow',
|
|
||||||
flag: 'publishFlowEndScreen'
|
|
||||||
},{
|
|
||||||
title: 'Post Analytics — Refresh',
|
|
||||||
description: 'Adds a refresh button to the post analytics screen',
|
|
||||||
flag: 'postAnalyticsRefresh'
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const AlphaFeatures: React.FC = () => {
|
const AlphaFeatures: React.FC = () => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div class="flex flex-column h-100 items-center overflow-auto" data-test-modal="publish-flow">
|
<div class="flex flex-column h-100 items-center overflow-auto" data-test-modal="publish-flow">
|
||||||
<header class="gh-publish-header">
|
<header class="gh-publish-header">
|
||||||
<button class="gh-btn-editor gh-publish-back-button" title="Close" type="button" {{on "click" @close}} data-test-button="close-publish-flow">
|
<button class="gh-btn-editor gh-publish-back-button" title="Close" type="button" {{on "click" @close}}>
|
||||||
<span>{{svg-jar "arrow-left"}} Editor</span>
|
<span>{{svg-jar "arrow-left"}} Editor</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -45,14 +45,6 @@
|
|||||||
@close={{@close}}
|
@close={{@close}}
|
||||||
/>
|
/>
|
||||||
{{else if this.isComplete}}
|
{{else if this.isComplete}}
|
||||||
{{#unless (feature "publishFlowEndScreen")}}
|
|
||||||
<Editor::Modals::PublishFlow::Complete
|
|
||||||
@publishOptions={{@data.publishOptions}}
|
|
||||||
@recipientType={{this.recipientType}}
|
|
||||||
@postCount={{this.postCount}}
|
|
||||||
@close={{@close}}
|
|
||||||
/>
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<Editor::Modals::PublishFlow::Options
|
<Editor::Modals::PublishFlow::Options
|
||||||
@publishOptions={{@data.publishOptions}}
|
@publishOptions={{@data.publishOptions}}
|
||||||
|
@ -93,31 +93,30 @@ export default class PublishFlowOptions extends Component {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
yield this.args.saveTask.perform();
|
yield this.args.saveTask.perform();
|
||||||
if (this.feature.publishFlowEndScreen) {
|
|
||||||
if (this.args.publishOptions.isScheduled) {
|
if (this.args.publishOptions.isScheduled) {
|
||||||
localStorage.setItem('ghost-last-scheduled-post', JSON.stringify({
|
localStorage.setItem('ghost-last-scheduled-post', JSON.stringify({
|
||||||
id: this.args.publishOptions.post.id,
|
id: this.args.publishOptions.post.id,
|
||||||
type: this.args.publishOptions.post.displayName
|
type: this.args.publishOptions.post.displayName
|
||||||
}));
|
}));
|
||||||
if (this.args.publishOptions.post.displayName !== 'page') {
|
if (this.args.publishOptions.post.displayName !== 'page') {
|
||||||
this.router.transitionTo('posts');
|
this.router.transitionTo('posts');
|
||||||
|
} else {
|
||||||
|
this.router.transitionTo('pages');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('ghost-last-published-post', JSON.stringify({
|
||||||
|
id: this.args.publishOptions.post.id,
|
||||||
|
type: this.args.publishOptions.post.displayName
|
||||||
|
}));
|
||||||
|
if (this.args.publishOptions.post.displayName !== 'page') {
|
||||||
|
if (this.args.publishOptions.post.hasEmail) {
|
||||||
|
this.router.transitionTo('posts.analytics', this.args.publishOptions.post.id);
|
||||||
} else {
|
} else {
|
||||||
this.router.transitionTo('pages');
|
this.router.transitionTo('posts');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem('ghost-last-published-post', JSON.stringify({
|
this.router.transitionTo('pages');
|
||||||
id: this.args.publishOptions.post.id,
|
|
||||||
type: this.args.publishOptions.post.displayName
|
|
||||||
}));
|
|
||||||
if (this.args.publishOptions.post.displayName !== 'page') {
|
|
||||||
if (this.args.publishOptions.post.hasEmail) {
|
|
||||||
this.router.transitionTo('posts.analytics', this.args.publishOptions.post.id);
|
|
||||||
} else {
|
|
||||||
this.router.transitionTo('posts');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.router.transitionTo('pages');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<img src="{{or this.post.featureImage this.post.twitterImage this.post.ogImage}}" alt="{{this.post.title}}">
|
<img src="{{or this.post.featureImage this.post.twitterImage this.post.ogImage}}" alt="{{this.post.title}}">
|
||||||
</figure>
|
</figure>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<h2>{{this.post.title}}</h2>
|
<h2>{{this.post.title}}</h2>
|
||||||
{{#if this.post.excerpt}}
|
{{#if this.post.excerpt}}
|
||||||
@ -103,10 +103,10 @@
|
|||||||
</footer>
|
</footer>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<button type="button" class="close" title="Close" {{on "click" @close}}>{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
<button type="button" class="close" title="Close" {{on "click" @close}} data-test-button="close-publish-flow">{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
||||||
|
|
||||||
{{#unless this.post.emailOnly}}
|
{{#unless this.post.emailOnly}}
|
||||||
<a href="{{this.post.url}}" target="_blank" rel="noopener noreferrer" title="View post: {{this.post.title}}">
|
<a href="{{this.post.url}}" target="_blank" rel="noopener noreferrer" title="View post: {{this.post.title}}" data-test-complete-bookmark>
|
||||||
<div class="gh-post-card">
|
<div class="gh-post-card">
|
||||||
{{#if this.post.featureImage}}
|
{{#if this.post.featureImage}}
|
||||||
<figure class="modal-image">
|
<figure class="modal-image">
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
<p class="gh-content-entry-status">
|
<p class="gh-content-entry-status" data-test-editor-post-status>
|
||||||
<span class="published">
|
<span class="published">
|
||||||
Published
|
Published
|
||||||
{{#if @post.hasEmail}}
|
{{#if @post.hasEmail}}
|
||||||
@ -87,7 +87,7 @@
|
|||||||
<span class="gh-content-entry-date">– Lexical</span>
|
<span class="gh-content-entry-date">– Lexical</span>
|
||||||
{{/if}} --}}
|
{{/if}} --}}
|
||||||
</p>
|
</p>
|
||||||
<p class="gh-content-entry-status">
|
<p class="gh-content-entry-status" data-test-editor-post-status>
|
||||||
{{#if @post.isScheduled}}
|
{{#if @post.isScheduled}}
|
||||||
<span class="scheduled">
|
<span class="scheduled">
|
||||||
Scheduled
|
Scheduled
|
||||||
|
@ -16,9 +16,7 @@ export default class PostsList extends Component {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
if (this.feature.publishFlowEndScreen) {
|
this.checkPublishFlowModal();
|
||||||
this.checkPublishFlowModal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkPublishFlowModal() {
|
async checkPublishFlowModal() {
|
||||||
|
@ -34,66 +34,58 @@
|
|||||||
{{moment-format publishedAt "HH:mm"}}
|
{{moment-format publishedAt "HH:mm"}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
{{#if (feature "publishFlowEndScreen")}}
|
<div style="display: flex; gap: 8px;">
|
||||||
<div style="display: flex; gap: 8px;">
|
<GhTaskButton
|
||||||
{{#if (feature "postAnalyticsRefresh")}}
|
@buttonText="Refresh"
|
||||||
<GhTaskButton
|
@task={{this.fetchPostTask}}
|
||||||
@buttonText="Refresh"
|
@showIcon={{true}}
|
||||||
@task={{this.fetchPostTask}}
|
@idleIcon="reload"
|
||||||
@showIcon={{true}}
|
@successText="Refreshed"
|
||||||
@idleIcon="reload"
|
@class="gh-btn gh-btn-icon refresh"
|
||||||
@successText="Refreshed"
|
@successClass="gh-btn gh-btn-icon refresh" />
|
||||||
@class="gh-btn gh-btn-icon refresh"
|
{{#unless this.post.emailOnly}}
|
||||||
@successClass="gh-btn gh-btn-icon refresh" />
|
<button type="button" class="gh-post-list-cta share" {{on "click" this.togglePublishFlowModal}}>
|
||||||
{{/if}}
|
{{svg-jar "share" title="Share post"}}<span>Share</span>
|
||||||
{{#unless this.post.emailOnly}}
|
</button>
|
||||||
<button type="button" class="gh-post-list-cta share" {{on "click" this.togglePublishFlowModal}}>
|
{{/unless}}
|
||||||
{{svg-jar "share" title="Share post"}}<span>Share</span>
|
|
||||||
</button>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
<span class="dropdown">
|
<span class="dropdown">
|
||||||
<GhDropdownButton
|
<GhDropdownButton
|
||||||
@dropdownName="analytics-actions-menu"
|
@dropdownName="analytics-actions-menu"
|
||||||
@classNames="gh-post-list-cta gh-btn-icon icon-only gh-btn-action-icon"
|
@classNames="gh-post-list-cta gh-btn-icon icon-only gh-btn-action-icon"
|
||||||
@title="Analytics Actions"
|
@title="Analytics Actions"
|
||||||
data-test-button="analytics-actions"
|
data-test-button="analytics-actions"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{svg-jar "dotdotdot"}}
|
{{svg-jar "dotdotdot"}}
|
||||||
<span class="hidden">Actions</span>
|
<span class="hidden">Actions</span>
|
||||||
</span>
|
</span>
|
||||||
</GhDropdownButton>
|
</GhDropdownButton>
|
||||||
<GhDropdown
|
<GhDropdown
|
||||||
@name="analytics-actions-menu"
|
@name="analytics-actions-menu"
|
||||||
@tagName="ul"
|
@tagName="ul"
|
||||||
@classNames="gh-analytics-actions-menu dropdown-menu dropdown-triangle-top-right"
|
@classNames="gh-analytics-actions-menu dropdown-menu dropdown-triangle-top-right"
|
||||||
@closeOnClick={{true}}
|
@closeOnClick={{true}}
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<LinkTo class="edit-post" @route="lexical-editor.edit" @models={{array this.post.displayName this.post.id}}>Edit post</LinkTo>
|
<LinkTo class="edit-post" @route="lexical-editor.edit" @models={{array this.post.displayName this.post.id}}>Edit post</LinkTo>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class="view-browser" href="{{this.post.url}}" target="_blank" rel="noopener noreferrer">View in browser</a>
|
<a class="view-browser" href="{{this.post.url}}" target="_blank" rel="noopener noreferrer">View in browser</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="delete-post mr2"
|
class="delete-post mr2"
|
||||||
{{on "click" this.confirmDeleteMember}}
|
{{on "click" this.confirmDeleteMember}}
|
||||||
data-test-button="delete-post"
|
data-test-button="delete-post"
|
||||||
>
|
>
|
||||||
<span class="red">Delete post</span>
|
<span class="red">Delete post</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</GhDropdown>
|
</GhDropdown>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
|
||||||
<LinkTo @route="lexical-editor.edit" @models={{array this.post.displayName this.post.id}} class="gh-post-list-cta edit" title="">
|
|
||||||
{{svg-jar "pen" title=""}}<span>Edit post</span>
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</GhCanvasHeader>
|
</GhCanvasHeader>
|
||||||
|
@ -50,9 +50,7 @@ export default class Analytics extends Component {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
if (this.feature.publishFlowEndScreen) {
|
this.checkPublishFlowModal();
|
||||||
this.checkPublishFlowModal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openPublishFlowModal() {
|
openPublishFlowModal() {
|
||||||
@ -73,11 +71,7 @@ export default class Analytics extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get post() {
|
get post() {
|
||||||
if (this.feature.publishFlowEndScreen) {
|
return this._post ?? this.args.post;
|
||||||
return this._post ?? this.args.post;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.args.post;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set post(value) {
|
set post(value) {
|
||||||
|
@ -15,14 +15,14 @@ export default class TopPages extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Params
|
* @typedef {Object} Params
|
||||||
* @property {string} cid
|
* @property {string} site_uuid
|
||||||
* @property {string} [date_from]
|
* @property {string} [date_from]
|
||||||
* @property {string} [date_to]
|
* @property {string} [date_to]
|
||||||
* @property {number} [limit]
|
* @property {number} [limit]
|
||||||
* @property {number} [skip]
|
* @property {number} [skip]
|
||||||
*/
|
*/
|
||||||
const params = {
|
const params = {
|
||||||
cid: this.config.stats.id,
|
site_uuid: this.config.stats.id,
|
||||||
date_from: startDate.format('YYYY-MM-DD'),
|
date_from: startDate.format('YYYY-MM-DD'),
|
||||||
date_to: endDate.format('YYYY-MM-DD')
|
date_to: endDate.format('YYYY-MM-DD')
|
||||||
};
|
};
|
||||||
|
@ -78,8 +78,6 @@ export default class FeatureService extends Service {
|
|||||||
@feature('ActivityPub') ActivityPub;
|
@feature('ActivityPub') ActivityPub;
|
||||||
@feature('editorExcerpt') editorExcerpt;
|
@feature('editorExcerpt') editorExcerpt;
|
||||||
@feature('contentVisibility') contentVisibility;
|
@feature('contentVisibility') contentVisibility;
|
||||||
@feature('publishFlowEndScreen') publishFlowEndScreen;
|
|
||||||
@feature('postAnalyticsRefresh') postAnalyticsRefresh;
|
|
||||||
|
|
||||||
_user = null;
|
_user = null;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghost-admin",
|
"name": "ghost-admin",
|
||||||
"version": "5.90.2",
|
"version": "5.91.0",
|
||||||
"description": "Ember.js admin client for Ghost",
|
"description": "Ember.js admin client for Ghost",
|
||||||
"author": "Ghost Foundation",
|
"author": "Ghost Foundation",
|
||||||
"homepage": "http://ghost.org",
|
"homepage": "http://ghost.org",
|
||||||
@ -207,4 +207,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -141,6 +141,21 @@ function getWebmentionDiscoveryLink() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTinybirdTrackerScript(dataRoot) {
|
||||||
|
const scriptUrl = config.get('tinybird:tracker:scriptUrl');
|
||||||
|
const endpoint = config.get('tinybird:tracker:endpoint');
|
||||||
|
const token = config.get('tinybird:tracker:token');
|
||||||
|
|
||||||
|
const tbParams = _.map({
|
||||||
|
site_uuid: config.get('tinybird:tracker:id'),
|
||||||
|
post_uuid: dataRoot.post?.uuid,
|
||||||
|
member_uuid: dataRoot.member?.uuid,
|
||||||
|
member_status: dataRoot.member?.status
|
||||||
|
}, (value, key) => `tb_${key}="${value}"`).join(' ');
|
||||||
|
|
||||||
|
return `<script defer src="${scriptUrl}" data-host="${endpoint}" data-token="${token}" ${tbParams}></script>`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **NOTE**
|
* **NOTE**
|
||||||
* Express adds `_locals`, see https://github.com/expressjs/express/blob/4.15.4/lib/response.js#L962.
|
* Express adds `_locals`, see https://github.com/expressjs/express/blob/4.15.4/lib/response.js#L962.
|
||||||
@ -319,6 +334,10 @@ module.exports = async function ghost_head(options) { // eslint-disable-line cam
|
|||||||
if (!_.isEmpty(tagCodeInjection)) {
|
if (!_.isEmpty(tagCodeInjection)) {
|
||||||
head.push(tagCodeInjection);
|
head.push(tagCodeInjection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.get('tinybird') && config.get('tinybird:tracker') && config.get('tinybird:tracker:scriptUrl')) {
|
||||||
|
head.push(getTinybirdTrackerScript(dataRoot));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug('end');
|
debug('end');
|
||||||
|
@ -39,11 +39,16 @@ class RecommendationServiceWrapper {
|
|||||||
incomingRecommendationService;
|
incomingRecommendationService;
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
const config = require('../../../shared/config');
|
||||||
|
if (config.get('services:recommendations:enabled') === false) {
|
||||||
|
logging.info('[Recommendations] Service is disabled via config');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.repository) {
|
if (this.repository) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = require('../../../shared/config');
|
|
||||||
const urlUtils = require('../../../shared/url-utils');
|
const urlUtils = require('../../../shared/url-utils');
|
||||||
const models = require('../../models');
|
const models = require('../../models');
|
||||||
const sentry = require('../../../shared/sentry');
|
const sentry = require('../../../shared/sentry');
|
||||||
|
@ -45,9 +45,7 @@ const ALPHA_FEATURES = [
|
|||||||
'importMemberTier',
|
'importMemberTier',
|
||||||
'lexicalIndicators',
|
'lexicalIndicators',
|
||||||
'adminXDemo',
|
'adminXDemo',
|
||||||
'contentVisibility',
|
'contentVisibility'
|
||||||
'publishFlowEndScreen',
|
|
||||||
'postAnalyticsRefresh'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports.GA_KEYS = [...GA_FEATURES];
|
module.exports.GA_KEYS = [...GA_FEATURES];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ghost",
|
"name": "ghost",
|
||||||
"version": "5.90.2",
|
"version": "5.91.0",
|
||||||
"description": "The professional publishing platform",
|
"description": "The professional publishing platform",
|
||||||
"author": "Ghost Foundation",
|
"author": "Ghost Foundation",
|
||||||
"homepage": "https://ghost.org",
|
"homepage": "https://ghost.org",
|
||||||
|
@ -29,8 +29,6 @@ Object {
|
|||||||
"members": true,
|
"members": true,
|
||||||
"newEmailAddresses": true,
|
"newEmailAddresses": true,
|
||||||
"outboundLinkTagging": true,
|
"outboundLinkTagging": true,
|
||||||
"postAnalyticsRefresh": true,
|
|
||||||
"publishFlowEndScreen": true,
|
|
||||||
"stripeAutomaticTax": true,
|
"stripeAutomaticTax": true,
|
||||||
"themeErrorsNotification": true,
|
"themeErrorsNotification": true,
|
||||||
"tipsAndDonations": true,
|
"tipsAndDonations": true,
|
||||||
|
@ -11,11 +11,11 @@ const {createTier, createMember, createPostDraft, impersonateMember} = require('
|
|||||||
* @param {string} [hoverStatus] Optional different status when you hover the status
|
* @param {string} [hoverStatus] Optional different status when you hover the status
|
||||||
*/
|
*/
|
||||||
const checkPostStatus = async (page, status, hoverStatus) => {
|
const checkPostStatus = async (page, status, hoverStatus) => {
|
||||||
await expect(page.locator('[data-test-editor-post-status]')).toContainText(status, {timeout: 5000});
|
await expect(page.locator('[data-test-editor-post-status]').first()).toContainText(status, {timeout: 5000});
|
||||||
|
|
||||||
if (hoverStatus) {
|
if (hoverStatus) {
|
||||||
await page.locator('[data-test-editor-post-status]').hover();
|
await page.locator('[data-test-editor-post-status]').first().hover();
|
||||||
await expect(page.locator('[data-test-editor-post-status]')).toContainText(hoverStatus, {timeout: 5000});
|
await expect(page.locator('[data-test-editor-post-status]').first()).toContainText(hoverStatus, {timeout: 5000});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -198,8 +198,6 @@ test.describe('Publishing', () => {
|
|||||||
await createPostDraft(sharedPage, postData);
|
await createPostDraft(sharedPage, postData);
|
||||||
await publishPost(sharedPage, {type: 'publish+send'});
|
await publishPost(sharedPage, {type: 'publish+send'});
|
||||||
await closePublishFlow(sharedPage);
|
await closePublishFlow(sharedPage);
|
||||||
|
|
||||||
await checkPostStatus(sharedPage, 'Published');
|
|
||||||
await checkPostPublished(sharedPage, postData);
|
await checkPostPublished(sharedPage, postData);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -232,8 +230,6 @@ test.describe('Publishing', () => {
|
|||||||
await createPostDraft(sharedPage, postData);
|
await createPostDraft(sharedPage, postData);
|
||||||
await publishPost(sharedPage, {type: 'send'});
|
await publishPost(sharedPage, {type: 'send'});
|
||||||
await closePublishFlow(sharedPage);
|
await closePublishFlow(sharedPage);
|
||||||
await checkPostStatus(sharedPage, 'Sent to '); // can't test for 1 member for now, because depends on test ordering :( (sometimes 2 members are created)
|
|
||||||
|
|
||||||
await checkPostNotPublished(sharedPage, postData);
|
await checkPostNotPublished(sharedPage, postData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -327,8 +323,9 @@ test.describe('Publishing', () => {
|
|||||||
await expect(publishedHeader).toContainText(date.toFormat('LLL d, yyyy'));
|
await expect(publishedHeader).toContainText(date.toFormat('LLL d, yyyy'));
|
||||||
|
|
||||||
// add some extra text to the post
|
// add some extra text to the post
|
||||||
|
await adminPage.locator('li[data-test-post-id]').first().click();
|
||||||
await adminPage.locator('[data-kg="editor"]').first().click();
|
await adminPage.locator('[data-kg="editor"]').first().click();
|
||||||
await adminPage.waitForTimeout(200); //
|
await adminPage.waitForTimeout(500);
|
||||||
await adminPage.keyboard.type(' This is some updated text.');
|
await adminPage.keyboard.type(' This is some updated text.');
|
||||||
|
|
||||||
// change some post settings
|
// change some post settings
|
||||||
@ -431,7 +428,7 @@ test.describe('Publishing', () => {
|
|||||||
// Schedule the post to publish asap (by setting it to 00:00, it will get auto corrected to the minimum time possible - 5 seconds in the future)
|
// Schedule the post to publish asap (by setting it to 00:00, it will get auto corrected to the minimum time possible - 5 seconds in the future)
|
||||||
await publishPost(sharedPage, {type: 'send', time: '00:00'});
|
await publishPost(sharedPage, {type: 'send', time: '00:00'});
|
||||||
await closePublishFlow(sharedPage);
|
await closePublishFlow(sharedPage);
|
||||||
await checkPostStatus(sharedPage, 'Scheduled', 'Scheduled to be sent to');
|
await checkPostStatus(sharedPage, 'Scheduled', 'Scheduled to be sent in a few seconds');
|
||||||
const editorUrl = await sharedPage.url();
|
const editorUrl = await sharedPage.url();
|
||||||
|
|
||||||
// Check not published yet
|
// Check not published yet
|
||||||
@ -472,6 +469,7 @@ test.describe('Publishing', () => {
|
|||||||
await checkPostNotPublished(testsharedPage, postData);
|
await checkPostNotPublished(testsharedPage, postData);
|
||||||
|
|
||||||
// Now unschedule this post
|
// Now unschedule this post
|
||||||
|
await sharedPage.locator('li[data-test-post-id]').first().click();
|
||||||
await sharedPage.locator('[data-test-button="update-flow"]').first().click();
|
await sharedPage.locator('[data-test-button="update-flow"]').first().click();
|
||||||
await sharedPage.locator('[data-test-button="revert-to-draft"]').click();
|
await sharedPage.locator('[data-test-button="revert-to-draft"]').click();
|
||||||
|
|
||||||
@ -566,6 +564,7 @@ test.describe('Updating post access', () => {
|
|||||||
|
|
||||||
// publish
|
// publish
|
||||||
await publishPost(sharedPage);
|
await publishPost(sharedPage);
|
||||||
|
await closePublishFlow(sharedPage);
|
||||||
const frontendPage = await openPublishedPostBookmark(sharedPage);
|
const frontendPage = await openPublishedPostBookmark(sharedPage);
|
||||||
|
|
||||||
// non-member doesn't have access
|
// non-member doesn't have access
|
||||||
@ -607,7 +606,6 @@ test.describe('Updating post access', () => {
|
|||||||
await closePublishFlow(page);
|
await closePublishFlow(page);
|
||||||
|
|
||||||
// go to settings and change the timezone
|
// go to settings and change the timezone
|
||||||
await page.locator('[data-test-link="posts"]').click();
|
|
||||||
await page.locator('[data-test-nav="settings"]').click();
|
await page.locator('[data-test-nav="settings"]').click();
|
||||||
await expect(page.getByTestId('timezone')).toContainText('UTC');
|
await expect(page.getByTestId('timezone')).toContainText('UTC');
|
||||||
|
|
||||||
|
@ -8,114 +8,102 @@ exports[`Incoming Recommendation Emails Sends a different email if we receive a
|
|||||||
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
||||||
<title>👍 New recommendation</title>
|
<title>👍 New recommendation</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
@media only screen and (max-width: 620px) {
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
table.body h1 {
|
||||||
------------------------------------- */
|
font-size: 22px !important;
|
||||||
@media only screen and (max-width: 620px) {
|
padding-bottom: 16px !important;
|
||||||
table[class=body] h1 {
|
}
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
table.body p,
|
||||||
}
|
table.body ul,
|
||||||
table[class=body] p,
|
table.body ol,
|
||||||
table[class=body] ul,
|
table.body td,
|
||||||
table[class=body] ol,
|
table.body span,
|
||||||
table[class=body] td,
|
table.body a {
|
||||||
table[class=body] span,
|
font-size: 16px !important;
|
||||||
table[class=body] a {
|
}
|
||||||
font-size: 16px !important;
|
|
||||||
}
|
table.body .wrapper,
|
||||||
table[class=body] .wrapper,
|
table.body .article {
|
||||||
table[class=body] .article {
|
padding: 10px !important;
|
||||||
padding: 10px !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .content {
|
table.body .content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
table.body .container {
|
||||||
width: 100% !important;
|
padding: 0 !important;
|
||||||
}
|
width: 100% !important;
|
||||||
table[class=body] .main {
|
}
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
table.body .main {
|
||||||
border-right-width: 0 !important;
|
border-left-width: 0 !important;
|
||||||
}
|
border-radius: 0 !important;
|
||||||
table[class=body] .btn table {
|
border-right-width: 0 !important;
|
||||||
width: 100% !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
table.body .img-responsive {
|
||||||
width: 100% !important;
|
height: auto !important;
|
||||||
}
|
max-width: 100% !important;
|
||||||
table[class=body] .img-responsive {
|
width: auto !important;
|
||||||
height: auto !important;
|
}
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
table.body p.large,
|
||||||
}
|
table.body p.large a {
|
||||||
table[class=body] p[class=small],
|
font-size: 18px !important;
|
||||||
table[class=body] a[class=small] {
|
}
|
||||||
font-size: 11px !important;
|
|
||||||
}
|
table.body p.small,
|
||||||
.new-mention-thumbnail {
|
table.body a.small {
|
||||||
display: none !important;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* -------------------------------------
|
.new-mention-thumbnail {
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
display: none !important;
|
||||||
------------------------------------- */
|
}
|
||||||
@media all {
|
}
|
||||||
.ExternalClass {
|
@media all {
|
||||||
width: 100%;
|
.ExternalClass {
|
||||||
}
|
width: 100%;
|
||||||
.ExternalClass,
|
}
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
.ExternalClass,
|
||||||
.ExternalClass font,
|
.ExternalClass p,
|
||||||
.ExternalClass td,
|
.ExternalClass span,
|
||||||
.ExternalClass div {
|
.ExternalClass font,
|
||||||
line-height: 100%;
|
.ExternalClass td,
|
||||||
}
|
.ExternalClass div {
|
||||||
/* Reset styles for Gmail (it wraps email address in link with custom styles) */
|
line-height: 100%;
|
||||||
.text-link a {
|
}
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
.text-link a {
|
||||||
font-size: inherit !important;
|
color: inherit !important;
|
||||||
font-weight: inherit !important;
|
font-family: inherit !important;
|
||||||
line-height: inherit !important;
|
font-size: inherit !important;
|
||||||
text-decoration: none !important;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
#MessageViewBody a {
|
text-decoration: none !important;
|
||||||
color: inherit;
|
}
|
||||||
text-decoration: none;
|
|
||||||
font-size: inherit;
|
.text-link-accent a {
|
||||||
font-family: inherit;
|
font-family: inherit !important;
|
||||||
font-weight: inherit;
|
font-size: inherit !important;
|
||||||
line-height: inherit;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
}
|
text-decoration: none !important;
|
||||||
hr {
|
}
|
||||||
border-width: 0;
|
|
||||||
height: 0;
|
#MessageViewBody a {
|
||||||
margin-top: 34px;
|
color: inherit;
|
||||||
margin-bottom: 34px;
|
text-decoration: none;
|
||||||
border-bottom-width: 1px;
|
font-size: inherit;
|
||||||
border-bottom-color: #EEF5F8;
|
font-family: inherit;
|
||||||
}
|
font-weight: inherit;
|
||||||
a {
|
line-height: inherit;
|
||||||
color: #15212A;
|
}
|
||||||
}
|
}
|
||||||
blockquote {
|
</style>
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
border-left: 3px solid #DDE1E5;
|
|
||||||
}
|
|
||||||
.recommendation-card--outlook {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #F9F9FA;
|
|
||||||
background: #F9F9FA;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
||||||
|
|
||||||
@ -138,7 +126,7 @@ exports[`Incoming Recommendation Emails Sends a different email if we receive a
|
|||||||
|
|
||||||
<!--[if !mso !vml]-->
|
<!--[if !mso !vml]-->
|
||||||
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
||||||
<a style=\\"display:flex;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;background:#F4F5F6;border-radius:8px;color:#15171A;text-decoration:none\\" href=\\"https://www.otherghostsite.com/\\">
|
<a style=\\"display: flex; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif; background: #F4F5F6; border-radius: 8px; color: #15171A; text-decoration: none;\\" href=\\"https://www.otherghostsite.com/\\">
|
||||||
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
||||||
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
||||||
|
|
||||||
@ -179,7 +167,7 @@ exports[`Incoming Recommendation Emails Sends a different email if we receive a
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
||||||
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations\\" target=\\"_blank\\" style=\\"border:solid 1px #FF1A75;border-radius:8px;box-sizing:border-box;display:inline-block;font-size:15px;font-weight:normal;margin:0;padding:10px 20px;text-decoration:none;background-color:#FF1A75;border-color:#FF1A75;color:#ffffff\\">View recommendations</a>
|
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations\\" target=\\"_blank\\" style=\\"border: solid 1px #FF1A75; border-radius: 8px; box-sizing: border-box; display: inline-block; font-size: 15px; font-weight: normal; margin: 0; padding: 10px 20px; text-decoration: none; background-color: #FF1A75; border-color: #FF1A75; color: #ffffff;\\">View recommendations</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -264,114 +252,102 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
||||||
<title>👍 New recommendation</title>
|
<title>👍 New recommendation</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
@media only screen and (max-width: 620px) {
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
table.body h1 {
|
||||||
------------------------------------- */
|
font-size: 22px !important;
|
||||||
@media only screen and (max-width: 620px) {
|
padding-bottom: 16px !important;
|
||||||
table[class=body] h1 {
|
}
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
table.body p,
|
||||||
}
|
table.body ul,
|
||||||
table[class=body] p,
|
table.body ol,
|
||||||
table[class=body] ul,
|
table.body td,
|
||||||
table[class=body] ol,
|
table.body span,
|
||||||
table[class=body] td,
|
table.body a {
|
||||||
table[class=body] span,
|
font-size: 16px !important;
|
||||||
table[class=body] a {
|
}
|
||||||
font-size: 16px !important;
|
|
||||||
}
|
table.body .wrapper,
|
||||||
table[class=body] .wrapper,
|
table.body .article {
|
||||||
table[class=body] .article {
|
padding: 10px !important;
|
||||||
padding: 10px !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .content {
|
table.body .content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
table.body .container {
|
||||||
width: 100% !important;
|
padding: 0 !important;
|
||||||
}
|
width: 100% !important;
|
||||||
table[class=body] .main {
|
}
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
table.body .main {
|
||||||
border-right-width: 0 !important;
|
border-left-width: 0 !important;
|
||||||
}
|
border-radius: 0 !important;
|
||||||
table[class=body] .btn table {
|
border-right-width: 0 !important;
|
||||||
width: 100% !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
table.body .img-responsive {
|
||||||
width: 100% !important;
|
height: auto !important;
|
||||||
}
|
max-width: 100% !important;
|
||||||
table[class=body] .img-responsive {
|
width: auto !important;
|
||||||
height: auto !important;
|
}
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
table.body p.large,
|
||||||
}
|
table.body p.large a {
|
||||||
table[class=body] p[class=small],
|
font-size: 18px !important;
|
||||||
table[class=body] a[class=small] {
|
}
|
||||||
font-size: 11px !important;
|
|
||||||
}
|
table.body p.small,
|
||||||
.new-mention-thumbnail {
|
table.body a.small {
|
||||||
display: none !important;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* -------------------------------------
|
.new-mention-thumbnail {
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
display: none !important;
|
||||||
------------------------------------- */
|
}
|
||||||
@media all {
|
}
|
||||||
.ExternalClass {
|
@media all {
|
||||||
width: 100%;
|
.ExternalClass {
|
||||||
}
|
width: 100%;
|
||||||
.ExternalClass,
|
}
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
.ExternalClass,
|
||||||
.ExternalClass font,
|
.ExternalClass p,
|
||||||
.ExternalClass td,
|
.ExternalClass span,
|
||||||
.ExternalClass div {
|
.ExternalClass font,
|
||||||
line-height: 100%;
|
.ExternalClass td,
|
||||||
}
|
.ExternalClass div {
|
||||||
/* Reset styles for Gmail (it wraps email address in link with custom styles) */
|
line-height: 100%;
|
||||||
.text-link a {
|
}
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
.text-link a {
|
||||||
font-size: inherit !important;
|
color: inherit !important;
|
||||||
font-weight: inherit !important;
|
font-family: inherit !important;
|
||||||
line-height: inherit !important;
|
font-size: inherit !important;
|
||||||
text-decoration: none !important;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
#MessageViewBody a {
|
text-decoration: none !important;
|
||||||
color: inherit;
|
}
|
||||||
text-decoration: none;
|
|
||||||
font-size: inherit;
|
.text-link-accent a {
|
||||||
font-family: inherit;
|
font-family: inherit !important;
|
||||||
font-weight: inherit;
|
font-size: inherit !important;
|
||||||
line-height: inherit;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
}
|
text-decoration: none !important;
|
||||||
hr {
|
}
|
||||||
border-width: 0;
|
|
||||||
height: 0;
|
#MessageViewBody a {
|
||||||
margin-top: 34px;
|
color: inherit;
|
||||||
margin-bottom: 34px;
|
text-decoration: none;
|
||||||
border-bottom-width: 1px;
|
font-size: inherit;
|
||||||
border-bottom-color: #EEF5F8;
|
font-family: inherit;
|
||||||
}
|
font-weight: inherit;
|
||||||
a {
|
line-height: inherit;
|
||||||
color: #15212A;
|
}
|
||||||
}
|
}
|
||||||
blockquote {
|
</style>
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
border-left: 3px solid #DDE1E5;
|
|
||||||
}
|
|
||||||
.recommendation-card--outlook {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #F9F9FA;
|
|
||||||
background: #F9F9FA;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
||||||
|
|
||||||
@ -395,7 +371,7 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
|
|
||||||
<!--[if !mso !vml]-->
|
<!--[if !mso !vml]-->
|
||||||
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
||||||
<a style=\\"display:flex;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;background:#F4F5F6;border-radius:8px;color:#15171A;text-decoration:none\\" href=\\"https://www.otherghostsite.com/\\">
|
<a style=\\"display: flex; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif; background: #F4F5F6; border-radius: 8px; color: #15171A; text-decoration: none;\\" href=\\"https://www.otherghostsite.com/\\">
|
||||||
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
||||||
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
||||||
|
|
||||||
@ -436,7 +412,7 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
||||||
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations/add?url=https%3A%2F%2Fwww.otherghostsite.com%2F\\" target=\\"_blank\\" style=\\"border:solid 1px #FF1A75;border-radius:8px;box-sizing:border-box;display:inline-block;font-size:15px;font-weight:normal;margin:0;padding:10px 20px;text-decoration:none;background-color:#FF1A75;border-color:#FF1A75;color:#ffffff\\">Recommend back</a>
|
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations/add?url=https%3A%2F%2Fwww.otherghostsite.com%2F\\" target=\\"_blank\\" style=\\"border: solid 1px #FF1A75; border-radius: 8px; box-sizing: border-box; display: inline-block; font-size: 15px; font-weight: normal; margin: 0; padding: 10px 20px; text-decoration: none; background-color: #FF1A75; border-color: #FF1A75; color: #ffffff;\\">Recommend back</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -499,114 +475,102 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\">
|
||||||
<title>👍 New recommendation</title>
|
<title>👍 New recommendation</title>
|
||||||
<style>
|
<style>
|
||||||
/* -------------------------------------
|
@media only screen and (max-width: 620px) {
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
table.body h1 {
|
||||||
------------------------------------- */
|
font-size: 22px !important;
|
||||||
@media only screen and (max-width: 620px) {
|
padding-bottom: 16px !important;
|
||||||
table[class=body] h1 {
|
}
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
table.body p,
|
||||||
}
|
table.body ul,
|
||||||
table[class=body] p,
|
table.body ol,
|
||||||
table[class=body] ul,
|
table.body td,
|
||||||
table[class=body] ol,
|
table.body span,
|
||||||
table[class=body] td,
|
table.body a {
|
||||||
table[class=body] span,
|
font-size: 16px !important;
|
||||||
table[class=body] a {
|
}
|
||||||
font-size: 16px !important;
|
|
||||||
}
|
table.body .wrapper,
|
||||||
table[class=body] .wrapper,
|
table.body .article {
|
||||||
table[class=body] .article {
|
padding: 10px !important;
|
||||||
padding: 10px !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .content {
|
table.body .content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
table.body .container {
|
||||||
width: 100% !important;
|
padding: 0 !important;
|
||||||
}
|
width: 100% !important;
|
||||||
table[class=body] .main {
|
}
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
table.body .main {
|
||||||
border-right-width: 0 !important;
|
border-left-width: 0 !important;
|
||||||
}
|
border-radius: 0 !important;
|
||||||
table[class=body] .btn table {
|
border-right-width: 0 !important;
|
||||||
width: 100% !important;
|
}
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
table.body .img-responsive {
|
||||||
width: 100% !important;
|
height: auto !important;
|
||||||
}
|
max-width: 100% !important;
|
||||||
table[class=body] .img-responsive {
|
width: auto !important;
|
||||||
height: auto !important;
|
}
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
table.body p.large,
|
||||||
}
|
table.body p.large a {
|
||||||
table[class=body] p[class=small],
|
font-size: 18px !important;
|
||||||
table[class=body] a[class=small] {
|
}
|
||||||
font-size: 11px !important;
|
|
||||||
}
|
table.body p.small,
|
||||||
.new-mention-thumbnail {
|
table.body a.small {
|
||||||
display: none !important;
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* -------------------------------------
|
.new-mention-thumbnail {
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
display: none !important;
|
||||||
------------------------------------- */
|
}
|
||||||
@media all {
|
}
|
||||||
.ExternalClass {
|
@media all {
|
||||||
width: 100%;
|
.ExternalClass {
|
||||||
}
|
width: 100%;
|
||||||
.ExternalClass,
|
}
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
.ExternalClass,
|
||||||
.ExternalClass font,
|
.ExternalClass p,
|
||||||
.ExternalClass td,
|
.ExternalClass span,
|
||||||
.ExternalClass div {
|
.ExternalClass font,
|
||||||
line-height: 100%;
|
.ExternalClass td,
|
||||||
}
|
.ExternalClass div {
|
||||||
/* Reset styles for Gmail (it wraps email address in link with custom styles) */
|
line-height: 100%;
|
||||||
.text-link a {
|
}
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
.text-link a {
|
||||||
font-size: inherit !important;
|
color: inherit !important;
|
||||||
font-weight: inherit !important;
|
font-family: inherit !important;
|
||||||
line-height: inherit !important;
|
font-size: inherit !important;
|
||||||
text-decoration: none !important;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
#MessageViewBody a {
|
text-decoration: none !important;
|
||||||
color: inherit;
|
}
|
||||||
text-decoration: none;
|
|
||||||
font-size: inherit;
|
.text-link-accent a {
|
||||||
font-family: inherit;
|
font-family: inherit !important;
|
||||||
font-weight: inherit;
|
font-size: inherit !important;
|
||||||
line-height: inherit;
|
font-weight: inherit !important;
|
||||||
}
|
line-height: inherit !important;
|
||||||
}
|
text-decoration: none !important;
|
||||||
hr {
|
}
|
||||||
border-width: 0;
|
|
||||||
height: 0;
|
#MessageViewBody a {
|
||||||
margin-top: 34px;
|
color: inherit;
|
||||||
margin-bottom: 34px;
|
text-decoration: none;
|
||||||
border-bottom-width: 1px;
|
font-size: inherit;
|
||||||
border-bottom-color: #EEF5F8;
|
font-family: inherit;
|
||||||
}
|
font-weight: inherit;
|
||||||
a {
|
line-height: inherit;
|
||||||
color: #15212A;
|
}
|
||||||
}
|
}
|
||||||
blockquote {
|
</style>
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 20px;
|
|
||||||
border-left: 3px solid #DDE1E5;
|
|
||||||
}
|
|
||||||
.recommendation-card--outlook {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #F9F9FA;
|
|
||||||
background: #F9F9FA;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
<body style=\\"background-color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.5em; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;\\">
|
||||||
|
|
||||||
@ -630,7 +594,7 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
|
|
||||||
<!--[if !mso !vml]-->
|
<!--[if !mso !vml]-->
|
||||||
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
<figure style=\\"margin:0 0 1.5em;padding:0;width:100%;background:#F4F5F6;border-radius:8px;\\">
|
||||||
<a style=\\"display:flex;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;background:#F4F5F6;border-radius:8px;color:#15171A;text-decoration:none\\" href=\\"https://www.otherghostsite.com/\\">
|
<a style=\\"display: flex; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif; background: #F4F5F6; border-radius: 8px; color: #15171A; text-decoration: none;\\" href=\\"https://www.otherghostsite.com/\\">
|
||||||
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
<div style=\\"display:inline-block; width:100%; padding:20px\\">
|
||||||
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
<div style=\\"color:#15171A;font-size:13px;font-weight:400\\">
|
||||||
|
|
||||||
@ -671,7 +635,7 @@ exports[`Incoming Recommendation Emails Sends an email if we receive a recommend
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
<td style=\\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; border-radius: 8px; text-align: center;\\">
|
||||||
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations/add?url=https%3A%2F%2Fwww.otherghostsite.com%2F\\" target=\\"_blank\\" style=\\"border:solid 1px #FF1A75;border-radius:8px;box-sizing:border-box;display:inline-block;font-size:15px;font-weight:normal;margin:0;padding:10px 20px;text-decoration:none;background-color:#FF1A75;border-color:#FF1A75;color:#ffffff\\">Recommend back</a>
|
<a href=\\"http://127.0.0.1:2369/ghost/#/settings/recommendations/add?url=https%3A%2F%2Fwww.otherghostsite.com%2F\\" target=\\"_blank\\" style=\\"border: solid 1px #FF1A75; border-radius: 8px; box-sizing: border-box; display: inline-block; font-size: 15px; font-weight: normal; margin: 0; padding: 10px 20px; text-decoration: none; background-color: #FF1A75; border-color: #FF1A75; color: #ffffff;\\">Recommend back</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -457,7 +457,7 @@ Object {
|
|||||||
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
||||||
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
||||||
|
|
||||||
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
||||||
<meta property=\\"og:type\\" content=\\"website\\" />
|
<meta property=\\"og:type\\" content=\\"website\\" />
|
||||||
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
||||||
@ -469,7 +469,7 @@ Object {
|
|||||||
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
||||||
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
||||||
|
|
||||||
<script type=\\"application/ld+json\\">
|
<script type=\\"application/ld+json\\">
|
||||||
{
|
{
|
||||||
\\"@context\\": \\"https://schema.org\\",
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
@ -572,7 +572,7 @@ Object {
|
|||||||
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
||||||
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
||||||
|
|
||||||
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
||||||
<meta property=\\"og:type\\" content=\\"website\\" />
|
<meta property=\\"og:type\\" content=\\"website\\" />
|
||||||
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
||||||
@ -584,7 +584,7 @@ Object {
|
|||||||
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
||||||
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
||||||
|
|
||||||
<script type=\\"application/ld+json\\">
|
<script type=\\"application/ld+json\\">
|
||||||
{
|
{
|
||||||
\\"@context\\": \\"https://schema.org\\",
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
@ -686,7 +686,7 @@ Object {
|
|||||||
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
"string": "<meta name=\\"description\\" content=\\"site description\\" />
|
||||||
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\" />
|
||||||
|
|
||||||
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\" />
|
||||||
<meta property=\\"og:type\\" content=\\"website\\" />
|
<meta property=\\"og:type\\" content=\\"website\\" />
|
||||||
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
<meta property=\\"og:title\\" content=\\"Ghost\\" />
|
||||||
@ -698,7 +698,7 @@ Object {
|
|||||||
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
<meta name=\\"twitter:description\\" content=\\"site description\\" />
|
||||||
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\" />
|
||||||
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\" />
|
||||||
|
|
||||||
<script type=\\"application/ld+json\\">
|
<script type=\\"application/ld+json\\">
|
||||||
{
|
{
|
||||||
\\"@context\\": \\"https://schema.org\\",
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
@ -727,7 +727,7 @@ Object {
|
|||||||
|
|
||||||
<meta name=\\"generator\\" content=\\"Ghost 4.3\\" />
|
<meta name=\\"generator\\" content=\\"Ghost 4.3\\" />
|
||||||
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\" />
|
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\" />
|
||||||
|
|
||||||
<script defer src=\\"https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~1.0/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~1.0/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>",
|
<script defer src=\\"https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~1.0/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~1.0/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>",
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -959,6 +959,248 @@ Object {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set Sets tb_post_uuid on post page 1 1`] = `
|
||||||
|
Object {
|
||||||
|
"rendered": "<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
|
||||||
|
<link rel=\\"amphtml\\" href=\\"http://127.0.0.1:2369/post/amp/\\">
|
||||||
|
|
||||||
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:type\\" content=\\"article\\">
|
||||||
|
<meta property=\\"og:title\\" content=\\"Testing stats\\">
|
||||||
|
<meta property=\\"og:description\\" content=\\"Creating stats for the site\\">
|
||||||
|
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta property=\\"article:published_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
|
||||||
|
<meta property=\\"article:modified_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
|
||||||
|
<meta property=\\"article:author\\" content=\\"https://www.facebook.com/testuser\\">
|
||||||
|
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
|
||||||
|
<meta name=\\"twitter:title\\" content=\\"Testing stats\\">
|
||||||
|
<meta name=\\"twitter:description\\" content=\\"Creating stats for the site\\">
|
||||||
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta name=\\"twitter:label1\\" content=\\"Written by\\">
|
||||||
|
<meta name=\\"twitter:data1\\" content=\\"Author name\\">
|
||||||
|
<meta name=\\"twitter:creator\\" content=\\"@testuser\\">
|
||||||
|
|
||||||
|
<script type=\\"application/ld+json\\">
|
||||||
|
{
|
||||||
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
|
\\"@type\\": \\"Article\\",
|
||||||
|
\\"publisher\\": {
|
||||||
|
\\"@type\\": \\"Organization\\",
|
||||||
|
\\"name\\": \\"Ghost\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"logo\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
\\"author\\": {
|
||||||
|
\\"@type\\": \\"Person\\",
|
||||||
|
\\"name\\": \\"Author name\\",
|
||||||
|
\\"image\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/content/images/test-author-image.png\\"
|
||||||
|
},
|
||||||
|
\\"url\\": \\"https://mysite.com/fakeauthor/\\",
|
||||||
|
\\"sameAs\\": [
|
||||||
|
\\"http://authorwebsite.com\\",
|
||||||
|
\\"https://www.facebook.com/testuser\\",
|
||||||
|
\\"https://twitter.com/testuser\\"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
\\"headline\\": \\"Testing stats\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/post/\\",
|
||||||
|
\\"datePublished\\": \\"1970-01-01T00:00:00.000Z\\",
|
||||||
|
\\"dateModified\\": \\"1970-01-01T00:00:00.000Z\\",
|
||||||
|
\\"description\\": \\"Creating stats for the site\\",
|
||||||
|
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/post/\\"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name=\\"generator\\" content=\\"Ghost 0.3\\">
|
||||||
|
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
|
||||||
|
|
||||||
|
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
|
||||||
|
|
||||||
|
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
|
||||||
|
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"post_uuid\\" tb_member_uuid=\\"undefined\\" tb_member_status=\\"undefined\\"></script>",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set sets both tb_member_x variables and tb_post_uuid on logged in post page 1 1`] = `
|
||||||
|
Object {
|
||||||
|
"rendered": "<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
|
||||||
|
<link rel=\\"amphtml\\" href=\\"http://127.0.0.1:2369/post/amp/\\">
|
||||||
|
|
||||||
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:type\\" content=\\"article\\">
|
||||||
|
<meta property=\\"og:title\\" content=\\"Testing stats\\">
|
||||||
|
<meta property=\\"og:description\\" content=\\"Creating stats for the site\\">
|
||||||
|
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta property=\\"article:published_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
|
||||||
|
<meta property=\\"article:modified_time\\" content=\\"1970-01-01T00:00:00.000Z\\">
|
||||||
|
<meta property=\\"article:author\\" content=\\"https://www.facebook.com/testuser\\">
|
||||||
|
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
|
||||||
|
<meta name=\\"twitter:title\\" content=\\"Testing stats\\">
|
||||||
|
<meta name=\\"twitter:description\\" content=\\"Creating stats for the site\\">
|
||||||
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/post/\\">
|
||||||
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta name=\\"twitter:label1\\" content=\\"Written by\\">
|
||||||
|
<meta name=\\"twitter:data1\\" content=\\"Author name\\">
|
||||||
|
<meta name=\\"twitter:creator\\" content=\\"@testuser\\">
|
||||||
|
|
||||||
|
<script type=\\"application/ld+json\\">
|
||||||
|
{
|
||||||
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
|
\\"@type\\": \\"Article\\",
|
||||||
|
\\"publisher\\": {
|
||||||
|
\\"@type\\": \\"Organization\\",
|
||||||
|
\\"name\\": \\"Ghost\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"logo\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
\\"author\\": {
|
||||||
|
\\"@type\\": \\"Person\\",
|
||||||
|
\\"name\\": \\"Author name\\",
|
||||||
|
\\"image\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/content/images/test-author-image.png\\"
|
||||||
|
},
|
||||||
|
\\"url\\": \\"https://mysite.com/fakeauthor/\\",
|
||||||
|
\\"sameAs\\": [
|
||||||
|
\\"http://authorwebsite.com\\",
|
||||||
|
\\"https://www.facebook.com/testuser\\",
|
||||||
|
\\"https://twitter.com/testuser\\"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
\\"headline\\": \\"Testing stats\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/post/\\",
|
||||||
|
\\"datePublished\\": \\"1970-01-01T00:00:00.000Z\\",
|
||||||
|
\\"dateModified\\": \\"1970-01-01T00:00:00.000Z\\",
|
||||||
|
\\"description\\": \\"Creating stats for the site\\",
|
||||||
|
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/post/\\"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
|
||||||
|
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
|
||||||
|
|
||||||
|
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
|
||||||
|
|
||||||
|
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
|
||||||
|
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"post_uuid\\" tb_member_uuid=\\"member_uuid\\" tb_member_status=\\"free\\"></script>",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set sets tb_member_x variables on logged in home page 1 1`] = `
|
||||||
|
Object {
|
||||||
|
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
|
||||||
|
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
|
||||||
|
|
||||||
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:type\\" content=\\"website\\">
|
||||||
|
<meta property=\\"og:title\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:description\\" content=\\"site description\\">
|
||||||
|
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
|
||||||
|
<meta name=\\"twitter:title\\" content=\\"Ghost\\">
|
||||||
|
<meta name=\\"twitter:description\\" content=\\"site description\\">
|
||||||
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
|
||||||
|
<script type=\\"application/ld+json\\">
|
||||||
|
{
|
||||||
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
|
\\"@type\\": \\"WebSite\\",
|
||||||
|
\\"publisher\\": {
|
||||||
|
\\"@type\\": \\"Organization\\",
|
||||||
|
\\"name\\": \\"Ghost\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"logo\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"image\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/content/images/site-cover.png\\"
|
||||||
|
},
|
||||||
|
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"description\\": \\"site description\\"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
|
||||||
|
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
|
||||||
|
|
||||||
|
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
|
||||||
|
|
||||||
|
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
|
||||||
|
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"undefined\\" tb_member_uuid=\\"member_uuid\\" tb_member_status=\\"paid\\"></script>",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`{{ghost_head}} helper includes tinybird tracker script when config is set with all tb_variables set to undefined on logged out home page 1 1`] = `
|
||||||
|
Object {
|
||||||
|
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
|
||||||
|
<link rel=\\"canonical\\" href=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta name=\\"referrer\\" content=\\"no-referrer-when-downgrade\\">
|
||||||
|
|
||||||
|
<meta property=\\"og:site_name\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:type\\" content=\\"website\\">
|
||||||
|
<meta property=\\"og:title\\" content=\\"Ghost\\">
|
||||||
|
<meta property=\\"og:description\\" content=\\"site description\\">
|
||||||
|
<meta property=\\"og:url\\" content=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta property=\\"og:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
<meta name=\\"twitter:card\\" content=\\"summary_large_image\\">
|
||||||
|
<meta name=\\"twitter:title\\" content=\\"Ghost\\">
|
||||||
|
<meta name=\\"twitter:description\\" content=\\"site description\\">
|
||||||
|
<meta name=\\"twitter:url\\" content=\\"http://127.0.0.1:2369/\\">
|
||||||
|
<meta name=\\"twitter:image\\" content=\\"http://127.0.0.1:2369/content/images/site-cover.png\\">
|
||||||
|
|
||||||
|
<script type=\\"application/ld+json\\">
|
||||||
|
{
|
||||||
|
\\"@context\\": \\"https://schema.org\\",
|
||||||
|
\\"@type\\": \\"WebSite\\",
|
||||||
|
\\"publisher\\": {
|
||||||
|
\\"@type\\": \\"Organization\\",
|
||||||
|
\\"name\\": \\"Ghost\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"logo\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/favicon.ico\\"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"image\\": {
|
||||||
|
\\"@type\\": \\"ImageObject\\",
|
||||||
|
\\"url\\": \\"http://127.0.0.1:2369/content/images/site-cover.png\\"
|
||||||
|
},
|
||||||
|
\\"mainEntityOfPage\\": \\"http://127.0.0.1:2369/\\",
|
||||||
|
\\"description\\": \\"site description\\"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<meta name=\\"generator\\" content=\\"Ghost 4.3\\">
|
||||||
|
<link rel=\\"alternate\\" type=\\"application/rss+xml\\" title=\\"Ghost\\" href=\\"http://localhost:65530/rss/\\">
|
||||||
|
|
||||||
|
<script defer src=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/sodo-search.min.js\\" data-key=\\"xyz\\" data-styles=\\"https://cdn.jsdelivr.net/ghost/sodo-search@~[[VERSION]]/umd/main.css\\" data-sodo-search=\\"http://127.0.0.1:2369/\\" crossorigin=\\"anonymous\\"></script>
|
||||||
|
|
||||||
|
<link href=\\"http://127.0.0.1:2369/webmentions/receive/\\" rel=\\"webmention\\">
|
||||||
|
<script defer src=\\"https://unpkg.com/@tinybirdco/flock.js\\" data-host=\\"https://api.tinybird.co\\" data-token=\\"tinybird_token\\" tb_site_uuid=\\"tb_test_site_uuid\\" tb_post_uuid=\\"undefined\\" tb_member_uuid=\\"undefined\\" tb_member_status=\\"undefined\\"></script>",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`{{ghost_head}} helper members scripts includes portal when members enabled 1 1`] = `
|
exports[`{{ghost_head}} helper members scripts includes portal when members enabled 1 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
|
"rendered": "<meta name=\\"description\\" content=\\"site description\\">
|
||||||
|
@ -340,6 +340,19 @@ describe('{{ghost_head}} helper', function () {
|
|||||||
published_at: new Date(0),
|
published_at: new Date(0),
|
||||||
updated_at: new Date(0)
|
updated_at: new Date(0)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
posts.push(createPost({ // Post 10
|
||||||
|
title: 'Testing stats',
|
||||||
|
uuid: 'post_uuid',
|
||||||
|
excerpt: 'Creating stats for the site',
|
||||||
|
mobiledoc: testUtils.DataGenerator.markdownToMobiledoc('Creating stats for the site'),
|
||||||
|
authors: [
|
||||||
|
authors[3]
|
||||||
|
],
|
||||||
|
primary_author: authors[3],
|
||||||
|
published_at: new Date(0),
|
||||||
|
updated_at: new Date(0)
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
@ -1185,4 +1198,80 @@ describe('{{ghost_head}} helper', function () {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('includes tinybird tracker script when config is set', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
configUtils.set({
|
||||||
|
tinybird: {
|
||||||
|
tracker: {
|
||||||
|
scriptUrl: 'https://unpkg.com/@tinybirdco/flock.js',
|
||||||
|
endpoint: 'https://api.tinybird.co',
|
||||||
|
token: 'tinybird_token',
|
||||||
|
id: 'tb_test_site_uuid'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('with all tb_variables set to undefined on logged out home page', async function () {
|
||||||
|
await testGhostHead(testUtils.createHbsResponse({
|
||||||
|
locals: {
|
||||||
|
relativeUrl: '/',
|
||||||
|
context: ['home', 'index'],
|
||||||
|
safeVersion: '4.3'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Sets tb_post_uuid on post page', async function () {
|
||||||
|
const renderObject = {
|
||||||
|
post: posts[10]
|
||||||
|
};
|
||||||
|
|
||||||
|
await testGhostHead(testUtils.createHbsResponse({
|
||||||
|
renderObject: renderObject,
|
||||||
|
locals: {
|
||||||
|
relativeUrl: '/post/',
|
||||||
|
context: ['post'],
|
||||||
|
safeVersion: '0.3'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets tb_member_x variables on logged in home page', async function () {
|
||||||
|
const renderObject = {
|
||||||
|
member: {
|
||||||
|
uuid: 'member_uuid',
|
||||||
|
status: 'paid'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await testGhostHead(testUtils.createHbsResponse({
|
||||||
|
renderObject: renderObject,
|
||||||
|
locals: {
|
||||||
|
relativeUrl: '/',
|
||||||
|
context: ['home', 'index'],
|
||||||
|
safeVersion: '4.3'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets both tb_member_x variables and tb_post_uuid on logged in post page', async function () {
|
||||||
|
const renderObject = {
|
||||||
|
member: {
|
||||||
|
uuid: 'member_uuid',
|
||||||
|
status: 'free'
|
||||||
|
},
|
||||||
|
post: posts[10]
|
||||||
|
};
|
||||||
|
|
||||||
|
await testGhostHead(testUtils.createHbsResponse({
|
||||||
|
renderObject: renderObject,
|
||||||
|
locals: {
|
||||||
|
relativeUrl: '/post/',
|
||||||
|
context: ['post'],
|
||||||
|
safeVersion: '4.3'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -495,10 +495,14 @@ class StaffServiceEmails {
|
|||||||
sharedData = await this.getSharedData(data.recipient);
|
sharedData = await this.getSharedData(data.recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
return htmlTemplate({
|
const html = htmlTemplate({
|
||||||
...data,
|
...data,
|
||||||
...sharedData
|
...sharedData
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const juice = require('juice');
|
||||||
|
|
||||||
|
return juice(html, {inlinePseudoElements: true, removeStyleTags: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderText(templateName, data) {
|
async renderText(templateName, data) {
|
||||||
|
@ -37,9 +37,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">From:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">From:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; padding-bottom: 24px; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.name}} {{#if memberData}}• <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline; color: {{accentColor}} !important; text-decoration: none !important;">View</a>{{/if}}</p>
|
<p class="text-link-accent-large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; padding-bottom: 24px; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.name}} {{#if memberData}}• <a href="{{memberData.adminUrl}}" target="_blank" style="display: inline; color: {{accentColor}} !important; text-decoration: none !important;">View</a>{{/if}}</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">Amount received:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 26px; padding: 0; text-align: left; margin: 0; color: #15171A; font-weight: 400;">Amount received:</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; padding-bottom: 24px; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.amount}}</p>
|
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; line-height: 26px; padding: 0; padding-bottom: 24px; text-align: left; margin: 0; color: #15171A; font-weight: 700;">{{donation.amount}}</p>
|
||||||
{{#if donation.donationMessage}}
|
{{#if donation.donationMessage}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 22px; padding: 0; padding-bottom: 28px; text-align: left; margin: 0; color: #15171A; font-weight: 400;">“{{donation.donationMessage}}”</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; line-height: 22px; padding: 0; padding-bottom: 28px; text-align: left; margin: 0; color: #15171A; font-weight: 400;">“{{donation.donationMessage}}”</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -42,13 +42,13 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
||||||
{{#if referrerSource}}
|
{{#if referrerSource}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
|
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
|
||||||
{{#if attributionTitle}}
|
{{#if attributionTitle}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
|
@ -46,15 +46,15 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Tier:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Tier:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{tierData.name}}{{#if tierData.details}} • {{tierData.details}}{{/if}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{tierData.name}}{{#if tierData.details}} • {{tierData.details}}{{/if}}</p>
|
||||||
{{#if subscriptionData.cancelNow}}
|
{{#if subscriptionData.cancelNow}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Expired on:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Expired on:</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Expires on:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Expires on:</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{subscriptionData.expiryAt}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{subscriptionData.expiryAt}}</p>
|
||||||
{{#if subscriptionData.cancellationReason}}
|
{{#if subscriptionData.cancellationReason}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">"{{subscriptionData.cancellationReason}}"</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 400;">"{{subscriptionData.cancellationReason}}"</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -42,19 +42,19 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
<td style="padding-right: 8px; background-color: #F4F5F6; text-align: left; vertical-align: middle;" valign="middle">
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Name:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{memberData.name}}{{#if memberData.showEmail}} ({{memberData.email}}){{/if}}</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Tier:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Tier:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{tierData.name}}{{#if tierData.details}} • {{tierData.details}}{{/if}}</p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{tierData.name}}{{#if tierData.details}} • {{tierData.details}}{{/if}}</p>
|
||||||
{{#if offerData}}
|
{{#if offerData}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Offer:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Offer:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{offerData.name}} • <span style="color: {{accentColor}};">{{offerData.details}}</span></p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{offerData.name}} • <span style="color: {{accentColor}};">{{offerData.details}}</span></p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if referrerSource}}
|
{{#if referrerSource}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Source:</p>
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
|
<p class="large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;">{{referrerSource}}</p>
|
||||||
{{#if attributionTitle}}
|
{{#if attributionTitle}}
|
||||||
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
|
<p style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 15px; margin: 0; padding-bottom: 4px; color: #15171A; font-weight: 400;">Page:</p>
|
||||||
<p class="text-link" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
|
<p class="text-link large" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 18px; margin: 0; padding-bottom: 24px; color: #15171A; font-weight: 600;"><a href="{{attributionUrl}}">{{attributionTitle}}</a></p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
|
@ -3,48 +3,46 @@
|
|||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||||
------------------------------------- */
|
------------------------------------- */
|
||||||
@media only screen and (max-width: 620px) {
|
@media only screen and (max-width: 620px) {
|
||||||
table[class=body] h1 {
|
table.body h1 {
|
||||||
font-size: 28px !important;
|
font-size: 22px !important;
|
||||||
margin-bottom: 10px !important;
|
padding-bottom: 16px !important;
|
||||||
}
|
}
|
||||||
table[class=body] p,
|
table.body p,
|
||||||
table[class=body] ul,
|
table.body ul,
|
||||||
table[class=body] ol,
|
table.body ol,
|
||||||
table[class=body] td,
|
table.body td,
|
||||||
table[class=body] span,
|
table.body span,
|
||||||
table[class=body] a {
|
table.body a {
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
table[class=body] .wrapper,
|
table.body .wrapper,
|
||||||
table[class=body] .article {
|
table.body .article {
|
||||||
padding: 10px !important;
|
padding: 10px !important;
|
||||||
}
|
}
|
||||||
table[class=body] .content {
|
table.body .content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
table[class=body] .container {
|
table.body .container {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
table[class=body] .main {
|
table.body .main {
|
||||||
border-left-width: 0 !important;
|
border-left-width: 0 !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
border-right-width: 0 !important;
|
border-right-width: 0 !important;
|
||||||
}
|
}
|
||||||
table[class=body] .btn table {
|
table.body .img-responsive {
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
}
|
}
|
||||||
table[class=body] p[class=small],
|
table.body p.large,
|
||||||
table[class=body] a[class=small] {
|
table.body p.large a {
|
||||||
font-size: 11px !important;
|
font-size: 18px !important;
|
||||||
|
}
|
||||||
|
table.body p.small,
|
||||||
|
table.body a.small {
|
||||||
|
font-size: 12px !important;
|
||||||
}
|
}
|
||||||
.new-mention-thumbnail {
|
.new-mention-thumbnail {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@ -74,6 +72,13 @@
|
|||||||
line-height: inherit !important;
|
line-height: inherit !important;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
.text-link-accent a {
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
font-weight: inherit !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
#MessageViewBody a {
|
#MessageViewBody a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -23,9 +23,10 @@
|
|||||||
"sinon": "15.2.0"
|
"sinon": "15.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "4.17.21",
|
"@tryghost/email-addresses": "0.0.0",
|
||||||
"moment": "2.29.1",
|
|
||||||
"handlebars": "4.7.8",
|
"handlebars": "4.7.8",
|
||||||
"@tryghost/email-addresses": "0.0.0"
|
"juice": "9.1.0",
|
||||||
|
"lodash": "4.17.21",
|
||||||
|
"moment": "2.29.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
ghost/tinybird/.tinyenv
Normal file
31
ghost/tinybird/.tinyenv
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
# VERSION format is major.minor.patch-post where major, minor, patch and post are integer numbers
|
||||||
|
# bump post to deploy to the current live Release, rollback to previous post version is not available
|
||||||
|
# bump patch or minor to deploy a new Release and auto-promote it to live. Add TB_AUTO_PROMOTE=0 to create the Release in preview status
|
||||||
|
# bump major to deploy a new Release in preview status
|
||||||
|
VERSION=0.0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
# OPTIONAL env vars
|
||||||
|
# Deploy a new Release in preview status (default is 1)
|
||||||
|
# TB_AUTO_PROMOTE=0
|
||||||
|
|
||||||
|
# Check if deploy requires backfilling on preview (default is 1)
|
||||||
|
# TB_CHECK_BACKFILL_REQUIRED=0
|
||||||
|
|
||||||
|
# Force old Releases deletion on promote (default is 0)
|
||||||
|
# Setting it to 1 will remove oldest rollback Releases even when some resource is still in use
|
||||||
|
# TB_FORCE_REMOVE_OLDEST_ROLLBACK=0
|
||||||
|
|
||||||
|
# Don't print CLI version warning message if there's a new available version
|
||||||
|
# TB_VERSION_WARNING=0
|
||||||
|
|
||||||
|
# Skip regression tests
|
||||||
|
# TB_SKIP_REGRESSION=0
|
||||||
|
|
||||||
|
# Use `OBFUSCATE_REGEX_PATTERN` and `OBFUSCATE_PATTERN_SEPARATOR` environment variables to define a regex pattern and a separator (in case of a single string with multiple regex) to obfuscate secrets in the CLI output.
|
||||||
|
# OBFUSCATE_REGEX_PATTERN="https://(www\.)?[^/]+||^Follow these instructions =>"
|
||||||
|
# OBFUSCATE_PATTERN_SEPARATOR=||
|
||||||
|
##########
|
18
ghost/tinybird/README.md
Normal file
18
ghost/tinybird/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Tinybird
|
||||||
|
|
||||||
|
This folder contains configuration for Tinybird, so that the stats feature can be used.
|
||||||
|
|
||||||
|
We sync this configuration with Tinybird via the Tinybird CLI.
|
||||||
|
|
||||||
|
## Tinybird CLI
|
||||||
|
|
||||||
|
The Tinybird CLI is used via Docker.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn tb
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation for the Tinybird CLI: https://docs.tinybird.co/v/0.22.0/cli/overview
|
||||||
|
Note: you can use python if you prefer, but we use Docker for consistency.
|
||||||
|
|
||||||
|
How to work with version control: https://docs.tinybird.co/v/0.22.0/guides/version-control
|
14
ghost/tinybird/datasources/analytics_events.datasource
Normal file
14
ghost/tinybird/datasources/analytics_events.datasource
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Analytics events landing data source
|
||||||
|
|
||||||
|
SCHEMA >
|
||||||
|
`timestamp` DateTime `json:$.timestamp`,
|
||||||
|
`session_id` String `json:$.session_id`,
|
||||||
|
`action` LowCardinality(String) `json:$.action`,
|
||||||
|
`version` LowCardinality(String) `json:$.version`,
|
||||||
|
`payload` String `json:$.payload`
|
||||||
|
|
||||||
|
ENGINE MergeTree
|
||||||
|
ENGINE_PARTITION_KEY toYYYYMM(timestamp)
|
||||||
|
ENGINE_SORTING_KEY timestamp
|
||||||
|
ENGINE_TTL timestamp + toIntervalDay(60)
|
18
ghost/tinybird/datasources/analytics_pages_mv.datasource
Normal file
18
ghost/tinybird/datasources/analytics_pages_mv.datasource
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
SCHEMA >
|
||||||
|
`site_uuid` String,
|
||||||
|
`member_uuid` String,
|
||||||
|
`member_status` String,
|
||||||
|
`post_uuid` String,
|
||||||
|
`date` Date,
|
||||||
|
`device` String,
|
||||||
|
`browser` String,
|
||||||
|
`location` String,
|
||||||
|
`pathname` String,
|
||||||
|
`visits` AggregateFunction(uniq, String),
|
||||||
|
`hits` AggregateFunction(count),
|
||||||
|
`logged_in_hits` AggregateFunction(count),
|
||||||
|
`logged_out_hits` AggregateFunction(count)
|
||||||
|
|
||||||
|
ENGINE AggregatingMergeTree
|
||||||
|
ENGINE_PARTITION_KEY toYYYYMM(date)
|
||||||
|
ENGINE_SORTING_KEY date, device, browser, location, pathname, member_uuid, member_status, post_uuid, site_uuid
|
14
ghost/tinybird/datasources/analytics_sessions_mv.datasource
Normal file
14
ghost/tinybird/datasources/analytics_sessions_mv.datasource
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
SCHEMA >
|
||||||
|
`site_uuid` String,
|
||||||
|
`date` Date,
|
||||||
|
`session_id` String,
|
||||||
|
`device` SimpleAggregateFunction(any, String),
|
||||||
|
`browser` SimpleAggregateFunction(any, String),
|
||||||
|
`location` SimpleAggregateFunction(any, String),
|
||||||
|
`first_hit` SimpleAggregateFunction(min, DateTime),
|
||||||
|
`latest_hit` SimpleAggregateFunction(max, DateTime),
|
||||||
|
`hits` AggregateFunction(count)
|
||||||
|
|
||||||
|
ENGINE AggregatingMergeTree
|
||||||
|
ENGINE_PARTITION_KEY toYYYYMM(date)
|
||||||
|
ENGINE_SORTING_KEY date, session_id, site_uuid
|
13
ghost/tinybird/datasources/analytics_sources_mv.datasource
Normal file
13
ghost/tinybird/datasources/analytics_sources_mv.datasource
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
SCHEMA >
|
||||||
|
`site_uuid` String,
|
||||||
|
`date` Date,
|
||||||
|
`device` String,
|
||||||
|
`browser` String,
|
||||||
|
`location` String,
|
||||||
|
`referrer` String,
|
||||||
|
`visits` AggregateFunction(uniq, String),
|
||||||
|
`hits` AggregateFunction(count)
|
||||||
|
|
||||||
|
ENGINE AggregatingMergeTree
|
||||||
|
ENGINE_PARTITION_KEY toYYYYMM(date)
|
||||||
|
ENGINE_SORTING_KEY date, device, browser, location, referrer, site_uuid
|
15
ghost/tinybird/datasources/fixtures/README.md
Normal file
15
ghost/tinybird/datasources/fixtures/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Datasource fixtures
|
||||||
|
|
||||||
|
The file mockingbird-schema.json is a schema for generating fake data using the Mockingbird CLI.
|
||||||
|
|
||||||
|
The CLI is installed via npm:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install -g @tinybirdco/mockingbird
|
||||||
|
```
|
||||||
|
|
||||||
|
The command I'm currently using to generate the data is:
|
||||||
|
|
||||||
|
```
|
||||||
|
mockingbird-cli tinybird --schema ghost/tinybird/datasources/fixtures/mockingbird-schema.json --endpoint gcp_europe_west3 --token xxxx --datasource analytics_events --eps 50 --limit 5000
|
||||||
|
```
|
72
ghost/tinybird/datasources/fixtures/mockingbird-schema.json
Normal file
72
ghost/tinybird/datasources/fixtures/mockingbird-schema.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"timestamp": {
|
||||||
|
"type": "mockingbird.datetimeBetween",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"start": "2024-07-01T00:00:00.000Z",
|
||||||
|
"end": "2024-08-20T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"session_id": {
|
||||||
|
"type": "string.uuid"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "mockingbird.pick",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"page_hit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "mockingbird.pick",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"payload": {
|
||||||
|
"type": "mockingbird.pickWeighted",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\", \"locale\":\"en-US\", \"referrer\":\"https://www.kike.io\", \"pathname\":\"/coming-soon/\", \"href\":\"https://web-analytics.ghost.is/coming-soon/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/104.0.5112.79 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"IT\", \"referrer\":\"https://www.hn.com\", \"pathname\":\"/about/\", \"href\":\"https://web-analytics.ghost.is/about/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0\", \"locale\":\"en-GB\", \"location\":\"ES\", \"referrer\":\"\", \"pathname\":\"/\", \"href\":\"https://web-analytics.ghost.is\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/\", \"href\":\"https://web-analytics.ghost.is\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://web-analytics.ghost.is/\", \"pathname\":\"/coming-soon/\", \"href\":\"https://web-analytics.ghost.is/coming-soon/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/hello-world/\", \"href\":\"https://web-analytics.ghost.is/hello-world/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"IL\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/hello-world/\", \"href\":\"https://web-analytics.ghost.is/hello-world/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1\", \"locale\":\"es-ES\", \"location\":\"ES\", \"referrer\":\"https://www.twitter.com\", \"pathname\":\"/\", \"href\":\"https://web-analytics.ghost.is/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"GB\", \"referrer\":\"https://www.facebook.com\", \"pathname\":\"/\", \"href\":\"https://web-analytics.ghost.is/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"CH\", \"referrer\":\"https://www.qq.ch\", \"pathname\":\"/coming-soon/\", \"href\":\"https://web-analytics.ghost.is/coming-soon/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.118 Mobile Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.yandex.com\", \"pathname\":\"/about/\", \"href\":\"https://web-analytics.ghost.is/about/\"}",
|
||||||
|
"{\"site_uuid\":\"mock_site_uuid\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 13; SM-A102U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.118 Mobile Safari/537.36\", \"locale\":\"en-US\", \"location\":\"FR\", \"referrer\":\"https://www.github.com\", \"pathname\":\"/coming-soon/\", \"href\":\"https://web-analytics.ghost.is/coming-soon/\"}",
|
||||||
|
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)\", \"locale\":\"en-US\", \"referrer\":\"https://www.kike.io\", \"pathname\":\"/products/\", \"href\":\"https://fake-site.ghost.is/products/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/104.0.5112.79 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"IT\", \"referrer\":\"https://www.hn.com\", \"pathname\":\"/blog/\", \"href\":\"https://fake-site.ghost.is/blog/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0\", \"locale\":\"en-GB\", \"location\":\"ES\", \"referrer\":\"\", \"pathname\":\"/contact/\", \"href\":\"https://fake-site.ghost.is/contact/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/faq/\", \"href\":\"https://fake-site.ghost.is/faq/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://fake-site.ghost.is/\", \"pathname\":\"/services/\", \"href\":\"https://fake-site.ghost.is/services/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/team/\", \"href\":\"https://fake-site.ghost.is/team/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"IL\", \"referrer\":\"https://www.google.com\", \"pathname\":\"/pricing/\", \"href\":\"https://fake-site.ghost.is/pricing/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1\", \"locale\":\"es-ES\", \"location\":\"ES\", \"referrer\":\"https://www.twitter.com\", \"pathname\":\"/resources/\", \"href\":\"https://fake-site.ghost.is/resources/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"GB\", \"referrer\":\"https://www.facebook.com\", \"pathname\":\"/careers/\", \"href\":\"https://fake-site.ghost.is/careers/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\", \"locale\":\"en-US\", \"location\":\"CH\", \"referrer\":\"https://www.qq.ch\", \"pathname\":\"/support/\", \"href\":\"https://fake-site.ghost.is/support/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.118 Mobile Safari/537.36\", \"locale\":\"en-US\", \"location\":\"US\", \"referrer\":\"https://www.yandex.com\", \"pathname\":\"/partners/\", \"href\":\"https://fake-site.ghost.is/partners/\"}",
|
||||||
|
"{\"site_uuid\":\"fake_site_id\", \"user-agent\":\"Mozilla/5.0 (Linux; Android 13; SM-A102U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.118 Mobile Safari/537.36\", \"locale\":\"en-US\", \"location\":\"FR\", \"referrer\":\"https://www.github.com\", \"pathname\":\"/events/\", \"href\":\"https://fake-site.ghost.is/events/\"}"
|
||||||
|
],
|
||||||
|
"weights": [
|
||||||
|
200, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 400,
|
||||||
|
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
71
ghost/tinybird/pipes/analytics_hits.pipe
Normal file
71
ghost/tinybird/pipes/analytics_hits.pipe
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Parsed `page_hit` events, implementing `browser` and `device` detection logic.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE parsed_hits
|
||||||
|
DESCRIPTION >
|
||||||
|
Parse raw page_hit events
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
SELECT
|
||||||
|
timestamp,
|
||||||
|
action,
|
||||||
|
version,
|
||||||
|
coalesce(session_id, '0') as session_id,
|
||||||
|
JSONExtractString(payload, 'locale') as locale,
|
||||||
|
JSONExtractString(payload, 'location') as location,
|
||||||
|
JSONExtractString(payload, 'referrer') as referrer,
|
||||||
|
JSONExtractString(payload, 'pathname') as pathname,
|
||||||
|
JSONExtractString(payload, 'href') as href,
|
||||||
|
JSONExtractString(payload, 'site_uuid') as site_uuid,
|
||||||
|
JSONExtractString(payload, 'member_uuid') as member_uuid,
|
||||||
|
JSONExtractString(payload, 'member_status') as member_status,
|
||||||
|
JSONExtractString(payload, 'post_uuid') as post_uuid,
|
||||||
|
lower(JSONExtractString(payload, 'user-agent')) as user_agent
|
||||||
|
FROM analytics_events
|
||||||
|
where action = 'page_hit'
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
SQL >
|
||||||
|
SELECT
|
||||||
|
site_uuid,
|
||||||
|
timestamp,
|
||||||
|
action,
|
||||||
|
version,
|
||||||
|
session_id,
|
||||||
|
case
|
||||||
|
when member_uuid REGEXP '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
|
||||||
|
then true
|
||||||
|
else false
|
||||||
|
END as logged_in,
|
||||||
|
member_uuid,
|
||||||
|
member_status,
|
||||||
|
post_uuid,
|
||||||
|
location,
|
||||||
|
referrer,
|
||||||
|
pathname,
|
||||||
|
href,
|
||||||
|
case
|
||||||
|
when match(user_agent, 'wget|ahrefsbot|curl|urllib|bitdiscovery|\+https://|googlebot')
|
||||||
|
then 'bot'
|
||||||
|
when match(user_agent, 'android')
|
||||||
|
then 'mobile-android'
|
||||||
|
when match(user_agent, 'ipad|iphone|ipod')
|
||||||
|
then 'mobile-ios'
|
||||||
|
else 'desktop'
|
||||||
|
END as device,
|
||||||
|
case
|
||||||
|
when match(user_agent, 'firefox')
|
||||||
|
then 'firefox'
|
||||||
|
when match(user_agent, 'chrome|crios')
|
||||||
|
then 'chrome'
|
||||||
|
when match(user_agent, 'opera')
|
||||||
|
then 'opera'
|
||||||
|
when match(user_agent, 'msie|trident')
|
||||||
|
then 'ie'
|
||||||
|
when match(user_agent, 'iphone|ipad|safari')
|
||||||
|
then 'safari'
|
||||||
|
else 'Unknown'
|
||||||
|
END as browser
|
||||||
|
FROM parsed_hits
|
24
ghost/tinybird/pipes/analytics_pages.pipe
Normal file
24
ghost/tinybird/pipes/analytics_pages.pipe
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
NODE analytics_pages_1
|
||||||
|
DESCRIPTION >
|
||||||
|
Aggregate by pathname and calculate session and hits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
SELECT
|
||||||
|
site_uuid,
|
||||||
|
member_uuid,
|
||||||
|
member_status,
|
||||||
|
post_uuid,
|
||||||
|
toDate(timestamp) AS date,
|
||||||
|
device,
|
||||||
|
browser,
|
||||||
|
location,
|
||||||
|
pathname,
|
||||||
|
uniqState(session_id) AS visits,
|
||||||
|
countState() AS hits,
|
||||||
|
countStateIf(logged_in = true) AS logged_in_hits,
|
||||||
|
countStateIf(logged_in = false) AS logged_out_hits
|
||||||
|
FROM analytics_hits
|
||||||
|
GROUP BY date, device, browser, location, pathname, member_uuid, member_status, post_uuid, site_uuid
|
||||||
|
|
||||||
|
TYPE MATERIALIZED
|
||||||
|
DATASOURCE analytics_pages_mv
|
20
ghost/tinybird/pipes/analytics_sessions.pipe
Normal file
20
ghost/tinybird/pipes/analytics_sessions.pipe
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
NODE analytics_sessions_1
|
||||||
|
DESCRIPTION >
|
||||||
|
Aggregate by session_id and calculate session metrics
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
SELECT
|
||||||
|
site_uuid,
|
||||||
|
toDate(timestamp) AS date,
|
||||||
|
session_id,
|
||||||
|
anySimpleState(device) AS device,
|
||||||
|
anySimpleState(browser) AS browser,
|
||||||
|
anySimpleState(location) AS location,
|
||||||
|
minSimpleState(timestamp) AS first_hit,
|
||||||
|
maxSimpleState(timestamp) AS latest_hit,
|
||||||
|
countState() AS hits
|
||||||
|
FROM analytics_hits
|
||||||
|
GROUP BY date, session_id, site_uuid
|
||||||
|
|
||||||
|
TYPE MATERIALIZED
|
||||||
|
DATASOURCE analytics_sessions_mv
|
21
ghost/tinybird/pipes/analytics_sources.pipe
Normal file
21
ghost/tinybird/pipes/analytics_sources.pipe
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
NODE analytics_sources_1
|
||||||
|
DESCRIPTION >
|
||||||
|
Aggregate by referral and calculate session and hits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
WITH (SELECT domainWithoutWWW(href) FROM analytics_hits LIMIT 1) AS current_domain
|
||||||
|
SELECT
|
||||||
|
site_uuid,
|
||||||
|
toDate(timestamp) AS date,
|
||||||
|
device,
|
||||||
|
browser,
|
||||||
|
location,
|
||||||
|
referrer,
|
||||||
|
uniqState(session_id) AS visits,
|
||||||
|
countState() AS hits
|
||||||
|
FROM analytics_hits
|
||||||
|
WHERE domainWithoutWWW(referrer) != current_domain
|
||||||
|
GROUP BY date, device, browser, location, referrer, site_uuid
|
||||||
|
|
||||||
|
TYPE MATERIALIZED
|
||||||
|
DATASOURCE analytics_sources_mv
|
130
ghost/tinybird/pipes/kpis.pipe
Normal file
130
ghost/tinybird/pipes/kpis.pipe
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Summary with general KPIs per date, including visits, page views, bounce rate and average session duration.
|
||||||
|
Accepts `date_from` and `date_to` date filter, all historical data if not passed.
|
||||||
|
Daily granularity, except when filtering one single day (hourly)
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE timeseries
|
||||||
|
DESCRIPTION >
|
||||||
|
Generate a timeseries for the specified time range, so we call fill empty data points.
|
||||||
|
Filters "future" data points.
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
{% set _single_day = defined(date_from) and day_diff(date_from, date_to) == 0 %}
|
||||||
|
with
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
toStartOfDay(
|
||||||
|
toDate(
|
||||||
|
{{
|
||||||
|
Date(
|
||||||
|
date_from,
|
||||||
|
description="Starting day for filtering a date range",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
) as start,
|
||||||
|
{% else %} toStartOfDay(timestampAdd(today(), interval -7 day)) as start,
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
toStartOfDay(
|
||||||
|
toDate(
|
||||||
|
{{
|
||||||
|
Date(
|
||||||
|
date_to,
|
||||||
|
description="Finishing day for filtering a date range",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
)
|
||||||
|
) as end
|
||||||
|
{% else %} toStartOfDay(today()) as end
|
||||||
|
{% end %}
|
||||||
|
{% if _single_day %}
|
||||||
|
select
|
||||||
|
arrayJoin(
|
||||||
|
arrayMap(
|
||||||
|
x -> toDateTime(x),
|
||||||
|
range(
|
||||||
|
toUInt32(toDateTime(start)), toUInt32(timestampAdd(end, interval 1 day)), 3600
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) as date
|
||||||
|
{% else %}
|
||||||
|
select
|
||||||
|
arrayJoin(
|
||||||
|
arrayMap(
|
||||||
|
x -> toDate(x),
|
||||||
|
range(toUInt32(start), toUInt32(timestampAdd(end, interval 1 day)), 24 * 3600)
|
||||||
|
)
|
||||||
|
) as date
|
||||||
|
{% end %}
|
||||||
|
where date <= now()
|
||||||
|
|
||||||
|
NODE hits
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by sessions and calculate metrics at that level
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
{% if defined(date_from) and day_diff(date_from, date_to) == 0 %}
|
||||||
|
select
|
||||||
|
site_uuid,
|
||||||
|
toStartOfHour(timestamp) as date,
|
||||||
|
session_id,
|
||||||
|
uniq(session_id) as visits,
|
||||||
|
count() as pageviews,
|
||||||
|
case when min(timestamp) = max(timestamp) then 1 else 0 end as is_bounce,
|
||||||
|
max(timestamp) as latest_hit_aux,
|
||||||
|
min(timestamp) as first_hit_aux
|
||||||
|
from analytics_hits
|
||||||
|
where toDate(timestamp) = {{ Date(date_from) }}
|
||||||
|
group by toStartOfHour(timestamp), session_id, site_uuid
|
||||||
|
{% else %}
|
||||||
|
select
|
||||||
|
site_uuid,
|
||||||
|
date,
|
||||||
|
session_id,
|
||||||
|
uniq(session_id) as visits,
|
||||||
|
countMerge(hits) as pageviews,
|
||||||
|
case when min(first_hit) = max(latest_hit) then 1 else 0 end as is_bounce,
|
||||||
|
max(latest_hit) as latest_hit_aux,
|
||||||
|
min(first_hit) as first_hit_aux
|
||||||
|
from analytics_sessions_mv
|
||||||
|
where
|
||||||
|
{% if defined(date_from) %} date >= {{ Date(date_from) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %} and date <= {{ Date(date_to) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
group by date, session_id, site_uuid
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
NODE data
|
||||||
|
DESCRIPTION >
|
||||||
|
General KPIs per date, works for both summary metrics and trends charts.
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
select
|
||||||
|
site_uuid,
|
||||||
|
date,
|
||||||
|
uniq(session_id) as visits,
|
||||||
|
sum(pageviews) as pageviews,
|
||||||
|
sum(case when latest_hit_aux = first_hit_aux then 1 end) / visits as bounce_rate,
|
||||||
|
avg(latest_hit_aux - first_hit_aux) as avg_session_sec
|
||||||
|
from hits
|
||||||
|
group by date, site_uuid
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Join and generate timeseries with metrics
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select a.date, b.visits, b.pageviews, b.bounce_rate, b.avg_session_sec
|
||||||
|
from timeseries a
|
||||||
|
left join data b using date
|
||||||
|
where site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}}
|
32
ghost/tinybird/pipes/top_browsers.pipe
Normal file
32
ghost/tinybird/pipes/top_browsers.pipe
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Top Browsers ordered by most visits.
|
||||||
|
Accepts `date_from` and `date_to` date filter. Defaults to last 7 days.
|
||||||
|
Also `skip` and `limit` parameters for pagination.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by browser and calculate hits and visits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select browser, uniqMerge(visits) as visits, countMerge(hits) as hits
|
||||||
|
from analytics_sources_mv
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
date
|
||||||
|
>=
|
||||||
|
{{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
and date
|
||||||
|
<=
|
||||||
|
{{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
group by browser
|
||||||
|
order by visits desc
|
||||||
|
limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
|
33
ghost/tinybird/pipes/top_devices.pipe
Normal file
33
ghost/tinybird/pipes/top_devices.pipe
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
DESCRIPTION >
|
||||||
|
Top Device Types ordered by most visits.
|
||||||
|
Accepts `date_from` and `date_to` date filter. Defaults to last 7 days.
|
||||||
|
Also `skip` and `limit` parameters for pagination.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by device and calculate hits and visits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select device, uniqMerge(visits) as visits, countMerge(hits) as hits
|
||||||
|
from analytics_sources_mv
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
date
|
||||||
|
>=
|
||||||
|
{{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
and date
|
||||||
|
<=
|
||||||
|
{{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
group by device
|
||||||
|
order by visits desc
|
||||||
|
limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
|
32
ghost/tinybird/pipes/top_locations.pipe
Normal file
32
ghost/tinybird/pipes/top_locations.pipe
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Top visiting Countries ordered by most visits.
|
||||||
|
Accepts `date_from` and `date_to` date filter. Defaults to last 7 days.
|
||||||
|
Also `skip` and `limit` parameters for pagination.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by pagepath and calculate hits and visits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select location, uniqMerge(visits) as visits, countMerge(hits) as hits
|
||||||
|
from analytics_pages_mv
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
date
|
||||||
|
>=
|
||||||
|
{{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
and date
|
||||||
|
<=
|
||||||
|
{{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
group by location
|
||||||
|
order by visits desc
|
||||||
|
limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
|
38
ghost/tinybird/pipes/top_pages.pipe
Normal file
38
ghost/tinybird/pipes/top_pages.pipe
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
DESCRIPTION >
|
||||||
|
Most visited pages for a given period.
|
||||||
|
Accepts `date_from` and `date_to` date filter. Defaults to last 7 days.
|
||||||
|
Also `skip` and `limit` parameters for pagination.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by pagepath and calculate hits and visits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select
|
||||||
|
pathname,
|
||||||
|
uniqMerge(visits) as visits,
|
||||||
|
countMerge(hits) as hits,
|
||||||
|
countMerge(logged_in_hits) as logged_in_hits,
|
||||||
|
countMerge(logged_out_hits) as logged_out_hits
|
||||||
|
from analytics_pages_mv
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
date
|
||||||
|
>=
|
||||||
|
{{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
and date
|
||||||
|
<=
|
||||||
|
{{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
|
||||||
|
group by pathname
|
||||||
|
order by visits desc
|
||||||
|
limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
|
33
ghost/tinybird/pipes/top_sources.pipe
Normal file
33
ghost/tinybird/pipes/top_sources.pipe
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
DESCRIPTION >
|
||||||
|
Top traffic sources (domains), ordered by most visits.
|
||||||
|
Accepts `date_from` and `date_to` date filter. Defaults to last 7 days.
|
||||||
|
Also `skip` and `limit` parameters for pagination.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Group by referral and calculate hits and visits
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select domainWithoutWWW(referrer) as referrer, uniqMerge(visits) as visits, countMerge(hits) as hits
|
||||||
|
from analytics_sources_mv
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
{% if defined(date_from) %}
|
||||||
|
date
|
||||||
|
>=
|
||||||
|
{{ Date(date_from, description="Starting day for filtering a date range", required=False) }}
|
||||||
|
{% else %} date >= timestampAdd(today(), interval -7 day)
|
||||||
|
{% end %}
|
||||||
|
{% if defined(date_to) %}
|
||||||
|
and date
|
||||||
|
<=
|
||||||
|
{{ Date(date_to, description="Finishing day for filtering a date range", required=False) }}
|
||||||
|
{% else %} and date <= today()
|
||||||
|
{% end %}
|
||||||
|
group by referrer
|
||||||
|
order by visits desc
|
||||||
|
limit {{ Int32(skip, 0) }},{{ Int32(limit, 50) }}
|
36
ghost/tinybird/pipes/trend.pipe
Normal file
36
ghost/tinybird/pipes/trend.pipe
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
DESCRIPTION >
|
||||||
|
Visits trend over time for the last 30 minutes, filling the blanks.
|
||||||
|
Works great for the realtime chart.
|
||||||
|
|
||||||
|
TOKEN "dashboard" READ
|
||||||
|
|
||||||
|
NODE timeseries
|
||||||
|
DESCRIPTION >
|
||||||
|
Generate a timeseries for the last 30 minutes, so we call fill empty data points
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
with (now() - interval 30 minute) as start
|
||||||
|
select addMinutes(toStartOfMinute(start), number) as t
|
||||||
|
from (select arrayJoin(range(1, 31)) as number)
|
||||||
|
|
||||||
|
NODE hits
|
||||||
|
DESCRIPTION >
|
||||||
|
Get last 30 minutes metrics gropued by minute
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
%
|
||||||
|
select toStartOfMinute(timestamp) as t, uniq(session_id) as visits
|
||||||
|
from analytics_hits
|
||||||
|
where
|
||||||
|
site_uuid = {{String(site_uuid, 'mock_site_uuid', description="Tenant ID", required=True)}} and
|
||||||
|
timestamp >= (now() - interval 30 minute)
|
||||||
|
group by toStartOfMinute(timestamp)
|
||||||
|
order by toStartOfMinute(timestamp)
|
||||||
|
|
||||||
|
NODE endpoint
|
||||||
|
DESCRIPTION >
|
||||||
|
Join and generate timeseries with metrics for the last 30 minutes
|
||||||
|
|
||||||
|
SQL >
|
||||||
|
select a.t, b.visits from timeseries a left join hits b on a.t = b.t order by a.t
|
1
ghost/tinybird/requirements.txt
Normal file
1
ghost/tinybird/requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
tinybird-cli>=4,<5
|
21
ghost/tinybird/scripts/append_fixtures.sh
Executable file
21
ghost/tinybird/scripts/append_fixtures.sh
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
directory="datasources/fixtures"
|
||||||
|
extensions=("csv" "ndjson")
|
||||||
|
|
||||||
|
absolute_directory=$(realpath "$directory")
|
||||||
|
|
||||||
|
for extension in "${extensions[@]}"; do
|
||||||
|
file_list=$(find "$absolute_directory" -type f -name "*.$extension")
|
||||||
|
|
||||||
|
for file_path in $file_list; do
|
||||||
|
file_name=$(basename "$file_path")
|
||||||
|
file_name_without_extension="${file_name%.*}"
|
||||||
|
|
||||||
|
command="tb datasource append $file_name_without_extension datasources/fixtures/$file_name"
|
||||||
|
echo $command
|
||||||
|
$command
|
||||||
|
done
|
||||||
|
done
|
58
ghost/tinybird/scripts/exec_test.sh
Executable file
58
ghost/tinybird/scripts/exec_test.sh
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
export TB_VERSION_WARNING=0
|
||||||
|
|
||||||
|
run_test() {
|
||||||
|
t=$1
|
||||||
|
echo "** Running $t **"
|
||||||
|
echo "** $(cat $t)"
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
retries=0
|
||||||
|
TOTAL_RETRIES=3
|
||||||
|
|
||||||
|
# When appending fixtures, we need to retry in case of the data is not replicated in time
|
||||||
|
while [ $retries -lt $TOTAL_RETRIES ]; do
|
||||||
|
# Run the test and store the output in a temporary file
|
||||||
|
bash $t $2 >$tmpfile
|
||||||
|
exit_code=$?
|
||||||
|
if [ "$exit_code" -eq 0 ]; then
|
||||||
|
# If the test passed, break the loop
|
||||||
|
if diff -B ${t}.result $tmpfile >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
# If the test failed, increment the retries counter and try again
|
||||||
|
else
|
||||||
|
retries=$((retries+1))
|
||||||
|
fi
|
||||||
|
# If the bash command failed, print an error message and break the loop
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if diff -B ${t}.result $tmpfile >/dev/null 2>&1; then
|
||||||
|
echo "✅ Test $t passed"
|
||||||
|
rm $tmpfile
|
||||||
|
return 0
|
||||||
|
elif [ $retries -eq $TOTAL_RETRIES ]; then
|
||||||
|
echo "🚨 ERROR: Test $t failed, diff:";
|
||||||
|
diff -B ${t}.result $tmpfile
|
||||||
|
rm $tmpfile
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "🚨 ERROR: Test $t failed with bash command exit code $?"
|
||||||
|
cat $tmpfile
|
||||||
|
rm $tmpfile
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
export -f run_test
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
find ./tests -name "*.test" -print0 | xargs -0 -I {} -P 4 bash -c 'run_test "$@"' _ {} || fail=1
|
||||||
|
|
||||||
|
if [ $fail == 1 ]; then
|
||||||
|
exit -1;
|
||||||
|
fi
|
@ -40,7 +40,8 @@
|
|||||||
"main": "yarn main:monorepo && yarn main:submodules",
|
"main": "yarn main:monorepo && yarn main:submodules",
|
||||||
"main:monorepo": "git checkout main && git pull ${GHOST_UPSTREAM:-origin} main && yarn",
|
"main:monorepo": "git checkout main && git pull ${GHOST_UPSTREAM:-origin} main && yarn",
|
||||||
"main:submodules": "git submodule sync && git submodule update && git submodule foreach \"git checkout main && git pull ${GHOST_UPSTREAM:-origin} main && yarn\"",
|
"main:submodules": "git submodule sync && git submodule update && git submodule foreach \"git checkout main && git pull ${GHOST_UPSTREAM:-origin} main && yarn\"",
|
||||||
"prepare": "husky install .github/hooks"
|
"prepare": "husky install .github/hooks",
|
||||||
|
"tb": "docker run --rm -v $(pwd):/ghost -w /ghost/ghost/tinybird -it tinybirdco/tinybird-cli-docker"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@tryghost/errors": "1.3.5",
|
"@tryghost/errors": "1.3.5",
|
||||||
|
Loading…
Reference in New Issue
Block a user