0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

Wired new analytics settings for tracking email and sources

refs https://github.com/TryGhost/Team/issues/2168

- wires new source tracking setting for members to UI
- splits the new analytics page settings to its own component, cleaned up the files and wired up the settings upstream
This commit is contained in:
Rishabh 2022-10-27 17:28:28 +05:30
parent cca0f7d7dc
commit 9503ca1c7b
6 changed files with 129 additions and 238 deletions

View file

@ -0,0 +1,78 @@
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Newsletters</h4>
<section class="gh-expandable">
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track newsletter opens</h4>
<p class="gh-expandable-description">
Record when member opens a newsletter email
</p>
</div>
<div class="for-switch">
<label class="switch" for="email-track-opens">
<input
id="email-track-opens"
type="checkbox"
checked={{this.settings.emailTrackOpens}}
{{on "change" this.toggleEmailTrackOpens}}
data-test-checkbox="email-track-opens"
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track newsletter clicks</h4>
<p class="gh-expandable-description">
Record when a member clicks on any link in a newsletter email
</p>
</div>
<div class="for-switch">
<label class="switch" for="email-track-clicks">
<input
id="email-track-clicks"
type="checkbox"
{{on "change" this.toggleEmailTrackClicks}}
checked={{this.settings.emailTrackClicks}}
data-test-checkbox="email-track-clicks"
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
</section>
</div>
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Sources</h4>
<section class="gh-expandable">
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track member sources</h4>
<p class="gh-expandable-description">
Record the post/page and referring domain for signups and subscriptions
</p>
</div>
<div class="for-switch">
<label class="switch" for="members-track-sources">
<input
id="members-track-sources"
type="checkbox"
checked={{this.settings.membersTrackSources}}
data-test-checkbox="members-track-sources"
{{on "change" this.toggleMembersTrackSources}}
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,33 @@
import Component from '@glimmer/component';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
export default class Analytics extends Component {
@service config;
@service settings;
@service feature;
@action
toggleEmailTrackOpens(event) {
if (event) {
event.preventDefault();
}
this.settings.emailTrackOpens = !this.settings.emailTrackOpens;
}
@action
toggleEmailTrackClicks(event) {
if (event) {
event.preventDefault();
}
this.settings.emailTrackClicks = !this.settings.emailTrackClicks;
}
@action
toggleMembersTrackSources(event) {
if (event) {
event.preventDefault();
}
this.settings.membersTrackSources = !this.settings.membersTrackSources;
}
}

View file

