mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Merge branch '0.4-maintenance'
This commit is contained in:
commit
be9afc439c
17 changed files with 116 additions and 57 deletions
|
@ -55,7 +55,7 @@
|
||||||
1. Login
|
1. Login
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
|
|
||||||
#login {
|
.login-form {
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
max-width: 530px;
|
max-width: 530px;
|
||||||
color: lighten($midgrey, 15%);
|
color: lighten($midgrey, 15%);
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
2. Signup and Reset
|
2. Signup and Reset
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
|
|
||||||
#signup, #reset {
|
.signup-form, .reset-form {
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
color: lighten($midgrey, 15%);
|
color: lighten($midgrey, 15%);
|
||||||
|
@ -270,7 +270,7 @@
|
||||||
3. Forgotten
|
3. Forgotten
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
|
|
||||||
#forgotten {
|
.forgotten-form {
|
||||||
@include box-sizing(border-box);
|
@include box-sizing(border-box);
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
color: lighten($midgrey, 15%);
|
color: lighten($midgrey, 15%);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
|
|
||||||
#notifications {
|
.notifications {
|
||||||
@include breakpoint($biggerthan-mobile) {
|
@include breakpoint($biggerthan-mobile) {
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +376,7 @@
|
||||||
|
|
||||||
body.zen {
|
body.zen {
|
||||||
background: lighten($lightbrown, 3%);
|
background: lighten($lightbrown, 3%);
|
||||||
#usermenu {display: none;}
|
.usermenu {display: none;}
|
||||||
#global-header, #publish-bar {
|
#global-header, #publish-bar {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
|
@ -540,7 +540,7 @@ nav {
|
||||||
}//.navbar
|
}//.navbar
|
||||||
|
|
||||||
// The user menu in the top right corner of the screen
|
// The user menu in the top right corner of the screen
|
||||||
#usermenu {
|
.usermenu.subnav {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top:0;
|
top:0;
|
||||||
right:0;
|
right:0;
|
||||||
|
@ -636,7 +636,7 @@ nav {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#usermenu {
|
.usermenu {
|
||||||
position:fixed;
|
position:fixed;
|
||||||
top:0;
|
top:0;
|
||||||
right:auto;
|
right:auto;
|
||||||
|
@ -928,7 +928,7 @@ nav {
|
||||||
Notifications
|
Notifications
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
#notifications {
|
.notifications {
|
||||||
@include breakpoint($biggerthan-mobile) {
|
@include breakpoint($biggerthan-mobile) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -1028,6 +1028,12 @@ nav {
|
||||||
background: $blue;
|
background: $blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide extra space taken up by update notification
|
||||||
|
.update-available main {
|
||||||
|
bottom: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
Modals
|
Modals
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form id="forgotten" method="post" novalidate="novalidate">
|
<form id="forgotten" class="forgotten-form" method="post" novalidate="novalidate">
|
||||||
<div class="email-wrap">
|
<div class="email-wrap">
|
||||||
<input class="email" type="email" placeholder="Email Address" name="email" autocapitalize="off" autocorrect="off">
|
<input class="email" type="email" placeholder="Email Address" name="email" autocapitalize="off" autocorrect="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form id="login" method="post" novalidate="novalidate">
|
<form id="login" class="login-form" method="post" novalidate="novalidate">
|
||||||
<div class="email-wrap">
|
<div class="email-wrap">
|
||||||
<input class="email" type="email" placeholder="Email Address" name="email" autocapitalize="off" autocorrect="off">
|
<input class="email" type="email" placeholder="Email Address" name="email" autocapitalize="off" autocorrect="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form id="reset" method="post" novalidate="novalidate">
|
<form id="reset" class="reset-form" method="post" novalidate="novalidate">
|
||||||
<div class="password-wrap">
|
<div class="password-wrap">
|
||||||
<input class="password" type="password" placeholder="Password" name="newpassword" />
|
<input class="password" type="password" placeholder="Password" name="newpassword" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<form id="signup" method="post" novalidate="novalidate">
|
<form id="signup" class="signup-form" method="post" novalidate="novalidate">
|
||||||
<div class="name-wrap">
|
<div class="name-wrap">
|
||||||
<input class="name" type="text" placeholder="Full Name" name="name" autocorrect="off" />
|
<input class="name" type="text" placeholder="Full Name" name="name" autocorrect="off" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,12 +5,17 @@
|
||||||
Ghost.Views.Debug = Ghost.View.extend({
|
Ghost.Views.Debug = Ghost.View.extend({
|
||||||
events: {
|
events: {
|
||||||
"click .settings-menu a": "handleMenuClick",
|
"click .settings-menu a": "handleMenuClick",
|
||||||
|
"click #startupload": "handleUploadClick",
|
||||||
"click .js-delete": "handleDeleteClick"
|
"click .js-delete": "handleDeleteClick"
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
var view = this;
|
||||||
|
|
||||||
|
this.uploadButton = this.$el.find('#startupload');
|
||||||
|
|
||||||
// Disable import button and initizalize BlueImp file upload
|
// Disable import button and initizalize BlueImp file upload
|
||||||
$('#startupload').prop('disabled', true);
|
this.uploadButton.prop('disabled', 'disabled');
|
||||||
$('#importfile').fileupload({
|
$('#importfile').fileupload({
|
||||||
url: Ghost.paths.apiRoot + '/db/',
|
url: Ghost.paths.apiRoot + '/db/',
|
||||||
limitMultiFileUploads: 1,
|
limitMultiFileUploads: 1,
|
||||||
|
@ -21,16 +26,12 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
add: function (e, data) {
|
add: function (e, data) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
// unregister click event to preveng duplicate binding
|
|
||||||
$('#startupload').off("click");
|
// Bind the upload data to the view, so it is
|
||||||
data.context = $('#startupload').prop('disabled', false)
|
// available to the click handler, and enable the
|
||||||
.click(function () {
|
// upload button.
|
||||||
$('#startupload').prop('disabled', true);
|
view.fileUploadData = data;
|
||||||
data.context = $('#startupload').text('Importing');
|
data.context = view.uploadButton.removeProp('disabled');
|
||||||
data.submit();
|
|
||||||
// unregister click event to allow different subsequent uploads
|
|
||||||
$('#startupload').off('click');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
done: function (e, data) {
|
done: function (e, data) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
|
@ -77,6 +78,18 @@
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleUploadClick: function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if (!this.uploadButton.prop('disabled')) {
|
||||||
|
this.fileUploadData.context = this.uploadButton.text('Importing');
|
||||||
|
this.fileUploadData.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent double post by disabling the button.
|
||||||
|
this.uploadButton.prop('disabled', 'disabled');
|
||||||
|
},
|
||||||
|
|
||||||
handleDeleteClick: function (ev) {
|
handleDeleteClick: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.addSubview(new Ghost.Views.Modal({
|
this.addSubview(new Ghost.Views.Modal({
|
||||||
|
@ -141,4 +154,4 @@
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
Ghost.Views.Signup = Ghost.View.extend({
|
Ghost.Views.Signup = Ghost.View.extend({
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
|
this.submitted = "no";
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -95,10 +96,12 @@
|
||||||
Ghost.Validate.check(name, "Please enter a name").len(1);
|
Ghost.Validate.check(name, "Please enter a name").len(1);
|
||||||
Ghost.Validate.check(email, "Please enter a correct email address").isEmail();
|
Ghost.Validate.check(email, "Please enter a correct email address").isEmail();
|
||||||
Ghost.Validate.check(password, "Your password is not long enough. It must be at least 8 characters long.").len(8);
|
Ghost.Validate.check(password, "Your password is not long enough. It must be at least 8 characters long.").len(8);
|
||||||
|
Ghost.Validate.check(this.submitted, "Ghost is signing you up. Please wait...").equals("no");
|
||||||
|
|
||||||
if (Ghost.Validate._errors.length > 0) {
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
Ghost.Validate.handleErrors();
|
Ghost.Validate.handleErrors();
|
||||||
} else {
|
} else {
|
||||||
|
this.submitted = "yes";
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: Ghost.paths.subdir + '/ghost/signup/',
|
url: Ghost.paths.subdir + '/ghost/signup/',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
|
@ -114,6 +117,7 @@
|
||||||
window.location.href = msg.redirect;
|
window.location.href = msg.redirect;
|
||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
|
this.submitted = "no";
|
||||||
Ghost.notifications.clearEverything();
|
Ghost.notifications.clearEverything();
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var parseDateFormats = ['DD MMM YY HH:mm', 'DD MMM YYYY HH:mm', 'DD/MM/YY HH:mm', 'DD/MM/YYYY HH:mm',
|
||||||
|
'DD-MM-YY HH:mm', 'DD-MM-YYYY HH:mm'],
|
||||||
|
displayDateFormat = 'DD MMM YY @ HH:mm';
|
||||||
|
|
||||||
Ghost.View.PostSettings = Ghost.View.extend({
|
Ghost.View.PostSettings = Ghost.View.extend({
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
@ -17,11 +21,10 @@
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
if (this.model) {
|
if (this.model) {
|
||||||
|
// These three items can be updated outside of the post settings menu, so have to be listened to.
|
||||||
this.listenTo(this.model, 'change:id', this.render);
|
this.listenTo(this.model, 'change:id', this.render);
|
||||||
this.listenTo(this.model, 'change:status', this.render);
|
|
||||||
this.listenTo(this.model, 'change:published_at', this.render);
|
|
||||||
this.listenTo(this.model, 'change:page', this.render);
|
|
||||||
this.listenTo(this.model, 'change:title', this.updateSlugPlaceholder);
|
this.listenTo(this.model, 'change:title', this.updateSlugPlaceholder);
|
||||||
|
this.listenTo(this.model, 'change:published_at', this.updatePublishedDate);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -29,8 +32,7 @@
|
||||||
var slug = this.model ? this.model.get('slug') : '',
|
var slug = this.model ? this.model.get('slug') : '',
|
||||||
pubDate = this.model ? this.model.get('published_at') : 'Not Published',
|
pubDate = this.model ? this.model.get('published_at') : 'Not Published',
|
||||||
$pubDateEl = this.$('.post-setting-date'),
|
$pubDateEl = this.$('.post-setting-date'),
|
||||||
$postSettingSlugEl = this.$('.post-setting-slug'),
|
$postSettingSlugEl = this.$('.post-setting-slug');
|
||||||
publishedDateFormat = 'DD MMM YY @ HH:mm';
|
|
||||||
|
|
||||||
$postSettingSlugEl.val(slug);
|
$postSettingSlugEl.val(slug);
|
||||||
|
|
||||||
|
@ -41,10 +43,10 @@
|
||||||
|
|
||||||
// Insert the published date, and make it editable if it exists.
|
// Insert the published date, and make it editable if it exists.
|
||||||
if (this.model && this.model.get('published_at')) {
|
if (this.model && this.model.get('published_at')) {
|
||||||
pubDate = moment(pubDate).format(publishedDateFormat);
|
pubDate = moment(pubDate).format(displayDateFormat);
|
||||||
$pubDateEl.attr('placeholder', '');
|
$pubDateEl.attr('placeholder', '');
|
||||||
} else {
|
} else {
|
||||||
$pubDateEl.attr('placeholder', moment().format(publishedDateFormat));
|
$pubDateEl.attr('placeholder', moment().format(displayDateFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.model && this.model.get('id')) {
|
if (this.model && this.model.get('id')) {
|
||||||
|
@ -130,6 +132,7 @@
|
||||||
},
|
},
|
||||||
error : function (model, xhr) {
|
error : function (model, xhr) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
|
slugEl.value = model.previous('slug');
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
|
@ -139,13 +142,23 @@
|
||||||
});
|
});
|
||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
|
|
||||||
|
updatePublishedDate: function () {
|
||||||
|
var pubDate = this.model.get('published_at') ? moment(this.model.get('published_at'))
|
||||||
|
.format(displayDateFormat) : '',
|
||||||
|
$pubDateEl = this.$('.post-setting-date');
|
||||||
|
|
||||||
|
// Only change the date if it's different
|
||||||
|
if (pubDate && $pubDateEl.val() !== pubDate) {
|
||||||
|
$pubDateEl.val(pubDate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
editDate: _.debounce(function (e) {
|
editDate: _.debounce(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var self = this,
|
var self = this,
|
||||||
parseDateFormats = ['DD MMM YY HH:mm', 'DD MMM YYYY HH:mm', 'DD/MM/YY HH:mm', 'DD/MM/YYYY HH:mm', 'DD-MM-YY HH:mm', 'DD-MM-YYYY HH:mm'],
|
|
||||||
displayDateFormat = 'DD MMM YY @ HH:mm',
|
|
||||||
errMessage = '',
|
errMessage = '',
|
||||||
pubDate = self.model.get('published_at'),
|
pubDate = moment(self.model.get('published_at')).format(displayDateFormat),
|
||||||
pubDateEl = e.currentTarget,
|
pubDateEl = e.currentTarget,
|
||||||
newPubDate = pubDateEl.value,
|
newPubDate = pubDateEl.value,
|
||||||
pubDateMoment,
|
pubDateMoment,
|
||||||
|
@ -228,6 +241,8 @@
|
||||||
},
|
},
|
||||||
error : function (model, xhr) {
|
error : function (model, xhr) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
|
// Reset back to original value
|
||||||
|
pubDateEl.value = pubDateMoment ? pubDateMoment.format(displayDateFormat) : '';
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
|
@ -266,6 +281,7 @@
|
||||||
},
|
},
|
||||||
error : function (model, xhr) {
|
error : function (model, xhr) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
|
pageEl.prop('checked', model.previous('page'));
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
|
|
|
@ -562,7 +562,7 @@ coreHelpers.adminUrl = function (options) {
|
||||||
return config.paths.urlFor(context, absolute);
|
return config.paths.urlFor(context, absolute);
|
||||||
};
|
};
|
||||||
|
|
||||||
coreHelpers.updateNotification = function () {
|
coreHelpers.updateNotification = function (options) {
|
||||||
var output = '';
|
var output = '';
|
||||||
|
|
||||||
if (config().updateCheck === false || !this.currentUser) {
|
if (config().updateCheck === false || !this.currentUser) {
|
||||||
|
@ -571,9 +571,13 @@ coreHelpers.updateNotification = function () {
|
||||||
|
|
||||||
return updateCheck.showUpdateNotification().then(function (result) {
|
return updateCheck.showUpdateNotification().then(function (result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
output = '<div class="notification-success">' +
|
if (options && options.hash && options.hash.classOnly) {
|
||||||
'A new version of Ghost is available! Hot damn. ' +
|
output = ' update-available';
|
||||||
'<a href="http://ghost.org/download">Upgrade now</a></div>';
|
} else {
|
||||||
|
output = '<div class="notification-success">' +
|
||||||
|
'A new version of Ghost is available! Hot damn. ' +
|
||||||
|
'<a href="http://ghost.org/download">Upgrade now</a></div>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
|
|
@ -183,11 +183,7 @@ function isSSLrequired(isAdmin) {
|
||||||
// and redirect if needed
|
// and redirect if needed
|
||||||
function checkSSL(req, res, next) {
|
function checkSSL(req, res, next) {
|
||||||
if (isSSLrequired(res.isAdmin)) {
|
if (isSSLrequired(res.isAdmin)) {
|
||||||
// Check if X-Forarded-Proto headers are sent, if they are check for https.
|
if (!req.secure) {
|
||||||
// If they are not assume true to avoid infinite redirect loop.
|
|
||||||
// If the X-Forwarded-Proto header is missing and Express cannot automatically sense HTTPS the redirect will not be made.
|
|
||||||
var httpsHeader = req.header('X-Forwarded-Proto') !== undefined ? req.header('X-Forwarded-Proto').toLowerCase() === 'https' ? true : false : true;
|
|
||||||
if (!req.secure && !httpsHeader) {
|
|
||||||
return res.redirect(301, url.format({
|
return res.redirect(301, url.format({
|
||||||
protocol: 'https:',
|
protocol: 'https:',
|
||||||
hostname: url.parse(config().url).hostname,
|
hostname: url.parse(config().url).hostname,
|
||||||
|
@ -208,6 +204,10 @@ module.exports = function (server, dbHash) {
|
||||||
expressServer = server;
|
expressServer = server;
|
||||||
middleware.cacheServer(expressServer);
|
middleware.cacheServer(expressServer);
|
||||||
|
|
||||||
|
// Make sure 'req.secure' is valid for proxied requests
|
||||||
|
// (X-Forwarded-Proto header will be checked, if present)
|
||||||
|
expressServer.enable('trust proxy');
|
||||||
|
|
||||||
// Logging configuration
|
// Logging configuration
|
||||||
if (expressServer.get('env') !== 'development') {
|
if (expressServer.get('env') !== 'development') {
|
||||||
expressServer.use(express.logger());
|
expressServer.use(express.logger());
|
||||||
|
@ -226,13 +226,16 @@ module.exports = function (server, dbHash) {
|
||||||
// First determine whether we're serving admin or theme content
|
// First determine whether we're serving admin or theme content
|
||||||
expressServer.use(manageAdminAndTheme);
|
expressServer.use(manageAdminAndTheme);
|
||||||
|
|
||||||
// Force SSL
|
|
||||||
expressServer.use(checkSSL);
|
|
||||||
|
|
||||||
|
|
||||||
// Admin only config
|
// Admin only config
|
||||||
expressServer.use(subdir + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets'), {maxAge: ONE_YEAR_MS})));
|
expressServer.use(subdir + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets'), {maxAge: ONE_YEAR_MS})));
|
||||||
|
|
||||||
|
// Force SSL
|
||||||
|
// NOTE: Importantly this is _after_ the check above for admin-theme static resources,
|
||||||
|
// which do not need HTTPS. In fact, if HTTPS is forced on them, then 404 page might
|
||||||
|
// not display properly when HTTPS is not available!
|
||||||
|
expressServer.use(checkSSL);
|
||||||
|
|
||||||
// Theme only config
|
// Theme only config
|
||||||
expressServer.use(subdir, middleware.whenEnabled(expressServer.get('activeTheme'), middleware.staticTheme()));
|
expressServer.use(subdir, middleware.whenEnabled(expressServer.get('activeTheme'), middleware.staticTheme()));
|
||||||
|
|
||||||
|
|
|
@ -213,8 +213,13 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
slug = base.trim();
|
||||||
|
|
||||||
|
// Remove non ascii characters
|
||||||
|
slug = unidecode(slug);
|
||||||
|
|
||||||
// Remove URL reserved chars: `:/?#[]@!$&'()*+,;=` as well as `\%<>|^~£"`
|
// Remove URL reserved chars: `:/?#[]@!$&'()*+,;=` as well as `\%<>|^~£"`
|
||||||
slug = base.trim().replace(/[:\/\?#\[\]@!$&'()*+,;=\\%<>\|\^~£"]/g, '')
|
slug = slug.replace(/[:\/\?#\[\]@!$&'()*+,;=\\%<>\|\^~£"]/g, '')
|
||||||
// Replace dots and spaces with a dash
|
// Replace dots and spaces with a dash
|
||||||
.replace(/(\s|\.)/g, '-')
|
.replace(/(\s|\.)/g, '-')
|
||||||
// Convert 2 or more dashes into a single dash
|
// Convert 2 or more dashes into a single dash
|
||||||
|
@ -224,8 +229,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||||
|
|
||||||
// Remove trailing hyphen
|
// Remove trailing hyphen
|
||||||
slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug;
|
slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug;
|
||||||
// Remove non ascii characters
|
|
||||||
slug = unidecode(slug);
|
|
||||||
// Check the filtered slug doesn't match any of the reserved keywords
|
// Check the filtered slug doesn't match any of the reserved keywords
|
||||||
slug = /^(ghost|ghost\-admin|admin|wp\-admin|wp\-login|dashboard|logout|login|signin|signup|signout|register|archive|archives|category|categories|tag|tags|page|pages|post|posts|user|users|rss)$/g
|
slug = /^(ghost|ghost\-admin|admin|wp\-admin|wp\-login|dashboard|logout|login|signin|signup|signout|register|archive|archives|category|categories|tag|tags|page|pages|post|posts|user|users|rss)$/g
|
||||||
.test(slug) ? slug + '-post' : slug;
|
.test(slug) ? slug + '-post' : slug;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<link rel="stylesheet" href="{{asset "css/screen.css" ghost="true"}}">
|
<link rel="stylesheet" href="{{asset "css/screen.css" ghost="true"}}">
|
||||||
{{{block "pageStyles"}}}
|
{{{block "pageStyles"}}}
|
||||||
</head>
|
</head>
|
||||||
<body class="{{bodyClass}}">
|
<body class="{{bodyClass}}{{updateNotification classOnly="true"}}">
|
||||||
{{#unless hideNavbar}}
|
{{#unless hideNavbar}}
|
||||||
{{> navbar}}
|
{{> navbar}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<main role="main" id="main">
|
<main role="main" id="main">
|
||||||
{{updateNotification}}
|
{{updateNotification}}
|
||||||
|
|
||||||
<aside id="notifications">
|
<aside id="notifications" class="notifications">
|
||||||
{{> notifications}}
|
{{> notifications}}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<li class="{{navClass}}{{#if selected}} active{{/if}}"><a href="{{adminUrl}}{{path}}">{{name}}</a></li>
|
<li class="{{navClass}}{{#if selected}} active{{/if}}"><a href="{{adminUrl}}{{path}}">{{name}}</a></li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
<li id="usermenu" class="subnav">
|
<li id="usermenu" class="usermenu subnav">
|
||||||
<a href="#" data-toggle="ul" class="dropdown">
|
<a href="#" data-toggle="ul" class="dropdown">
|
||||||
<img class="avatar" src="{{#if currentUser.image}}{{currentUser.image}}{{else}}{{asset "shared/img/user-image.png"}}{{/if}}" alt="Avatar" />
|
<img class="avatar" src="{{#if currentUser.image}}{{currentUser.image}}{{else}}{{asset "shared/img/user-image.png"}}{{/if}}" alt="Avatar" />
|
||||||
<span class="name">{{#if currentUser.name}}{{currentUser.name}}{{else}}{{currentUser.email}}{{/if}}</span>
|
<span class="name">{{#if currentUser.name}}{{currentUser.name}}{{else}}{{currentUser.email}}{{/if}}</span>
|
||||||
|
|
|
@ -940,21 +940,30 @@ describe('Core Helpers', function () {
|
||||||
});
|
});
|
||||||
describe('updateNotification', function () {
|
describe('updateNotification', function () {
|
||||||
it('outputs a correctly formatted notification when db version is higher than package version', function (done) {
|
it('outputs a correctly formatted notification when db version is higher than package version', function (done) {
|
||||||
var output = '<div class="notification-success">' +
|
var defaultOutput = '<div class="notification-success">' +
|
||||||
'A new version of Ghost is available! Hot damn. ' +
|
'A new version of Ghost is available! Hot damn. ' +
|
||||||
'<a href="http://ghost.org/download">Upgrade now</a></div>';
|
'<a href="http://ghost.org/download">Upgrade now</a></div>',
|
||||||
|
classOutput = ' update-available';
|
||||||
|
|
||||||
apiStub.restore();
|
apiStub.restore();
|
||||||
apiStub = sandbox.stub(api.settings, 'read', function () {
|
apiStub = sandbox.stub(api.settings, 'read', function () {
|
||||||
var futureversion = packageInfo.version.split('.');
|
var futureversion = packageInfo.version.split('.');
|
||||||
futureversion[futureversion.length-1] = parseInt(futureversion[futureversion.length-1], 10) + 1;
|
futureversion[futureversion.length - 1] = parseInt(futureversion[futureversion.length - 1], 10) + 1;
|
||||||
return when({value: futureversion.join('.')});
|
return when({value: futureversion.join('.')});
|
||||||
});
|
});
|
||||||
|
|
||||||
helpers.updateNotification.call({currentUser: {name: 'bob'}}).then(function (rendered) {
|
helpers.updateNotification.call({currentUser: {name: 'bob'}}).then(function (rendered) {
|
||||||
should.exist(rendered);
|
should.exist(rendered);
|
||||||
|
|
||||||
rendered.should.equal(output);
|
rendered.should.equal(defaultOutput);
|
||||||
|
|
||||||
|
// Test classOnly option
|
||||||
|
return helpers.updateNotification.call({currentUser: {name: 'bob'}}, {'hash': {'classOnly': 'true'}});
|
||||||
|
}).then(function (rendered) {
|
||||||
|
should.exist(rendered);
|
||||||
|
|
||||||
|
rendered.should.equal(classOutput);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
}).then(null, done);
|
}).then(null, done);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name" : "ghost",
|
"name" : "ghost",
|
||||||
"version" : "0.4.0",
|
"version" : "0.4.1-rc1",
|
||||||
"description" : "Just a blogging platform.",
|
"description" : "Just a blogging platform.",
|
||||||
"author" : "Ghost Foundation",
|
"author" : "Ghost Foundation",
|
||||||
"homepage" : "http://ghost.org",
|
"homepage" : "http://ghost.org",
|
||||||
|
|
Loading…
Add table
Reference in a new issue