Discourse Macro Helpers + Minor Fix to Admin User View
This commit is contained in:
parent
89152116c6
commit
5eaae063f0
|
@ -78,9 +78,7 @@ Discourse.AdminUsersListController = Ember.ArrayController.extend(Discourse.Pres
|
||||||
|
|
||||||
@property hasSelection
|
@property hasSelection
|
||||||
**/
|
**/
|
||||||
hasSelection: function() {
|
hasSelection: Em.computed.gt('selectedCount', 0),
|
||||||
return this.get('selectedCount') > 0;
|
|
||||||
}.property('selectedCount'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Refresh the current list of users.
|
Refresh the current list of users.
|
||||||
|
|
|
@ -98,19 +98,14 @@ Discourse.AdminUser = Discourse.User.extend({
|
||||||
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
||||||
},
|
},
|
||||||
|
|
||||||
isBanned: (function() {
|
isBanned: Em.computed.equal('is_banned', true),
|
||||||
return this.get('is_banned') === true;
|
canBan: Em.computed.not('staff'),
|
||||||
}).property('is_banned'),
|
|
||||||
|
|
||||||
canBan: (function() {
|
banDuration: function() {
|
||||||
return !this.get('admin') && !this.get('moderator');
|
|
||||||
}).property('admin', 'moderator'),
|
|
||||||
|
|
||||||
banDuration: (function() {
|
|
||||||
var banned_at = moment(this.banned_at);
|
var banned_at = moment(this.banned_at);
|
||||||
var banned_till = moment(this.banned_till);
|
var banned_till = moment(this.banned_till);
|
||||||
return banned_at.format('L') + " - " + banned_till.format('L');
|
return banned_at.format('L') + " - " + banned_till.format('L');
|
||||||
}).property('banned_till', 'banned_at'),
|
}.property('banned_till', 'banned_at'),
|
||||||
|
|
||||||
ban: function() {
|
ban: function() {
|
||||||
var duration = parseInt(window.prompt(I18n.t('admin.user.ban_duration')), 10);
|
var duration = parseInt(window.prompt(I18n.t('admin.user.ban_duration')), 10);
|
||||||
|
|
|
@ -16,11 +16,6 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||||
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
|
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController: function(controller, model) {
|
|
||||||
controller.set('model', model);
|
|
||||||
model.setOriginalTrustLevel();
|
|
||||||
},
|
|
||||||
|
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
this.render({into: 'admin/templates/admin'});
|
this.render({into: 'admin/templates/admin'});
|
||||||
},
|
},
|
||||||
|
@ -28,6 +23,7 @@ Discourse.AdminUserRoute = Discourse.Route.extend(Discourse.ModelReady, {
|
||||||
modelReady: function(controller, adminUser) {
|
modelReady: function(controller, adminUser) {
|
||||||
adminUser.loadDetails();
|
adminUser.loadDetails();
|
||||||
controller.set('model', adminUser);
|
controller.set('model', adminUser);
|
||||||
|
adminUser.setOriginalTrustLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,49 @@ Discourse.computed = {
|
||||||
return Ember.computed(function() {
|
return Ember.computed(function() {
|
||||||
return this.get(p1) === this.get(p2);
|
return this.get(p1) === this.get(p2);
|
||||||
}).property(p1, 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,8 +25,7 @@ Discourse.ListCategoriesController = Discourse.ObjectController.extend({
|
||||||
}.property('categories.@each'),
|
}.property('categories.@each'),
|
||||||
|
|
||||||
canEdit: function() {
|
canEdit: function() {
|
||||||
var u = Discourse.User.current();
|
Discourse.User.current('staff');
|
||||||
return u && u.staff;
|
|
||||||
}.property(),
|
}.property(),
|
||||||
|
|
||||||
// clear a pinned topic
|
// clear a pinned topic
|
||||||
|
|
|
@ -198,8 +198,6 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected
|
||||||
Discourse.URL.routeTo(this.get('lastPostUrl'));
|
Discourse.URL.routeTo(this.get('lastPostUrl'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
replyAsNewTopic: function(post) {
|
replyAsNewTopic: function(post) {
|
||||||
// TODO shut down topic draft cleanly if it exists ...
|
// TODO shut down topic draft cleanly if it exists ...
|
||||||
var composerController = this.get('controllers.composer');
|
var composerController = this.get('controllers.composer');
|
||||||
|
|
|
@ -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'));
|
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'),
|
}.property('post_number', 'topic_id', 'topic.slug'),
|
||||||
|
|
||||||
originalPostUrl: function() {
|
usernameUrl: Discourse.computed.url('username', '/users/%@'),
|
||||||
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'),
|
|
||||||
|
|
||||||
showUserReplyTab: function() {
|
showUserReplyTab: function() {
|
||||||
return this.get('reply_to_user') && (
|
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'),
|
}.property('reply_to_user', 'reply_to_post_number', 'post_number'),
|
||||||
|
|
||||||
byTopicCreator: function() {
|
byTopicCreator: Discourse.computed.propertyEqual('topic.details.created_by.id', 'user_id'),
|
||||||
return this.get('topic.details.created_by.id') === this.get('user_id');
|
|
||||||
}.property('topic.details.created_by.id', 'user_id'),
|
|
||||||
|
|
||||||
hasHistory: Em.computed.gt('version', 1),
|
hasHistory: Em.computed.gt('version', 1),
|
||||||
|
postElementId: Discourse.computed.fmt('post_number', 'post_%@'),
|
||||||
postElementId: function() {
|
|
||||||
return "post_" + (this.get('post_number'));
|
|
||||||
}.property('post_number'),
|
|
||||||
|
|
||||||
// The class for the read icon of the post. It starts with read-icon then adds 'seen' or
|
// 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.
|
// 'last-read' if the post has been seen or is the highest post number seen so far respectively.
|
||||||
|
|
|
@ -132,7 +132,6 @@ Discourse.Topic = Discourse.Model.extend({
|
||||||
archetypeObject: function() {
|
archetypeObject: function() {
|
||||||
return Discourse.Site.instance().get('archetypes').findProperty('id', this.get('archetype'));
|
return Discourse.Site.instance().get('archetypes').findProperty('id', this.get('archetype'));
|
||||||
}.property('archetype'),
|
}.property('archetype'),
|
||||||
|
|
||||||
isPrivateMessage: Em.computed.equal('archetype', 'private_message'),
|
isPrivateMessage: Em.computed.equal('archetype', 'private_message'),
|
||||||
|
|
||||||
toggleStatus: function(property) {
|
toggleStatus: function(property) {
|
||||||
|
@ -225,7 +224,6 @@ Discourse.Topic = Discourse.Model.extend({
|
||||||
@method clearPin
|
@method clearPin
|
||||||
**/
|
**/
|
||||||
clearPin: function() {
|
clearPin: function() {
|
||||||
|
|
||||||
var topic = this;
|
var topic = this;
|
||||||
|
|
||||||
// Clear the pin optimistically from the object
|
// 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?
|
// Is the reply to a post directly below it?
|
||||||
isReplyDirectlyBelow: function(post) {
|
isReplyDirectlyBelow: function(post) {
|
||||||
var postBelow, posts;
|
var posts = this.get('postStream.posts');
|
||||||
posts = this.get('postStream.posts');
|
|
||||||
if (!posts) return;
|
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
|
// If the post directly below's reply_to_post_number is our post number, it's
|
||||||
// considered directly below.
|
// considered directly below.
|
||||||
return postBelow && postBelow.get('reply_to_post_number') === post.get('post_number');
|
return postBelow && postBelow.get('reply_to_post_number') === post.get('post_number');
|
||||||
},
|
},
|
||||||
|
|
||||||
hasExcerpt: function() {
|
excerptNotEmpty: Em.computed.notEmpty('excerpt'),
|
||||||
return this.get('pinned') && this.get('excerpt') && this.get('excerpt').length > 0;
|
hasExcerpt: Em.computed.and('pinned', 'excerptNotEmpty'),
|
||||||
}.property('pinned', 'excerpt'),
|
|
||||||
|
|
||||||
excerptTruncated: function() {
|
excerptTruncated: function() {
|
||||||
var e = this.get('excerpt');
|
var e = this.get('excerpt');
|
||||||
return( e && e.substr(e.length - 8,8) === '…' );
|
return( e && e.substr(e.length - 8,8) === '…' );
|
||||||
}.property('excerpt'),
|
}.property('excerpt'),
|
||||||
|
|
||||||
canClearPin: function() {
|
readLastPost: Discourse.computed.propertyEqual('last_read_post_number', 'highest_post_number'),
|
||||||
return this.get('pinned') && (this.get('last_read_post_number') === this.get('highest_post_number'));
|
canCleanPin: Em.computed.and('pinned', 'readLastPost')
|
||||||
}.property('pinned', 'last_read_post_number', 'highest_post_number')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Discourse.Topic.reopenClass({
|
Discourse.Topic.reopenClass({
|
||||||
|
|
|
@ -8,15 +8,23 @@
|
||||||
**/
|
**/
|
||||||
Discourse.User = Discourse.Model.extend({
|
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.
|
Large version of this user's avatar.
|
||||||
|
|
||||||
@property avatarLarge
|
@property avatarLarge
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
avatarLarge: (function() {
|
avatarLarge: function() {
|
||||||
return Discourse.Utilities.avatarUrl(this.get('username'), 'large', this.get('avatar_template'));
|
return Discourse.Utilities.avatarUrl(this.get('username'), 'large', this.get('avatar_template'));
|
||||||
}).property('username'),
|
}.property('username'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Small version of this user's avatar.
|
Small version of this user's avatar.
|
||||||
|
@ -39,11 +47,10 @@ Discourse.User = Discourse.Model.extend({
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
websiteName: function() {
|
websiteName: function() {
|
||||||
return this.get('website').split("/")[2];
|
var website = this.get('website');
|
||||||
}.property('website'),
|
if (Em.isEmpty(website)) { return; }
|
||||||
|
|
||||||
hasWebsite: function() {
|
return this.get('website').split("/")[2];
|
||||||
return this.present('website');
|
|
||||||
}.property('website'),
|
}.property('website'),
|
||||||
|
|
||||||
statusIcon: function() {
|
statusIcon: function() {
|
||||||
|
@ -65,9 +72,7 @@ Discourse.User = Discourse.Model.extend({
|
||||||
@property path
|
@property path
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
path: function() {
|
path: Discourse.computed.url('username_lower', "/users/%@"),
|
||||||
return Discourse.getURL("/users/") + (this.get('username_lower'));
|
|
||||||
}.property('username'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Path to this user's administration
|
Path to this user's administration
|
||||||
|
@ -75,9 +80,7 @@ Discourse.User = Discourse.Model.extend({
|
||||||
@property adminPath
|
@property adminPath
|
||||||
@type {String}
|
@type {String}
|
||||||
**/
|
**/
|
||||||
adminPath: function() {
|
adminPath: Discourse.computed.url('username_lower', "/admin/users/%@"),
|
||||||
return Discourse.getURL("/admin/users/") + (this.get('username_lower'));
|
|
||||||
}.property('username'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This user's username in lowercase.
|
This user's username in lowercase.
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<div class='show'>
|
<div class='show'>
|
||||||
<dl>
|
<dl>
|
||||||
{{#if hasWebsite}}
|
{{#if websiteName}}
|
||||||
<dt>{{i18n user.website}}:</dt><dd><a {{bindAttr href="website"}} target="_blank">{{websiteName}}</a></dd>
|
<dt>{{i18n user.website}}:</dt><dd><a {{bindAttr href="website"}} target="_blank">{{websiteName}}</a></dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if created_at}}
|
{{#if created_at}}
|
||||||
|
|
|
@ -192,15 +192,15 @@ class PostsController < ApplicationController
|
||||||
|
|
||||||
def create_params
|
def create_params
|
||||||
permitted = [
|
permitted = [
|
||||||
:raw,
|
:raw,
|
||||||
:topic_id,
|
:topic_id,
|
||||||
:title,
|
:title,
|
||||||
:archetype,
|
:archetype,
|
||||||
:category,
|
:category,
|
||||||
:target_usernames,
|
:target_usernames,
|
||||||
:reply_to_post_number,
|
:reply_to_post_number,
|
||||||
:image_sizes,
|
:image_sizes,
|
||||||
:auto_close_days
|
:auto_close_days
|
||||||
]
|
]
|
||||||
|
|
||||||
if api_key_valid?
|
if api_key_valid?
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
});
|
|
@ -80,5 +80,6 @@ Discourse.Router.map(function() {
|
||||||
QUnit.testStart(function() {
|
QUnit.testStart(function() {
|
||||||
// Allow our tests to change site settings and have them reset before the next test
|
// Allow our tests to change site settings and have them reset before the next test
|
||||||
Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);
|
Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);
|
||||||
|
Discourse.BaseUri = "/";
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue