Skip to content

Commit

Permalink
Editable user profiles in settings screen
Browse files Browse the repository at this point in the history
closes TryGhost#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
  • Loading branch information
ErisDS committed Aug 5, 2013
1 parent 8d7336a commit 52dc22c
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 48 deletions.
13 changes: 13 additions & 0 deletions core/client/models/user.js
Original file line number Diff line number Diff line change
@@ -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'
// });

}());
5 changes: 1 addition & 4 deletions core/client/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@
},

settings: function (pane) {
var settings = new Ghost.Models.Settings();
settings.fetch().then(function () {
Ghost.currentView = new Ghost.Views.Settings({ el: '#main', model: settings, pane: pane });
});
Ghost.currentView = new Ghost.Views.Settings({ el: '#main', pane: pane });
},

editor: function (id) {
Expand Down
1 change: 1 addition & 0 deletions core/client/tpl/settings/sidebar.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
<ul>
<li class="general"><a href="#general">General</a></li>
<li class="publishing"><a href="#content">Content</a></li>
<li class="users"><a href="#user">User</a></li>
</ul>
</nav>
76 changes: 76 additions & 0 deletions core/client/tpl/settings/user-profile.hbs
Original file line number Diff line number Diff line change
@@ -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>
135 changes: 91 additions & 44 deletions core/client/views/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
this.showContent(options.pane || 'general');
},

models: {},

events: {
'click .settings-menu li' : 'switchPane'
},
Expand All @@ -42,13 +44,30 @@
},

showContent: function (id) {
var self = this,
model;

Backbone.history.navigate('/settings/' + id);
if (this.pane && id === this.pane.el.id) {
return;
}
_.result(this.pane, 'destroy');
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();
},

Expand All @@ -63,6 +82,9 @@
// Content panes
// --------------
Settings.Pane = Ghost.View.extend({
options: {
modelType: 'Settings'
},
destroy: function () {
this.$el.removeClass('active');
this.undelegateEvents();
Expand All @@ -71,6 +93,25 @@
afterRender: function () {
this.$el.attr('id', this.id);
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'
}]
}));
}
});

Expand All @@ -85,29 +126,12 @@
},

saveSettings: function () {
var self = this;
this.model.save({
title: this.$('#blog-title').val(),
email: this.$('#email-address').val()
}, {
success: function () {
self.addSubview(new Ghost.Views.NotificationCollection({
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'
}]
}));
}
success: this.saveSuccess,
error: this.saveError
});
},

Expand All @@ -127,29 +151,11 @@
'click .button-save': 'saveSettings'
},
saveSettings: function () {
var self = this;
this.model.save({
description: this.$('#blog-description').val()
}, {
success: function () {
self.addSubview(new Ghost.Views.NotificationCollection({
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'
}]
}));
}
success: this.saveSuccess,
error: this.saveError
});
},

Expand All @@ -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
Settings.users = Settings.Pane.extend({
el: '#users',
id: 'users',
events: {
}
});

// ### Appearance settings
Settings.appearance = Settings.Pane.extend({
el: '#appearance',
id: 'appearance',
events: {
}
});

// ### Services settings
Settings.services = Settings.Pane.extend({
el: '#services',
id: 'services',
events: {
}
});

// ### Plugins settings
Settings.plugins = Settings.Pane.extend({
el: '#plugins',
id: 'plugins',
events: {
}
});
Expand Down
9 changes: 9 additions & 0 deletions core/server/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ posts = {

// # 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) {
return dataProvider.User.add(postData);
},
Expand Down
1 change: 1 addition & 0 deletions core/server/views/default.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@

<!-- // require '/public/models/*' -->
<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/settings.js"></script>
<!-- // require '/public/views/*' -->
Expand Down
Binary file added core/shared/img/default-user-cover-picture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added core/shared/img/default-user-profile-picture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/:key', authAPI, disableCachedResult, api.cachedSettingsRequestHandler(api.settings.read));
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..
Expand Down

0 comments on commit 52dc22c

Please sign in to comment.