diff --git a/ghost/admin/app/components/settings/analytics.hbs b/ghost/admin/app/components/settings/analytics.hbs new file mode 100644 index 0000000000..414b124d5f --- /dev/null +++ b/ghost/admin/app/components/settings/analytics.hbs @@ -0,0 +1,78 @@ +
+

Newsletters

+
+
+
+
+

Track newsletter opens

+

+ Record when member opens a newsletter email +

+
+
+ +
+
+
+ +
+
+
+

Track newsletter clicks

+

+ Record when a member clicks on any link in a newsletter email +

+
+
+ +
+
+
+
+
+ +
+

Sources

+
+
+
+
+

Track member sources

+

+ Record the post/page and referring domain for signups and subscriptions +

+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/ghost/admin/app/components/settings/analytics.js b/ghost/admin/app/components/settings/analytics.js new file mode 100644 index 0000000000..270f12c684 --- /dev/null +++ b/ghost/admin/app/components/settings/analytics.js @@ -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; + } +} diff --git a/ghost/admin/app/controllers/settings/analytics.js b/ghost/admin/app/controllers/settings/analytics.js index 0bd6769027..2f51689d40 100644 --- a/ghost/admin/app/controllers/settings/analytics.js +++ b/ghost/admin/app/controllers/settings/analytics.js @@ -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; } } diff --git a/ghost/admin/app/models/setting.js b/ghost/admin/app/models/setting.js index 490ac7de4f..56caa2da68 100644 --- a/ghost/admin/app/models/setting.js +++ b/ghost/admin/app/models/setting.js @@ -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'), diff --git a/ghost/admin/app/templates/settings/analytics.hbs b/ghost/admin/app/templates/settings/analytics.hbs index ee5d901396..60fd6f4ee0 100644 --- a/ghost/admin/app/templates/settings/analytics.hbs +++ b/ghost/admin/app/templates/settings/analytics.hbs @@ -1,4 +1,4 @@ -
+
@@ -12,85 +12,20 @@
- +
-
-

Newsletters

-
-
-
-
-

Track newsletter opens

-

- Record when member opens a newsletter email -

-
-
- -
-
-
- -
-
-
-

Track newsletter clicks

-

- Record when a member clicks on any link in a newsletter email -

-
-
- -
-
-
-
-
- -
-

Sources

-
-
-
-
-

Track member sources

-

- Record the post/page and referring domain for signups and subscriptions -

-
-
- -
-
-
-
-
- +
{{outlet}} \ No newline at end of file diff --git a/ghost/admin/mirage/fixtures/settings.js b/ghost/admin/mirage/fixtures/settings.js index 24d8b7e2f2..5b41fbad7a 100644 --- a/ghost/admin/mirage/fixtures/settings.js +++ b/ghost/admin/mirage/fixtures/settings.js @@ -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'),