From 489cd08d97b88d8b26c9439d3c190a0f1f577453 Mon Sep 17 00:00:00 2001 From: Maurice Williams Date: Thu, 31 Jul 2014 00:25:42 -0400 Subject: [PATCH] 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 --- core/client/controllers/post-settings-menu.js | 3 +- .../client/controllers/settings/users/user.js | 58 ++++++++++++++++++- core/client/models/slug-generator.js | 3 +- core/client/templates/settings/users/user.hbs | 9 +-- core/server/models/user.js | 4 ++ 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/core/client/controllers/post-settings-menu.js b/core/client/controllers/post-settings-menu.js index 22e318c41c..71efc8641d 100644 --- a/core/client/controllers/post-settings-menu.js +++ b/core/client/controllers/post-settings-menu.js @@ -59,7 +59,8 @@ var PostSettingsMenuController = Ember.ObjectController.extend({ //Lazy load the slug generator for slugPlaceholder slugGenerator: Ember.computed(function () { return SlugGenerator.create({ - ghostPaths: this.get('ghostPaths') + ghostPaths: this.get('ghostPaths'), + slugType: 'post' }); }), //Requests slug from title diff --git a/core/client/controllers/settings/users/user.js b/core/client/controllers/settings/users/user.js index ce076cbc1d..d3a89c5142 100644 --- a/core/client/controllers/settings/users/user.js +++ b/core/client/controllers/settings/users/user.js @@ -1,3 +1,6 @@ +import SlugGenerator from 'ghost/models/slug-generator'; +import boundOneWay from 'ghost/utils/bound-one-way'; + var SettingsUserController = Ember.ObjectController.extend({ user: Ember.computed.alias('model'), @@ -43,7 +46,17 @@ var SettingsUserController = Ember.ObjectController.extend({ return createdAt ? createdAt.fromNow() : ''; }.property('user.created_at'), - + + //Lazy load the slug generator for slugPlaceholder + slugGenerator: Ember.computed(function () { + return SlugGenerator.create({ + ghostPaths: this.get('ghostPaths'), + slugType: 'user' + }); + }), + + slugValue: boundOneWay('user.slug'), + actions: { changeRole: function (newRole) { this.set('model.role', newRole); @@ -120,6 +133,49 @@ var SettingsUserController = Ember.ObjectController.extend({ } else { self.notifications.showErrors(user.get('passwordValidationErrors')); } + }, + + updateSlug: function (newSlug) { + var slug = this.get('user.slug'), + self = this; + + newSlug = newSlug || slug; + + newSlug = newSlug.trim(); + + // Ignore unchanged slugs or candidate slugs that are empty + if (!newSlug || slug === newSlug) { + return; + } + + this.get('slugGenerator').generateSlug(newSlug).then(function (serverSlug) { + + // If after getting the sanitized and unique slug back from the API + // we end up with a slug that matches the existing slug, abort the change + if (serverSlug === slug) { + return; + } + + // Because the server transforms the candidate slug by stripping + // certain characters and appending a number onto the end of slugs + // to enforce uniqueness, there are cases where we can get back a + // candidate slug that is a duplicate of the original except for + // the trailing incrementor (e.g., this-is-a-slug and this-is-a-slug-2) + + // get the last token out of the slug candidate and see if it's a number + var slugTokens = serverSlug.split('-'), + check = Number(slugTokens.pop()); + + // if the candidate slug is the same as the existing slug except + // for the incrementor then the existing slug should be used + if (_.isNumber(check) && check > 0) { + if (slug === slugTokens.join('-') && serverSlug !== newSlug) { + return; + } + } + + self.set('user.slug', serverSlug); + }); } } }); diff --git a/core/client/models/slug-generator.js b/core/client/models/slug-generator.js index 1059a3691a..eedfe5d84c 100644 --- a/core/client/models/slug-generator.js +++ b/core/client/models/slug-generator.js @@ -1,5 +1,6 @@ var SlugGenerator = Ember.Object.extend({ ghostPaths: null, + slugType: null, value: null, toString: function () { return this.get('value'); @@ -12,7 +13,7 @@ var SlugGenerator = Ember.Object.extend({ return Ember.RSVP.resolve(''); } - url = this.get('ghostPaths.url').api('slugs', 'post', encodeURIComponent(textToSlugify)); + url = this.get('ghostPaths.url').api('slugs', this.get('slugType'), encodeURIComponent(textToSlugify)); return ic.ajax.request(url, { type: 'GET' diff --git a/core/client/templates/settings/users/user.hbs b/core/client/templates/settings/users/user.hbs index bba20f0a7b..46e7d02e1f 100644 --- a/core/client/templates/settings/users/user.hbs +++ b/core/client/templates/settings/users/user.hbs @@ -58,11 +58,12 @@
- {{!--
+
- {{input value=user.slug id="user-slug" placeholder="Slug" autocorrect="off"}} -

http://blog-url.com/user/{{user.slug}}

-
--}} + {{!-- {{input value=user.slug id="user-slug" placeholder="Slug" autocorrect="off"}} --}} + {{gh-blur-input class="user-name" id="user-slug" value=slugValue name="user" action="updateSlug" placeholder="Slug" selectOnClick="true" autocorrect="off"}} +

http://blog-url.com/user/{{slugValue}}

+
diff --git a/core/server/models/user.js b/core/server/models/user.js index bcbd85142c..49e7a18231 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -329,6 +329,10 @@ User = ghostBookshelf.Model.extend({ delete data.role; } + if (data.status === 'all') { + delete data.status; + } + return ghostBookshelf.Model.findOne.call(this, data, options); },