Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the "update available" notification to the about page #5670

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/client/app/components/gh-upgrade-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Ember from 'ember';

export default Ember.Component.extend({
tagName: 'section',
classNames: ['gh-upgrade-notification'],
upgradeNotification: Ember.inject.service('upgrade-notification'),
message: Ember.computed.alias('upgradeNotification.content')
});
7 changes: 6 additions & 1 deletion core/client/app/routes/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
config: Ember.inject.service(),
dropdown: Ember.inject.service(),
notifications: Ember.inject.service(),
upgradeNotification: Ember.inject.service('upgrade-notification'),

afterModel: function (model, transition) {
if (this.get('session').isAuthenticated) {
Expand Down Expand Up @@ -146,7 +147,11 @@ export default Ember.Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
if (!user.get('isAuthor') && !user.get('isEditor')) {
self.store.findAll('notification', {reload: true}).then(function (serverNotifications) {
serverNotifications.forEach(function (notification) {
self.get('notifications').handleNotification(notification, isDelayed);
if (Ember.get(notification, 'type') === 'upgrade') {
self.get('upgradeNotification').set('content', notification.get('message'));
} else {
self.get('notifications').handleNotification(notification, isDelayed);
}
});
});
}
Expand Down
5 changes: 5 additions & 0 deletions core/client/app/services/upgrade-notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Ember from 'ember';

export default Ember.Service.extend({
content: ''
});
10 changes: 10 additions & 0 deletions core/client/app/styles/layouts/about.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@
font-size: 2.4rem;
}

/* Contributors
/* ---------------------------------------------------------- */
.gh-upgrade-notification {
color: var(--red);
}

.gh-upgrade-notification a {
color: var(--red);
text-decoration: underline;
}

/* Contributors
/* ---------------------------------------------------------- */
Expand Down
4 changes: 2 additions & 2 deletions core/client/app/templates/about.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<section class="view-content">
<header class="gh-about-header">
<img class="gh-logo" src="{{gh-path 'admin' '/img/ghost-logo.png'}}" alt="Ghost" />
{{!-- TODO: fix about notifications --}}
{{gh-notifications location="about-upgrade" notify="updateNotificationChange"}}
</header>

{{gh-upgrade-notification}}

<section class="gh-env-details">
<ul class="gh-env-list">
<li class="gh-env-list-version"><strong>Version</strong> {{model.version}}</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{#if message}}
<span>{{gh-format-html message}}</span>
{{/if}}
39 changes: 39 additions & 0 deletions core/client/tests/unit/components/gh-upgrade-notification-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* jshint expr:true */
import Ember from 'ember';
import { expect } from 'chai';
import {
describeComponent,
it
}
from 'ember-mocha';
import sinon from 'sinon';

describeComponent(
'gh-upgrade-notification',
'GhUpgradeNotificationComponent',
{
needs: ['helper:gh-format-html']
},
function () {
beforeEach(function () {
var upgradeMessage = Ember.Object.create();
upgradeMessage.set('content', 'Ghost 10.02.91 is available! Hot Damn. <a href="http://support.ghost.org/how-to-upgrade/" target="_blank">Click here</a> to upgrade.');
this.subject().set('upgradeNotification', upgradeMessage);
});

it('renders', function () {
// creates the component instance
var component = this.subject();
expect(component._state).to.equal('preRender');

// renders the component on the page
this.render();
expect(component._state).to.equal('inDOM');

expect(this.$().prop('tagName')).to.equal('SECTION');
expect(this.$().hasClass('gh-upgrade-notification')).to.be.true;
// caja tools sanitize target='_blank' attribute
expect(this.$().html()).to.contain('Hot Damn. <a href="http://support.ghost.org/how-to-upgrade/">Click here</a>');
});
}
);
29 changes: 25 additions & 4 deletions core/server/api/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ var Promise = require('bluebird'),
utils = require('./utils'),
pipeline = require('../utils/pipeline'),
canThis = permissions.canThis,
settings = require('./settings'),

// Holds the persistent notifications
// Holds the persistent notifications
notificationsStore = [],
// Holds the last used id
// Holds the last used id
notificationCounter = 0,
notifications;

function isRemoteNotifications(notification) {
// only notifications that come from remote ghost endpoint have uuids set
return notification.uuid !== undefined;
}
/**
* ## Notification API Methods
*
Expand Down Expand Up @@ -86,7 +91,7 @@ notifications = {
_.each(options.data.notifications, function (notification) {
notificationCounter = notificationCounter + 1;

notification = _.assign(defaults, notification, {
notification = _.assign(notification, defaults, {
id: notificationCounter
// status: 'alert'
});
Expand Down Expand Up @@ -153,7 +158,23 @@ notifications = {
});
notificationCounter = notificationCounter - 1;

return notification;
if (isRemoteNotifications(notification)) {
return settings.read({
context: {internal: true},
key: 'seenNotifications'
}).then(function (response) {
var seenNotifications = JSON.parse(response.settings[0].value),
seenNotificationsSettings = {
settings: [{
key: 'seenNotifications',
value: _.unique(seenNotifications.concat([notification.uuid]))
}]
};
return settings.edit(seenNotificationsSettings, options);
});
} else {
return notification;
}
}

tasks = [
Expand Down
33 changes: 9 additions & 24 deletions core/server/controllers/admin.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
var _ = require('lodash'),
api = require('../api'),
errors = require('../errors'),
updateCheck = require('../update-check'),
config = require('../config'),
var _ = require('lodash'),
api = require('../api'),
errors = require('../errors'),
updateCheck = require('../update-check'),
config = require('../config'),
updateNotifications = require('../update-notifications'),
adminControllers;

adminControllers = {
Expand Down Expand Up @@ -34,25 +35,9 @@ adminControllers = {
}

updateCheck().then(function then() {
return updateCheck.showUpdateNotification();
}).then(function then(updateVersion) {
if (!updateVersion) {
return;
}

var notification = {
type: 'upgrade',
location: 'settings-about-upgrade',
dismissible: false,
status: 'alert',
message: 'Ghost ' + updateVersion + ' is available! Hot Damn. <a href="http://support.ghost.org/how-to-upgrade/" target="_blank">Click here</a> to upgrade.'
};

return api.notifications.browse({context: {internal: true}}).then(function then(results) {
if (!_.some(results.notifications, {message: notification.message})) {
return api.notifications.add({notifications: [notification]}, {context: {internal: true}});
}
});
return updateCheck.getUpdateNotifications();
}).then(function then(notifications) {
return updateNotifications.process(notifications);
}).finally(function noMatterWhat() {
renderIndex();
}).catch(errors.logError);
Expand Down
5 changes: 5 additions & 0 deletions core/server/data/default-settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,10 @@
"password": {
"defaultValue": ""
}
},
"notifications": {
"seenNotifications": {
"defaultValue": "[]"
}
}
}
2 changes: 1 addition & 1 deletion core/server/data/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var db = {
uuid: {type: 'string', maxlength: 36, nullable: false, validations: {isUUID: true}},
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
value: {type: 'text', maxlength: 65535, nullable: true},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private']]}},
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core', validations: {isIn: [['core', 'blog', 'theme', 'app', 'plugin', 'private', 'notifications']]}},
created_at: {type: 'dateTime', nullable: false},
created_by: {type: 'integer', nullable: false},
updated_at: {type: 'dateTime', nullable: true},
Expand Down
34 changes: 19 additions & 15 deletions core/server/update-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ function updateCheckData() {
return errors.rejectError(e);
}

return _.reduce(apps, function (memo, item) { return memo === '' ? memo + item : memo + ', ' + item; }, '');
return _.reduce(apps, function (memo, item) {
return memo === '' ? memo + item : memo + ', ' + item;
}, '');
}).catch(errors.rejectError));
ops.push(api.posts.browse().catch(errors.rejectError));
ops.push(api.users.browse(internal).catch(errors.rejectError));
Expand Down Expand Up @@ -203,24 +205,26 @@ function updateCheck() {
}
}

function showUpdateNotification() {
function getUpdateNotifications() {
return api.settings.read(_.extend({key: 'displayUpdateNotification'}, internal)).then(function then(response) {
var display = response.settings[0];

// Version 0.4 used boolean to indicate the need for an update. This special case is
// translated to the version string.
// TODO: remove in future version.
if (display.value === 'false' || display.value === 'true' || display.value === '1' || display.value === '0') {
display.value = '0.4.0';
}
var notifications = [];

_.each(response.settings, function (notification) {
// Version 0.4 used boolean to indicate the need for an update. This special case is
// translated to the version string.
// TODO: remove in future version.
if (notification.value === 'false' || notification.value === 'true' || notification.value === '1' || notification.value === '0') {
notification.value = '0.4.0';
}

if (display && display.value && currentVersion && semver.gt(display.value, currentVersion)) {
return display.value;
}
if (notification && notification.value && currentVersion && semver.gt(notification.value, currentVersion)) {
notifications.push(notification);
}
});

return false;
return notifications;
});
}

module.exports = updateCheck;
module.exports.showUpdateNotification = showUpdateNotification;
module.exports.getUpdateNotifications = getUpdateNotifications;
68 changes: 68 additions & 0 deletions core/server/update-notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
var _ = require('lodash'),
api = require('./api');

function shouldAddNotification(currentNotifications, rawNotification, seenNotifications) {
if (!rawNotification.uuid) {
return false;
}
var hasBeenSeen = _.contains(seenNotifications, rawNotification.uuid);
return !hasBeenSeen && !_.some(currentNotifications, {uuid: rawNotification.uuid});
}

function filterOutNotifications(currentNotifications, possibleNotifications) {
var filteredNotifications = {
notifications: []
};

return api.settings.read({
context: {internal: true},
key: 'seenNotifications'
})
.then(function (response) {
var seenNotifications = JSON.parse(response.settings[0].value);
_.each(possibleNotifications, function (rawNotification) {
if (shouldAddNotification(currentNotifications, rawNotification, seenNotifications)) {
var defaultUpdateMessage = '<span>Hey there! ' + rawNotification.value + ' is available, visit <a href=\"https://ghost.org/download\">Ghost.org</a> to grab your copy now!</span>',
notification = {
dismissible: true,
location: 'bottom',
status: 'alert',
type: 'success',
uuid: rawNotification.uuid || rawNotification.id,
message: rawNotification.content || defaultUpdateMessage
};
filteredNotifications.notifications.push(notification);
}
});

if (!_.some(currentNotifications, {status: 'upgrade'})) {
// todo: temporary solution add logic for checking if any notifications exist
filteredNotifications.notifications.push({
type: 'upgrade',
status: 'upgrade',
dismissible: false,
message: 'Ghost is available! Hot Damn. <a href="http://support.ghost.org/how-to-upgrade/" target="_blank">Click here</a> to upgrade.'
});
}

return filteredNotifications;
});
}

function process(possibleNotifications) {
if (_.isEmpty(possibleNotifications)) {
return;
}

return api.notifications.browse({context: {internal: true}})
.then(function then(results) {
return filterOutNotifications(results.notifications, possibleNotifications);
})
.then(function (notificationsToAdd) {
if (!_.isEmpty(notificationsToAdd)) {
return api.notifications.add(notificationsToAdd, {context: {internal: true}});
}
});
}

module.exports.process = process;