Discourse Macro Helpers + Minor Fix to Admin User View

This commit is contained in:
Robin Ward 2013-07-11 19:35:52 -04:00
parent 89152116c6
commit 5eaae063f0
13 changed files with 136 additions and 69 deletions

View File

@ -78,9 +78,7 @@ Discourse.AdminUsersListController = Ember.ArrayController.extend(Discourse.Pres
@property hasSelection
**/
hasSelection: function() {
return this.get('selectedCount') > 0;
}.property('selectedCount'),
hasSelection: Em.computed.gt('selectedCount', 0),
/**
Refresh the current list of users.

View File

@ -98,19 +98,14 @@ Discourse.AdminUser = Discourse.User.extend({
this.set('trustLevel.id', this.get('originalTrustLevel'));
},
isBanned: (function() {
return this.get('is_banned') === true;
}).property('is_banned'),
isBanned: Em.computed.equal('is_banned', true),
canBan: Em.computed.not('staff'),
canBan: (function() {
return !this.get('admin') && !this.get('moderator');
}).property('admin', 'moderator'),
banDuration: (function() {
banDuration: function() {
var banned_at = moment(this.banned_at);
var banned_till = moment(this.banned_till);
return banned_at.format('L') + " - " + banned_till.format('L');
}).property('banned_till', 'banned_at'),
}.property('banned_till', 'banned_at'),
ban: function() {
var duration = parseInt(window.prompt(I18n.t('admin.user.ban_duration')), 10);

View File

@ -16,11 +16,6 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
},
setupController: function(controller, model) {
controller.set('model', model);
model.setOriginalTrustLevel();
},
renderTemplate: function() {
this.render({into: 'admin/templates/admin'});
},
@ -28,6 +23,7 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
modelReady: function(controller, adminUser) {
adminUser.loadDetails();
controller.set('model', adminUser);
adminUser.setOriginalTrustLevel();
}
});

View File

@ -12,6 +12,49 @@ Discourse.computed = {
return Ember.computed(function() {
return this.get(p1) === this.get(p2);
}).property(p1, p2);
},
/**
Uses an Ember String `fmt` call to format a string. See:
http://emberjs.com/api/classes/Ember.String.html#method_fmt
@method fmt
@params {String} properties* to format
@params {String} format the format string
@return {Function} computedProperty function
**/
fmt: function() {
var args = Array.prototype.slice.call(arguments, 0);
var format = args.pop();
var computed = Ember.computed(function() {
var context = this;
return format.fmt.apply(format, args.map(function (a) {
return context.get(a);
}));
})
return computed.property.apply(computed, args);
},
/**
Creates a URL using Discourse.getURL. It takes a fmt string just like
fmt does.
@method url
@params {String} properties* to format
@params {String} format the format string for the URL
@return {Function} computedProperty function returning a URL
**/
url: function() {
var args = Array.prototype.slice.call(arguments, 0);
var format = args.pop();
var computed = Ember.computed(function() {
var context = this;
return Discourse.getURL(format.fmt.apply(format, args.map(function (a) {
return context.get(a);
})));
})
return computed.property.apply(computed, args);
}
};

View File

