mirror of
https://github.com/discourse/discourse.git
synced 2025-09-08 12:06:51 +08:00
FEATURE: support for external letter avatars service
This commit is contained in:
parent
e43034f08f
commit
2742602254
16 changed files with 250 additions and 282 deletions
|
@ -9,12 +9,14 @@ Em.Handlebars.helper('bound-avatar', function(user, size, uploadId) {
|
||||||
return new safe("<div class='avatar-placeholder'></div>");
|
return new safe("<div class='avatar-placeholder'></div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = Em.get(user, 'username');
|
const username = Em.get(user, 'username'),
|
||||||
|
letterAvatarColor = Em.get(user, 'letter_avatar_color');
|
||||||
|
|
||||||
if (arguments.length < 4) { uploadId = Em.get(user, 'uploaded_avatar_id'); }
|
if (arguments.length < 4) { uploadId = Em.get(user, 'uploaded_avatar_id'); }
|
||||||
const avatar = Em.get(user, 'avatar_template') || avatarTemplate(username, uploadId);
|
const avatar = Em.get(user, 'avatar_template') || avatarTemplate(username, uploadId, letterAvatarColor);
|
||||||
|
|
||||||
return new safe(Discourse.Utilities.avatarImg({ size: size, avatarTemplate: avatar }));
|
return new safe(Discourse.Utilities.avatarImg({ size: size, avatarTemplate: avatar }));
|
||||||
}, 'username', 'uploaded_avatar_id', 'avatar_template');
|
}, 'username', 'uploaded_avatar_id', 'letter_avatar_color', 'avatar_template');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used when we only have a template
|
* Used when we only have a template
|
||||||
|
|
|
@ -5,20 +5,20 @@ function renderAvatar(user, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
var username = Em.get(user, 'username');
|
let username = Em.get(user, 'username');
|
||||||
if (!username) {
|
if (!username) {
|
||||||
if (!options.usernamePath) { return ''; }
|
if (!options.usernamePath) { return ''; }
|
||||||
username = Em.get(user, options.usernamePath);
|
username = Em.get(user, options.usernamePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var title;
|
let title;
|
||||||
if (!options.ignoreTitle) {
|
if (!options.ignoreTitle) {
|
||||||
// first try to get a title
|
// first try to get a title
|
||||||
title = Em.get(user, 'title');
|
title = Em.get(user, 'title');
|
||||||
// if there was no title provided
|
// if there was no title provided
|
||||||
if (!title) {
|
if (!title) {
|
||||||
// try to retrieve a description
|
// try to retrieve a description
|
||||||
var description = Em.get(user, 'description');
|
const description = Em.get(user, 'description');
|
||||||
// if a description has been provided
|
// if a description has been provided
|
||||||
if (description && description.length > 0) {
|
if (description && description.length > 0) {
|
||||||
// preprend the username before the description
|
// preprend the username before the description
|
||||||
|
@ -28,13 +28,14 @@ function renderAvatar(user, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is simply done to ensure we cache images correctly
|
// this is simply done to ensure we cache images correctly
|
||||||
var uploadedAvatarId = Em.get(user, 'uploaded_avatar_id') || Em.get(user, 'user.uploaded_avatar_id');
|
const uploadedAvatarId = Em.get(user, 'uploaded_avatar_id') || Em.get(user, 'user.uploaded_avatar_id'),
|
||||||
|
letterAvatarColor = Em.get(user, 'letter_avatar_color') || Em.get(user, 'user.letter_avatar_color');
|
||||||
|
|
||||||
return Discourse.Utilities.avatarImg({
|
return Discourse.Utilities.avatarImg({
|
||||||
size: options.imageSize,
|
size: options.imageSize,
|
||||||
extraClasses: Em.get(user, 'extras') || options.extraClasses,
|
extraClasses: Em.get(user, 'extras') || options.extraClasses,
|
||||||
title: title || username,
|
title: title || username,
|
||||||
avatarTemplate: avatarTemplate(username, uploadedAvatarId)
|
avatarTemplate: Em.get("avatar_template") || avatarTemplate(username, uploadedAvatarId, letterAvatarColor)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
@ -2,8 +2,10 @@ import { hashString } from 'discourse/lib/hash';
|
||||||
|
|
||||||
let _splitAvatars;
|
let _splitAvatars;
|
||||||
|
|
||||||
function defaultAvatar(username) {
|
function defaultAvatar(username, letterAvatarColor) {
|
||||||
const defaultAvatars = Discourse.SiteSettings.default_avatars;
|
const defaultAvatars = Discourse.SiteSettings.default_avatars,
|
||||||
|
version = Discourse.LetterAvatarVersion;
|
||||||
|
|
||||||
if (defaultAvatars && defaultAvatars.length) {
|
if (defaultAvatars && defaultAvatars.length) {
|
||||||
_splitAvatars = _splitAvatars || defaultAvatars.split("\n");
|
_splitAvatars = _splitAvatars || defaultAvatars.split("\n");
|
||||||
|
|
||||||
|
@ -13,20 +15,17 @@ function defaultAvatar(username) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Discourse.getURLWithCDN("/letter_avatar/" +
|
if (Discourse.SiteSettings.external_letter_avatars_enabled) {
|
||||||
username.toLowerCase() +
|
const url = Discourse.SiteSettings.external_letter_avatars_url;
|
||||||
"/{size}/" +
|
return `${url}/letter/${username[0]}?color=${letterAvatarColor}&size={size}`;
|
||||||
Discourse.LetterAvatarVersion + ".png");
|
} else {
|
||||||
|
return Discourse.getURLWithCDN(`/letter_avatar/${username.toLowerCase()}/{size}/${version}.png`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(username, uploadedAvatarId) {
|
export default function(username, uploadedAvatarId, letterAvatarColor) {
|
||||||
if (uploadedAvatarId) {
|
if (uploadedAvatarId) {
|
||||||
return Discourse.getURLWithCDN("/user_avatar/" +
|
return Discourse.getURLWithCDN(`/user_avatar/${Discourse.BaseUrl}/${username.toLowerCase()}/{size}/${uploadedAvatarId}.png`);
|
||||||
Discourse.BaseUrl +
|
|
||||||
"/" +
|
|
||||||
username.toLowerCase() +
|
|
||||||
"/{size}/" +
|
|
||||||
uploadedAvatarId + ".png");
|
|
||||||
}
|
}
|
||||||
return defaultAvatar(username);
|
return defaultAvatar(username, letterAvatarColor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import RestModel from 'discourse/models/rest';
|
import RestModel from 'discourse/models/rest';
|
||||||
import { url } from 'discourse/lib/computed';
|
import { url } from 'discourse/lib/computed';
|
||||||
|
import { on } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
const UserActionTypes = {
|
const UserActionTypes = {
|
||||||
likes_given: 1,
|
likes_given: 1,
|
||||||
|
@ -17,21 +19,22 @@ const UserActionTypes = {
|
||||||
};
|
};
|
||||||
const InvertedActionTypes = {};
|
const InvertedActionTypes = {};
|
||||||
|
|
||||||
_.each(UserActionTypes, function (k, v) {
|
_.each(UserActionTypes, (k, v) => {
|
||||||
InvertedActionTypes[k] = v;
|
InvertedActionTypes[k] = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
const UserAction = RestModel.extend({
|
const UserAction = RestModel.extend({
|
||||||
|
|
||||||
_attachCategory: function() {
|
@on("init")
|
||||||
|
_attachCategory() {
|
||||||
const categoryId = this.get('category_id');
|
const categoryId = this.get('category_id');
|
||||||
if (categoryId) {
|
if (categoryId) {
|
||||||
this.set('category', Discourse.Category.findById(categoryId));
|
this.set('category', Discourse.Category.findById(categoryId));
|
||||||
}
|
}
|
||||||
}.on('init'),
|
},
|
||||||
|
|
||||||
descriptionKey: function() {
|
@computed("action_type")
|
||||||
const action = this.get('action_type');
|
descriptionKey(action) {
|
||||||
if (action === null || Discourse.UserAction.TO_SHOW.indexOf(action) >= 0) {
|
if (action === null || Discourse.UserAction.TO_SHOW.indexOf(action) >= 0) {
|
||||||
if (this.get('isPM')) {
|
if (this.get('isPM')) {
|
||||||
return this.get('sameUser') ? 'sent_by_you' : 'sent_by_user';
|
return this.get('sameUser') ? 'sent_by_you' : 'sent_by_user';
|
||||||
|
@ -59,34 +62,39 @@ const UserAction = RestModel.extend({
|
||||||
return this.get('targetUser') ? 'user_mentioned_you' : 'user_mentioned_user';
|
return this.get('targetUser') ? 'user_mentioned_you' : 'user_mentioned_user';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.property('action_type'),
|
},
|
||||||
|
|
||||||
sameUser: function() {
|
@computed("username")
|
||||||
return this.get('username') === Discourse.User.currentProp('username');
|
sameUser(username) {
|
||||||
}.property('username'),
|
return username === Discourse.User.currentProp('username');
|
||||||
|
},
|
||||||
|
|
||||||
targetUser: function() {
|
@computed("target_username")
|
||||||
return this.get('target_username') === Discourse.User.currentProp('username');
|
targetUser(targetUsername) {
|
||||||
}.property('target_username'),
|
return targetUsername === Discourse.User.currentProp('username');
|
||||||
|
},
|
||||||
|
|
||||||
presentName: Em.computed.any('name', 'username'),
|
presentName: Em.computed.any('name', 'username'),
|
||||||
targetDisplayName: Em.computed.any('target_name', 'target_username'),
|
targetDisplayName: Em.computed.any('target_name', 'target_username'),
|
||||||
actingDisplayName: Em.computed.any('acting_name', 'acting_username'),
|
actingDisplayName: Em.computed.any('acting_name', 'acting_username'),
|
||||||
targetUserUrl: url('target_username', '/users/%@'),
|
targetUserUrl: url('target_username', '/users/%@'),
|
||||||
|
|
||||||
usernameLower: function() {
|
@computed("username")
|
||||||
return this.get('username').toLowerCase();
|
usernameLower(username) {
|
||||||
}.property('username'),
|
return username.toLowerCase();
|
||||||
|
},
|
||||||
|
|
||||||
userUrl: url('usernameLower', '/users/%@'),
|
userUrl: url('usernameLower', '/users/%@'),
|
||||||
|
|
||||||
postUrl: function() {
|
@computed()
|
||||||
|
postUrl() {
|
||||||
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
|
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
|
||||||
}.property(),
|
},
|
||||||
|
|
||||||
replyUrl: function() {
|
@computed()
|
||||||
|
replyUrl() {
|
||||||
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('reply_to_post_number'));
|
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('reply_to_post_number'));
|
||||||
}.property(),
|
},
|
||||||
|
|
||||||
replyType: Em.computed.equal('action_type', UserActionTypes.replies),
|
replyType: Em.computed.equal('action_type', UserActionTypes.replies),
|
||||||
postType: Em.computed.equal('action_type', UserActionTypes.posts),
|
postType: Em.computed.equal('action_type', UserActionTypes.posts),
|
||||||
|
@ -99,7 +107,7 @@ const UserAction = RestModel.extend({
|
||||||
postReplyType: Em.computed.or('postType', 'replyType'),
|
postReplyType: Em.computed.or('postType', 'replyType'),
|
||||||
removableBookmark: Em.computed.and('bookmarkType', 'sameUser'),
|
removableBookmark: Em.computed.and('bookmarkType', 'sameUser'),
|
||||||
|
|
||||||
addChild: function(action) {
|
addChild(action) {
|
||||||
let groups = this.get("childGroups");
|
let groups = this.get("childGroups");
|
||||||
if (!groups) {
|
if (!groups) {
|
||||||
groups = {
|
groups = {
|
||||||
|
@ -143,22 +151,23 @@ const UserAction = RestModel.extend({
|
||||||
"childGroups.edits.items", "childGroups.edits.items.@each",
|
"childGroups.edits.items", "childGroups.edits.items.@each",
|
||||||
"childGroups.bookmarks.items", "childGroups.bookmarks.items.@each"),
|
"childGroups.bookmarks.items", "childGroups.bookmarks.items.@each"),
|
||||||
|
|
||||||
switchToActing: function() {
|
switchToActing() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
username: this.get('acting_username'),
|
username: this.get('acting_username'),
|
||||||
uploaded_avatar_id: this.get('acting_uploaded_avatar_id'),
|
uploaded_avatar_id: this.get('acting_uploaded_avatar_id'),
|
||||||
|
letter_avatar_color: this.get('action_letter_avatar_color'),
|
||||||
name: this.get('actingDisplayName')
|
name: this.get('actingDisplayName')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UserAction.reopenClass({
|
UserAction.reopenClass({
|
||||||
collapseStream: function(stream) {
|
collapseStream(stream) {
|
||||||
const uniq = {};
|
const uniq = {};
|
||||||
const collapsed = [];
|
const collapsed = [];
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
|
|
||||||
stream.forEach(function(item) {
|
stream.forEach(item => {
|
||||||
const key = "" + item.topic_id + "-" + item.post_number;
|
const key = "" + item.topic_id + "-" + item.post_number;
|
||||||
const found = uniq[key];
|
const found = uniq[key];
|
||||||
if (found === void 0) {
|
if (found === void 0) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import UserPostsStream from 'discourse/models/user-posts-stream';
|
||||||
import Singleton from 'discourse/mixins/singleton';
|
import Singleton from 'discourse/mixins/singleton';
|
||||||
import { longDate } from 'discourse/lib/formatter';
|
import { longDate } from 'discourse/lib/formatter';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||||
import Badge from 'discourse/models/badge';
|
import Badge from 'discourse/models/badge';
|
||||||
import UserBadge from 'discourse/models/user-badge';
|
import UserBadge from 'discourse/models/user-badge';
|
||||||
|
|
||||||
|
@ -18,13 +19,15 @@ const User = RestModel.extend({
|
||||||
hasNotPosted: Em.computed.not("hasPosted"),
|
hasNotPosted: Em.computed.not("hasPosted"),
|
||||||
canBeDeleted: Em.computed.and("can_be_deleted", "hasNotPosted"),
|
canBeDeleted: Em.computed.and("can_be_deleted", "hasNotPosted"),
|
||||||
|
|
||||||
stream: function() {
|
@computed()
|
||||||
|
stream() {
|
||||||
return UserStream.create({ user: this });
|
return UserStream.create({ user: this });
|
||||||
}.property(),
|
},
|
||||||
|
|
||||||
postsStream: function() {
|
@computed()
|
||||||
|
postsStream() {
|
||||||
return UserPostsStream.create({ user: this });
|
return UserPostsStream.create({ user: this });
|
||||||
}.property(),
|
},
|
||||||
|
|
||||||
staff: Em.computed.or('admin', 'moderator'),
|
staff: Em.computed.or('admin', 'moderator'),
|
||||||
|
|
||||||
|
@ -32,27 +35,22 @@ const User = RestModel.extend({
|
||||||
return Discourse.ajax(`/session/${this.get('username')}`, { type: 'DELETE'});
|
return Discourse.ajax(`/session/${this.get('username')}`, { type: 'DELETE'});
|
||||||
},
|
},
|
||||||
|
|
||||||
searchContext: function() {
|
@computed("username_lower")
|
||||||
|
searchContext(username) {
|
||||||
return {
|
return {
|
||||||
type: 'user',
|
type: 'user',
|
||||||
id: this.get('username_lower'),
|
id: username,
|
||||||
user: this
|
user: this
|
||||||
};
|
};
|
||||||
}.property('username_lower'),
|
},
|
||||||
|
|
||||||
/**
|
@computed("username", "name")
|
||||||
This user's display name. Returns the name if possible, otherwise returns the
|
displayName(username, name) {
|
||||||
username.
|
if (Discourse.SiteSettings.enable_names && !Ember.isEmpty(name)) {
|
||||||
|
return name;
|
||||||
@property displayName
|
|
||||||
@type {String}
|
|
||||||
**/
|
|
||||||
displayName: function() {
|
|
||||||
if (Discourse.SiteSettings.enable_names && !Ember.isEmpty(this.get('name'))) {
|
|
||||||
return this.get('name');
|
|
||||||
}
|
}
|
||||||
return this.get('username');
|
return username;
|
||||||
}.property('username', 'name'),
|
},
|
||||||
|
|
||||||
@computed('profile_background')
|
@computed('profile_background')
|
||||||
profileBackground(bgUrl) {
|
profileBackground(bgUrl) {
|
||||||
|
@ -60,38 +58,23 @@ const User = RestModel.extend({
|
||||||
return ('background-image: url(' + Discourse.getURLWithCDN(bgUrl) + ')').htmlSafe();
|
return ('background-image: url(' + Discourse.getURLWithCDN(bgUrl) + ')').htmlSafe();
|
||||||
},
|
},
|
||||||
|
|
||||||
path: function(){
|
@computed()
|
||||||
return Discourse.getURL('/users/' + this.get('username_lower'));
|
path() {
|
||||||
// no need to observe, requires a hard refresh to update
|
// no need to observe, requires a hard refresh to update
|
||||||
}.property(),
|
return Discourse.getURL(`/users/${this.get('username_lower')}`);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
Path to this user's administration
|
|
||||||
|
|
||||||
@property adminPath
|
|
||||||
@type {String}
|
|
||||||
**/
|
|
||||||
adminPath: url('username_lower', "/admin/users/%@"),
|
adminPath: url('username_lower', "/admin/users/%@"),
|
||||||
|
|
||||||
/**
|
@computed("username")
|
||||||
This user's username in lowercase.
|
username_lower(username) {
|
||||||
|
return username.toLowerCase();
|
||||||
|
},
|
||||||
|
|
||||||
@property username_lower
|
@computed("trust_level")
|
||||||
@type {String}
|
trustLevel(trustLevel) {
|
||||||
**/
|
return Discourse.Site.currentProp('trustLevels').findProperty('id', parseInt(trustLevel, 10));
|
||||||
username_lower: function() {
|
},
|
||||||
return this.get('username').toLowerCase();
|
|
||||||
}.property('username'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
This user's trust level.
|
|
||||||
|
|
||||||
@property trustLevel
|
|
||||||
@type {Integer}
|
|
||||||
**/
|
|
||||||
trustLevel: function() {
|
|
||||||
return Discourse.Site.currentProp('trustLevels').findProperty('id', parseInt(this.get('trust_level'), 10));
|
|
||||||
}.property('trust_level'),
|
|
||||||
|
|
||||||
isBasic: Em.computed.equal('trust_level', 0),
|
isBasic: Em.computed.equal('trust_level', 0),
|
||||||
isLeader: Em.computed.equal('trust_level', 3),
|
isLeader: Em.computed.equal('trust_level', 3),
|
||||||
|
@ -100,61 +83,36 @@ const User = RestModel.extend({
|
||||||
|
|
||||||
isSuspended: Em.computed.equal('suspended', true),
|
isSuspended: Em.computed.equal('suspended', true),
|
||||||
|
|
||||||
suspended: function() {
|
@computed("suspended_till")
|
||||||
return this.get('suspended_till') && moment(this.get('suspended_till')).isAfter();
|
suspended(suspendedTill) {
|
||||||
}.property('suspended_till'),
|
return suspendedTill && moment(suspendedTill).isAfter();
|
||||||
|
},
|
||||||
|
|
||||||
suspendedTillDate: function() {
|
@computed("suspended_till")
|
||||||
return longDate(this.get('suspended_till'));
|
suspendedTillDate(suspendedTill) {
|
||||||
}.property('suspended_till'),
|
return longDate(suspendedTill);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
changeUsername(new_username) {
|
||||||
Changes this user's username.
|
return Discourse.ajax(`/users/${this.get('username_lower')}/preferences/username`, {
|
||||||
|
|
||||||
@method changeUsername
|
|
||||||
@param {String} newUsername The user's new username
|
|
||||||
@returns Result of ajax call
|
|
||||||
**/
|
|
||||||
changeUsername: function(newUsername) {
|
|
||||||
return Discourse.ajax("/users/" + this.get('username_lower') + "/preferences/username", {
|
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: { new_username: newUsername }
|
data: { new_username }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
changeEmail(email) {
|
||||||
Changes this user's email address.
|
return Discourse.ajax(`/users/${this.get('username_lower')}/preferences/email`, {
|
||||||
|
|
||||||
@method changeEmail
|
|
||||||
@param {String} email The user's new email address\
|
|
||||||
@returns Result of ajax call
|
|
||||||
**/
|
|
||||||
changeEmail: function(email) {
|
|
||||||
return Discourse.ajax("/users/" + this.get('username_lower') + "/preferences/email", {
|
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: { email: email }
|
data: { email }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
copy() {
|
||||||
Returns a copy of this user.
|
|
||||||
|
|
||||||
@method copy
|
|
||||||
@returns {User}
|
|
||||||
**/
|
|
||||||
copy: function() {
|
|
||||||
return Discourse.User.create(this.getProperties(Ember.keys(this)));
|
return Discourse.User.create(this.getProperties(Ember.keys(this)));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
save() {
|
||||||
Save's this user's properties over AJAX via a PUT request.
|
const data = this.getProperties(
|
||||||
|
|
||||||
@method save
|
|
||||||
@returns {Promise} the result of the operation
|
|
||||||
**/
|
|
||||||
save: function() {
|
|
||||||
const self = this,
|
|
||||||
data = this.getProperties(
|
|
||||||
'auto_track_topics_after_msecs',
|
'auto_track_topics_after_msecs',
|
||||||
'bio_raw',
|
'bio_raw',
|
||||||
'website',
|
'website',
|
||||||
|
@ -179,10 +137,10 @@ const User = RestModel.extend({
|
||||||
'card_background'
|
'card_background'
|
||||||
);
|
);
|
||||||
|
|
||||||
['muted','watched','tracked'].forEach(function(s){
|
['muted','watched','tracked'].forEach(s => {
|
||||||
var cats = self.get(s + 'Categories').map(function(c){ return c.get('id')});
|
let cats = this.get(s + 'Categories').map(c => c.get('id'));
|
||||||
// HACK: denote lack of categories
|
// HACK: denote lack of categories
|
||||||
if(cats.length === 0) { cats = [-1]; }
|
if (cats.length === 0) { cats = [-1]; }
|
||||||
data[s + '_category_ids'] = cats;
|
data[s + '_category_ids'] = cats;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -192,26 +150,19 @@ const User = RestModel.extend({
|
||||||
|
|
||||||
// TODO: We can remove this when migrated fully to rest model.
|
// TODO: We can remove this when migrated fully to rest model.
|
||||||
this.set('isSaving', true);
|
this.set('isSaving', true);
|
||||||
return Discourse.ajax("/users/" + this.get('username_lower'), {
|
return Discourse.ajax(`/users/${this.get('username_lower')}`, {
|
||||||
data: data,
|
data: data,
|
||||||
type: 'PUT'
|
type: 'PUT'
|
||||||
}).then(function(result) {
|
}).then(result => {
|
||||||
self.set('bio_excerpt', result.user.bio_excerpt);
|
this.set('bio_excerpt', result.user.bio_excerpt);
|
||||||
|
const userProps = this.getProperties('enable_quoting', 'external_links_in_new_tab', 'dynamic_favicon');
|
||||||
const userProps = self.getProperties('enable_quoting', 'external_links_in_new_tab', 'dynamic_favicon');
|
|
||||||
Discourse.User.current().setProperties(userProps);
|
Discourse.User.current().setProperties(userProps);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.set('isSaving', false);
|
this.set('isSaving', false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
changePassword() {
|
||||||
Changes the password and calls the callback function on AJAX.complete.
|
|
||||||
|
|
||||||
@method changePassword
|
|
||||||
@returns {Promise} the result of the change password operation
|
|
||||||
**/
|
|
||||||
changePassword: function() {
|
|
||||||
return Discourse.ajax("/session/forgot_password", {
|
return Discourse.ajax("/session/forgot_password", {
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
data: { login: this.get('username') },
|
data: { login: this.get('username') },
|
||||||
|
@ -219,73 +170,63 @@ const User = RestModel.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
loadUserAction(id) {
|
||||||
Loads a single user action by id.
|
const stream = this.get('stream');
|
||||||
|
return Discourse.ajax(`/user_actions/${id}.json`, { cache: 'false' }).then(result => {
|
||||||
@method loadUserAction
|
|
||||||
@param {Integer} id The id of the user action being loaded
|
|
||||||
@returns A stream of the user's actions containing the action of id
|
|
||||||
**/
|
|
||||||
loadUserAction: function(id) {
|
|
||||||
var self = this,
|
|
||||||
stream = this.get('stream');
|
|
||||||
return Discourse.ajax("/user_actions/" + id + ".json", { cache: 'false' }).then(function(result) {
|
|
||||||
if (result && result.user_action) {
|
if (result && result.user_action) {
|
||||||
var ua = result.user_action;
|
const ua = result.user_action;
|
||||||
|
|
||||||
if ((self.get('stream.filter') || ua.action_type) !== ua.action_type) return;
|
if ((this.get('stream.filter') || ua.action_type) !== ua.action_type) return;
|
||||||
if (!self.get('stream.filter') && !self.inAllStream(ua)) return;
|
if (!this.get('stream.filter') && !this.inAllStream(ua)) return;
|
||||||
|
|
||||||
var action = Discourse.UserAction.collapseStream([Discourse.UserAction.create(ua)]);
|
const action = Discourse.UserAction.collapseStream([Discourse.UserAction.create(ua)]);
|
||||||
stream.set('itemsLoaded', stream.get('itemsLoaded') + 1);
|
stream.set('itemsLoaded', stream.get('itemsLoaded') + 1);
|
||||||
stream.get('content').insertAt(0, action[0]);
|
stream.get('content').insertAt(0, action[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
inAllStream: function(ua) {
|
inAllStream(ua) {
|
||||||
return ua.action_type === Discourse.UserAction.TYPES.posts ||
|
return ua.action_type === Discourse.UserAction.TYPES.posts ||
|
||||||
ua.action_type === Discourse.UserAction.TYPES.topics;
|
ua.action_type === Discourse.UserAction.TYPES.topics;
|
||||||
},
|
},
|
||||||
|
|
||||||
// The user's stat count, excluding PMs.
|
// The user's stat count, excluding PMs.
|
||||||
statsCountNonPM: function() {
|
@computed("statsExcludingPms.@each.count")
|
||||||
var self = this;
|
statsCountNonPM() {
|
||||||
|
|
||||||
if (Ember.isEmpty(this.get('statsExcludingPms'))) return 0;
|
if (Ember.isEmpty(this.get('statsExcludingPms'))) return 0;
|
||||||
var count = 0;
|
let count = 0;
|
||||||
_.each(this.get('statsExcludingPms'), function(val) {
|
_.each(this.get('statsExcludingPms'), val => {
|
||||||
if (self.inAllStream(val)){
|
if (this.inAllStream(val)) {
|
||||||
count += val.count;
|
count += val.count;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return count;
|
return count;
|
||||||
}.property('statsExcludingPms.@each.count'),
|
},
|
||||||
|
|
||||||
// The user's stats, excluding PMs.
|
// The user's stats, excluding PMs.
|
||||||
statsExcludingPms: function() {
|
@computed("stats.@each.isPM")
|
||||||
|
statsExcludingPms() {
|
||||||
if (Ember.isEmpty(this.get('stats'))) return [];
|
if (Ember.isEmpty(this.get('stats'))) return [];
|
||||||
return this.get('stats').rejectProperty('isPM');
|
return this.get('stats').rejectProperty('isPM');
|
||||||
}.property('stats.@each.isPM'),
|
},
|
||||||
|
|
||||||
findDetails: function(options) {
|
findDetails(options) {
|
||||||
var user = this;
|
const user = this;
|
||||||
|
|
||||||
return PreloadStore.getAndRemove("user_" + user.get('username'), function() {
|
return PreloadStore.getAndRemove(`user_${user.get('username')}`, () => {
|
||||||
return Discourse.ajax("/users/" + user.get('username') + '.json', {data: options});
|
return Discourse.ajax(`/users/${user.get('username')}.json`, { data: options });
|
||||||
}).then(function (json) {
|
}).then(json => {
|
||||||
|
|
||||||
if (!Em.isEmpty(json.user.stats)) {
|
if (!Em.isEmpty(json.user.stats)) {
|
||||||
json.user.stats = Discourse.User.groupStats(_.map(json.user.stats,function(s) {
|
json.user.stats = Discourse.User.groupStats(_.map(json.user.stats, s => {
|
||||||
if (s.count) s.count = parseInt(s.count, 10);
|
if (s.count) s.count = parseInt(s.count, 10);
|
||||||
return Discourse.UserActionStat.create(s);
|
return Discourse.UserActionStat.create(s);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Em.isEmpty(json.user.custom_groups)) {
|
if (!Em.isEmpty(json.user.custom_groups)) {
|
||||||
json.user.custom_groups = json.user.custom_groups.map(function (g) {
|
json.user.custom_groups = json.user.custom_groups.map(g => Discourse.Group.create(g));
|
||||||
return Discourse.Group.create(g);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.user.invited_by) {
|
if (json.user.invited_by) {
|
||||||
|
@ -294,12 +235,10 @@ const User = RestModel.extend({
|
||||||
|
|
||||||
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
|
if (!Em.isEmpty(json.user.featured_user_badge_ids)) {
|
||||||
const userBadgesMap = {};
|
const userBadgesMap = {};
|
||||||
UserBadge.createFromJson(json).forEach(function(userBadge) {
|
UserBadge.createFromJson(json).forEach(userBadge => {
|
||||||
userBadgesMap[ userBadge.get('id') ] = userBadge;
|
userBadgesMap[ userBadge.get('id') ] = userBadge;
|
||||||
});
|
});
|
||||||
json.user.featured_user_badges = json.user.featured_user_badge_ids.map(function(id) {
|
json.user.featured_user_badges = json.user.featured_user_badge_ids.map(id => userBadgesMap[id]);
|
||||||
return userBadgesMap[id];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.user.card_badge) {
|
if (json.user.card_badge) {
|
||||||
|
@ -311,30 +250,26 @@ const User = RestModel.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
findStaffInfo: function() {
|
findStaffInfo() {
|
||||||
if (!Discourse.User.currentProp("staff")) { return Ember.RSVP.resolve(null); }
|
if (!Discourse.User.currentProp("staff")) { return Ember.RSVP.resolve(null); }
|
||||||
var self = this;
|
return Discourse.ajax(`/users/${this.get("username_lower")}/staff-info.json`).then(info => {
|
||||||
return Discourse.ajax("/users/" + this.get("username_lower") + "/staff-info.json").then(function(info) {
|
this.setProperties(info);
|
||||||
self.setProperties(info);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
avatarTemplate: function() {
|
@computed("username", "uploaded_avatar_id", "letter_avatar_color")
|
||||||
return avatarTemplate(this.get('username'), this.get('uploaded_avatar_id'));
|
avatarTemplate(username, uploadedAvatarId, letterAvatarColor) {
|
||||||
}.property('uploaded_avatar_id', 'username'),
|
return avatarTemplate(username, uploadedAvatarId, letterAvatarColor);
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Change avatar selection
|
Change avatar selection
|
||||||
*/
|
*/
|
||||||
pickAvatar: function(uploadId) {
|
pickAvatar(uploadId) {
|
||||||
var self = this;
|
return Discourse.ajax(`/users/${this.get("username_lower")}/preferences/avatar/pick`, {
|
||||||
|
|
||||||
return Discourse.ajax("/users/" + this.get("username_lower") + "/preferences/avatar/pick", {
|
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: { upload_id: uploadId }
|
data: { upload_id: uploadId }
|
||||||
}).then(function(){
|
}).then(() => this.set('uploaded_avatar_id', uploadId));
|
||||||
self.set('uploaded_avatar_id', uploadId);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,7 +279,7 @@ const User = RestModel.extend({
|
||||||
@param {String} type The type of the upload (image, attachment)
|
@param {String} type The type of the upload (image, attachment)
|
||||||
@returns true if the current user is allowed to upload a file
|
@returns true if the current user is allowed to upload a file
|
||||||
**/
|
**/
|
||||||
isAllowedToUploadAFile: function(type) {
|
isAllowedToUploadAFile(type) {
|
||||||
return this.get('staff') ||
|
return this.get('staff') ||
|
||||||
this.get('trust_level') > 0 ||
|
this.get('trust_level') > 0 ||
|
||||||
Discourse.SiteSettings['newuser_max_' + type + 's'] > 0;
|
Discourse.SiteSettings['newuser_max_' + type + 's'] > 0;
|
||||||
|
@ -357,35 +292,39 @@ const User = RestModel.extend({
|
||||||
@param {String} email The email address of the user to invite to the site
|
@param {String} email The email address of the user to invite to the site
|
||||||
@returns {Promise} the result of the server call
|
@returns {Promise} the result of the server call
|
||||||
**/
|
**/
|
||||||
createInvite: function(email, groupNames) {
|
createInvite(email, groupNames) {
|
||||||
return Discourse.ajax('/invites', {
|
return Discourse.ajax('/invites', {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {email: email, group_names: groupNames}
|
data: {email: email, group_names: groupNames}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
generateInviteLink: function(email, groupNames, topicId) {
|
generateInviteLink(email, groupNames, topicId) {
|
||||||
return Discourse.ajax('/invites/link', {
|
return Discourse.ajax('/invites/link', {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {email: email, group_names: groupNames, topic_id: topicId}
|
data: {email: email, group_names: groupNames, topic_id: topicId}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
updateMutedCategories: function() {
|
@observes("muted_category_ids")
|
||||||
|
updateMutedCategories() {
|
||||||
this.set("mutedCategories", Discourse.Category.findByIds(this.muted_category_ids));
|
this.set("mutedCategories", Discourse.Category.findByIds(this.muted_category_ids));
|
||||||
}.observes("muted_category_ids"),
|
},
|
||||||
|
|
||||||
updateTrackedCategories: function() {
|
@observes("tracked_category_ids")
|
||||||
|
updateTrackedCategories() {
|
||||||
this.set("trackedCategories", Discourse.Category.findByIds(this.tracked_category_ids));
|
this.set("trackedCategories", Discourse.Category.findByIds(this.tracked_category_ids));
|
||||||
}.observes("tracked_category_ids"),
|
},
|
||||||
|
|
||||||
updateWatchedCategories: function() {
|
@observes("watched_category_ids")
|
||||||
|
updateWatchedCategories() {
|
||||||
this.set("watchedCategories", Discourse.Category.findByIds(this.watched_category_ids));
|
this.set("watchedCategories", Discourse.Category.findByIds(this.watched_category_ids));
|
||||||
}.observes("watched_category_ids"),
|
},
|
||||||
|
|
||||||
canDeleteAccount: function() {
|
@computed("can_delete_account", "reply_count", "topic_count")
|
||||||
return !Discourse.SiteSettings.enable_sso && this.get('can_delete_account') && ((this.get('reply_count')||0) + (this.get('topic_count')||0)) <= 1;
|
canDeleteAccount(canDeleteAccount, replyCount, topicCount) {
|
||||||
}.property('can_delete_account', 'reply_count', 'topic_count'),
|
return !Discourse.SiteSettings.enable_sso && canDeleteAccount && ((replyCount || 0) + (topicCount || 0)) <= 1;
|
||||||
|
},
|
||||||
|
|
||||||
"delete": function() {
|
"delete": function() {
|
||||||
if (this.get('can_delete_account')) {
|
if (this.get('can_delete_account')) {
|
||||||
|
@ -398,27 +337,26 @@ const User = RestModel.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
dismissBanner: function (bannerKey) {
|
dismissBanner(bannerKey) {
|
||||||
this.set("dismissed_banner_key", bannerKey);
|
this.set("dismissed_banner_key", bannerKey);
|
||||||
Discourse.ajax("/users/" + this.get('username'), {
|
Discourse.ajax(`/users/${this.get('username')}`, {
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
data: { dismissed_banner_key: bannerKey }
|
data: { dismissed_banner_key: bannerKey }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
checkEmail: function () {
|
checkEmail() {
|
||||||
var self = this;
|
return Discourse.ajax(`/users/${this.get("username_lower")}/emails.json`, {
|
||||||
return Discourse.ajax("/users/" + this.get("username_lower") + "/emails.json", {
|
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data: { context: window.location.pathname }
|
data: { context: window.location.pathname }
|
||||||
}).then(function (result) {
|
}).then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
self.setProperties({
|
this.setProperties({
|
||||||
email: result.email,
|
email: result.email,
|
||||||
associated_accounts: result.associated_accounts
|
associated_accounts: result.associated_accounts
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, function () {});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -426,14 +364,14 @@ const User = RestModel.extend({
|
||||||
User.reopenClass(Singleton, {
|
User.reopenClass(Singleton, {
|
||||||
|
|
||||||
// Find a `Discourse.User` for a given username.
|
// Find a `Discourse.User` for a given username.
|
||||||
findByUsername: function(username, options) {
|
findByUsername(username, options) {
|
||||||
const user = User.create({username: username});
|
const user = User.create({username: username});
|
||||||
return user.findDetails(options);
|
return user.findDetails(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Use app.register and junk Singleton
|
// TODO: Use app.register and junk Singleton
|
||||||
createCurrent: function() {
|
createCurrent() {
|
||||||
var userJson = PreloadStore.get('currentUser');
|
const userJson = PreloadStore.get('currentUser');
|
||||||
if (userJson) {
|
if (userJson) {
|
||||||
const store = Discourse.__container__.lookup('store:main');
|
const store = Discourse.__container__.lookup('store:main');
|
||||||
return store.createRecord('user', userJson);
|
return store.createRecord('user', userJson);
|
||||||
|
@ -441,56 +379,38 @@ User.reopenClass(Singleton, {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
checkUsername(username, email, for_user_id) {
|
||||||
Checks if given username is valid for this email address
|
|
||||||
|
|
||||||
@method checkUsername
|
|
||||||
@param {String} username A username to check
|
|
||||||
@param {String} email An email address to check
|
|
||||||
@param {Number} forUserId user id - provide when changing username
|
|
||||||
**/
|
|
||||||
checkUsername: function(username, email, forUserId) {
|
|
||||||
return Discourse.ajax('/users/check_username', {
|
return Discourse.ajax('/users/check_username', {
|
||||||
data: { username: username, email: email, for_user_id: forUserId }
|
data: { username, email, for_user_id }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
groupStats(stats) {
|
||||||
Groups the user's statistics
|
const responses = Discourse.UserActionStat.create({
|
||||||
|
|
||||||
@method groupStats
|
|
||||||
@param {Array} stats Given stats
|
|
||||||
@returns {Object}
|
|
||||||
**/
|
|
||||||
groupStats: function(stats) {
|
|
||||||
var responses = Discourse.UserActionStat.create({
|
|
||||||
count: 0,
|
count: 0,
|
||||||
action_type: Discourse.UserAction.TYPES.replies
|
action_type: Discourse.UserAction.TYPES.replies
|
||||||
});
|
});
|
||||||
|
|
||||||
stats.filterProperty('isResponse').forEach(function (stat) {
|
stats.filterProperty('isResponse').forEach(stat => {
|
||||||
responses.set('count', responses.get('count') + stat.get('count'));
|
responses.set('count', responses.get('count') + stat.get('count'));
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = Em.A();
|
const result = Em.A();
|
||||||
result.pushObjects(stats.rejectProperty('isResponse'));
|
result.pushObjects(stats.rejectProperty('isResponse'));
|
||||||
|
|
||||||
var insertAt = 0;
|
let insertAt = 0;
|
||||||
result.forEach(function(item, index){
|
result.forEach((item, index) => {
|
||||||
if(item.action_type === Discourse.UserAction.TYPES.topics || item.action_type === Discourse.UserAction.TYPES.posts){
|
if (item.action_type === Discourse.UserAction.TYPES.topics || item.action_type === Discourse.UserAction.TYPES.posts) {
|
||||||
insertAt = index + 1;
|
insertAt = index + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(responses.count > 0) {
|
if (responses.count > 0) {
|
||||||
result.insertAt(insertAt, responses);
|
result.insertAt(insertAt, responses);
|
||||||
}
|
}
|
||||||
return(result);
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
createAccount(attrs) {
|
||||||
Creates a new account
|
|
||||||
**/
|
|
||||||
createAccount: function(attrs) {
|
|
||||||
return Discourse.ajax("/users", {
|
return Discourse.ajax("/users", {
|
||||||
data: {
|
data: {
|
||||||
name: attrs.accountName,
|
name: attrs.accountName,
|
||||||
|
|
|
@ -252,9 +252,12 @@ const ComposerView = Ember.View.extend(Ember.Evented, {
|
||||||
const quotedPost = posts.findProperty("post_number", postNumber);
|
const quotedPost = posts.findProperty("post_number", postNumber);
|
||||||
if (quotedPost) {
|
if (quotedPost) {
|
||||||
const username = quotedPost.get('username'),
|
const username = quotedPost.get('username'),
|
||||||
uploadId = quotedPost.get('uploaded_avatar_id');
|
uploadId = quotedPost.get('uploaded_avatar_id'),
|
||||||
|
letterAvatarColor = quotedPost.get("letter_avatar_color");
|
||||||
|
|
||||||
return Discourse.Utilities.tinyAvatar(avatarTemplate(username, uploadId));
|
debugger;
|
||||||
|
|
||||||
|
return Discourse.Utilities.tinyAvatar(avatarTemplate(username, uploadId, letterAvatarColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -518,7 +518,7 @@ class UsersController < ApplicationController
|
||||||
user_fields = [:username, :upload_avatar_template, :uploaded_avatar_id]
|
user_fields = [:username, :upload_avatar_template, :uploaded_avatar_id]
|
||||||
user_fields << :name if SiteSetting.enable_names?
|
user_fields << :name if SiteSetting.enable_names?
|
||||||
|
|
||||||
to_render = { users: results.as_json(only: user_fields, methods: :avatar_template) }
|
to_render = { users: results.as_json(only: user_fields, methods: [:avatar_template, :letter_avatar_color]) }
|
||||||
|
|
||||||
if params[:include_groups] == "true"
|
if params[:include_groups] == "true"
|
||||||
to_render[:groups] = Group.search_group(term, current_user).map {|m| {:name=>m.name, :usernames=> m.usernames.split(",")} }
|
to_render[:groups] = Group.search_group(term, current_user).map {|m| {:name=>m.name, :usernames=> m.usernames.split(",")} }
|
||||||
|
|
|
@ -457,11 +457,11 @@ class User < ActiveRecord::Base
|
||||||
avatar_template = split_avatars[hash.abs % split_avatars.size]
|
avatar_template = split_avatars[hash.abs % split_avatars.size]
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.png"
|
letter_avatar_template(username)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.avatar_template(username,uploaded_avatar_id)
|
def self.avatar_template(username, uploaded_avatar_id)
|
||||||
return default_template(username) if !uploaded_avatar_id
|
return default_template(username) if !uploaded_avatar_id
|
||||||
username ||= ""
|
username ||= ""
|
||||||
hostname = RailsMultisite::ConnectionManagement.current_hostname
|
hostname = RailsMultisite::ConnectionManagement.current_hostname
|
||||||
|
@ -469,11 +469,26 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.letter_avatar_template(username)
|
def self.letter_avatar_template(username)
|
||||||
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.png"
|
if SiteSetting.external_letter_avatars_enabled
|
||||||
|
color = letter_avatar_color(username)
|
||||||
|
"#{SiteSetting.external_letter_avatars_url}/letter/#{username[0]}?color=#{color}&size={size}"
|
||||||
|
else
|
||||||
|
"#{Discourse.base_uri}/letter_avatar/#{username.downcase}/{size}/#{LetterAvatar.version}.png"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def letter_avatar_color
|
||||||
|
self.class.letter_avatar_color(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.letter_avatar_color(username)
|
||||||
|
username = username || ""
|
||||||
|
color = LetterAvatar::COLORS[Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length]
|
||||||
|
color.map { |c| c.to_s(16) }.join
|
||||||
end
|
end
|
||||||
|
|
||||||
def avatar_template
|
def avatar_template
|
||||||
self.class.avatar_template(username,uploaded_avatar_id)
|
self.class.avatar_template(username, uploaded_avatar_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# The following count methods are somewhat slow - definitely don't use them in a loop.
|
# The following count methods are somewhat slow - definitely don't use them in a loop.
|
||||||
|
|
|
@ -5,6 +5,7 @@ class BasicPostSerializer < ApplicationSerializer
|
||||||
:username,
|
:username,
|
||||||
:avatar_template,
|
:avatar_template,
|
||||||
:uploaded_avatar_id,
|
:uploaded_avatar_id,
|
||||||
|
:letter_avatar_color,
|
||||||
:created_at,
|
:created_at,
|
||||||
:cooked,
|
:cooked,
|
||||||
:cooked_hidden
|
:cooked_hidden
|
||||||
|
@ -25,9 +26,14 @@ class BasicPostSerializer < ApplicationSerializer
|
||||||
object.user.try(:uploaded_avatar_id)
|
object.user.try(:uploaded_avatar_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def letter_avatar_color
|
||||||
|
object.user.try(:letter_avatar_color)
|
||||||
|
end
|
||||||
|
|
||||||
def cooked_hidden
|
def cooked_hidden
|
||||||
object.hidden && !scope.is_staff?
|
object.hidden && !scope.is_staff?
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_cooked_hidden?
|
def include_cooked_hidden?
|
||||||
cooked_hidden
|
cooked_hidden
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class BasicUserSerializer < ApplicationSerializer
|
class BasicUserSerializer < ApplicationSerializer
|
||||||
attributes :id, :username, :uploaded_avatar_id, :avatar_template
|
attributes :id, :username, :uploaded_avatar_id, :avatar_template, :letter_avatar_color
|
||||||
|
|
||||||
def include_name?
|
def include_name?
|
||||||
SiteSetting.enable_names?
|
SiteSetting.enable_names?
|
||||||
|
@ -17,4 +17,12 @@ class BasicUserSerializer < ApplicationSerializer
|
||||||
object[:user] || object
|
object[:user] || object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def letter_avatar_color
|
||||||
|
if Hash === object
|
||||||
|
User.letter_avatar_color(user[:username])
|
||||||
|
else
|
||||||
|
object.letter_avatar_color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -178,7 +178,8 @@ class PostSerializer < BasicPostSerializer
|
||||||
{
|
{
|
||||||
username: object.reply_to_user.username,
|
username: object.reply_to_user.username,
|
||||||
avatar_template: object.reply_to_user.avatar_template,
|
avatar_template: object.reply_to_user.avatar_template,
|
||||||
uploaded_avatar_id: object.reply_to_user.uploaded_avatar_id
|
uploaded_avatar_id: object.reply_to_user.uploaded_avatar_id,
|
||||||
|
letter_avatar_color: object.reply_to_user.letter_avatar_color,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,11 @@ class UserActionSerializer < ApplicationSerializer
|
||||||
:edit_reason,
|
:edit_reason,
|
||||||
:category_id,
|
:category_id,
|
||||||
:uploaded_avatar_id,
|
:uploaded_avatar_id,
|
||||||
|
:letter_avatar_color,
|
||||||
:closed,
|
:closed,
|
||||||
:archived,
|
:archived,
|
||||||
:acting_uploaded_avatar_id
|
:acting_uploaded_avatar_id,
|
||||||
|
:acting_letter_avatar_color
|
||||||
|
|
||||||
def excerpt
|
def excerpt
|
||||||
cooked = object.cooked || PrettyText.cook(object.raw)
|
cooked = object.cooked || PrettyText.cook(object.raw)
|
||||||
|
@ -84,4 +86,12 @@ class UserActionSerializer < ApplicationSerializer
|
||||||
object.topic_archived
|
object.topic_archived
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def letter_avatar_color
|
||||||
|
User.letter_avatar_color(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
def acting_letter_avatar_color
|
||||||
|
User.letter_avatar_color(acting_username)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +1,3 @@
|
||||||
class UserNameSerializer < ApplicationSerializer
|
class UserNameSerializer < BasicUserSerializer
|
||||||
attributes :id, :username, :name, :title, :uploaded_avatar_id, :avatar_template
|
attributes :name, :title
|
||||||
|
|
||||||
def include_name?
|
|
||||||
SiteSetting.enable_names?
|
|
||||||
end
|
|
||||||
|
|
||||||
def avatar_template
|
|
||||||
if Hash === object
|
|
||||||
User.avatar_template(user[:username], user[:uploaded_avatar_id])
|
|
||||||
else
|
|
||||||
object.avatar_template
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def user
|
|
||||||
object[:user] || object
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -323,4 +323,5 @@ class UserSerializer < BasicUserSerializer
|
||||||
def pending_count
|
def pending_count
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -979,6 +979,9 @@ en:
|
||||||
|
|
||||||
avatar_sizes: "List of automatically generated avatar sizes."
|
avatar_sizes: "List of automatically generated avatar sizes."
|
||||||
|
|
||||||
|
external_letter_avatars_enabled: "Use external letter avatars service."
|
||||||
|
external_letter_avatars_url: "URL of the external letter avatars service."
|
||||||
|
|
||||||
enable_flash_video_onebox: "Enable embedding of swf and flv (Adobe Flash) links in oneboxes. WARNING: may introduce security risks."
|
enable_flash_video_onebox: "Enable embedding of swf and flv (Adobe Flash) links in oneboxes. WARNING: may introduce security risks."
|
||||||
|
|
||||||
default_invitee_trust_level: "Default trust level (0-4) for invited users."
|
default_invitee_trust_level: "Default trust level (0-4) for invited users."
|
||||||
|
|
|
@ -572,6 +572,13 @@ files:
|
||||||
avatar_sizes:
|
avatar_sizes:
|
||||||
default: '20|25|32|45|60|120'
|
default: '20|25|32|45|60|120'
|
||||||
type: list
|
type: list
|
||||||
|
external_letter_avatars_enabled:
|
||||||
|
default: false
|
||||||
|
client: true
|
||||||
|
external_letter_avatars_url:
|
||||||
|
default: "https://avatars.discourse.org"
|
||||||
|
client: true
|
||||||
|
regex: '^https?:\/\/.+[^\/]$'
|
||||||
|
|
||||||
trust:
|
trust:
|
||||||
default_trust_level:
|
default_trust_level:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue