Skip to content

Commit 489cd08

Browse files
committed
custom slugging capabilities for individual user pages
closes #3401 - modifying slug-generator to be more generic - adding slugging capabilities for /settings/users/:slug - modified posts to use the updated slug-generator
1 parent a6c205a commit 489cd08

File tree

5 files changed

+70
-7
lines changed

5 files changed

+70
-7
lines changed

core/client/controllers/post-settings-menu.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ var PostSettingsMenuController = Ember.ObjectController.extend({
5959
//Lazy load the slug generator for slugPlaceholder
6060
slugGenerator: Ember.computed(function () {
6161
return SlugGenerator.create({
62-
ghostPaths: this.get('ghostPaths')
62+
ghostPaths: this.get('ghostPaths'),
63+
slugType: 'post'
6364
});
6465
}),
6566
//Requests slug from title

core/client/controllers/settings/users/user.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import SlugGenerator from 'ghost/models/slug-generator';
2+
import boundOneWay from 'ghost/utils/bound-one-way';
3+
14
var SettingsUserController = Ember.ObjectController.extend({
25

36
user: Ember.computed.alias('model'),
@@ -43,7 +46,17 @@ var SettingsUserController = Ember.ObjectController.extend({
4346

4447
return createdAt ? createdAt.fromNow() : '';
4548
}.property('user.created_at'),
46-
49+
50+
//Lazy load the slug generator for slugPlaceholder
51+
slugGenerator: Ember.computed(function () {
52+
return SlugGenerator.create({
53+
ghostPaths: this.get('ghostPaths'),
54+
slugType: 'user'
55+
});
56+
}),
57+
58+
slugValue: boundOneWay('user.slug'),
59+
4760
actions: {
4861
changeRole: function (newRole) {
4962
this.set('model.role', newRole);
@@ -120,6 +133,49 @@ var SettingsUserController = Ember.ObjectController.extend({
120133
} else {
121134
self.notifications.showErrors(user.get('passwordValidationErrors'));
122135
}
136+
},
137+
138+
updateSlug: function (newSlug) {
139+
var slug = this.get('user.slug'),
140+
self = this;
141+
142+
newSlug = newSlug || slug;
143+
144+
newSlug = newSlug.trim();
145+
146+
// Ignore unchanged slugs or candidate slugs that are empty
147+
if (!newSlug || slug === newSlug) {
148+
return;
149+
}
150+
151+
this.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) {
152+
153+
// If after getting the sanitized and unique slug back from the API
154+
// we end up with a slug that matches the existing slug, abort the change
155+
if (serverSlug === slug) {
156+
return;
157+
}
158+
159+
// Because the server transforms the candidate slug by stripping
160+
// certain characters and appending a number onto the end of slugs
161+
// to enforce uniqueness, there are cases where we can get back a
162+
// candidate slug that is a duplicate of the original except for
163+
// the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2)
164+
165+
// get the last token out of the slug candidate and see if it's a number
166+
var slugTokens = serverSlug.split('-'),
167+
check = Number(slugTokens.pop());
168+
169+
// if the candidate slug is the same as the existing slug except
170+
// for the incrementor then the existing slug should be used
171+
if (_.isNumber(check) && check > 0) {
172+
if (slug === slugTokens.join('-') && serverSlug !== newSlug) {
173+
return;
174+
}
175+
}
176+
177+
self.set('user.slug', serverSlug);
178+
});
123179
}
124180
}
125181
});

core/client/models/slug-generator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var SlugGenerator = Ember.Object.extend({
22
ghostPaths: null,
3+
slugType: null,
34
value: null,
45
toString: function () {
56
return this.get('value');
@@ -12,7 +13,7 @@ var SlugGenerator = Ember.Object.extend({
1213
return Ember.RSVP.resolve('');
1314
}
1415

15-
url = this.get('ghostPaths.url').api('slugs', 'post', encodeURIComponent(textToSlugify));
16+
url = this.get('ghostPaths.url').api('slugs', this.get('slugType'), encodeURIComponent(textToSlugify));
1617

1718
return ic.ajax.request(url, {
1819
type: 'GET'

core/client/templates/settings/users/user.hbs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@
5858

5959
<fieldset class="user-details-bottom">
6060

61-
{{!-- <div class="form-group">
61+
<div class="form-group">
6262
<label for="user-slug">Slug</label>
63-
{{input value=user.slug id="user-slug" placeholder="Slug" autocorrect="off"}}
64-
<p>http://blog-url.com/user/{{user.slug}}</p>
65-
</div> --}}
63+
{{!-- {{input value=user.slug id="user-slug" placeholder="Slug" autocorrect="off"}} --}}
64+
{{gh-blur-input class="user-name" id="user-slug" value=slugValue name="user" action="updateSlug" placeholder="Slug" selectOnClick="true" autocorrect="off"}}
65+
<p>http://blog-url.com/user/{{slugValue}}</p>
66+
</div>
6667

6768
<div class="form-group">
6869
<label for="user-email">Email</label>

core/server/models/user.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ User = ghostBookshelf.Model.extend({
329329
delete data.role;
330330
}
331331

332+
if (data.status === 'all') {
333+
delete data.status;
334+
}
335+
332336
return ghostBookshelf.Model.findOne.call(this, data, options);
333337
},
334338

0 commit comments

Comments
 (0)