@ -25,8 +25,7 @@ Discourse.ListCategoriesController = Discourse.ObjectController.extend({
}.property('categories.@each'),
canEdit: function() {
var u = Discourse.User.current();
return u && u.staff;
Discourse.User.current('staff');
}.property(),
// clear a pinned topic

View File

@ -198,8 +198,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
Discourse.URL.routeTo(this.get('lastPostUrl'));
},
replyAsNewTopic: function(post) {
// TODO shut down topic draft cleanly if it exists ...
var composerController = this.get('controllers.composer');

View File

@ -21,13 +21,7 @@ Discourse.Post = Discourse.Model.extend({
return Discourse.Utilities.postUrl(this.get('topic.slug') || this.get('topic_slug'), this.get('topic_id'), this.get('post_number'));
}.property('post_number', 'topic_id', 'topic.slug'),
originalPostUrl: function() {
return Discourse.getURL("/t/") + (this.get('topic_id')) + "/" + (this.get('reply_to_post_number'));
}.property('reply_to_post_number'),
usernameUrl: function() {
return Discourse.getURL("/users/" + this.get('username'));
}.property('username'),
usernameUrl: Discourse.computed.url('username', '/users/%@'),
showUserReplyTab: function() {
return this.get('reply_to_user') && (
@ -36,15 +30,9 @@ Discourse.Post = Discourse.Model.extend({
);
}.property('reply_to_user', 'reply_to_post_number', 'post_number'),
byTopicCreator: function() {
return this.get('topic.details.created_by.id') === this.get('user_id');
}.property('topic.details.created_by.id', 'user_id'),
byTopicCreator: Discourse.computed.propertyEqual('topic.details.created_by.id', 'user_id'),
hasHistory: Em.computed.gt('version', 1),
postElementId: function() {
return "post_" + (this.get('post_number'));
}.property('post_number'),
postElementId: Discourse.computed.fmt('post_number', 'post_%@'),
// The class for the read icon of the post. It starts with read-icon then adds 'seen' or
// 'last-read' if the post has been seen or is the highest post number seen so far respectively.

View File

@ -132,7 +132,6 @@ Discourse.Topic = Discourse.Model.extend({
archetypeObject: function() {
return Discourse.Site.instance().get('archetypes').findProperty('id', this.get('archetype'));
}.property('archetype'),
isPrivateMessage: Em.computed.equal('archetype', 'private_message'),
toggleStatus: function(property) {
@ -225,7 +224,6 @@ Discourse.Topic = Discourse.Model.extend({
@method clearPin
**/
clearPin: function() {
var topic = this;
// Clear the pin optimistically from the object
@ -241,29 +239,27 @@ Discourse.Topic = Discourse.Model.extend({
// Is the reply to a post directly below it?
isReplyDirectlyBelow: function(post) {
var postBelow, posts;
posts = this.get('postStream.posts');
var posts = this.get('postStream.posts');
if (!posts) return;
postBelow = posts[posts.indexOf(post) + 1];
var postBelow = posts[posts.indexOf(post) + 1];
// If the post directly below's reply_to_post_number is our post number, it's
// considered directly below.
return postBelow && postBelow.get('reply_to_post_number') === post.get('post_number');
},
hasExcerpt: function() {
return this.get('pinned') && this.get('excerpt') && this.get('excerpt').length > 0;
}.property('pinned', 'excerpt'),
excerptNotEmpty: Em.computed.notEmpty('excerpt'),
hasExcerpt: Em.computed.and('pinned', 'excerptNotEmpty'),
excerptTruncated: function() {
var e = this.get('excerpt');
return( e && e.substr(e.length - 8,8) === '…' );
}.property('excerpt'),
canClearPin: function() {
return this.get('pinned') && (this.get('last_read_post_number') === this.get('highest_post_number'));
}.property('pinned', 'last_read_post_number', 'highest_post_number')
readLastPost: Discourse.computed.propertyEqual('last_read_post_number', 'highest_post_number'),
canCleanPin: Em.computed.and('pinned', 'readLastPost')
});
Discourse.Topic.reopenClass({

View File

@ -8,15 +8,23 @@
**/
Discourse.User = Discourse.Model.extend({
/**
Is this user a member of staff?
@property staff
@type {Boolean}
**/
staff: Em.computed.or('admin', 'moderator'),
/**
Large version of this user's avatar.
@property avatarLarge
@type {String}
**/
avatarLarge: (function() {
avatarLarge: function() {
return Discourse.Utilities.avatarUrl(this.get('username'), 'large', this.get('avatar_template'));
}).property('username'),
}.property('username'),
/**
Small version of this user's avatar.
@ -39,11 +47,10 @@ Discourse.User = Discourse.Model.extend({
@type {String}
**/
websiteName: function() {
return this.get('website').split("/")[2];
}.property('website'),
var website = this.get('website');
if (Em.isEmpty(website)) { return; }
hasWebsite: function() {
return this.present('website');
return this.get('website').split("/")[2];
}.property('website'),
statusIcon: function() {
@ -65,9 +72,7 @@ Discourse.User = Discourse.Model.extend({
@property path
@type {String}
**/
path: function() {
return Discourse.getURL("/users/") + (this.get('username_lower'));
}.property('username'),
path: Discourse.computed.url('username_lower', "/users/%@"),
/**
Path to this user's administration
@ -75,9 +80,7 @@ Discourse.User = Discourse.Model.extend({
@property adminPath
@type {String}
**/
adminPath: function() {
return Discourse.getURL("/admin/users/") + (this.get('username_lower'));
}.property('username'),
adminPath: Discourse.computed.url('username_lower', "/admin/users/%@"),
/**
This user's username in lowercase.

View File

@ -22,7 +22,7 @@
</ul>
<div class='show'>
<dl>
{{#if hasWebsite}}
{{#if websiteName}}
<dt>{{i18n user.website}}:</dt><dd><a {{bindAttr href="website"}} target="_blank">{{websiteName}}</a></dd>
{{/if}}
{{#if created_at}}

View File

@ -192,15 +192,15 @@ class PostsController < ApplicationController
def create_params
permitted = [
:raw,
:topic_id,
:title,
:archetype,
:category,
:target_usernames,
:reply_to_post_number,
:image_sizes,
:auto_close_days
:raw,
:topic_id,
:title,
:archetype,
:category,
:target_usernames,
:reply_to_post_number,
:image_sizes,
:auto_close_days
]
if api_key_valid?

View File

@ -0,0 +1,50 @@
module("Discourse.Computed");
var testClass = Em.Object.extend({
same: Discourse.computed.propertyEqual('cookies', 'biscuits'),
exclaimyUsername: Discourse.computed.fmt('username', "!!! %@ !!!"),
multiple: Discourse.computed.fmt('username', 'mood', "%@ is %@"),
userUrl: Discourse.computed.url('username', "/users/%@")
});
test("propertyEqual", function() {
var t = testClass.create({
cookies: 10,
biscuits: 10
});
ok(t.get('same'), "it is true when the properties are the same");
t.set('biscuits', 9);
ok(!t.get('same'), "it isn't true when one property is different");
});
test("fmt", function() {
var t = testClass.create({
username: 'eviltrout',
mood: "happy"
});
equal(t.get('exclaimyUsername'), '!!! eviltrout !!!', "it inserts the string");
equal(t.get('multiple'), "eviltrout is happy");
t.set('username', 'codinghorror');
equal(t.get('multiple'), "codinghorror is happy", "supports changing proerties");
t.set('mood', 'ecstatic');
equal(t.get('multiple'), "codinghorror is ecstatic", "supports changing another property");
});
test("url without a prefix", function() {
var t = testClass.create({ username: 'eviltrout' });
equal(t.get('userUrl'), "/users/eviltrout");
});
test("url with a prefix", function() {
Discourse.BaseUri = "/prefixed/";
var t = testClass.create({ username: 'eviltrout' });
equal(t.get('userUrl'), "/prefixed/users/eviltrout");
});

View File

@ -80,5 +80,6 @@ Discourse.Router.map(function() {
QUnit.testStart(function() {
// Allow our tests to change site settings and have them reset before the next test
Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);
Discourse.BaseUri = "/";
})