FEATURE: add flair to avatars using new settings in the groups admin UI

This commit is contained in:
Neil Lalonde 2016-08-16 12:34:04 -04:00
parent 72d7c055f4
commit d079f69b7b
18 changed files with 195 additions and 9 deletions

View File

@ -1,5 +1,6 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { propertyEqual } from 'discourse/lib/computed';
import { escapeExpression } from 'discourse/lib/utilities';
export default Ember.Controller.extend({
needs: ['adminGroupsType'],
@ -35,6 +36,23 @@ export default Ember.Controller.extend({
];
}.property(),
flairPreviewStyle: function() {
var style = '';
if (this.get('model.flair_url')) {
style += 'background-image: url(' + escapeExpression(this.get('model.flair_url')) + '); ';
}
if (this.get('model.flairBackgroundHexColor')) {
style += 'background-color: #' + this.get('model.flairBackgroundHexColor') + ';';
}
return style;
}.property('model.flair_url', 'model.flairBackgroundHexColor'),
flairPreviewClasses: function() {
if (this.get('model.flairBackgroundHexColor')) {
return 'rounded';
}
}.property('model.flairBackgroundHexColor'),
actions: {
next() {
if (this.get("showingLast")) { return; }

View File

@ -101,6 +101,38 @@
{{/if}}
{{/unless}}
{{#unless model.automatic}}
<div class="flair_inputs">
<div class="flair_left">
<div>
<label for="flair_url">{{i18n 'admin.groups.flair_url'}}</label>
{{text-field name="flair_url" value=model.flair_url placeholderKey="admin.groups.flair_url_placeholder"}}
</div>
<div>
<label for="flair_bg_color">{{i18n 'admin.groups.flair_bg_color'}}</label>
{{text-field name="flair_bg_color" class="flair_bg_color" value=model.flair_bg_color placeholderKey="admin.groups.flair_bg_color_placeholder"}}
</div>
</div>
{{#if flairPreviewStyle}}
<div class="flair_right">
<div>
<label>{{i18n 'admin.groups.flair_preview'}}</label>
<div class="avatar-flair-preview">
<div class="avatar-wrapper">
<img alt width="45" height="45" src="https://avatars.discourse.org/v3/letter/a/a3d4f5/45.png" class="avatar actor" title="demo">
</div>
<div class="avatar-flair demo {{flairPreviewClasses}}" style={{flairPreviewStyle}}></div>
</div>
</div>
</div>
{{/if}}
<div class="clearfix"></div>
</div>
{{/unless}}
<div class='buttons'>
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button>
{{#unless model.automatic}}

View File

@ -28,7 +28,9 @@ export function transformBasicPost(post) {
isDeleted: post.deleted_at || post.user_deleted,
deletedByAvatarTemplate: null,
deletedByUsername: null,
primary_group_name: post.primary_group_name,
primaryGroupName: post.primary_group_name,
primaryGroupFlairUrl: post.primary_group_flair_url,
primaryGroupFlairBgColor: post.primary_group_flair_bg_color,
wiki: post.wiki,
firstPost: post.post_number === 1,
post_number: post.post_number,

View File

@ -90,6 +90,11 @@ const Group = Discourse.Model.extend({
});
},
@computed('flair_bg_color')
flairBackgroundHexColor() {
return this.get('flair_bg_color') ? this.get('flair_bg_color').replace(new RegExp("[^0-9a-fA-F]", "g"), "") : null;
},
asJSON() {
return {
name: this.get('name'),
@ -101,6 +106,8 @@ const Group = Discourse.Model.extend({
primary_group: !!this.get('primary_group'),
grant_trust_level: this.get('grant_trust_level'),
incoming_email: this.get("incoming_email"),
flair_url: this.get('flair_url'),
flair_bg_color: this.get('flairBackgroundHexColor'),
};
},

View File

@ -77,6 +77,29 @@ createWidget('reply-to-tab', {
}
});
createWidget('post-avatar-flair', {
tagName: 'div.avatar-flair',
title(attrs) {
return attrs.primaryGroupName;
},
buildClasses(attrs) {
return 'avatar-flair-' + attrs.primaryGroupName + (attrs.primaryGroupFlairBgColor ? ' rounded' : '');
},
buildAttributes(attrs) {
var style = '';
if (attrs.primaryGroupFlairUrl) {
style += 'background-image: url(' + attrs.primaryGroupFlairUrl + '); ';
}
if (attrs.primaryGroupFlairBgColor) {
style += 'background-color: #' + attrs.primaryGroupFlairBgColor + '; ';
}
return {style: style};
}
});
createWidget('post-avatar', {
tagName: 'div.topic-avatar',
@ -93,11 +116,21 @@ createWidget('post-avatar', {
template: attrs.avatar_template,
username: attrs.username,
url: attrs.usernameUrl,
className: 'main-avatar'
className: 'main-avatar',
flairUrl: attrs.primaryGroupFlairUrl,
flairBgColor: attrs.primaryGroupFlairBgColor
});
}
return [body, h('div.poster-avatar-extra')];
const result = [body];
if (attrs.primaryGroupFlairUrl || attrs.primaryGroupFlairBgColor) {
result.push(this.attach('post-avatar-flair', attrs));
}
result.push(h('div.poster-avatar-extra'));
return result;
}
});
@ -406,7 +439,7 @@ export default createWidget('post', {
if (attrs.topicOwner) { classNames.push('topic-owner'); }
if (attrs.hidden) { classNames.push('post-hidden'); }
if (attrs.deleted) { classNames.push('deleted'); }
if (attrs.primary_group_name) { classNames.push(`group-${attrs.primary_group_name}`); }
if (attrs.primaryGroupName) { classNames.push(`group-${attrs.primaryGroupName}`); }
if (attrs.wiki) { classNames.push(`wiki`); }
if (attrs.isWhisper) { classNames.push('whisper'); }
if (attrs.isModeratorAction || (attrs.isWarning && attrs.firstPost)) {

View File

@ -35,7 +35,7 @@ export default createWidget('poster-name', {
if (attrs.moderator) { classNames.push('moderator'); }
if (attrs.new_user) { classNames.push('new-user'); }
const primaryGroupName = attrs.primary_group_name;
const primaryGroupName = attrs.primaryGroupName;
if (primaryGroupName && primaryGroupName.length) {
classNames.push(primaryGroupName);
}

View File

@ -684,6 +684,39 @@ section.details {
width: 100%;
border-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%));
}
.avatar-flair-preview {
position: relative;
width: 45px;
.avatar-flair.demo {
top: 25px;
&.rounded {
top: 23px;
}
}
}
.form-horizontal {
.flair_inputs {
margin-top: 30px;
margin-bottom: 30px;
.flair_left {
float: left;
width: 60%;
input[name=flair_url] {
width: 90%;
}
}
.flair_right {
float: left;
margin-left: 30px;
}
}
}
}
.row.groups input[type='text'].flair_bg_color {
width: 200px;
}
// Customise area

View File

@ -153,6 +153,25 @@ aside.quote {
}
}
.topic-avatar .avatar-flair, .avatar-flair-preview .avatar-flair {
display: block;
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: center;
width: 20px;
height: 20px;
position: absolute;
top: 40px;
right: -6px;
&.rounded {
background-size: 18px 18px;
border-radius: 12px;
width: 24px;
height: 24px;
top: 38px;
right: -8px;
}
}
.topic-avatar .poster-avatar-extra {
display: none;
}

View File

@ -637,7 +637,7 @@ $topic-avatar-width: 45px;
width: $topic-avatar-width;
float: left;
position: relative;
z-index: 2;
z-index: 3;
}
.gap {

View File

@ -65,6 +65,9 @@ class Admin::GroupsController < Admin::AdminController
title = params[:title] if params[:title].present?
group.title = group.automatic ? nil : title
group.flair_url = params[:flair_url].presence
group.flair_bg_color = params[:flair_bg_color].presence
if group.save
Group.reset_counters(group.id, :group_users)
render_serialized(group, BasicGroupSerializer)

View File

@ -28,6 +28,7 @@ class Group < ActiveRecord::Base
validates_uniqueness_of :name, case_sensitive: false
validate :automatic_membership_email_domains_format_validator
validate :incoming_email_validator
validates :flair_url, url: true
AUTO_GROUPS = {
:everyone => 0,

View File

@ -14,7 +14,9 @@ class BasicGroupSerializer < ApplicationSerializer
:notification_level,
:has_messages,
:is_member,
:mentionable
:mentionable,
:flair_url,
:flair_bg_color
def include_incoming_email?
scope.is_staff?

View File

@ -34,6 +34,8 @@ class PostSerializer < BasicPostSerializer
:category_id,
:display_username,
:primary_group_name,
:primary_group_flair_url,
:primary_group_flair_bg_color,
:version,
:can_edit,
:can_delete,
@ -150,6 +152,16 @@ class PostSerializer < BasicPostSerializer
end
end
def primary_group_flair_url
return nil unless object.user && object.user.primary_group_id
object.user.primary_group.try(:flair_url)
end
def primary_group_flair_bg_color
return nil unless object.user && object.user.primary_group_id
object.user.primary_group.try(:flair_bg_color)
end
def link_counts
return @single_post_link_counts if @single_post_link_counts.present?

View File

@ -2402,6 +2402,11 @@ en:
add_owners: Add owners
incoming_email: "Custom incoming email address"
incoming_email_placeholder: "enter email address"
flair_url: "Avatar Flair URL"
flair_url_placeholder: "(Optional) Image URL"
flair_bg_color: "Avatar Flair Background Color"
flair_bg_color_placeholder: "(Optional) Hex color value"
flair_preview: "Preview"
api:

View File

@ -0,0 +1,6 @@
class AddFlairUrlToGroups < ActiveRecord::Migration
def change
add_column :groups, :flair_url, :string
add_column :groups, :flair_bg_color, :string
end
end

View File

@ -0,0 +1,11 @@
class UrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.present?
uri = URI.parse(value) rescue nil
unless uri
record.errors[attribute] << (options[:message] || I18n.t('errors.messages.invalid'))
end
end
end
end

View File

@ -35,7 +35,9 @@ describe Admin::GroupsController do
"notification_level"=>2,
"has_messages"=>false,
"is_member"=>true,
"mentionable"=>false
"mentionable"=>false,
"flair_url"=>nil,
"flair_bg_color"=>nil
}])
end

View File

@ -32,7 +32,7 @@ widgetTest('extra classes and glyphs', {
admin: true,
moderator: true,
new_user: true,
primary_group_name: 'fish'
primaryGroupName: 'fish'
});
},
test(assert) {