@ -1,170 +1,13 @@
import classic from 'ember-classic-decorator';
import {action, computed} from '@ember/object';
import {inject as service} from '@ember/service';
/* eslint-disable ghost/ember/alias-model-in-controller */
import Controller from '@ember/controller';
import generatePassword from 'ghost-admin/utils/password-generator';
import {
IMAGE_EXTENSIONS,
IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {TrackedObject} from 'tracked-built-ins';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';
function randomPassword() {
let word = generatePassword(6);
let randomN = Math.floor(Math.random() * 1000);
return word + randomN;
}
@classic
export default class AnalyticsController extends Controller {
@service config;
@service ghostPaths;
@service notifications;
@service session;
@service settings;
@service frontend;
@service ui;
@tracked scratchValues = new TrackedObject();
availableTimezones = this.config.availableTimezones;
imageExtensions = IMAGE_EXTENSIONS;
imageMimeTypes = IMAGE_MIME_TYPES;
@computed('config.blogUrl', 'settings.publicHash')
get privateRSSUrl() {
let blogUrl = this.config.blogUrl;
let publicHash = this.settings.publicHash;
return `${blogUrl}/${publicHash}/rss`;
}
@action
save() {
this.saveTask.perform();
}
@action
setTimezone(timezone) {
this.settings.timezone = timezone.name;
}
@action
removeImage(image) {
// setting `null` here will error as the server treats it as "null"
this.settings[image] = '';
}
/**
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
* searches for the hidden file input within the .gh-setting element
* containing the clicked button then simulates a click
* @param {MouseEvent} event - MouseEvent fired by the button click
*/
@action
triggerFileDialog(event) {
event?.target.closest('.gh-setting-action')?.querySelector('input[type="file"]')?.click();
}
/**
* Fired after an image upload completes
* @param {string} property - Property name to be set on `this.settings`
* @param {UploadResult[]} results - Array of UploadResult objects
* @return {string} The URL that was set on `this.settings.property`
*/
@action
imageUploaded(property, results) {
if (results[0]) {
return this.settings[property] = results[0].url;
}
}
@action
toggleIsPrivate(isPrivate) {
let settings = this.settings;
settings.isPrivate = isPrivate;
settings.errors.remove('password');
let changedAttrs = settings.changedAttributes();
// set a new random password when isPrivate is enabled
if (isPrivate && changedAttrs.isPrivate) {
settings.password = randomPassword();
// reset the password when isPrivate is disabled
} else if (changedAttrs.password) {
settings.password = changedAttrs.password[0];
}
}
@action
setScratchValue(property, value) {
this.scratchValues[property] = value;
}
clearScratchValues() {
this.scratchValues = new TrackedObject();
}
_deleteTheme() {
let theme = this.store.peekRecord('theme', this.themeToDelete.name);
if (!theme) {
return;
}
return theme.destroyRecord().catch((error) => {
this.notifications.showAPIError(error);
});
}
@task
*saveTask() {
let notifications = this.notifications;
let config = this.config;
try {
let changedAttrs = this.settings.changedAttributes();
let settings = yield this.settings.save();
this.clearScratchValues();
config.set('blogTitle', settings.title);
if (changedAttrs.password) {
this.frontend.loginIfNeeded();
}
// this forces the document title to recompute after a blog title change
this.ui.updateDocumentTitle();
return settings;
} catch (error) {
if (error) {
notifications.showAPIError(error, {key: 'settings.save'});
}
throw error;
}
}
@action
saveViaKeyboard(event) {
event.preventDefault();
// trigger any set-on-blur actions
const focusedElement = document.activeElement;
focusedElement?.blur();
// schedule save for when set-on-blur actions have finished
run.schedule('actions', this, function () {
focusedElement?.focus();
this.saveTask.perform();
});
@task({drop: true})
*saveSettings() {
const response = yield this.settings.save();
return response;
}
}

View file

@ -59,6 +59,7 @@ export default Model.extend(ValidationEngine, {
membersSupportAddress: attr('string'),
membersMonthlyPriceId: attr('string'),
membersYearlyPriceId: attr('string'),
membersTrackSources: attr('boolean'),
stripeSecretKey: attr('string'),
stripePublishableKey: attr('string'),
stripePlans: attr('json-string'),

View file

@ -1,4 +1,4 @@
<section class="gh-canvas" {{on-key "cmd+s" this.saveViaKeyboard}}>
<section class="gh-canvas">
<GhCanvasHeader class="gh-canvas-header">
<div class="flex flex-column">
<div class="gh-canvas-breadcrumb">
@ -12,85 +12,20 @@
</h2>
</div>
<section class="view-actions">
<GhTaskButton @buttonText="Save" @task={{this.saveTask}} @class="gh-btn gh-btn-primary gh-btn-icon" data-test-button="save" />
<GhTaskButton
@buttonText="Save"
@task={{this.saveSettings}}
@successText="Saved"
@runningText="Saving"
{{on-key "cmd+s"}}
@class="gh-btn gh-btn-primary gh-btn-icon"
data-test-button="save-analytics-settings"
/>
</section>
</GhCanvasHeader>
<div>
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Newsletters</h4>
<section class="gh-expandable">
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track newsletter opens</h4>
<p class="gh-expandable-description">
Record when member opens a newsletter email
</p>
</div>
<div class="for-switch">
<label class="switch" for="settings-private">
<input
type="checkbox"
checked={{this.settings.isPrivate}}
id="settings-private"
data-test-private-checkbox
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track newsletter clicks</h4>
<p class="gh-expandable-description">
Record when a member clicks on any link in a newsletter email
</p>
</div>
<div class="for-switch">
<label class="switch" for="settings-private">
<input
type="checkbox"
checked={{this.settings.isPrivate}}
data-test-private-checkbox
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
</section>
</div>
<div class="gh-main-section">
<h4 class="gh-main-section-header small bn">Sources</h4>
<section class="gh-expandable">
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Track member sources</h4>
<p class="gh-expandable-description">
Record the post/page and referring domain for signups and subscriptions
</p>
</div>
<div class="for-switch">
<label class="switch" for="settings-private">
<input
type="checkbox"
checked={{this.settings.isPrivate}}
data-test-private-checkbox
>
<span class="input-toggle-component"></span>
</label>
</div>
</div>
</div>
</section>
</div>
<Settings::Analytics />
</div>
</section>
{{outlet}}

View file

@ -71,6 +71,7 @@ export default [
setting('members', 'stripe_connect_account_id', null),
setting('members', 'members_monthly_price_id', null),
setting('members', 'members_yearly_price_id', null),
setting('members', 'members_track_sources', null),
// PORTAL
setting('portal', 'portal_name', 'true'),