FEATURE: selectable avatars
This commit is contained in:
parent
a24b9981c6
commit
6d6e026e3c
|
@ -0,0 +1,15 @@
|
|||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
actions: {
|
||||
showUploadModal({ value, setting }) {
|
||||
showModal("admin-uploaded-image-list", {
|
||||
admin: true,
|
||||
title: `admin.site_settings.${setting.setting}.title`,
|
||||
model: { value, setting },
|
||||
}).setProperties({
|
||||
save: v => this.set("value", v)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
|
||||
@on("init")
|
||||
@observes("model.value")
|
||||
_setup() {
|
||||
const value = this.get("model.value");
|
||||
this.set("images", value && value.length ? value.split("\n") : []);
|
||||
},
|
||||
|
||||
actions: {
|
||||
uploadDone({ url }) {
|
||||
this.get("images").addObject(url);
|
||||
},
|
||||
|
||||
remove(url) {
|
||||
this.get("images").removeObject(url);
|
||||
},
|
||||
|
||||
close() {
|
||||
this.save(this.get("images").join("\n"));
|
||||
this.send("closeModal");
|
||||
}
|
||||
}
|
||||
});
|
|
@ -9,7 +9,8 @@ const CUSTOM_TYPES = [
|
|||
"host_list",
|
||||
"category_list",
|
||||
"value_list",
|
||||
"category"
|
||||
"category",
|
||||
"uploaded_image_list",
|
||||
];
|
||||
|
||||
export default Ember.Mixin.create({
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
{{d-button label="admin.site_settings.uploaded_image_list.label" action="showUploadModal" actionParam=(hash value=value setting=setting)}}
|
||||
<div class='desc'>{{{unbound setting.description}}}</div>
|
|
@ -0,0 +1,15 @@
|
|||
{{#d-modal-body class="uploaded-image-list"}}
|
||||
<div class="selectable-avatars">
|
||||
{{#each images as |image|}}
|
||||
<div class="selectable-avatar" {{action "remove" image}}>
|
||||
{{bound-avatar-template image "huge"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<p>{{i18n "admin.site_settings.uploaded_image_list.empty"}}</p>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
{{d-button action=(action "close") label="close"}}
|
||||
{{images-uploader uploading=uploading done="uploadDone" class="pull-right"}}
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
|
||||
export default Em.Component.extend(UploadMixin, {
|
||||
type: "avatar",
|
||||
tagName: "span",
|
||||
|
||||
@computed("uploading")
|
||||
uploadButtonText(uploading) {
|
||||
return uploading ? I18n.t("uploading") : I18n.t("user.change_avatar.upload_picture");
|
||||
},
|
||||
|
||||
validateUploadedFilesOptions() {
|
||||
return { imagesOnly: true };
|
||||
},
|
||||
|
||||
uploadDone(upload) {
|
||||
this.sendAction("done", upload);
|
||||
},
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
import { allowsImages } from "discourse/lib/utilities";
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
|
|
|
@ -508,19 +508,21 @@ const User = RestModel.extend({
|
|||
data: { upload_id, type }
|
||||
}
|
||||
).then(() =>
|
||||
this.setProperties({
|
||||
avatar_template,
|
||||
uploaded_avatar_id: upload_id
|
||||
})
|
||||
this.setProperties({ avatar_template, uploaded_avatar_id: upload_id })
|
||||
);
|
||||
},
|
||||
|
||||
selectAvatar(avatarUrl) {
|
||||
return ajax(
|
||||
userPath(`${this.get("username_lower")}/preferences/avatar/select`),
|
||||
{ type: "PUT", data: { url: avatarUrl } }
|
||||
).then(result => this.setProperties(result));
|
||||
},
|
||||
|
||||
isAllowedToUploadAFile(type) {
|
||||
return (
|
||||
this.get("staff") ||
|
||||
return this.get("staff") ||
|
||||
this.get("trust_level") > 0 ||
|
||||
Discourse.SiteSettings["newuser_max_" + type + "s"] > 0
|
||||
);
|
||||
Discourse.SiteSettings[`newuser_max_${type}s`] > 0;
|
||||
},
|
||||
|
||||
createInvite(email, group_names, custom_message) {
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
model() {
|
||||
return this.modelFor("user");
|
||||
},
|
||||
|
||||
setupController(controller, user) {
|
||||
controller.setProperties({
|
||||
model: user
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
showAvatarSelector() {
|
||||
showModal("avatar-selector");
|
||||
|
||||
// all the properties needed for displaying the avatar selector modal
|
||||
const props = this.modelFor("user").getProperties(
|
||||
"id",
|
||||
"email",
|
||||
|
@ -42,15 +34,32 @@ export default RestrictedUserRoute.extend({
|
|||
props.selected = "uploaded";
|
||||
}
|
||||
|
||||
this.controllerFor("avatar-selector").setProperties(props);
|
||||
const controller = showModal("avatar-selector");
|
||||
controller.setProperties(props);
|
||||
|
||||
if (this.siteSettings.selectable_avatars_enabled) {
|
||||
ajax("/site/selectable-avatars.json")
|
||||
.then(avatars => controller.set("selectableAvatars", avatars));
|
||||
}
|
||||
},
|
||||
|
||||
selectAvatar(url) {
|
||||
const user = this.modelFor("user");
|
||||
const controller = this.controllerFor("avatar-selector");
|
||||
|
||||
user
|
||||
.selectAvatar(url)
|
||||
.then(() => bootbox.alert(I18n.t("user.change_avatar.cache_notice")))
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => controller.send("closeModal"));
|
||||
},
|
||||
|
||||
saveAvatarSelection() {
|
||||
const user = this.modelFor("user"),
|
||||
controller = this.controllerFor("avatar-selector"),
|
||||
selectedUploadId = controller.get("selectedUploadId"),
|
||||
selectedAvatarTemplate = controller.get("selectedAvatarTemplate"),
|
||||
type = controller.get("selected");
|
||||
const user = this.modelFor("user");
|
||||
const controller = this.controllerFor("avatar-selector");
|
||||
const selectedUploadId = controller.get("selectedUploadId");
|
||||
const selectedAvatarTemplate = controller.get("selectedAvatarTemplate");
|
||||
const type = controller.get("selected");
|
||||
|
||||
user
|
||||
.pickAvatar(selectedUploadId, type, selectedAvatarTemplate)
|
||||
|
@ -64,10 +73,8 @@ export default RestrictedUserRoute.extend({
|
|||
);
|
||||
bootbox.alert(I18n.t("user.change_avatar.cache_notice"));
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
|
||||
// saves the data back
|
||||
controller.send("closeModal");
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => controller.send("closeModal"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<label class="btn" disabled={{uploading}} title="{{i18n "admin.site_settings.uploaded_image_list.upload.title"}}">
|
||||
{{d-icon "picture-o"}} {{uploadButtonText}}
|
||||
<input disabled={{uploading}} type="file" accept="image/*" multiple style="visibility: hidden; position: absolute;" />
|
||||
</label>
|
||||
{{#if uploading}}
|
||||
<span>{{i18n 'upload_selector.uploading'}} {{uploadProgress}}%</span>
|
||||
{{/if}}
|
|
@ -1,37 +1,49 @@
|
|||
{{#d-modal-body title="user.change_avatar.title" class="avatar-selector"}}
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="system-avatar" name="avatar" value="system" selection=selected}}
|
||||
<label class="radio" for="system-avatar">{{bound-avatar-template system_avatar_template "large"}} {{{i18n 'user.change_avatar.letter_based'}}}</label>
|
||||
</div>
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="gravatar" name="avatar" value="gravatar" selection=selected}}
|
||||
<label class="radio" for="gravatar">{{bound-avatar-template gravatar_avatar_template "large"}} {{{i18n 'user.change_avatar.gravatar'}}} {{email}}</label>
|
||||
{{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
|
||||
{{#if gravatarFailed}}
|
||||
<p class="error">{{I18n 'user.change_avatar.gravatar_failed'}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if allowAvatarUpload}}
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="uploaded-avatar" name="avatar" value="uploaded" selection=selected}}
|
||||
<label class="radio" for="uploaded-avatar">
|
||||
{{#if custom_avatar_template}}
|
||||
{{bound-avatar-template custom_avatar_template "large"}}
|
||||
{{i18n 'user.change_avatar.uploaded_avatar'}}
|
||||
{{else}}
|
||||
{{i18n 'user.change_avatar.uploaded_avatar_empty'}}
|
||||
{{/if}}
|
||||
</label>
|
||||
{{avatar-uploader user_id=id
|
||||
uploadedAvatarTemplate=custom_avatar_template
|
||||
uploadedAvatarId=custom_avatar_upload_id
|
||||
uploading=uploading
|
||||
done="uploadComplete"}}
|
||||
{{#if siteSettings.selectable_avatars_enabled}}
|
||||
<div class="selectable-avatars">
|
||||
{{#each selectableAvatars as |avatar|}}
|
||||
<div class="selectable-avatar" {{action "selectAvatar" avatar}}>
|
||||
{{bound-avatar-template avatar "huge"}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="system-avatar" name="avatar" value="system" selection=selected}}
|
||||
<label class="radio" for="system-avatar">{{bound-avatar-template system_avatar_template "large"}} {{{i18n 'user.change_avatar.letter_based'}}}</label>
|
||||
</div>
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="gravatar" name="avatar" value="gravatar" selection=selected}}
|
||||
<label class="radio" for="gravatar">{{bound-avatar-template gravatar_avatar_template "large"}} {{{i18n 'user.change_avatar.gravatar'}}} {{email}}</label>
|
||||
{{d-button action="refreshGravatar" title="user.change_avatar.refresh_gravatar_title" disabled=gravatarRefreshDisabled icon="refresh"}}
|
||||
{{#if gravatarFailed}}
|
||||
<p class="error">{{I18n 'user.change_avatar.gravatar_failed'}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#if allowAvatarUpload}}
|
||||
<div class="avatar-choice">
|
||||
{{radio-button id="uploaded-avatar" name="avatar" value="uploaded" selection=selected}}
|
||||
<label class="radio" for="uploaded-avatar">
|
||||
{{#if custom_avatar_template}}
|
||||
{{bound-avatar-template custom_avatar_template "large"}}
|
||||
{{i18n 'user.change_avatar.uploaded_avatar'}}
|
||||
{{else}}
|
||||
{{i18n 'user.change_avatar.uploaded_avatar_empty'}}
|
||||
{{/if}}
|
||||
</label>
|
||||
{{avatar-uploader user_id=id
|
||||
uploadedAvatarTemplate=custom_avatar_template
|
||||
uploadedAvatarId=custom_avatar_upload_id
|
||||
uploading=uploading
|
||||
done="uploadComplete"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class="modal-footer">
|
||||
{{d-button action="saveAvatarSelection" class="btn-primary" disabled=uploading label="save"}}
|
||||
{{d-modal-cancel close=(action "closeModal")}}
|
||||
</div>
|
||||
{{#unless siteSettings.selectable_avatars_enabled}}
|
||||
<div class="modal-footer">
|
||||
{{d-button action="saveAvatarSelection" class="btn-primary" disabled=uploading label="save"}}
|
||||
{{d-modal-cancel close=(action "closeModal")}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
|
|
@ -106,3 +106,4 @@
|
|||
color: $danger;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -661,3 +661,20 @@
|
|||
#user-card .staged {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.selectable-avatars {
|
||||
max-height: 350px;
|
||||
margin-bottom: 1em;
|
||||
text-align: justify;
|
||||
.selectable-avatar {
|
||||
margin: 5px;
|
||||
display: inline-block;
|
||||
.avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
&:hover {
|
||||
box-shadow: 0 0 10px $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,16 @@ class SiteController < ApplicationController
|
|||
render json: custom_emoji
|
||||
end
|
||||
|
||||
def selectable_avatars
|
||||
avatars = if SiteSetting.selectable_avatars_enabled?
|
||||
(SiteSetting.selectable_avatars.presence || "").split("\n")
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
render json: avatars, root: false
|
||||
end
|
||||
|
||||
def basic_info
|
||||
results = {
|
||||
logo_url: UrlHelper.absolute(SiteSetting.logo_url),
|
||||
|
|
|
@ -11,8 +11,9 @@ class UsersController < ApplicationController
|
|||
|
||||
requires_login only: [
|
||||
:username, :update, :user_preferences_redirect, :upload_user_image,
|
||||
:pick_avatar, :destroy_user_image, :destroy, :check_emails, :topic_tracking_state,
|
||||
:preferences, :create_second_factor, :update_second_factor, :create_second_factor_backup
|
||||
:pick_avatar, :destroy_user_image, :destroy, :check_emails,
|
||||
:topic_tracking_state, :preferences, :create_second_factor,
|
||||
:update_second_factor, :create_second_factor_backup, :select_avatar
|
||||
]
|
||||
|
||||
skip_before_action :check_xhr, only: [
|
||||
|
@ -885,6 +886,46 @@ class UsersController < ApplicationController
|
|||
render json: success_json
|
||||
end
|
||||
|
||||
def select_avatar
|
||||
user = fetch_user_from_params
|
||||
guardian.ensure_can_edit!(user)
|
||||
|
||||
url = params[:url]
|
||||
|
||||
if url.blank?
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
unless SiteSetting.selectable_avatars_enabled
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
if SiteSetting.selectable_avatars.blank?
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
unless SiteSetting.selectable_avatars[url]
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
unless upload = Upload.find_by(url: url)
|
||||
return render json: failed_json, status: 422
|
||||
end
|
||||
|
||||
user.uploaded_avatar_id = upload.id
|
||||
user.save!
|
||||
|
||||
avatar = user.user_avatar || user.create_user_avatar
|
||||
avatar.custom_upload_id = upload.id
|
||||
avatar.save!
|
||||
|
||||
render json: {
|
||||
avatar_template: user.avatar_template,
|
||||
custom_avatar_template: user.avatar_template,
|
||||
uploaded_avatar_id: upload.id,
|
||||
}
|
||||
end
|
||||
|
||||
def destroy_user_image
|
||||
user = fetch_user_from_params
|
||||
guardian.ensure_can_edit!(user)
|
||||
|
|
|
@ -24,7 +24,8 @@ module Jobs
|
|||
SiteSetting.logo_small_url,
|
||||
SiteSetting.favicon_url,
|
||||
SiteSetting.apple_touch_icon_url,
|
||||
].map do |url|
|
||||
*SiteSetting.selectable_avatars.split("\n"),
|
||||
].flatten.map do |url|
|
||||
if url.present?
|
||||
url = url.dup
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ class User < ActiveRecord::Base
|
|||
after_create :create_user_stat
|
||||
after_create :create_user_option
|
||||
after_create :create_user_profile
|
||||
after_create :set_random_avatar
|
||||
after_create :ensure_in_trust_level_group
|
||||
after_create :set_default_categories_preferences
|
||||
|
||||
|
@ -612,8 +613,7 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def self.gravatar_template(email)
|
||||
email_hash = self.email_hash(email)
|
||||
"//www.gravatar.com/avatar/#{email_hash}.png?s={size}&r=pg&d=identicon"
|
||||
"//www.gravatar.com/avatar/#{self.email_hash(email)}.png?s={size}&r=pg&d=identicon"
|
||||
end
|
||||
|
||||
# Don't pass this up to the client - it's meant for server side use
|
||||
|
@ -628,19 +628,19 @@ class User < ActiveRecord::Base
|
|||
UrlHelper.schemaless UrlHelper.absolute avatar_template
|
||||
end
|
||||
|
||||
def self.username_hash(username)
|
||||
username.each_char.reduce(0) do |result, char|
|
||||
[((result << 5) - result) + char.ord].pack('L').unpack('l').first
|
||||
end.abs
|
||||
end
|
||||
|
||||
def self.default_template(username)
|
||||
if SiteSetting.default_avatars.present?
|
||||
split_avatars = SiteSetting.default_avatars.split("\n")
|
||||
if split_avatars.present?
|
||||
hash = username.each_char.reduce(0) do |result, char|
|
||||
[((result << 5) - result) + char.ord].pack('L').unpack('l').first
|
||||
end
|
||||
|
||||
split_avatars[hash.abs % split_avatars.size]
|
||||
end
|
||||
else
|
||||
system_avatar_template(username)
|
||||
urls = SiteSetting.default_avatars.split("\n")
|
||||
return urls[username_hash(username) % urls.size] if urls.present?
|
||||
end
|
||||
|
||||
system_avatar_template(username)
|
||||
end
|
||||
|
||||
def self.avatar_template(username, uploaded_avatar_id)
|
||||
|
@ -1018,6 +1018,18 @@ class User < ActiveRecord::Base
|
|||
UserProfile.create(user_id: id)
|
||||
end
|
||||
|
||||
def set_random_avatar
|
||||
if SiteSetting.selectable_avatars_enabled? && SiteSetting.selectable_avatars.present?
|
||||
urls = SiteSetting.selectable_avatars.split("\n")
|
||||
if urls.present?
|
||||
if upload = Upload.find_by(url: urls.sample)
|
||||
update_column(:uploaded_avatar_id, upload.id)
|
||||
UserAvatar.create(user_id: id, custom_upload_id: upload.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def anonymous?
|
||||
SiteSetting.allow_anonymous_posting &&
|
||||
trust_level >= 1 &&
|
||||
|
|
|
@ -3797,6 +3797,14 @@ en:
|
|||
clear_filter: "Clear"
|
||||
add_url: "add URL"
|
||||
add_host: "add host"
|
||||
uploaded_image_list:
|
||||
label: "Edit list"
|
||||
empty: "There are no pictures yet. Please upload one."
|
||||
upload:
|
||||
label: "Upload"
|
||||
title: "Upload image(s)"
|
||||
selectable_avatars:
|
||||
title: "List of avatars users can choose from"
|
||||
categories:
|
||||
all_results: 'All'
|
||||
required: 'Required'
|
||||
|
|
|
@ -1339,6 +1339,9 @@ en:
|
|||
external_system_avatars_enabled: "Use external system avatars service."
|
||||
external_system_avatars_url: "URL of the external system avatars service. Allowed substitutions are {username} {first_letter} {color} {size}"
|
||||
|
||||
selectable_avatars_enabled: "Force users to choose an avatar from the list."
|
||||
selectable_avatars: "List of avatars users can choose from."
|
||||
|
||||
default_opengraph_image_url: "URL of the default opengraph image."
|
||||
twitter_summary_large_image_url: "URL of the default Twitter summary card image (should be at least 280px in width, and at least 150px in height)."
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ Discourse::Application.routes.draw do
|
|||
|
||||
get "site/basic-info" => 'site#basic_info'
|
||||
get "site/statistics" => 'site#statistics'
|
||||
get "site/selectable-avatars" => "site#selectable_avatars"
|
||||
|
||||
get "srv/status" => "forums#status"
|
||||
|
||||
|
@ -405,6 +406,7 @@ Discourse::Application.routes.draw do
|
|||
get "#{root_path}/:username/preferences/second-factor-backup" => "users#preferences", constraints: { username: RouteFormat.username }
|
||||
delete "#{root_path}/:username/preferences/user_image" => "users#destroy_user_image", constraints: { username: RouteFormat.username }
|
||||
put "#{root_path}/:username/preferences/avatar/pick" => "users#pick_avatar", constraints: { username: RouteFormat.username }
|
||||
put "#{root_path}/:username/preferences/avatar/select" => "users#select_avatar", constraints: { username: RouteFormat.username }
|
||||
get "#{root_path}/:username/staff-info" => "users#staff_info", constraints: { username: RouteFormat.username }
|
||||
get "#{root_path}/:username/summary" => "users#summary", constraints: { username: RouteFormat.username }
|
||||
get "#{root_path}/:username/invited" => "users#invited", constraints: { username: RouteFormat.username }
|
||||
|
|
|
@ -959,6 +959,12 @@ files:
|
|||
client: true
|
||||
regex: '^((https?:)?\/)?\/.+[^\/]'
|
||||
shadowed_by_global: true
|
||||
selectable_avatars_enabled:
|
||||
default: false
|
||||
client: true
|
||||
selectable_avatars:
|
||||
default: ''
|
||||
type: uploaded_image_list
|
||||
allow_all_attachments_for_group_messages: false
|
||||
png_to_jpg_quality:
|
||||
default: 95
|
||||
|
|
|
@ -29,7 +29,8 @@ class SiteSettings::TypeSupervisor
|
|||
regex: 13,
|
||||
email: 14,
|
||||
username: 15,
|
||||
category: 16
|
||||
category: 16,
|
||||
uploaded_image_list: 17,
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -73,6 +73,12 @@ describe SiteSettings::TypeSupervisor do
|
|||
it "'username' should be at 15th position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:username]).to eq(15)
|
||||
end
|
||||
it "'category' should be at 16th position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:category]).to eq(16)
|
||||
end
|
||||
it "'uploaded_image_list' should be at 17th position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:uploaded_image_list]).to eq(17)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -46,12 +46,27 @@ describe Jobs::CleanUpUploads do
|
|||
|
||||
it "does not clean up uploads in site settings" do
|
||||
logo_upload = fabricate_upload
|
||||
logo_small_upload = fabricate_upload
|
||||
favicon_upload = fabricate_upload
|
||||
apple_touch_icon_upload = fabricate_upload
|
||||
avatar1_upload = fabricate_upload
|
||||
avatar2_upload = fabricate_upload
|
||||
|
||||
SiteSetting.logo_url = logo_upload.url
|
||||
SiteSetting.logo_small_url = logo_small_upload.url
|
||||
SiteSetting.favicon_url = favicon_upload.url
|
||||
SiteSetting.apple_touch_icon_url = apple_touch_icon_upload.url
|
||||
SiteSetting.selectable_avatars = [avatar1_upload.url, avatar2_upload.url].join("\n")
|
||||
|
||||
Jobs::CleanUpUploads.new.execute(nil)
|
||||
|
||||
expect(Upload.exists?(id: @upload.id)).to eq(false)
|
||||
expect(Upload.exists?(id: logo_upload.id)).to eq(true)
|
||||
expect(Upload.exists?(id: logo_small_upload.id)).to eq(true)
|
||||
expect(Upload.exists?(id: favicon_upload.id)).to eq(true)
|
||||
expect(Upload.exists?(id: apple_touch_icon_upload.id)).to eq(true)
|
||||
expect(Upload.exists?(id: avatar1_upload.id)).to eq(true)
|
||||
expect(Upload.exists?(id: avatar2_upload.id)).to eq(true)
|
||||
end
|
||||
|
||||
it "does not clean up uploads in site settings when they use the CDN" do
|
||||
|
|
|
@ -1771,4 +1771,18 @@ describe User do
|
|||
end
|
||||
end
|
||||
|
||||
describe "set_random_avatar" do
|
||||
it "sets a random avatar when selectable avatars is enabled" do
|
||||
avatar1 = Fabricate(:upload)
|
||||
avatar2 = Fabricate(:upload)
|
||||
SiteSetting.selectable_avatars_enabled = true
|
||||
SiteSetting.selectable_avatars = [avatar1.url, avatar2.url].join("\n")
|
||||
|
||||
user = Fabricate(:user)
|
||||
expect(user.uploaded_avatar_id).not_to be(nil)
|
||||
expect([avatar1.id, avatar2.id]).to include(user.uploaded_avatar_id)
|
||||
expect(user.user_avatar.custom_upload_id).to eq(user.uploaded_avatar_id)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -56,4 +56,30 @@ describe SiteController do
|
|||
expect(response).to redirect_to '/'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.selectable_avatars' do
|
||||
before do
|
||||
SiteSetting.selectable_avatars = "https://www.discourse.org\nhttps://meta.discourse.org"
|
||||
end
|
||||
|
||||
it 'returns empty array when selectable avatars is disabled' do
|
||||
SiteSetting.selectable_avatars_enabled = false
|
||||
|
||||
get "/site/selectable-avatars.json"
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json).to eq([])
|
||||
end
|
||||
|
||||
it 'returns an array when selectable avatars is enabled' do
|
||||
SiteSetting.selectable_avatars_enabled = true
|
||||
|
||||
get "/site/selectable-avatars.json"
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json).to contain_exactly("https://www.discourse.org", "https://meta.discourse.org")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1785,6 +1785,61 @@ describe UsersController do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#select_avatar' do
|
||||
it 'raises an error when not logged in' do
|
||||
put "/u/asdf/preferences/avatar/select.json", params: { url: "https://meta.discourse.org" }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
context 'while logged in' do
|
||||
|
||||
let!(:user) { sign_in(Fabricate(:user)) }
|
||||
let(:avatar1) { Fabricate(:upload) }
|
||||
let(:avatar2) { Fabricate(:upload) }
|
||||
let(:url) { "https://www.discourse.org" }
|
||||
|
||||
it 'raises an error when url is blank' do
|
||||
put "/u/#{user.username}/preferences/avatar/select.json", params: { url: "" }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it 'raises an error when selectable avatars is disabled' do
|
||||
put "/u/#{user.username}/preferences/avatar/select.json", params: { url: url }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
context 'selectable avatars is enabled' do
|
||||
|
||||
before { SiteSetting.selectable_avatars_enabled = true }
|
||||
|
||||
it 'raises an error when selectable avatars is empty' do
|
||||
put "/u/#{user.username}/preferences/avatar/select.json", params: { url: url }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
context 'selectable avatars is properly setup' do
|
||||
|
||||
before do
|
||||
SiteSetting.selectable_avatars = [avatar1.url, avatar2.url].join("\n")
|
||||
end
|
||||
|
||||
it 'raises an error when url is not in selectable avatars list' do
|
||||
put "/u/#{user.username}/preferences/avatar/select.json", params: { url: url }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it 'can successfully select an avatar' do
|
||||
put "/u/#{user.username}/preferences/avatar/select.json", params: { url: avatar1.url }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(user.reload.uploaded_avatar_id).to eq(avatar1.id)
|
||||
expect(user.user_avatar.reload.custom_upload_id).to eq(avatar1.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy_user_image' do
|
||||
|
||||
it 'raises an error when not logged in' do
|
||||
|
|
|
@ -163,11 +163,41 @@ QUnit.test("second factor backup", assert => {
|
|||
});
|
||||
});
|
||||
|
||||
QUnit.test("default avatar selector", assert => {
|
||||
visit("/u/eviltrout/preferences");
|
||||
|
||||
click(".pref-avatar .btn");
|
||||
andThen(() => {
|
||||
assert.ok(exists(".avatar-choice", "opens the avatar selection modal"));
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Avatar selector when selectable avatars is enabled", {
|
||||
loggedIn: true,
|
||||
settings: { selectable_avatars_enabled: true },
|
||||
beforeEach() {
|
||||
// prettier-ignore
|
||||
server.get("/site/selectable-avatars.json", () => { //eslint-disable-line
|
||||
return [200, { "Content-Type": "application/json" }, [
|
||||
"https://www.discourse.org",
|
||||
"https://meta.discourse.org",
|
||||
]];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test("selectable avatars", assert => {
|
||||
visit("/u/eviltrout/preferences");
|
||||
|
||||
click(".pref-avatar .btn");
|
||||
andThen(() => {
|
||||
assert.ok(exists(".selectable-avatars", "opens the avatar selection modal"));
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("User Preferences when badges are disabled", {
|
||||
loggedIn: true,
|
||||
settings: {
|
||||
enable_badges: false
|
||||
}
|
||||
settings: { enable_badges: false }
|
||||
});
|
||||
|
||||
QUnit.test("visit my preferences", assert => {
|
||||
|
|
Loading…
Reference in New Issue