Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
leonyangela committed Oct 26, 2022
2 parents 1710843 + 2bacff0 commit 0847dfa
Show file tree
Hide file tree
Showing 29 changed files with 656 additions and 108 deletions.
8 changes: 3 additions & 5 deletions ghost/admin/app/components/gh-nav-menu/main.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@
</a>
</li>
{{#if (gh-user-can-admin this.session.user)}}
{{#if (feature "exploreApp")}}
<li class="relative">
<LinkTo @route="explore" title="Ghost Explore" data-test-nav="explore">{{svg-jar "globe-simple"}} Explore</LinkTo>
</li>
{{/if}}
<li class="relative">
<LinkTo @route="explore" title="Ghost Explore" data-test-nav="explore">{{svg-jar "globe-simple"}} Explore</LinkTo>
</li>
{{/if}}
</ul>
<ul class="gh-nav-list gh-nav-manage">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

{{#if (and @newsletter.feedbackEnabled (feature "audienceFeedback"))}}
<div class="gh-members-emailpreview-feedback gh-members-emailpreview-content">
<h4 class="gh-members-emailpreview-feedback-title">What did you think of this post?</h4>
<h4 class="gh-members-emailpreview-feedback-title">Give feedback on this post</h4>

<div class="gh-members-emailpreview-feedback-buttons-group">
<Modals::Newsletters::Components::FeedbackButton
Expand Down
54 changes: 30 additions & 24 deletions ghost/admin/app/components/posts/post-activity-feed.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</div>
</div>
{{else}}
<div class="gh-dashboard-list-body {{if (eq this.eventType "conversion") "gh-dashboard-list-four-cols" }}">
<div class="gh-dashboard-list-body {{if (eq this.eventType "clicked") "gh-dashboard-list-larger-cols" }} {{if (eq this.eventType "conversion") "gh-dashboard-list-four-cols" }}">
{{#each eventsFetcher.data as |event|}}
{{#let (parse-member-event event) as |parsedEvent|}}
<div class="gh-dashboard-list-item">
Expand All @@ -53,6 +53,10 @@
<span class="gh-dashboard-list-subtext">
<span class="gh-members-activity-description">
<span class="gh-members-activity-event-text">{{capitalize-first-letter parsedEvent.action}}</span>
{{#if parsedEvent.url}}
<span class="gh-members-activity-event-dash">–</span>
<a class="ghost-members-activity-object-link" href="{{parsedEvent.url}}" target="_blank" rel="noopener noreferrer">{{parsedEvent.description}}</a>
{{/if}}
</span>
</span>
</div>
Expand All @@ -76,7 +80,7 @@
{{/let}}
{{/each}}

{{#if (not (compute (fn this.isPaginationNeeded eventsFetcher)))}}
{{#if (compute (fn this.areStubsNeeded eventsFetcher))}}
{{#let (compute (fn this.getAmountOfStubs eventsFetcher)) as |stubs|}}
{{#each stubs}}
<div class="gh-dashboard-list-item"></div>
Expand All @@ -94,36 +98,38 @@
{{svg-jar "filter"}}
See members
</LinkTo>
{{else}}
<div></div>
{{/if}}

<div class="gh-post-activity-feed-pagination">
{{#if (compute (fn this.isPaginationNeeded eventsFetcher))}}
{{#if (compute (fn this.isPaginationNotNeeded eventsFetcher))}}
Showing {{eventsFetcher.totalEvents}} in total
{{else}}
Showing {{eventsFetcher.previousEvents}}-{{eventsFetcher.shownEvents}} of {{eventsFetcher.totalEvents}}
{{/if}}

<div class="gh-post-activity-feed-pagination-group">
<button
class="gh-post-activity-feed-pagination-button gh-post-activity-feed-prev-button"
type="button"
title="Previous page"
disabled={{compute (fn this.isPreviousButtonDisabled eventsFetcher)}}
{{on "click" eventsFetcher.loadPreviousPage}}
>
{{svg-jar "arrow-left-pagination"}}
</button>
<div class="gh-post-activity-feed-pagination-group">
<button
class="gh-post-activity-feed-pagination-button gh-post-activity-feed-prev-button"
type="button"
title="Previous page"
disabled={{compute (fn this.isPreviousButtonDisabled eventsFetcher)}}
{{on "click" eventsFetcher.loadPreviousPage}}
>
{{svg-jar "arrow-left-pagination"}}
</button>

<button
class="gh-post-activity-feed-pagination-button gh-post-activity-feed-next-button"
type="button"
title="Next page"
disabled={{compute (fn this.isNextButtonDisabled eventsFetcher)}}
{{on "click" eventsFetcher.loadNextPage}}
>
{{svg-jar "arrow-right-pagination"}}
</button>
</div>
<button
class="gh-post-activity-feed-pagination-button gh-post-activity-feed-next-button"
type="button"
title="Next page"
disabled={{compute (fn this.isNextButtonDisabled eventsFetcher)}}
{{on "click" eventsFetcher.loadNextPage}}
>
{{svg-jar "arrow-right-pagination"}}
</button>
</div>
{{/if}}
</div>
</div>
</div>
Expand Down
7 changes: 6 additions & 1 deletion ghost/admin/app/components/posts/post-activity-feed.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ export default class PostActivityFeed extends Component {
}

@action
isPaginationNeeded({totalEvents}) {
isPaginationNotNeeded({totalEvents}) {
return (totalEvents <= this._pageSize);
}

@action
areStubsNeeded({totalEvents}) {
return totalEvents > this._pageSize || this.args.eventType === 'feedback';
}
}
170 changes: 170 additions & 0 deletions ghost/admin/app/controllers/settings/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
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 {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();
});
}
}
1 change: 1 addition & 0 deletions ghost/admin/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Router.map(function () {
this.route('settings.membership', {path: '/settings/members'});
this.route('settings.code-injection', {path: '/settings/code-injection'});
this.route('settings.history', {path: '/settings/history'});
this.route('settings.analytics', {path: '/settings/analytics'});

// redirect from old /settings/members-email to /settings/newsletters
this.route('settings.members-email', {path: '/settings/members-email'});
Expand Down
4 changes: 2 additions & 2 deletions ghost/admin/app/routes/explore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export default class ExploreIndexRoute extends AuthenticatedRoute {
// older versions of Ghost where the `connect` part lives in the
// explore route directly. By using the query param, we avoid causing
// a 404 and handle the redirect here.
if (transition.to?.queryParams?.new === 'true' || !this.feature.exploreApp) {
if (transition.to?.queryParams?.new === 'true') {
this.explore.isIframeTransition = false;
return this.router.transitionTo('explore.connect');
}

// Ensure the explore window is set to open
if (this.feature.get('exploreApp') && transition.to?.localName === 'index' && !this.explore.exploreWindowOpen) {
if (transition.to?.localName === 'index' && !this.explore.exploreWindowOpen) {
this.explore.openExploreWindow(this.router.currentURL);
}
}
Expand Down
14 changes: 13 additions & 1 deletion ghost/admin/app/routes/member.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,19 @@ export default class MembersRoute extends AdminRoute {
controller.fetchMemberTask.perform(member.id);
}

if (transition.from?.name === 'members.index' && transition.from?.parent?.name === 'members') {
if (transition.from?.name === 'posts.analytics') {
// Sadly transition.from.params is not reliable to use (not populated on transitions)
const oldParams = transition.router?.oldState?.params['posts.analytics'] ?? {};

// We need to store analytics in 'this' to have it accessible for the member route
this.fromAnalytics = Object.values(oldParams);
controller.fromAnalytics = this.fromAnalytics;
} else if (transition.from?.metadata?.fromAnalytics) {
// Handle returning from member route
const fromAnalytics = transition.from?.metadata.fromAnalytics ?? null;
controller.fromAnalytics = fromAnalytics;
this.fromAnalytics = fromAnalytics;
} else if (transition.from?.name === 'members.index' && transition.from?.parent?.name === 'members') {
const fromAnalytics = transition.from?.parent?.metadata.fromAnalytics ?? null;
controller.fromAnalytics = fromAnalytics;
this.fromAnalytics = fromAnalytics;
Expand Down
Loading

0 comments on commit 0847dfa

Please sign in to comment.