FEATURE: Allow group owners to edit group name and avatar flair.
This commit is contained in:
parent
b45fd21ed9
commit
31acd311e5
|
@ -1,7 +1,5 @@
|
||||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
import { propertyEqual } from 'discourse/lib/computed';
|
import { propertyEqual } from 'discourse/lib/computed';
|
||||||
import { escapeExpression } from 'discourse/lib/utilities';
|
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
adminGroupsType: Ember.inject.controller(),
|
adminGroupsType: Ember.inject.controller(),
|
||||||
|
@ -37,43 +35,6 @@ export default Ember.Controller.extend({
|
||||||
];
|
];
|
||||||
}.property(),
|
}.property(),
|
||||||
|
|
||||||
@computed
|
|
||||||
demoAvatarUrl() {
|
|
||||||
return Discourse.getURL('/images/avatar.png');
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed('model.flair_url')
|
|
||||||
flairPreviewIcon() {
|
|
||||||
return this.get('model.flair_url') && this.get('model.flair_url').substr(0,3) === 'fa-';
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed('flairPreviewIcon')
|
|
||||||
flairPreviewImage() {
|
|
||||||
return this.get('model.flair_url') && !this.get('flairPreviewIcon');
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed('flairPreviewImage', 'model.flair_url', 'model.flairBackgroundHexColor', 'model.flairHexColor')
|
|
||||||
flairPreviewStyle() {
|
|
||||||
var style = '';
|
|
||||||
if (this.get('flairPreviewImage')) {
|
|
||||||
style += 'background-image: url(' + escapeExpression(this.get('model.flair_url')) + '); ';
|
|
||||||
}
|
|
||||||
if (this.get('model.flairBackgroundHexColor')) {
|
|
||||||
style += 'background-color: #' + this.get('model.flairBackgroundHexColor') + ';';
|
|
||||||
}
|
|
||||||
if (this.get('model.flairHexColor')) {
|
|
||||||
style += 'color: #' + this.get('model.flairHexColor') + ';';
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed('model.flairBackgroundHexColor')
|
|
||||||
flairPreviewClasses() {
|
|
||||||
if (this.get('model.flairBackgroundHexColor')) {
|
|
||||||
return 'rounded';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
next() {
|
next() {
|
||||||
if (this.get("showingLast")) { return; }
|
if (this.get("showingLast")) { return; }
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
{{#if model.automatic}}
|
{{#if model.automatic}}
|
||||||
<h3>{{model.name}}</h3>
|
<h3>{{model.name}}</h3>
|
||||||
{{else}}
|
{{else}}
|
||||||
<label for="name">{{i18n 'admin.groups.name'}}</label>
|
<label for="name">{{i18n 'group.name'}}</label>
|
||||||
{{text-field name="name" value=model.name placeholderKey="admin.groups.name_placeholder"}}
|
{{text-field name="name" value=model.name placeholderKey="group.name_placeholder"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -101,59 +101,7 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#unless model.automatic}}
|
{{#unless model.automatic}}
|
||||||
<div class="flair-inputs">
|
{{group-flair-inputs model=model}}
|
||||||
<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>
|
|
||||||
|
|
||||||
{{#if flairPreviewIcon}}
|
|
||||||
<div>
|
|
||||||
<label for="flair_color">{{i18n 'admin.groups.flair_color'}}</label>
|
|
||||||
{{text-field name="flair_color" class="flair-color" value=model.flair_color placeholderKey="admin.groups.flair_color_placeholder"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<div>
|
|
||||||
<strong>{{i18n 'admin.groups.flair_note'}}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if flairPreviewIcon}}
|
|
||||||
<div class="flair-right">
|
|
||||||
<label>{{i18n 'admin.groups.flair_preview'}} Icon</label>
|
|
||||||
<div class="avatar-flair-preview">
|
|
||||||
<div class="avatar-wrapper">
|
|
||||||
<img alt width="45" height="45" src="{{demoAvatarUrl}}" class="avatar actor">
|
|
||||||
</div>
|
|
||||||
<div class="avatar-flair demo {{flairPreviewClasses}}" style={{flairPreviewStyle}}>
|
|
||||||
<i class="fa {{model.flair_url}}"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if flairPreviewImage}}
|
|
||||||
<div class="flair-right">
|
|
||||||
<label>{{i18n 'admin.groups.flair_preview'}} Image</label>
|
|
||||||
<div class="avatar-flair-preview">
|
|
||||||
<div class="avatar-wrapper">
|
|
||||||
<img alt width="45" height="45" src="{{demoAvatarUrl}}" class="avatar actor">
|
|
||||||
</div>
|
|
||||||
<div class="avatar-flair demo {{flairPreviewClasses}}" style={{flairPreviewStyle}}></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
<div class='buttons'>
|
<div class='buttons'>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import MountWidget from 'discourse/components/mount-widget';
|
||||||
|
|
||||||
|
export default MountWidget.extend({
|
||||||
|
widget: 'avatar-flair',
|
||||||
|
|
||||||
|
@observes('flairURL', 'flairBgColor', 'flairColor')
|
||||||
|
_rerender() {
|
||||||
|
this.queueRerender();
|
||||||
|
},
|
||||||
|
|
||||||
|
buildArgs() {
|
||||||
|
return {
|
||||||
|
primary_group_flair_url: this.get('flairURL'),
|
||||||
|
primary_group_flair_bg_color: this.get('flairBgColor'),
|
||||||
|
primary_group_flair_color: this.get('flairColor'),
|
||||||
|
primary_group_name: this.get('groupName')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { escapeExpression } from 'discourse/lib/utilities';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
|
||||||
|
classNames: ['group-flair-inputs'],
|
||||||
|
|
||||||
|
@computed
|
||||||
|
demoAvatarUrl() {
|
||||||
|
return Discourse.getURL('/images/avatar.png');
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('model.flair_url')
|
||||||
|
flairPreviewIcon(flairURL) {
|
||||||
|
return flairURL && flairURL.substr(0,3) === 'fa-';
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('model.flair_url', 'flairPreviewIcon')
|
||||||
|
flairPreviewImage(flairURL, flairPreviewIcon) {
|
||||||
|
return flairURL && !flairPreviewIcon;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('model.flair_url', 'flairPreviewImage', 'model.flairBackgroundHexColor', 'model.flairHexColor')
|
||||||
|
flairPreviewStyle(flairURL, flairPreviewImage, flairBackgroundHexColor, flairHexColor) {
|
||||||
|
let style = '';
|
||||||
|
|
||||||
|
if (flairPreviewImage) {
|
||||||
|
style += `background-image: url(${escapeExpression(flairURL)});`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flairBackgroundHexColor) {
|
||||||
|
style += `background-color: #${flairBackgroundHexColor};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flairHexColor) style += `color: #${flairHexColor};`;
|
||||||
|
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('model.flairBackgroundHexColor')
|
||||||
|
flairPreviewClasses(flairBackgroundHexColor) {
|
||||||
|
if (flairBackgroundHexColor) return 'rounded';
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('flairPreviewImage')
|
||||||
|
flairPreviewLabel(flairPreviewImage) {
|
||||||
|
const key = flairPreviewImage ? 'image' : 'icon';
|
||||||
|
return I18n.t(`group.flair_preview_${key}`);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
saving: false,
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
save() {
|
||||||
|
this.set('saving', true);
|
||||||
|
|
||||||
|
this.get('model').save().then(() => {
|
||||||
|
this.transitionToRoute('group', this.get('model.name'));
|
||||||
|
this.send('closeModal');
|
||||||
|
}).catch(error => {
|
||||||
|
popupAjaxError(error);
|
||||||
|
}).finally(() => {
|
||||||
|
this.set('saving', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,22 +1,11 @@
|
||||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
|
||||||
import Group from 'discourse/models/group';
|
import Group from 'discourse/models/group';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
loading: false,
|
loading: false,
|
||||||
limit: null,
|
limit: null,
|
||||||
offset: null,
|
offset: null,
|
||||||
|
isOwner: Ember.computed.alias('model.is_group_owner'),
|
||||||
@computed('model.owners.[]')
|
|
||||||
isOwner(owners) {
|
|
||||||
if (this.get('currentUser.admin')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const currentUserId = this.get('currentUser.id');
|
|
||||||
if (currentUserId) {
|
|
||||||
return !!owners.findBy('id', currentUserId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
removeMember(user) {
|
removeMember(user) {
|
||||||
|
|
|
@ -23,6 +23,11 @@ export default Ember.Controller.extend({
|
||||||
Tab.create({ name: 'messages', requiresMembership: true })
|
Tab.create({ name: 'messages', requiresMembership: true })
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@computed('model.is_group_owner', 'model.automatic')
|
||||||
|
canEditGroup(isGroupOwner, automatic) {
|
||||||
|
return !automatic && isGroupOwner;
|
||||||
|
},
|
||||||
|
|
||||||
@computed('model.name')
|
@computed('model.name')
|
||||||
groupName(name) {
|
groupName(name) {
|
||||||
return name.capitalize();
|
return name.capitalize();
|
||||||
|
|
|
@ -119,13 +119,19 @@ const Group = Discourse.Model.extend({
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
var self = this;
|
var self = this;
|
||||||
return ajax("/admin/groups", { type: "POST", data: this.asJSON() }).then(function(resp) {
|
return ajax("/admin/groups", { type: "POST", data: { group: this.asJSON() } }).then(function(resp) {
|
||||||
self.set('id', resp.basic_group.id);
|
self.set('id', resp.basic_group.id);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
return ajax("/admin/groups/" + this.get('id'), { type: "PUT", data: this.asJSON() });
|
const id = this.get('id');
|
||||||
|
const url = this.get('is_group_owner') ? `/groups/${id}` : `/admin/groups/${id}`;
|
||||||
|
|
||||||
|
return ajax(url, {
|
||||||
|
type: "PUT",
|
||||||
|
data: { group: this.asJSON() }
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Group from 'discourse/models/group';
|
import Group from 'discourse/models/group';
|
||||||
|
import showModal from 'discourse/lib/show-modal';
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
|
|
||||||
|
@ -16,5 +17,12 @@ export default Discourse.Route.extend({
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
controller.setProperties({ model, counts: this.get('counts') });
|
controller.setProperties({ model, counts: this.get('counts') });
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
showGroupEditor() {
|
||||||
|
showModal('edit-group');
|
||||||
|
this.controllerFor('edit-group').set('model', this.modelFor('group'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<div class="group-flair-left">
|
||||||
|
<div>
|
||||||
|
<label for="flair_url">{{i18n 'group.flair_url'}}</label>
|
||||||
|
{{text-field name="flair_url"
|
||||||
|
value=model.flair_url
|
||||||
|
placeholderKey="group.flair_url_placeholder"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="flair_bg_color">{{i18n 'group.flair_bg_color'}}</label>
|
||||||
|
{{text-field name="flair_bg_color"
|
||||||
|
class="group-flair-bg-color"
|
||||||
|
value=model.flair_bg_color
|
||||||
|
placeholderKey="group.flair_bg_color_placeholder"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if flairPreviewIcon}}
|
||||||
|
<div>
|
||||||
|
<label for="flair_color">{{i18n 'group.flair_color'}}</label>
|
||||||
|
{{text-field name="flair_color"
|
||||||
|
class="group-flair-color"
|
||||||
|
value=model.flair_color
|
||||||
|
placeholderKey="group.flair_color_placeholder"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<strong>{{i18n 'group.flair_note'}}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="group-flair-right">
|
||||||
|
<label>{{flairPreviewLabel}}</label>
|
||||||
|
<div class="avatar-flair-preview">
|
||||||
|
<div class="avatar-wrapper">
|
||||||
|
<img width="45" height="45" src="{{demoAvatarUrl}}" class="avatar actor">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if flairPreviewImage}}
|
||||||
|
<div class="avatar-flair demo {{flairPreviewClasses}}" style={{flairPreviewStyle}}></div>
|
||||||
|
{{else}}
|
||||||
|
<div class="avatar-flair demo {{flairPreviewClasses}}" style={{flairPreviewStyle}}>
|
||||||
|
<i class="fa {{model.flair_url}}"></i>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -4,7 +4,11 @@
|
||||||
<div class="user-card-avatar">
|
<div class="user-card-avatar">
|
||||||
<a href={{user.path}} {{action "showUser"}} class="card-huge-avatar">{{bound-avatar avatar "huge"}}</a>
|
<a href={{user.path}} {{action "showUser"}} class="card-huge-avatar">{{bound-avatar avatar "huge"}}</a>
|
||||||
{{#if user.primary_group_name}}
|
{{#if user.primary_group_name}}
|
||||||
{{mount-widget widget="avatar-flair" args=user}}
|
{{avatar-flair
|
||||||
|
flairURL=user.primary_group_flair_url
|
||||||
|
flairBgColor=user.primary_group_flair_bg_color
|
||||||
|
flairColor=user.primary_group_flair_color
|
||||||
|
groupName=user.primary_group_name}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,27 @@
|
||||||
<section class='user-main'>
|
<section class='user-main'>
|
||||||
<section class='user-right groups'>
|
<section class='user-right groups'>
|
||||||
<section class='about group'>
|
<section class='about group'>
|
||||||
<div class='details'>
|
<div class='details group-details'>
|
||||||
<h1 class='group-header'>
|
<span>
|
||||||
{{#if model.flair_url}}
|
<h1 class='group-header'>
|
||||||
<span class='group-avatar-flair'>
|
{{#if model.flair_url}}
|
||||||
{{mount-widget widget="avatar-flair" args=avatarFlairAttributes}}
|
<span class='group-avatar-flair'>
|
||||||
</span>
|
{{avatar-flair
|
||||||
{{/if}}
|
flairURL=model.flair_url
|
||||||
<span class='group-name'>{{groupName}}</span>
|
flairBgColor=model.flair_bg_color
|
||||||
</h1>
|
flairColor=model.flair_color
|
||||||
|
groupName=model.name}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
<span class='group-name'>{{groupName}}</span>
|
||||||
|
</h1>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{#if canEditGroup}}
|
||||||
|
<span>
|
||||||
|
{{d-button action="showGroupEditor" class="group-edit-btn btn-small" icon="pencil"}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{#d-modal-body title="group.edit.title" class="edit-group groups"}}
|
||||||
|
<form class="form-horizontal">
|
||||||
|
{{group-flair-inputs model=model}}
|
||||||
|
</form>
|
||||||
|
{{/d-modal-body}}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
{{d-button action="save" class="btn-primary" disabled=saving label="save"}}
|
||||||
|
<a {{action "closeModal"}}>{{i18n 'cancel'}}</a>
|
||||||
|
</div>
|
|
@ -696,38 +696,6 @@ section.details {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%));
|
border-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%));
|
||||||
}
|
}
|
||||||
.avatar-flair-preview {
|
|
||||||
position: relative;
|
|
||||||
width: 45px;
|
|
||||||
|
|
||||||
.avatar-wrapper {
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.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, input[type='text'].flair-color {
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Customise area
|
// Customise area
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
.groups {
|
.groups {
|
||||||
.group-header {
|
.group-header, .group-details {
|
||||||
display: table;
|
display: table;
|
||||||
}
|
|
||||||
|
|
||||||
.group-avatar-flair {
|
span {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-flair {
|
.avatar-flair {
|
||||||
$size: 40px;
|
$size: 40px;
|
||||||
|
|
||||||
|
@ -17,8 +20,43 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-avatar-flair, .group-name {
|
.group-edit-btn {
|
||||||
display: table-cell;
|
margin-left: 5px;
|
||||||
vertical-align: middle;
|
}
|
||||||
|
|
||||||
|
.form-horizontal {
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 80% !important;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-flair-inputs {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.group-flair-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-flair-right {
|
||||||
|
float: left;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-flair-preview {
|
||||||
|
position: relative;
|
||||||
|
width: 45px;
|
||||||
|
|
||||||
|
.avatar-wrapper {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Admin::GroupsController < Admin::AdminController
|
||||||
def create
|
def create
|
||||||
group = Group.new
|
group = Group.new
|
||||||
|
|
||||||
group.name = (params[:name] || '').strip
|
group.name = (group_params[:name] || '').strip
|
||||||
save_group(group)
|
save_group(group)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,29 +44,29 @@ class Admin::GroupsController < Admin::AdminController
|
||||||
group = Group.find(params[:id])
|
group = Group.find(params[:id])
|
||||||
|
|
||||||
# group rename is ignored for automatic groups
|
# group rename is ignored for automatic groups
|
||||||
group.name = params[:name] if params[:name] && !group.automatic
|
group.name = group_params[:name] if group_params[:name] && !group.automatic
|
||||||
save_group(group)
|
save_group(group)
|
||||||
end
|
end
|
||||||
|
|
||||||
def save_group(group)
|
def save_group(group)
|
||||||
group.alias_level = params[:alias_level].to_i if params[:alias_level].present?
|
group.alias_level = group_params[:alias_level].to_i if group_params[:alias_level].present?
|
||||||
group.visible = params[:visible] == "true"
|
group.visible = group_params[:visible] == "true"
|
||||||
grant_trust_level = params[:grant_trust_level].to_i
|
grant_trust_level = group_params[:grant_trust_level].to_i
|
||||||
group.grant_trust_level = (grant_trust_level > 0 && grant_trust_level <= 4) ? grant_trust_level : nil
|
group.grant_trust_level = (grant_trust_level > 0 && grant_trust_level <= 4) ? grant_trust_level : nil
|
||||||
|
|
||||||
group.automatic_membership_email_domains = params[:automatic_membership_email_domains] unless group.automatic
|
group.automatic_membership_email_domains = group_params[:automatic_membership_email_domains] unless group.automatic
|
||||||
group.automatic_membership_retroactive = params[:automatic_membership_retroactive] == "true" unless group.automatic
|
group.automatic_membership_retroactive = group_params[:automatic_membership_retroactive] == "true" unless group.automatic
|
||||||
|
|
||||||
group.primary_group = group.automatic ? false : params["primary_group"] == "true"
|
group.primary_group = group.automatic ? false : group_params["primary_group"] == "true"
|
||||||
|
|
||||||
group.incoming_email = group.automatic ? nil : params[:incoming_email]
|
group.incoming_email = group.automatic ? nil : group_params[:incoming_email]
|
||||||
|
|
||||||
title = params[:title] if params[:title].present?
|
title = group_params[:title] if group_params[:title].present?
|
||||||
group.title = group.automatic ? nil : title
|
group.title = group.automatic ? nil : title
|
||||||
|
|
||||||
group.flair_url = params[:flair_url].presence
|
group.flair_url = group_params[:flair_url].presence
|
||||||
group.flair_bg_color = params[:flair_bg_color].presence
|
group.flair_bg_color = group_params[:flair_bg_color].presence
|
||||||
group.flair_color = params[:flair_color].presence
|
group.flair_color = group_params[:flair_color].presence
|
||||||
|
|
||||||
if group.save
|
if group.save
|
||||||
Group.reset_counters(group.id, :group_users)
|
Group.reset_counters(group.id, :group_users)
|
||||||
|
@ -124,7 +124,18 @@ class Admin::GroupsController < Admin::AdminController
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def can_not_modify_automatic
|
def can_not_modify_automatic
|
||||||
render json: {errors: I18n.t('groups.errors.can_not_modify_automatic')}, status: 422
|
render json: {errors: I18n.t('groups.errors.can_not_modify_automatic')}, status: 422
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def group_params
|
||||||
|
params.require(:group).permit(
|
||||||
|
:name, :alias_level, :visible, :automatic_membership_email_domains,
|
||||||
|
:automatic_membership_retroactive, :title, :primary_group,
|
||||||
|
:grant_trust_level, :incoming_email, :flair_url, :flair_bg_color,
|
||||||
|
:flair_color
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
class GroupsController < ApplicationController
|
class GroupsController < ApplicationController
|
||||||
|
|
||||||
before_filter :ensure_logged_in, only: [:set_notifications, :mentionable]
|
before_filter :ensure_logged_in, only: [
|
||||||
|
:set_notifications,
|
||||||
|
:mentionable,
|
||||||
|
:update
|
||||||
|
]
|
||||||
|
|
||||||
skip_before_filter :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed]
|
skip_before_filter :preload_json, :check_xhr, only: [:posts_feed, :mentions_feed]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render_serialized(find_group(:id), GroupShowSerializer, root: 'basic_group')
|
render_serialized(find_group(:id), GroupShowSerializer, root: 'basic_group')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
group = Group.find(params[:id])
|
||||||
|
guardian.ensure_can_edit!(group)
|
||||||
|
|
||||||
|
if group.update_attributes(group_params)
|
||||||
|
render json: success_json
|
||||||
|
else
|
||||||
|
render_json_error(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def posts
|
def posts
|
||||||
group = find_group(:group_id)
|
group = find_group(:group_id)
|
||||||
posts = group.posts_for(guardian, params[:before_post_id]).limit(20)
|
posts = group.posts_for(guardian, params[:before_post_id]).limit(20)
|
||||||
|
@ -152,11 +168,15 @@ class GroupsController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_group(param_name)
|
def group_params
|
||||||
name = params.require(param_name)
|
params.require(:group).permit(:flair_url, :flair_bg_color, :flair_color)
|
||||||
group = Group.find_by("lower(name) = ?", name.downcase)
|
end
|
||||||
guardian.ensure_can_see!(group)
|
|
||||||
group
|
def find_group(param_name)
|
||||||
end
|
name = params.require(param_name)
|
||||||
|
group = Group.find_by("lower(name) = ?", name.downcase)
|
||||||
|
guardian.ensure_can_see!(group)
|
||||||
|
group
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -349,7 +349,11 @@ class Group < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_owner(user)
|
def add_owner(user)
|
||||||
self.group_users.create(user_id: user.id, owner: true)
|
if group_user = self.group_users.find_by(user: user)
|
||||||
|
group_user.update_attributes!(owner: true) if !group_user.owner
|
||||||
|
else
|
||||||
|
GroupUser.create!(user: user, group: self, owner: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_by_email(email)
|
def self.find_by_email(email)
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
class GroupShowSerializer < BasicGroupSerializer
|
class GroupShowSerializer < BasicGroupSerializer
|
||||||
attributes :is_group_user
|
attributes :is_group_user, :is_group_owner
|
||||||
|
|
||||||
def include_is_group_user?
|
def include_is_group_user?
|
||||||
scope.authenticated?
|
scope.authenticated?
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_group_user
|
def is_group_user
|
||||||
object.users.include?(scope.user)
|
!!fetch_group_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_is_group_owner?
|
||||||
|
scope.authenticated?
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_group_owner
|
||||||
|
scope.is_admin? || fetch_group_user&.owner
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_group_user
|
||||||
|
@group_user ||= object.group_users.find_by(user: scope.user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1845,6 +1845,21 @@ en:
|
||||||
title: "Show the raw source diffs side-by-side"
|
title: "Show the raw source diffs side-by-side"
|
||||||
button: '<i class="fa fa-columns"></i> Raw'
|
button: '<i class="fa fa-columns"></i> Raw'
|
||||||
|
|
||||||
|
group:
|
||||||
|
edit:
|
||||||
|
title: 'Edit Group'
|
||||||
|
name: "Name"
|
||||||
|
name_placeholder: "Group name, no spaces, same as username rule"
|
||||||
|
flair_url: "Avatar Flair Image"
|
||||||
|
flair_url_placeholder: "(Optional) Image URL or Font Awesome class"
|
||||||
|
flair_bg_color: "Avatar Flair Background Color"
|
||||||
|
flair_bg_color_placeholder: "(Optional) Hex color value"
|
||||||
|
flair_color: "Avatar Flair Color"
|
||||||
|
flair_color_placeholder: "(Optional) Hex color value"
|
||||||
|
flair_preview_icon: "Preview Icon"
|
||||||
|
flair_preview_image: "Preview Image"
|
||||||
|
flair_note: "Note: Flair will only show for a user's primary group."
|
||||||
|
|
||||||
category:
|
category:
|
||||||
can: 'can… '
|
can: 'can… '
|
||||||
none: '(no category)'
|
none: '(no category)'
|
||||||
|
@ -2451,7 +2466,6 @@ en:
|
||||||
refresh: "Refresh"
|
refresh: "Refresh"
|
||||||
new: "New"
|
new: "New"
|
||||||
selector_placeholder: "enter username"
|
selector_placeholder: "enter username"
|
||||||
name_placeholder: "Group name, no spaces, same as username rule"
|
|
||||||
about: "Edit your group membership and names here"
|
about: "Edit your group membership and names here"
|
||||||
group_members: "Group members"
|
group_members: "Group members"
|
||||||
delete: "Delete"
|
delete: "Delete"
|
||||||
|
@ -2459,7 +2473,6 @@ en:
|
||||||
delete_failed: "Unable to delete group. If this is an automatic group, it cannot be destroyed."
|
delete_failed: "Unable to delete group. If this is an automatic group, it cannot be destroyed."
|
||||||
delete_member_confirm: "Remove '%{username}' from the '%{group}' group?"
|
delete_member_confirm: "Remove '%{username}' from the '%{group}' group?"
|
||||||
delete_owner_confirm: "Remove owner privilege for '%{username}'?"
|
delete_owner_confirm: "Remove owner privilege for '%{username}'?"
|
||||||
name: "Name"
|
|
||||||
add: "Add"
|
add: "Add"
|
||||||
add_members: "Add members"
|
add_members: "Add members"
|
||||||
custom: "Custom"
|
custom: "Custom"
|
||||||
|
@ -2476,14 +2489,6 @@ en:
|
||||||
add_owners: Add owners
|
add_owners: Add owners
|
||||||
incoming_email: "Custom incoming email address"
|
incoming_email: "Custom incoming email address"
|
||||||
incoming_email_placeholder: "enter email address"
|
incoming_email_placeholder: "enter email address"
|
||||||
flair_url: "Avatar Flair Image"
|
|
||||||
flair_url_placeholder: "(Optional) Image URL or Font Awesome class"
|
|
||||||
flair_bg_color: "Avatar Flair Background Color"
|
|
||||||
flair_bg_color_placeholder: "(Optional) Hex color value"
|
|
||||||
flair_color: "Avatar Flair Color"
|
|
||||||
flair_color_placeholder: "(Optional) Hex color value"
|
|
||||||
flair_preview: "Preview"
|
|
||||||
flair_note: "Note: Flair will only show for a user's primary group."
|
|
||||||
|
|
||||||
api:
|
api:
|
||||||
generate_master: "Generate Master API Key"
|
generate_master: "Generate Master API Key"
|
||||||
|
|
|
@ -66,7 +66,7 @@ describe Admin::GroupsController do
|
||||||
context ".create" do
|
context ".create" do
|
||||||
|
|
||||||
it "strip spaces on the group name" do
|
it "strip spaces on the group name" do
|
||||||
xhr :post, :create, name: " bob "
|
xhr :post, :create, { group: { name: " bob " } }
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ describe Admin::GroupsController do
|
||||||
context ".update" do
|
context ".update" do
|
||||||
|
|
||||||
it "ignore name change on automatic group" do
|
it "ignore name change on automatic group" do
|
||||||
xhr :put, :update, id: 1, name: "WAT", visible: "true"
|
xhr :put, :update, { id: 1, group: { name: "WAT", visible: "true" } }
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
|
|
||||||
group = Group.find(1)
|
group = Group.find(1)
|
||||||
|
@ -92,14 +92,14 @@ describe Admin::GroupsController do
|
||||||
it "doesn't launch the 'automatic group membership' job when it's not retroactive" do
|
it "doesn't launch the 'automatic group membership' job when it's not retroactive" do
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
xhr :put, :update, id: group.id, automatic_membership_retroactive: "false"
|
xhr :put, :update, { id: group.id, group: { automatic_membership_retroactive: "false" } }
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
it "launches the 'automatic group membership' job when it's retroactive" do
|
it "launches the 'automatic group membership' job when it's retroactive" do
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
Jobs.expects(:enqueue).with(:automatic_group_membership, group_id: group.id)
|
Jobs.expects(:enqueue).with(:automatic_group_membership, group_id: group.id)
|
||||||
xhr :put, :update, id: group.id, automatic_membership_retroactive: "true"
|
xhr :put, :update, { id: group.id, group: { automatic_membership_retroactive: "true" } }
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe "Groups" do
|
describe "Groups" do
|
||||||
describe "checking if a group can be mentioned" do
|
let(:password) { 'somecomplicatedpassword' }
|
||||||
let(:password) { 'somecomplicatedpassword' }
|
let(:email_token) { Fabricate(:email_token, confirmed: true) }
|
||||||
let(:email_token) { Fabricate(:email_token, confirmed: true) }
|
let(:user) { email_token.user }
|
||||||
let(:user) { email_token.user }
|
|
||||||
let(:group) { Fabricate(:group, name: 'test', users: [user]) }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
user.update_attributes!(password: password)
|
user.update_attributes!(password: password)
|
||||||
end
|
post "/session.json", { login: user.username, password: password }
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "checking if a group can be mentioned" do
|
||||||
|
let(:group) { Fabricate(:group, name: 'test', users: [user]) }
|
||||||
|
|
||||||
it "should return the right response" do
|
it "should return the right response" do
|
||||||
group
|
group
|
||||||
|
|
||||||
post "/session.json", { login: user.username, password: password }
|
|
||||||
expect(response).to be_success
|
|
||||||
|
|
||||||
get "/groups/test/mentionable.json", { name: group.name }
|
get "/groups/test/mentionable.json", { name: group.name }
|
||||||
|
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
|
@ -33,4 +33,51 @@ describe "Groups" do
|
||||||
expect(response_body["mentionable"]).to eq(true)
|
expect(response_body["mentionable"]).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "group can be updated" do
|
||||||
|
let(:group) { Fabricate(:group, name: 'test', users: [user]) }
|
||||||
|
|
||||||
|
context "when user is group owner" do
|
||||||
|
before do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able update the group" do
|
||||||
|
xhr :put, "/groups/#{group.id}", { group: {
|
||||||
|
flair_bg_color: 'FFF',
|
||||||
|
flair_color: 'BBB',
|
||||||
|
flair_url: 'fa-adjust'
|
||||||
|
} }
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
|
||||||
|
group.reload
|
||||||
|
|
||||||
|
expect(group.flair_bg_color).to eq('FFF')
|
||||||
|
expect(group.flair_color).to eq('BBB')
|
||||||
|
expect(group.flair_url).to eq('fa-adjust')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when user is group admin" do
|
||||||
|
before do
|
||||||
|
user.update_attributes!(admin: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be able to update the group' do
|
||||||
|
xhr :put, "/groups/#{group.id}", { group: { flair_color: 'BBB' } }
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
expect(group.reload.flair_color).to eq('BBB')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when user is not a group owner or admin" do
|
||||||
|
it 'should not be able to update the group' do
|
||||||
|
xhr :put, "/groups/#{group.id}", { group: { name: 'testing' } }
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe GroupShowSerializer do
|
||||||
|
context 'admin user' do
|
||||||
|
let(:user) { Fabricate(:admin) }
|
||||||
|
let(:group) { Fabricate(:group, users: [user]) }
|
||||||
|
|
||||||
|
it 'should return the right attributes' do
|
||||||
|
json = GroupShowSerializer.new(group, scope: Guardian.new(user)).as_json
|
||||||
|
|
||||||
|
expect(json[:group_show][:is_group_owner]).to eq(true)
|
||||||
|
expect(json[:group_show][:is_group_user]).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'group owner' do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return the right attributes' do
|
||||||
|
json = GroupShowSerializer.new(group, scope: Guardian.new(user)).as_json
|
||||||
|
|
||||||
|
expect(json[:group_show][:is_group_owner]).to eq(true)
|
||||||
|
expect(json[:group_show][:is_group_user]).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -32,7 +32,7 @@ test("Browsing Groups", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Messages tab", () => {
|
test("Admin Browsing Groups", () => {
|
||||||
logIn();
|
logIn();
|
||||||
Discourse.reset();
|
Discourse.reset();
|
||||||
|
|
||||||
|
@ -41,4 +41,10 @@ test("Messages tab", () => {
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
ok($('.action-list li').length === 5, 'it should show messages tab if user is admin');
|
ok($('.action-list li').length === 5, 'it should show messages tab if user is admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
click('.group-edit-btn');
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
ok(find('.group-flair-inputs').length === 1, 'it should display avatar flair inputs');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
moduleFor("controller:group");
|
||||||
|
|
||||||
|
test("canEditGroup", function() {
|
||||||
|
const GroupController = this.subject();
|
||||||
|
|
||||||
|
GroupController.setProperties({
|
||||||
|
model: { is_group_owner: true, automatic: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
equal(GroupController.get("canEditGroup"), false, "automatic groups cannot be edited");
|
||||||
|
|
||||||
|
GroupController.set("model.automatic", false);
|
||||||
|
|
||||||
|
equal(GroupController.get("canEditGroup"), true, "owners can edit groups");
|
||||||
|
|
||||||
|
GroupController.set("model.is_group_owner", false);
|
||||||
|
|
||||||
|
equal(GroupController.get("canEditGroup"), false, "normal users cannot edit groups");
|
||||||
|
});
|
|
@ -7,7 +7,8 @@ export default {
|
||||||
"user_count":8,
|
"user_count":8,
|
||||||
"alias_level":0,
|
"alias_level":0,
|
||||||
"visible":true,
|
"visible":true,
|
||||||
"flair_url": 'fa-adjust'
|
"flair_url": 'fa-adjust',
|
||||||
|
"is_group_owner":true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/groups/discourse/counts.json":{
|
"/groups/discourse/counts.json":{
|
||||||
|
|
Loading…
Reference in New Issue