mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-18 02:21:47 -05:00
Removed emailClicks feature flag (#15556)
fixes https://github.com/TryGhost/Team/issues/2028 Since link clicks became GA, some older components and templates are no longer used.
This commit is contained in:
parent
2612b44c21
commit
7e3b41f643
21 changed files with 302 additions and 707 deletions
ghost
admin
app
components
member
members-activity
members
posts-list
settings
routes
services
templates
tests/acceptance
core
core
server
shared
test/e2e-api/admin/__snapshots__
members-api/lib/repositories
|
@ -31,11 +31,11 @@
|
|||
{{/if}}
|
||||
{{#if event.url}}
|
||||
<span class="gh-members-activity-event-join">{{event.join}}</span>
|
||||
{{#if (feature "emailClicks")}}<span class="gh-members-activity-event-dash">–</span>{{/if}}
|
||||
<span class="gh-members-activity-event-dash">–</span>
|
||||
<a class="ghost-members-activity-object-link" href="{{event.url}}" target="_blank" rel="noopener noreferrer">{{event.object}}</a>
|
||||
{{/if}}
|
||||
{{#if event.email}}
|
||||
{{#if (feature "emailClicks")}}<span class="gh-members-activity-event-dash">–</span>{{/if}}
|
||||
<span class="gh-members-activity-event-dash">–</span>
|
||||
<GhEmailPreviewLink @data={{event.email}} />
|
||||
{{/if}}
|
||||
</span>
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
{{/if}}
|
||||
{{#if event.url}}
|
||||
<span class="gh-members-activity-event-join">{{event.join}}</span>
|
||||
{{#if (feature "emailClicks")}}<span class="gh-members-activity-event-dash">–</span>{{/if}}
|
||||
<span class="gh-members-activity-event-dash">–</span>
|
||||
<a class="ghost-members-activity-object-link" href="{{event.url}}" target="_blank" rel="noopener noreferrer">{{event.object}}</a>
|
||||
{{/if}}
|
||||
{{#if event.email}}
|
||||
{{#if (feature "emailClicks")}}<span class="gh-members-activity-event-dash">–</span>{{/if}}
|
||||
<span class="gh-members-activity-event-dash">–</span>
|
||||
<GhEmailPreviewLink @data={{event.email}} />
|
||||
{{/if}}
|
||||
</span>
|
||||
|
|
|
@ -31,9 +31,9 @@ const FILTER_PROPERTIES = [
|
|||
{label: 'Emails sent (all time)', name: 'email_count', group: 'Email'},
|
||||
{label: 'Emails opened (all time)', name: 'email_opened_count', group: 'Email'},
|
||||
{label: 'Open rate (all time)', name: 'email_open_rate', group: 'Email'},
|
||||
{label: 'Received email', name: 'emails.post_id', group: 'Email', valueType: 'array', feature: 'emailClicks'},
|
||||
{label: 'Opened email', name: 'opened_emails.post_id', group: 'Email', valueType: 'array', feature: 'emailClicks'},
|
||||
{label: 'Clicked email', name: 'clicked_links.post_id', group: 'Email', valueType: 'array', feature: 'emailClicks'}
|
||||
{label: 'Received email', name: 'emails.post_id', group: 'Email', valueType: 'array'},
|
||||
{label: 'Opened email', name: 'opened_emails.post_id', group: 'Email', valueType: 'array'},
|
||||
{label: 'Clicked email', name: 'clicked_links.post_id', group: 'Email', valueType: 'array'}
|
||||
|
||||
// {label: 'Emails sent (30 days)', name: 'x', group: 'Email'},
|
||||
// {label: 'Emails opened (30 days)', name: 'x', group: 'Email'},
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
{{!-- template-lint-disable no-invalid-interactive --}}
|
||||
<li class="gh-list-row gh-posts-list-item gh-posts-list-item-labs gh-post-list-plain-status"
|
||||
{{on "mouseover" this.mouseOver}}
|
||||
{{on "mouseleave" this.mouseLeave}}
|
||||
...attributes
|
||||
>
|
||||
|
||||
{{!-- Title column --}}
|
||||
{{#if (and this.session.user.isContributor @post.isPublished)}}
|
||||
<a href={{@post.url}} class="permalink gh-list-data gh-post-list-title" target="_blank" rel="noopener noreferrer">
|
||||
<h3 class="gh-content-entry-title">
|
||||
{{@post.title}} {{svg-jar "external" class="gh-post-list-external"}}
|
||||
</h3>
|
||||
{{#unless @hideAuthor }}
|
||||
<p class="gh-content-entry-meta">
|
||||
<span class="gh-content-entry-author">
|
||||
By {{post-author-names @post}}
|
||||
|
||||
{{#if @post.primaryTag}}
|
||||
in <span class="midgrey-l2 fw5">{{@post.primaryTag.name}}</span>
|
||||
{{/if}}
|
||||
|
||||
-
|
||||
</span>
|
||||
<span class="gh-content-entry-date">
|
||||
{{#if this.isHovered}}
|
||||
{{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}
|
||||
{{else}}
|
||||
{{gh-format-post-time @post.updatedAtUTC draft=true}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</p>
|
||||
<p class="gh-content-entry-status">
|
||||
<span class="published">
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
{{#if this.isHovered}}
|
||||
and sent to {{gh-pluralize @post.email.emailCount "member"}}
|
||||
{{else}}
|
||||
and sent
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</p>
|
||||
{{/unless}}
|
||||
</a>
|
||||
{{else}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-title">
|
||||
<h3 class="gh-content-entry-title">
|
||||
{{@post.title}}
|
||||
{{#if @post.lexical}}
|
||||
<span class="gh-lexical-indicator">L</span>
|
||||
{{/if}}
|
||||
</h3>
|
||||
{{#unless @hideAuthor }}
|
||||
<p class="gh-content-entry-meta">
|
||||
<span class="gh-content-entry-author">
|
||||
By {{post-author-names @post}}
|
||||
|
||||
{{#if @post.primaryTag}}
|
||||
in <span class="midgrey-l2 fw5">{{@post.primaryTag.name}}</span>
|
||||
{{/if}}
|
||||
-
|
||||
</span>
|
||||
<span class="gh-content-entry-date" {{on "mouseover" (fn (mut this.isDateHovered) true)}} {{on "mouseleave" (fn (mut this.isDateHovered) false)}}>
|
||||
{{gh-format-post-time @post.updatedAtUTC draft=true}}
|
||||
{{#if this.isDateHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>on {{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{!-- {{#if @post.lexical}}
|
||||
<span class="gh-content-entry-date">– Lexical</span>
|
||||
{{/if}} --}}
|
||||
</p>
|
||||
<p class="gh-content-entry-status">
|
||||
{{#if @post.isScheduled}}
|
||||
<span class="scheduled" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
<span class="status-dot"></span>
|
||||
Scheduled
|
||||
{{#if this.isStateHovered}}
|
||||
<span class="schedule-details" {{css-transition "anim-fade-in-scale"}}>to be published {{if @post.newsletter "and sent "}}{{this.scheduledText}} to {{humanize-recipient-filter @post.emailSegment}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isDraft}}
|
||||
<span class="draft">
|
||||
<span class="status-dot"></span>
|
||||
Draft
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isPublished}}
|
||||
<span class="published {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
Published
|
||||
{{#if @post.didEmailFail}}
|
||||
but failed to send newsletter
|
||||
{{else if @post.hasBeenEmailed}}
|
||||
and sent
|
||||
{{#if this.isHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isSent}}
|
||||
<span class="sent {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
{{#if @post.didEmailFail}}
|
||||
Failed to send newsletter
|
||||
{{else}}
|
||||
Sent
|
||||
{{#if this.isHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
{{/unless}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{!-- Opened / Signups column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-opens">
|
||||
{{#if (and @post.showEmailOpenAnalytics @post.showEmailClickAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "opened_emails.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.email.openedCount}}
|
||||
{{else}}
|
||||
{{@post.email.openRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
opened
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{else if (and @post.isPage @post.showAttributionAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "signup:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{@post.count.signups}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
signups
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
{{!-- Clicked / Conversions column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-clicks">
|
||||
{{#unless @post.showEmailClickAnalytics}}
|
||||
{{#if @post.showEmailOpenAnalytics }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "opened_emails.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.email.openedCount}}
|
||||
{{else}}
|
||||
{{@post.email.openRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
opened
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{#if @post.showEmailClickAnalytics }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "clicked_links.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isClickStatHovered) true)}} {{on "mouseleave" (fn (mut this.isClickStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.count.clicks}}
|
||||
{{else}}
|
||||
{{@post.clickRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
clicked
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{else if (and @post.isPage @post.showPaidAttributionAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "conversion:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isClickStatHovered) true)}} {{on "mouseleave" (fn (mut this.isClickStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{@post.count.paid_conversions}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
conversions
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
{{!-- Button column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-button">
|
||||
<div class="gh-list-data-inner">
|
||||
{{#if @post.hasAnalyticsPage }}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="gh-post-list-cta stats {{if this.isHovered "is-hovered"}}" title="">
|
||||
{{svg-jar "stats" title=""}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="gh-post-list-cta edit {{if this.isHovered "is-hovered"}}" title="">
|
||||
{{svg-jar "pen" title=""}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</LinkTo>
|
||||
</li>
|
|
@ -1,60 +0,0 @@
|
|||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {formatPostTime} from 'ghost-admin/helpers/gh-format-post-time';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class PostsListItemClicks extends Component {
|
||||
@service feature;
|
||||
@service session;
|
||||
@service settings;
|
||||
|
||||
@tracked isHovered = false;
|
||||
|
||||
get post() {
|
||||
return this.args.post;
|
||||
}
|
||||
|
||||
get errorClass() {
|
||||
if (this.post.didEmailFail) {
|
||||
return 'error';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
get scheduledText() {
|
||||
let text = [];
|
||||
|
||||
let formattedTime = formatPostTime(
|
||||
this.post.publishedAtUTC,
|
||||
{timezone: this.settings.get('timezone'), scheduled: true}
|
||||
);
|
||||
text.push(formattedTime);
|
||||
|
||||
return text.join(' ');
|
||||
}
|
||||
|
||||
get routeForLink() {
|
||||
if (this.post.hasAnalyticsPage) {
|
||||
return 'posts.analytics';
|
||||
}
|
||||
return 'editor.edit';
|
||||
}
|
||||
|
||||
get modelsForLink() {
|
||||
if (this.post.hasAnalyticsPage) {
|
||||
return [this.post];
|
||||
}
|
||||
return [this.post.displayName, this.post.id];
|
||||
}
|
||||
|
||||
@action
|
||||
mouseOver() {
|
||||
this.isHovered = true;
|
||||
}
|
||||
|
||||
@action
|
||||
mouseLeave() {
|
||||
this.isHovered = false;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{{!-- template-lint-disable no-invalid-interactive --}}
|
||||
<li class="gh-list-row gh-posts-list-item {{if this.feature.memberAttribution "gh-post-list-plain-status"}}"
|
||||
<li class="gh-list-row gh-posts-list-item gh-posts-list-item-labs gh-post-list-plain-status"
|
||||
{{on "mouseover" this.mouseOver}}
|
||||
{{on "mouseleave" this.mouseLeave}}
|
||||
...attributes
|
||||
|
@ -12,22 +12,40 @@
|
|||
{{@post.title}} {{svg-jar "external" class="gh-post-list-external"}}
|
||||
</h3>
|
||||
{{#unless @hideAuthor }}
|
||||
<p>
|
||||
<span class="gh-content-entry-meta">
|
||||
By <span class="midgrey-l2 fw5">{{post-author-names @post}}</span>
|
||||
<p class="gh-content-entry-meta">
|
||||
<span class="gh-content-entry-author">
|
||||
By {{post-author-names @post}}
|
||||
|
||||
{{#if @post.primaryTag}}
|
||||
in <span class="midgrey-l2 fw5">{{@post.primaryTag.name}}</span>
|
||||
{{/if}}
|
||||
|
||||
• <span data-tooltip="{{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}">{{gh-format-post-time @post.updatedAtUTC draft=true}}</span>
|
||||
|
||||
-
|
||||
</span>
|
||||
<span class="gh-content-entry-date">
|
||||
{{#if this.isHovered}}
|
||||
{{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}
|
||||
{{else}}
|
||||
{{gh-format-post-time @post.updatedAtUTC draft=true}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</p>
|
||||
<p class="gh-content-entry-status">
|
||||
<span class="published">
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
{{#if this.isHovered}}
|
||||
and sent to {{gh-pluralize @post.email.emailCount "member"}}
|
||||
{{else}}
|
||||
and sent
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</p>
|
||||
{{/unless}}
|
||||
</a>
|
||||
{{else}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-title">
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-title">
|
||||
<h3 class="gh-content-entry-title">
|
||||
{{@post.title}}
|
||||
{{#if @post.lexical}}
|
||||
|
@ -35,194 +53,156 @@
|
|||
{{/if}}
|
||||
</h3>
|
||||
{{#unless @hideAuthor }}
|
||||
<p>
|
||||
<span class="gh-content-entry-meta">
|
||||
By <span class="midgrey-l2 fw5">{{post-author-names @post}}</span>
|
||||
<p class="gh-content-entry-meta">
|
||||
<span class="gh-content-entry-author">
|
||||
By {{post-author-names @post}}
|
||||
|
||||
{{#if @post.primaryTag}}
|
||||
in <span class="midgrey-l2 fw5">{{@post.primaryTag.name}}</span>
|
||||
{{/if}}
|
||||
|
||||
• <span data-tooltip="{{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}">{{gh-format-post-time @post.updatedAtUTC draft=true}}</span>
|
||||
|
||||
-
|
||||
</span>
|
||||
<span class="gh-content-entry-date" {{on "mouseover" (fn (mut this.isDateHovered) true)}} {{on "mouseleave" (fn (mut this.isDateHovered) false)}}>
|
||||
{{gh-format-post-time @post.updatedAtUTC draft=true}}
|
||||
{{#if this.isDateHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>on {{gh-format-post-time @post.updatedAtUTC format="D MMM YYYY"}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{!-- {{#if @post.lexical}}
|
||||
<span class="gh-content-entry-date">– Lexical</span>
|
||||
{{/if}} --}}
|
||||
</p>
|
||||
<p class="gh-content-entry-status">
|
||||
{{#if @post.isScheduled}}
|
||||
<span class="scheduled" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
<span class="status-dot"></span>
|
||||
Scheduled
|
||||
{{#if this.isStateHovered}}
|
||||
<span class="schedule-details" {{css-transition "anim-fade-in-scale"}}>to be published {{if @post.newsletter "and sent "}}{{this.scheduledText}} to {{humanize-recipient-filter @post.emailSegment}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isDraft}}
|
||||
<span class="draft">
|
||||
<span class="status-dot"></span>
|
||||
Draft
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isPublished}}
|
||||
<span class="published {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
Published
|
||||
{{#if @post.didEmailFail}}
|
||||
but failed to send newsletter
|
||||
{{else if @post.hasBeenEmailed}}
|
||||
and sent
|
||||
{{#if this.isHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isSent}}
|
||||
<span class="sent {{this.errorClass}}" {{on "mouseover" (fn (mut this.isStateHovered) true)}} {{on "mouseleave" (fn (mut this.isStateHovered) false)}}>
|
||||
{{#if @post.didEmailFail}}
|
||||
Failed to send newsletter
|
||||
{{else}}
|
||||
Sent
|
||||
{{#if this.isHovered}}
|
||||
<span {{css-transition "anim-fade-in-scale"}}>to {{gh-pluralize @post.email.emailCount "member"}}</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
{{/unless}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{!-- Statuses for when the feature flag for Member Attribution is on only --}}
|
||||
{{#if this.feature.memberAttribution}}
|
||||
{{#unless @hideStatusColumn }}
|
||||
{{#if (and this.session.user.isContributor @post.isPublished)}}
|
||||
<a href={{@post.url}} class="permalink gh-list-data gh-post-list-status" target="_blank" rel="noopener noreferrer">
|
||||
<div class="flex items-center">
|
||||
<span class="gh-content-status-published nowrap">
|
||||
{{svg-jar "check" class="gh-post-status-icon"}}
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
& Sent
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
{{else}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-status">
|
||||
<div class="flex items-center">
|
||||
{{#if @post.isScheduled}}
|
||||
<span class="gh-content-status-scheduled gh-badge nowrap" title="Scheduled" data-tooltip="To be published {{if @post.newsletter "& sent at "}}{{capitalize this.scheduledText}} to {{@post.emailSegment}} members">
|
||||
{{svg-jar "clock" class="gh-post-status-icon"}}
|
||||
Scheduled
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isDraft}}
|
||||
<span class="gh-content-status-draft gh-badge gh-badge-pink nowrap">
|
||||
{{svg-jar "pen" class="gh-post-status-icon"}}
|
||||
Draft
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isPublished}}
|
||||
<span class="gh-content-status-published nowrap">
|
||||
{{svg-jar "check" class="gh-post-status-icon"}}
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
& Sent
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isSent}}
|
||||
<span class="gh-content-status-emailed nowrap">
|
||||
{{svg-jar "email-stroke" class="gh-post-status-icon"}}
|
||||
Sent
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (and this.feature.memberAttribution (not this.session.user.isContributor)) }}
|
||||
{{#if @post.count.signups}}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "signup:[" @post.id "]") }} class="permalink gh-list-data gh-post-list-signups active">
|
||||
<span class="midlightgrey {{if (not (eq @post.count.signups 0)) 'darkgrey'}} fw5 gh-content-attribution-stats">{{@post.count.signups}}</span>
|
||||
<span class="midgrey-l2 fw4 gh-content-attribution-stats-mobile">{{gh-pluralize @post.count.signups "signup"}}</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-signups">
|
||||
<span class="midlightgrey {{if (not (eq @post.count.signups 0)) 'darkgrey'}} fw5 gh-content-attribution-stats">{{@post.count.signups}}</span>
|
||||
<span class="midgrey-l2 fw4 gh-content-attribution-stats-mobile">{{gh-pluralize @post.count.signups "signup"}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (and this.feature.memberAttribution (not this.session.user.isContributor)) }}
|
||||
{{#if @post.count.paid_conversions}}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "conversion:[" @post.id "]") }} class="permalink gh-list-data gh-post-list-conversions active">
|
||||
<span class="midlightgrey {{if (not (eq @post.count.paid_conversions 0)) 'darkgrey'}} fw5 gh-content-attribution-stats">{{@post.count.paid_conversions}}</span>
|
||||
<span class="midgrey-l2 fw4 gh-content-attribution-stats-mobile">{{gh-pluralize @post.count.paid_conversions "conversion"}}</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-conversions">
|
||||
<span class="midlightgrey {{if (not (eq @post.count.paid_conversions 0)) 'darkgrey'}} fw5 gh-content-attribution-stats">{{@post.count.paid_conversions}}</span>
|
||||
<span class="midgrey-l2 fw4 gh-content-attribution-stats-mobile">{{gh-pluralize @post.count.paid_conversions "conversion"}}</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{!-- Sends/Opens columns --}}
|
||||
{{#if (and (not-eq this.settings.membersSignupAccess "none") (not-eq this.settings.editorDefaultEmailRecipients "disabled") (not this.session.user.isContributor))}}
|
||||
{{#if (and this.feature.emailAnalytics (eq @post.displayName "post"))}}
|
||||
{{!-- Sends column --}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-recipients">
|
||||
<div class="flex fw4">
|
||||
{{#if (eq @post.email.status "submitted")}}
|
||||
<span class="flex" data-tooltip={{humanize-recipient-filter @post.email.recipientFilter}}>
|
||||
<span class="darkgrey fw5 gh-content-email-stats">{{format-number @post.email.emailCount}}</span>
|
||||
<span class="midgrey-l2 fw4 gh-content-email-stats-mobile">{{gh-pluralize @post.email.emailCount "send"}}</span>
|
||||
</span>
|
||||
{{!-- Opened / Signups column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-opens">
|
||||
{{#if (and @post.showEmailOpenAnalytics @post.showEmailClickAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "opened_emails.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.email.openedCount}}
|
||||
{{else}}
|
||||
<span class="gh-list-nodata">—</span>
|
||||
{{@post.email.openRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
</div>
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
opened
|
||||
</span>
|
||||
</LinkTo>
|
||||
|
||||
{{!-- Opens column --}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-opens">
|
||||
{{#if (and @post.email.trackOpens (eq @post.email.status "submitted"))}}
|
||||
<div class="flex">
|
||||
{{#if this.feature.memberAttribution}}
|
||||
<span class="gh-list-rate-bar">
|
||||
<span class="gh-list-rate-number" data-tooltip="Opens: {{format-number @post.email.openedCount}}">{{@post.email.openRate}}% </span>
|
||||
<span class="gh-list-rate-amount"><span style={{html-safe (concat "width: " @post.email.openRate "%;")}}/></span>
|
||||
</span>
|
||||
{{else}}
|
||||
<span class="darkgrey fw5 gh-content-email-stats">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.email.openedCount}}
|
||||
{{else}}
|
||||
{{@post.email.openRate}}%
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="midgrey-l2 fw4 gh-content-email-stats-mobile">{{@post.email.openRate}}% opens</span>
|
||||
</div>
|
||||
{{else}}
|
||||
<span class="gh-list-nodata">—</span>
|
||||
{{/if}}
|
||||
{{else if (and @post.isPage @post.showAttributionAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "signup:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{@post.count.signups}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
signups
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{!-- Statuses for without the Member Attribution feature flag --}}
|
||||
{{#unless this.feature.memberAttribution}}
|
||||
{{#unless @hideStatusColumn }}
|
||||
{{#if (and this.session.user.isContributor @post.isPublished)}}
|
||||
<a href={{@post.url}} class="permalink gh-list-data gh-post-list-status" target="_blank" rel="noopener noreferrer">
|
||||
<div class="flex items-center">
|
||||
<span class="gh-content-status-published nowrap">
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
{{svg-jar "email-stroke" class="gh-post-status-email"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
{{else}}
|
||||
<LinkTo @route="editor.edit" @models={{array @post.displayName @post.id}} class="permalink gh-list-data gh-post-list-status">
|
||||
<div class="flex items-center">
|
||||
{{#if @post.isScheduled}}
|
||||
<span class="gh-content-status-scheduled gh-badge nowrap" title="Scheduled" data-tooltip="To be published {{if @post.newsletter "& sent at "}}{{capitalize this.scheduledText}} to {{@post.emailSegment}} members">
|
||||
Scheduled
|
||||
</span>
|
||||
</LinkTo>
|
||||
|
||||
{{!-- Clicked / Conversions column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-clicks">
|
||||
{{#unless @post.showEmailClickAnalytics}}
|
||||
{{#if @post.showEmailOpenAnalytics }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "opened_emails.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isOpenStatHovered) true)}} {{on "mouseleave" (fn (mut this.isOpenStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.email.openedCount}}
|
||||
{{else}}
|
||||
{{@post.email.openRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isDraft}}
|
||||
<span class="gh-content-status-draft gh-badge gh-badge-pink nowrap">
|
||||
Draft
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isPublished}}
|
||||
<span class="gh-content-status-published nowrap">
|
||||
Published
|
||||
{{#if @post.hasEmail}}
|
||||
{{svg-jar "email-stroke" class="gh-post-status-email"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if @post.isSent}}
|
||||
<span class="gh-content-status-emailed nowrap">
|
||||
{{svg-jar "email-stroke" class="gh-post-status-icon"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
opened
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/unless}}
|
||||
{{#if @post.showEmailClickAnalytics }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "clicked_links.post_id:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isClickStatHovered) true)}} {{on "mouseleave" (fn (mut this.isClickStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{#if this.isHovered}}
|
||||
{{format-number @post.count.clicks}}
|
||||
{{else}}
|
||||
{{@post.clickRate}}<sup>%</sup>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
clicked
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{else if (and @post.isPage @post.showPaidAttributionAnalytics) }}
|
||||
<LinkTo @route="members" @query={{hash filterParam=(concat "conversion:[" @post.id "]") }} class="flex flex-column gh-post-row-event" {{on "mouseover" (fn (mut this.isClickStatHovered) true)}} {{on "mouseleave" (fn (mut this.isClickStatHovered) false)}}>
|
||||
<span class="gh-content-email-stats-value">
|
||||
{{@post.count.paid_conversions}}
|
||||
</span>
|
||||
<span class="gh-content-email-stats">
|
||||
conversions
|
||||
</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
{{!-- Button column --}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="permalink gh-list-data gh-post-list-button">
|
||||
<div class="gh-list-data-inner">
|
||||
{{#if @post.hasAnalyticsPage }}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="gh-post-list-cta stats {{if this.isHovered "is-hovered"}}" title="">
|
||||
{{svg-jar "stats" title=""}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route={{this.routeForLink}} @models={{this.modelsForLink}} class="gh-post-list-cta edit {{if this.isHovered "is-hovered"}}" title="">
|
||||
{{svg-jar "pen" title=""}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
|
|
@ -4,19 +4,29 @@ import {formatPostTime} from 'ghost-admin/helpers/gh-format-post-time';
|
|||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
export default class PostsListItem extends Component {
|
||||
export default class PostsListItemClicks extends Component {
|
||||
@service feature;
|
||||
@service session;
|
||||
@service settings;
|
||||
|
||||
@tracked isHovered = false;
|
||||
|
||||
get post() {
|
||||
return this.args.post;
|
||||
}
|
||||
|
||||
get errorClass() {
|
||||
if (this.post.didEmailFail) {
|
||||
return 'error';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
get scheduledText() {
|
||||
let {post} = this.args;
|
||||
let text = [];
|
||||
|
||||
let formattedTime = formatPostTime(
|
||||
post.publishedAtUTC,
|
||||
this.post.publishedAtUTC,
|
||||
{timezone: this.settings.get('timezone'), scheduled: true}
|
||||
);
|
||||
text.push(formattedTime);
|
||||
|
@ -24,6 +34,20 @@ export default class PostsListItem extends Component {
|
|||
return text.join(' ');
|
||||
}
|
||||
|
||||
get routeForLink() {
|
||||
if (this.post.hasAnalyticsPage) {
|
||||
return 'posts.analytics';
|
||||
}
|
||||
return 'editor.edit';
|
||||
}
|
||||
|
||||
get modelsForLink() {
|
||||
if (this.post.hasAnalyticsPage) {
|
||||
return [this.post];
|
||||
}
|
||||
return [this.post.displayName, this.post.id];
|
||||
}
|
||||
|
||||
@action
|
||||
mouseOver() {
|
||||
this.isHovered = true;
|
||||
|
|
|
@ -141,89 +141,61 @@
|
|||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.feature.emailClicks }}
|
||||
|
||||
<div class="gh-expandable-block">
|
||||
<div class="gh-expandable-header">
|
||||
<div>
|
||||
<h4 class="gh-expandable-title">Newsletter analytics</h4>
|
||||
<p class="gh-expandable-description">Track how many members are opening emails and clicking links</p>
|
||||
</div>
|
||||
<button type="button" class="gh-btn" {{on "click" (toggle-action "newsletterTrackingOpen" this)}} data-test-toggle-membersemail>
|
||||
<span>{{if this.newsletterTrackingOpen "Close" "Expand"}}</span>
|
||||
</button>
|
||||
<div class="gh-expandable-block">
|
||||
<div class="gh-expandable-header">
|
||||
<div>
|
||||
<h4 class="gh-expandable-title">Newsletter analytics</h4>
|
||||
<p class="gh-expandable-description">Track how many members are opening emails and clicking links</p>
|
||||
</div>
|
||||
<div class="gh-expandable-content">
|
||||
{{#liquid-if this.newsletterTrackingOpen}}
|
||||
<div class="mb6">
|
||||
<div class="gh-newsletter-tracking">
|
||||
<div class="gh-newsletter-tracking-row">
|
||||
<div>
|
||||
<h4 class="gh-newsletter-tracking-title">Track newsletter opens</h4>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" data-test-label="email-track-opens">
|
||||
<input
|
||||
id="email-track-opens"
|
||||
type="checkbox"
|
||||
checked={{this.settings.emailTrackOpens}}
|
||||
class="gh-input"
|
||||
{{on "change" this.toggleEmailTrackOpens}}
|
||||
data-test-checkbox="email-track-opens"
|
||||
>
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="gh-btn" {{on "click" (toggle-action "newsletterTrackingOpen" this)}} data-test-toggle-analytics>
|
||||
<span>{{if this.newsletterTrackingOpen "Close" "Expand"}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="gh-expandable-content">
|
||||
{{#liquid-if this.newsletterTrackingOpen}}
|
||||
<div class="mb6">
|
||||
<div class="gh-newsletter-tracking">
|
||||
<div class="gh-newsletter-tracking-row">
|
||||
<div>
|
||||
<h4 class="gh-newsletter-tracking-title">Track newsletter opens</h4>
|
||||
</div>
|
||||
<div class="gh-newsletter-tracking-row">
|
||||
<div>
|
||||
<h4 class="gh-newsletter-tracking-title">Track newsletter link clicks</h4>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" data-test-label="email-track-opens">
|
||||
<input
|
||||
id="email-track-clicks"
|
||||
type="checkbox"
|
||||
checked={{this.settings.emailTrackClicks}}
|
||||
class="gh-input"
|
||||
{{on "change" this.toggleEmailTrackClicks}}
|
||||
data-test-checkbox="email-track-clicks"
|
||||
>
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" data-test-label="email-track-opens">
|
||||
<input
|
||||
id="email-track-opens"
|
||||
type="checkbox"
|
||||
checked={{this.settings.emailTrackOpens}}
|
||||
class="gh-input"
|
||||
{{on "change" this.toggleEmailTrackOpens}}
|
||||
data-test-checkbox="email-track-opens"
|
||||
>
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-newsletter-tracking-row">
|
||||
<div>
|
||||
<h4 class="gh-newsletter-tracking-title">Track newsletter link clicks</h4>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" data-test-label="email-track-clicks">
|
||||
<input
|
||||
id="email-track-clicks"
|
||||
type="checkbox"
|
||||
checked={{this.settings.emailTrackClicks}}
|
||||
class="gh-input"
|
||||
{{on "change" this.toggleEmailTrackClicks}}
|
||||
data-test-checkbox="email-track-clicks"
|
||||
>
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{else}}
|
||||
|
||||
<div class="gh-expandable-block">
|
||||
<div class="gh-expandable-header">
|
||||
<div>
|
||||
<h4 class="gh-expandable-title">Enable newsletter open-rate</h4>
|
||||
<p class="gh-expandable-description">Track how many members are reading your emails</p>
|
||||
</div>
|
||||
<div class="for-switch">
|
||||
<label class="switch" data-test-label="email-track-opens">
|
||||
<input
|
||||
id="email-track-opens"
|
||||
type="checkbox"
|
||||
checked={{this.settings.emailTrackOpens}}
|
||||
class="gh-input"
|
||||
{{on "change" this.toggleEmailTrackOpens}}
|
||||
data-test-checkbox="email-track-opens"
|
||||
>
|
||||
<span class="input-toggle-component mt1"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/liquid-if}}
|
||||
</div>
|
||||
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
|
|
@ -83,14 +83,6 @@ export default class PostsRoute extends AuthenticatedRoute {
|
|||
setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
|
||||
if (this.modelName === 'post') {
|
||||
if (this.feature.get('emailClicks')) {
|
||||
this.templateName = 'posts-clicks';
|
||||
} else {
|
||||
this.templateName = 'posts';
|
||||
}
|
||||
}
|
||||
|
||||
if (!controller._hasLoadedTags) {
|
||||
this.store.query('tag', {limit: 'all'}).then(() => {
|
||||
controller._hasLoadedTags = true;
|
||||
|
|
|
@ -63,7 +63,6 @@ export default class FeatureService extends Service {
|
|||
@feature('compExpiring') compExpiring;
|
||||
@feature('memberAttribution') memberAttribution;
|
||||
@feature('emailAlerts') emailAlerts;
|
||||
@feature('emailClicks') emailClicks;
|
||||
@feature('sourceAttribution') sourceAttribution;
|
||||
@feature('lexicalEditor') lexicalEditor;
|
||||
|
||||
|
|
|
@ -29,32 +29,11 @@
|
|||
<section class="view-container content-list">
|
||||
<div class="{{if this.feature.memberAttribution 'gh-list-sticky'}}">
|
||||
<ol class="pages-list gh-list {{unless this.postsInfinityModel "no-posts"}} {{if this.feature.memberAttribution 'feature-memberAttribution'}}">
|
||||
{{#unless this.feature.emailClicks}}
|
||||
{{#if this.postsInfinityModel}}
|
||||
<li class="gh-list-row header">
|
||||
<div class="gh-list-header gh-posts-title-header">Title</div>
|
||||
<div class="gh-list-header gh-posts-status-header">{{#unless this.feature.memberAttribution}}Status{{/unless}}</div>
|
||||
{{#if this.feature.memberAttribution}}
|
||||
<div class="gh-list-header gh-posts-signups-header">Signups</div>
|
||||
<div class="gh-list-header gh-posts-conversions-header">Conversions</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#each this.postsInfinityModel as |page|}}
|
||||
{{#if this.feature.emailClicks}}
|
||||
<PostsList::ListItemClicks
|
||||
@post={{page}}
|
||||
data-test-page-id={{page.id}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#unless this.feature.emailClicks}}
|
||||
<PostsList::ListItem
|
||||
@post={{page}}
|
||||
data-test-page-id={{page.id}}
|
||||
/>
|
||||
{{/unless}}
|
||||
<PostsList::ListItem
|
||||
@post={{page}}
|
||||
data-test-page-id={{page.id}}
|
||||
/>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
<section class="gh-canvas gh-canvas-sticky">
|
||||
<GhCanvasHeader class="gh-canvas-header sticky break tablet post-header">
|
||||
<GhCustomViewTitle @title={{if this.session.user.isContributor (concat this.config.blogTitle " posts") "Posts"}} @query={{reset-query-params "posts"}} />
|
||||
|
||||
<section class="view-actions">
|
||||
<PostsList::ContentFilter
|
||||
@currentUser={{this.session.user}}
|
||||
@selectedType={{this.selectedType}}
|
||||
@availableTypes={{this.availableTypes}}
|
||||
@onTypeChange={{action "changeType"}}
|
||||
@selectedVisibility={{this.selectedVisibility}}
|
||||
@availableVisibilities={{this.availableVisibilities}}
|
||||
@onVisibilityChange={{action "changeVisibility"}}
|
||||
@selectedAuthor={{this.selectedAuthor}}
|
||||
@availableAuthors={{this.availableAuthors}}
|
||||
@onAuthorChange={{action "changeAuthor"}}
|
||||
@selectedTag={{this.selectedTag}}
|
||||
@availableTags={{this.availableTags}}
|
||||
@onTagChange={{action "changeTag"}}
|
||||
@selectedOrder={{this.selectedOrder}}
|
||||
@availableOrders={{this.availableOrders}}
|
||||
@onOrderChange={{action "changeOrder"}}
|
||||
/>
|
||||
|
||||
<LinkTo @route="editor.new" @model="post" class="gh-btn gh-btn-primary view-actions-top-row" data-test-new-post-button={{true}}><span>New post</span></LinkTo>
|
||||
</section>
|
||||
</GhCanvasHeader>
|
||||
|
||||
<section class="view-container content-list">
|
||||
<ol class="posts-list gh-list {{unless this.postsInfinityModel "no-posts"}} feature-memberAttribution">
|
||||
|
||||
{{#each this.postsInfinityModel as |post|}}
|
||||
<PostsList::ListItemClicks
|
||||
@post={{post}}
|
||||
data-test-post-id={{post.id}}
|
||||
/>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{#if this.showingAll}}
|
||||
{{svg-jar "posts-placeholder" class="gh-posts-placeholder"}}
|
||||
<h4>Start creating content.</h4>
|
||||
<LinkTo @route="editor.new" @model="post" class="gh-btn gh-btn-green">
|
||||
<span>Write a new post</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<h4>No posts match the current filter</h4>
|
||||
<LinkTo @route="posts" @query={{hash type=null author=null tag=null}} class="gh-btn">
|
||||
<span>Show all posts</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
||||
<GhInfinityLoader
|
||||
@infinityModel={{this.postsInfinityModel}}
|
||||
@scrollable=".gh-main"
|
||||
@triggerOffset={{1000}} />
|
||||
</section>
|
||||
|
||||
{{outlet}}
|
||||
</section>
|
|
@ -27,52 +27,32 @@
|
|||
</GhCanvasHeader>
|
||||
|
||||
<section class="view-container content-list">
|
||||
<div class="{{if this.feature.memberAttribution 'gh-list-sticky'}}">
|
||||
<ol class="posts-list gh-list {{unless this.postsInfinityModel "no-posts"}} {{if this.feature.memberAttribution 'feature-memberAttribution'}}">
|
||||
{{#if this.postsInfinityModel}}
|
||||
<li class="gh-list-row header">
|
||||
<div class="gh-list-header gh-posts-title-header">Title</div>
|
||||
<ol class="posts-list gh-list {{unless this.postsInfinityModel "no-posts"}} feature-memberAttribution">
|
||||
|
||||
{{#if (and this.feature.memberAttribution (not this.session.user.isContributor)) }}
|
||||
<div class="gh-list-header gh-posts-status-header"></div>
|
||||
<div class="gh-list-header gh-posts-signups-header">Signups</div>
|
||||
<div class="gh-list-header gh-posts-conversions-header">Paid</div>
|
||||
{{/if}}
|
||||
{{#if (and (not-eq this.settings.membersSignupAccess "none") (not-eq this.settings.editorDefaultEmailRecipients "disabled") (not this.session.user.isContributor) this.feature.emailAnalytics)}}
|
||||
<div class="gh-list-header gh-posts-sends-header">Sends</div>
|
||||
<div class="gh-list-header gh-posts-opens-header">Opens</div>
|
||||
{{/if}}
|
||||
{{#if (or (not this.feature.memberAttribution) this.session.user.isContributor) }}
|
||||
<div class="gh-list-header gh-posts-status-header">Status</div>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#each this.postsInfinityModel as |post|}}
|
||||
<PostsList::ListItem
|
||||
@post={{post}}
|
||||
data-test-post-id={{post.id}}
|
||||
/>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{#if this.showingAll}}
|
||||
{{svg-jar "posts-placeholder" class="gh-posts-placeholder"}}
|
||||
<h4>Start creating content.</h4>
|
||||
<LinkTo @route="editor.new" @model="post" class="gh-btn gh-btn-green">
|
||||
<span>Write a new post</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<h4>No posts match the current filter</h4>
|
||||
<LinkTo @route="posts" @query={{hash type=null author=null tag=null}} class="gh-btn">
|
||||
<span>Show all posts</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
||||
{{#each this.postsInfinityModel as |post|}}
|
||||
<PostsList::ListItem
|
||||
@post={{post}}
|
||||
data-test-post-id={{post.id}}
|
||||
/>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
<div class="no-posts">
|
||||
{{#if this.showingAll}}
|
||||
{{svg-jar "posts-placeholder" class="gh-posts-placeholder"}}
|
||||
<h4>Start creating content.</h4>
|
||||
<LinkTo @route="editor.new" @model="post" class="gh-btn gh-btn-green">
|
||||
<span>Write a new post</span>
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<h4>No posts match the current filter</h4>
|
||||
<LinkTo @route="posts" @query={{hash type=null author=null tag=null}} class="gh-btn">
|
||||
<span>Show all posts</span>
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
||||
<GhInfinityLoader
|
||||
@infinityModel={{this.postsInfinityModel}}
|
||||
|
|
|
@ -35,7 +35,7 @@ describe('Acceptance: Error Handling', function () {
|
|||
this.server.put('/posts/:id', versionMismatchResponse);
|
||||
|
||||
await visit('/posts');
|
||||
await click('.posts-list li:nth-of-type(2) a'); // select second post
|
||||
await click('.posts-list li:nth-of-type(1) a'); // select first draft post (otherwise no automatic saving on blur)
|
||||
await fillIn('[data-test-editor-title-input]', 'Updated post');
|
||||
await blur('[data-test-editor-title-input]');
|
||||
|
||||
|
|
|
@ -126,6 +126,8 @@ describe('Acceptance: Settings - Newsletters', function () {
|
|||
this.server.db.settings.update({key: 'email_track_opens'}, {value: 'true'});
|
||||
|
||||
await visit('/settings/newsletters');
|
||||
|
||||
await click('[data-test-toggle-analytics]');
|
||||
expect(find('[data-test-checkbox="email-track-opens"]')).to.be.checked;
|
||||
|
||||
await click('[data-test-label="email-track-opens"]');
|
||||
|
@ -136,6 +138,22 @@ describe('Acceptance: Settings - Newsletters', function () {
|
|||
expect(this.server.db.settings.findBy({key: 'email_track_opens'}).value).to.equal(false);
|
||||
});
|
||||
|
||||
it('can manage click tracking', async function () {
|
||||
this.server.db.settings.update({key: 'email_track_clicks'}, {value: 'true'});
|
||||
|
||||
await visit('/settings/newsletters');
|
||||
|
||||
await click('[data-test-toggle-analytics]');
|
||||
expect(find('[data-test-checkbox="email-track-clicks"]')).to.be.checked;
|
||||
|
||||
await click('[data-test-label="email-track-clicks"]');
|
||||
expect(find('[data-test-checkbox="email-track-clicks"]')).to.not.be.checked;
|
||||
|
||||
await click('[data-test-button="save-members-settings"]');
|
||||
|
||||
expect(this.server.db.settings.findBy({key: 'email_track_clicks'}).value).to.equal(false);
|
||||
});
|
||||
|
||||
describe('Creating newsletters', function () {
|
||||
it('can create new newsletter', async function () {
|
||||
await visit('/settings/newsletters');
|
||||
|
|
|
@ -6,7 +6,6 @@ const localUtils = require('../../index');
|
|||
const mobiledoc = require('../../../../../lib/mobiledoc');
|
||||
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
|
||||
const clean = require('./utils/clean');
|
||||
const labs = require('../../../../../../shared/labs');
|
||||
|
||||
function removeSourceFormats(frame) {
|
||||
if (frame.options.formats?.includes('mobiledoc') || frame.options.formats?.includes('lexical')) {
|
||||
|
@ -25,11 +24,7 @@ function defaultRelations(frame) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (labs.isSet('emailClicks')) {
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
|
||||
} else {
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions'];
|
||||
}
|
||||
frame.options.withRelated = ['tags', 'authors', 'authors.roles', 'email', 'tiers', 'newsletter', 'count.signups', 'count.paid_conversions', 'count.clicks'];
|
||||
}
|
||||
|
||||
function setDefaultOrder(frame) {
|
||||
|
|
|
@ -10,7 +10,6 @@ const extraAttrs = require('../utils/extra-attrs');
|
|||
const gating = require('../utils/post-gating');
|
||||
const url = require('../utils/url');
|
||||
|
||||
const labs = require('../../../../../../../shared/labs');
|
||||
const utils = require('../../../index');
|
||||
|
||||
const postsMetaSchema = require('../../../../../../data/schema').tables.posts_meta;
|
||||
|
@ -110,20 +109,14 @@ module.exports = async (model, frame, options = {}) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (labs.isSet('emailClicks')) {
|
||||
if (jsonModel.email && jsonModel.count) {
|
||||
jsonModel.email.opened_count = Math.min(
|
||||
Math.max(
|
||||
jsonModel.email.opened_count || 0,
|
||||
jsonModel.count.clicks || 0
|
||||
),
|
||||
jsonModel.email.email_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!labs.isSet('memberAttribution') && !labs.isSet('emailClicks')) {
|
||||
delete jsonModel.count;
|
||||
if (jsonModel.email && jsonModel.count) {
|
||||
jsonModel.email.opened_count = Math.min(
|
||||
Math.max(
|
||||
jsonModel.email.opened_count || 0,
|
||||
jsonModel.count.clicks || 0
|
||||
),
|
||||
jsonModel.email.email_count
|
||||
);
|
||||
}
|
||||
|
||||
return jsonModel;
|
||||
|
|
|
@ -3,7 +3,6 @@ const api = require('../../../../api').endpoints;
|
|||
const {http} = require('@tryghost/api-framework');
|
||||
const apiMw = require('../../middleware');
|
||||
const mw = require('./middleware');
|
||||
const labs = require('../../../../../shared/labs');
|
||||
|
||||
const shared = require('../../../shared');
|
||||
|
||||
|
@ -310,7 +309,7 @@ module.exports = function apiRoutes() {
|
|||
router.put('/newsletters/verifications/', mw.authAdminApi, http(api.newsletters.verifyPropertyUpdate));
|
||||
router.put('/newsletters/:id', mw.authAdminApi, http(api.newsletters.edit));
|
||||
|
||||
router.get('/links', labs.enabledMiddleware('emailClicks'), mw.authAdminApi, http(api.links.browse));
|
||||
router.get('/links', mw.authAdminApi, http(api.links.browse));
|
||||
|
||||
return router;
|
||||
};
|
||||
|
|
|
@ -19,8 +19,7 @@ const GA_FEATURES = [
|
|||
'freeTrial',
|
||||
'compExpiring',
|
||||
'searchHelper',
|
||||
'emailAlerts',
|
||||
'emailClicks'
|
||||
'emailAlerts'
|
||||
];
|
||||
|
||||
// NOTE: this allowlist is meant to be used to filter out any unexpected
|
||||
|
|
|
@ -627,7 +627,7 @@ exports[`Settings API Edit Can edit a setting 2: [headers] 1`] = `
|
|||
Object {
|
||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||
"content-length": "3471",
|
||||
"content-length": "3450",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||
|
|
|
@ -46,13 +46,10 @@ module.exports = class EventRepository {
|
|||
{type: 'login_event', action: 'getLoginEvents'},
|
||||
{type: 'payment_event', action: 'getPaymentEvents'},
|
||||
{type: 'signup_event', action: 'getSignupEvents'},
|
||||
{type: 'comment_event', action: 'getCommentEvents'}
|
||||
{type: 'comment_event', action: 'getCommentEvents'},
|
||||
{type: 'click_event', action: 'getClickEvents'}
|
||||
];
|
||||
|
||||
if (this._labsService.isSet('emailClicks')) {
|
||||
pageActions.push({type: 'click_event', action: 'getClickEvents'});
|
||||
}
|
||||
|
||||
if (this._EmailRecipient) {
|
||||
pageActions.push({type: 'email_delivered_event', action: 'getEmailDeliveredEvents'});
|
||||
pageActions.push({type: 'email_opened_event', action: 'getEmailOpenedEvents'});
|
||||
|
|
Loading…
Add table
Reference in a new issue