mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
Editable user profiles in settings screen
closes #276 - settings screen now loads a model when a pane is requested, rather than when the whole screen is requested - added browse, read and edit methods and routes for users to the API - added user model & template to client and wired everything up. - provided default images for cover and profile picture
This commit is contained in:
parent
8d7336a1fb
commit
52dc22c952
13
core/client/models/user.js
Normal file
13
core/client/models/user.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*global window, document, Ghost, $, _, Backbone */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Ghost.Models.User = Backbone.Model.extend({
|
||||||
|
url: Ghost.settings.apiRoot + '/users/1'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ghost.Collections.Users = Backbone.Collection.extend({
|
||||||
|
// url: Ghost.settings.apiRoot + '/users'
|
||||||
|
// });
|
||||||
|
|
||||||
|
}());
|
@ -33,10 +33,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
settings: function (pane) {
|
settings: function (pane) {
|
||||||
var settings = new Ghost.Models.Settings();
|
Ghost.currentView = new Ghost.Views.Settings({ el: '#main', pane: pane });
|
||||||
settings.fetch().then(function () {
|
|
||||||
Ghost.currentView = new Ghost.Views.Settings({ el: '#main', model: settings, pane: pane });
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
editor: function (id) {
|
editor: function (id) {
|
||||||
|
@ -5,5 +5,6 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li class="general"><a href="#general">General</a></li>
|
<li class="general"><a href="#general">General</a></li>
|
||||||
<li class="publishing"><a href="#content">Content</a></li>
|
<li class="publishing"><a href="#content">Content</a></li>
|
||||||
|
<li class="users"><a href="#user">User</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
76
core/client/tpl/settings/user-profile.hbs
Normal file
76
core/client/tpl/settings/user-profile.hbs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<header>
|
||||||
|
<h2 class="title">Your Profile</h2>
|
||||||
|
<section class="page-actions">
|
||||||
|
<button class="button-save">Save</button>
|
||||||
|
</section>
|
||||||
|
</header>
|
||||||
|
<section class="content no-padding">
|
||||||
|
<header class="user-profile-header">
|
||||||
|
<figure class="cover-image">
|
||||||
|
<img id="user-cover-picture" src="{{#if cover_picture}}{{cover_picture}}{{else}}/shared/img/default-user-cover-picture.jpg{{/if}}" title="{{full_name}} Cover Image"/>
|
||||||
|
<button class="button-change-cover">Change Cover</button>
|
||||||
|
</figure>
|
||||||
|
</header>
|
||||||
|
<form class="user-details-container">
|
||||||
|
<fieldset class="user-details-top">
|
||||||
|
<figure class="user-avatar-image">
|
||||||
|
<img id="user-profile-picture" src="{{#if profile_picture}}{{profile_picture}}{{else}}/shared/img/default-user-profile-picture.jpg{{/if}}" title="{{full_name}}"/>
|
||||||
|
<button class="button-change-avatar">Update Avatar</button>
|
||||||
|
</figure>
|
||||||
|
<label>
|
||||||
|
<input type="text" value="{{full_name}}" id="user-name" placeholder="Joe Bloggs">
|
||||||
|
<p>Use your real name so people can recognise you.</p>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="user-details-bottom">
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>Email</strong></label>
|
||||||
|
<input type="text" value="{{email_address}}" id="user-email">
|
||||||
|
<p>Email will not be publicly displayed. <a class="highlight" href="#" >Learn more</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>Location</strong></label>
|
||||||
|
<input type="text" value="{{location}}" id="user-location">
|
||||||
|
<p>Where in the world do you live?</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>Website</strong></label>
|
||||||
|
<input type="text" value="{{url}}" id="user-website">
|
||||||
|
<p>Have a website or blog other than this one? Link it.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group bio-container">
|
||||||
|
<label><strong>Bio</strong></label>
|
||||||
|
<textarea id="user-bio">{{bio}}</textarea>
|
||||||
|
<p class="bio-desc">Write about you, in <strong>200</strong> characters or less.</p>
|
||||||
|
<span class="word-count">97</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="user-details-bottom">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>Old Password</strong></label>
|
||||||
|
<input type="password" id="user-password-old">
|
||||||
|
<p><a href="#" >Forgot your password?</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>New Password</strong></label>
|
||||||
|
<input type="password" id="user-password-new">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label><strong>Verify Password</strong></label>
|
||||||
|
<input type="password" id="user-new-password-verification">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</section>
|
@ -30,6 +30,8 @@
|
|||||||
this.showContent(options.pane || 'general');
|
this.showContent(options.pane || 'general');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
models: {},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click .settings-menu li' : 'switchPane'
|
'click .settings-menu li' : 'switchPane'
|
||||||
},
|
},
|
||||||
@ -42,13 +44,30 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
showContent: function (id) {
|
showContent: function (id) {
|
||||||
|
var self = this,
|
||||||
|
model;
|
||||||
|
|
||||||
Backbone.history.navigate('/settings/' + id);
|
Backbone.history.navigate('/settings/' + id);
|
||||||
if (this.pane && id === this.pane.el.id) {
|
if (this.pane && id === this.pane.el.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_.result(this.pane, 'destroy');
|
_.result(this.pane, 'destroy');
|
||||||
this.setActive(id);
|
this.setActive(id);
|
||||||
this.pane = new Settings[id]({ el: '.settings-content', model: this.model });
|
this.pane = new Settings[id]({ el: '.settings-content'});
|
||||||
|
|
||||||
|
if (!this.models.hasOwnProperty(this.pane.options.modelType)) {
|
||||||
|
model = this.models[this.pane.options.modelType] = new Ghost.Models[this.pane.options.modelType]();
|
||||||
|
model.fetch().then(function () {
|
||||||
|
self.renderPane(model);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
model = this.models[this.pane.options.modelType];
|
||||||
|
self.renderPane(model);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
renderPane: function (model) {
|
||||||
|
this.pane.model = model;
|
||||||
this.pane.render();
|
this.pane.render();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -63,6 +82,9 @@
|
|||||||
// Content panes
|
// Content panes
|
||||||
// --------------
|
// --------------
|
||||||
Settings.Pane = Ghost.View.extend({
|
Settings.Pane = Ghost.View.extend({
|
||||||
|
options: {
|
||||||
|
modelType: 'Settings'
|
||||||
|
},
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
this.$el.removeClass('active');
|
this.$el.removeClass('active');
|
||||||
this.undelegateEvents();
|
this.undelegateEvents();
|
||||||
@ -71,6 +93,25 @@
|
|||||||
afterRender: function () {
|
afterRender: function () {
|
||||||
this.$el.attr('id', this.id);
|
this.$el.attr('id', this.id);
|
||||||
this.$el.addClass('active');
|
this.$el.addClass('active');
|
||||||
|
},
|
||||||
|
saveSuccess: function () {
|
||||||
|
this.addSubview(new Ghost.Views.NotificationCollection({
|
||||||
|
model: [{
|
||||||
|
type: 'success',
|
||||||
|
message: 'Saved',
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
|
||||||
|
},
|
||||||
|
saveError: function () {
|
||||||
|
this.addSubview(new Ghost.Views.NotificationCollection({
|
||||||
|
model: [{
|
||||||
|
type: 'error',
|
||||||
|
message: 'Something went wrong, not saved :(',
|
||||||
|
status: 'passive'
|
||||||
|
}]
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,29 +126,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
saveSettings: function () {
|
saveSettings: function () {
|
||||||
var self = this;
|
|
||||||
this.model.save({
|
this.model.save({
|
||||||
title: this.$('#blog-title').val(),
|
title: this.$('#blog-title').val(),
|
||||||
email: this.$('#email-address').val()
|
email: this.$('#email-address').val()
|
||||||
}, {
|
}, {
|
||||||
success: function () {
|
success: this.saveSuccess,
|
||||||
self.addSubview(new Ghost.Views.NotificationCollection({
|
error: this.saveError
|
||||||
model: [{
|
|
||||||
type: 'success',
|
|
||||||
message: 'Saved',
|
|
||||||
status: 'passive'
|
|
||||||
}]
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
self.addSubview(new Ghost.Views.NotificationCollection({
|
|
||||||
model: [{
|
|
||||||
type: 'error',
|
|
||||||
message: 'Something went wrong, not saved :(',
|
|
||||||
status: 'passive'
|
|
||||||
}]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -127,29 +151,11 @@
|
|||||||
'click .button-save': 'saveSettings'
|
'click .button-save': 'saveSettings'
|
||||||
},
|
},
|
||||||
saveSettings: function () {
|
saveSettings: function () {
|
||||||
var self = this;
|
|
||||||
this.model.save({
|
this.model.save({
|
||||||
description: this.$('#blog-description').val()
|
description: this.$('#blog-description').val()
|
||||||
}, {
|
}, {
|
||||||
success: function () {
|
success: this.saveSuccess,
|
||||||
self.addSubview(new Ghost.Views.NotificationCollection({
|
error: this.saveError
|
||||||
model: [{
|
|
||||||
type: 'success',
|
|
||||||
message: 'Saved',
|
|
||||||
status: 'passive'
|
|
||||||
}]
|
|
||||||
}));
|
|
||||||
|
|
||||||
},
|
|
||||||
error: function () {
|
|
||||||
self.addSubview(new Ghost.Views.NotificationCollection({
|
|
||||||
model: [{
|
|
||||||
type: 'error',
|
|
||||||
message: 'Something went wrong, not saved :(',
|
|
||||||
status: 'passive'
|
|
||||||
}]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -161,30 +167,71 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ### User profile
|
||||||
|
Settings.user = Settings.Pane.extend({
|
||||||
|
id: 'user',
|
||||||
|
|
||||||
|
options: {
|
||||||
|
modelType: 'User'
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'click .button-save': 'saveUser'
|
||||||
|
},
|
||||||
|
|
||||||
|
saveUser: function () {
|
||||||
|
this.model.save({
|
||||||
|
'full_name': this.$('#user-name').val(),
|
||||||
|
'email_address': this.$('#user-email').val(),
|
||||||
|
'location': this.$('#user-location').val(),
|
||||||
|
'url': this.$('#user-website').val(),
|
||||||
|
'bio': this.$('#user-bio').val(),
|
||||||
|
'profile_picture': this.$('#user-profile-picture').attr('src'),
|
||||||
|
'cover_picture': this.$('#user-cover-picture').attr('src')
|
||||||
|
}, {
|
||||||
|
success: this.saveSuccess,
|
||||||
|
error: this.saveError
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
templateName: 'settings/user-profile',
|
||||||
|
|
||||||
|
beforeRender: function () {
|
||||||
|
var user = this.model.toJSON();
|
||||||
|
this.$('#user-name').val(user.full_name);
|
||||||
|
this.$('#user-email').val(user.email_address);
|
||||||
|
this.$('#user-location').val(user.location);
|
||||||
|
this.$('#user-website').val(user.url);
|
||||||
|
this.$('#user-bio').val(user.bio);
|
||||||
|
this.$('#user-profile-picture').attr('src', user.profile_picture);
|
||||||
|
this.$('#user-cover-picture').attr('src', user.cover_picture);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ### User settings
|
// ### User settings
|
||||||
Settings.users = Settings.Pane.extend({
|
Settings.users = Settings.Pane.extend({
|
||||||
el: '#users',
|
id: 'users',
|
||||||
events: {
|
events: {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ### Appearance settings
|
// ### Appearance settings
|
||||||
Settings.appearance = Settings.Pane.extend({
|
Settings.appearance = Settings.Pane.extend({
|
||||||
el: '#appearance',
|
id: 'appearance',
|
||||||
events: {
|
events: {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ### Services settings
|
// ### Services settings
|
||||||
Settings.services = Settings.Pane.extend({
|
Settings.services = Settings.Pane.extend({
|
||||||
el: '#services',
|
id: 'services',
|
||||||
events: {
|
events: {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ### Plugins settings
|
// ### Plugins settings
|
||||||
Settings.plugins = Settings.Pane.extend({
|
Settings.plugins = Settings.Pane.extend({
|
||||||
el: '#plugins',
|
id: 'plugins',
|
||||||
events: {
|
events: {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -51,6 +51,15 @@ posts = {
|
|||||||
|
|
||||||
// # Users
|
// # Users
|
||||||
users = {
|
users = {
|
||||||
|
browse: function browse(options) {
|
||||||
|
return dataProvider.User.browse(options);
|
||||||
|
},
|
||||||
|
read: function read(args) {
|
||||||
|
return dataProvider.User.read(args);
|
||||||
|
},
|
||||||
|
edit: function edit(postData) {
|
||||||
|
return dataProvider.User.edit(postData);
|
||||||
|
},
|
||||||
add: function add(postData) {
|
add: function add(postData) {
|
||||||
return dataProvider.User.add(postData);
|
return dataProvider.User.add(postData);
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
<!-- // require '/public/models/*' -->
|
<!-- // require '/public/models/*' -->
|
||||||
<script src="/public/models/post.js"></script>
|
<script src="/public/models/post.js"></script>
|
||||||
|
<script src="/public/models/user.js"></script>
|
||||||
<script src="/public/models/widget.js"></script>
|
<script src="/public/models/widget.js"></script>
|
||||||
<script src="/public/models/settings.js"></script>
|
<script src="/public/models/settings.js"></script>
|
||||||
<!-- // require '/public/views/*' -->
|
<!-- // require '/public/views/*' -->
|
||||||
|
BIN
core/shared/img/default-user-cover-picture.jpg
Normal file
BIN
core/shared/img/default-user-cover-picture.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
BIN
core/shared/img/default-user-profile-picture.jpg
Normal file
BIN
core/shared/img/default-user-profile-picture.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
4
index.js
4
index.js
@ -163,6 +163,10 @@ when.all([ghost.init(), filters.loadCoreFilters(ghost), helpers.loadCoreHelpers(
|
|||||||
ghost.app().get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse));
|
ghost.app().get('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.browse));
|
||||||
ghost.app().get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
|
ghost.app().get('/api/v0.1/settings/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
|
||||||
ghost.app().put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit));
|
ghost.app().put('/api/v0.1/settings', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.edit));
|
||||||
|
ghost.app().get('/api/v0.1/users', authAPI, disableCachedResult, api.requestHandler(api.users.browse));
|
||||||
|
ghost.app().get('/api/v0.1/users/:id', authAPI, disableCachedResult, api.requestHandler(api.users.read));
|
||||||
|
ghost.app().put('/api/v0.1/users/:id', authAPI, disableCachedResult, api.requestHandler(api.users.edit));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Admin routes..
|
* Admin routes..
|
||||||
|
Loading…
Reference in New Issue
Block a user