mirror of
https://github.com/codeninjasllc/discourse.git
synced 2025-02-25 07:54:11 -05:00
FEATURE: new hide_user_profiles_from_public site setting
This commit is contained in:
parent
d9997a6b9e
commit
bb79e6aff7
11 changed files with 127 additions and 59 deletions
|
@ -56,6 +56,13 @@ export default Ember.Component.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed()
|
||||||
|
showUserDirectoryLink() {
|
||||||
|
if (!this.siteSettings.enable_user_directory) return false;
|
||||||
|
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) return false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
keyboardShortcuts() {
|
keyboardShortcuts() {
|
||||||
this.sendAction('showKeyboardAction');
|
this.sendAction('showKeyboardAction');
|
||||||
|
|
|
@ -39,6 +39,11 @@ export default Ember.Controller.extend({
|
||||||
// XSS protection (should be encapsulated)
|
// XSS protection (should be encapsulated)
|
||||||
username = username.toString().replace(/[^A-Za-z0-9_\.\-]/g, "");
|
username = username.toString().replace(/[^A-Za-z0-9_\.\-]/g, "");
|
||||||
|
|
||||||
|
// No user card for anon
|
||||||
|
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't show on mobile
|
// Don't show on mobile
|
||||||
if (Discourse.Mobile.mobileView) {
|
if (Discourse.Mobile.mobileView) {
|
||||||
const url = "/users/" + username;
|
const url = "/users/" + username;
|
||||||
|
|
|
@ -33,6 +33,12 @@ export default Discourse.Route.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
beforeModel() {
|
||||||
|
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||||
|
this.replaceWith("discovery");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
// If we're viewing the currently logged in user, return that object instead
|
// If we're viewing the currently logged in user, return that object instead
|
||||||
const currentUser = this.currentUser;
|
const currentUser = this.currentUser;
|
||||||
|
|
|
@ -23,6 +23,12 @@ export default Discourse.Route.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
beforeModel() {
|
||||||
|
if (this.siteSettings.hide_user_profiles_from_public && !this.currentUser) {
|
||||||
|
this.replaceWith("discovery");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
// If we refresh via `refreshModel` set the old model to loading
|
// If we refresh via `refreshModel` set the old model to loading
|
||||||
this._params = params;
|
this._params = params;
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<li>{{d-link route="badges" class="badge-link" label="badges.title"}}</li>
|
<li>{{d-link route="badges" class="badge-link" label="badges.title"}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if siteSettings.enable_user_directory}}
|
{{#if showUserDirectoryLink}}
|
||||||
<li>{{d-link route="users" class="user-directory-link" label="directory.title"}}</li>
|
<li>{{d-link route="users" class="user-directory-link" label="directory.title"}}</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
raise Discourse::InvalidAccess if SiteSetting.hide_user_profiles_from_public && !current_user
|
||||||
|
|
||||||
@user = fetch_user_from_params
|
@user = fetch_user_from_params
|
||||||
user_serializer = UserSerializer.new(@user, scope: guardian, root: 'user')
|
user_serializer = UserSerializer.new(@user, scope: guardian, root: 'user')
|
||||||
if params[:stats].to_s == "false"
|
if params[:stats].to_s == "false"
|
||||||
|
@ -162,7 +164,6 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def my_redirect
|
def my_redirect
|
||||||
|
|
||||||
raise Discourse::NotFound if params[:path] !~ /^[a-z\-\/]+$/
|
raise Discourse::NotFound if params[:path] !~ /^[a-z\-\/]+$/
|
||||||
|
|
||||||
if current_user.blank?
|
if current_user.blank?
|
||||||
|
|
|
@ -1162,6 +1162,8 @@ en:
|
||||||
anonymous_posting_min_trust_level: "Minimum trust level required to enable anonymous posting"
|
anonymous_posting_min_trust_level: "Minimum trust level required to enable anonymous posting"
|
||||||
anonymous_account_duration_minutes: "To protect anonymity create a new anonymous account every N minutes for each user. Example: if set to 600, as soon as 600 minutes elapse from last post AND user switches to anon, a new anonymous account is created."
|
anonymous_account_duration_minutes: "To protect anonymity create a new anonymous account every N minutes for each user. Example: if set to 600, as soon as 600 minutes elapse from last post AND user switches to anon, a new anonymous account is created."
|
||||||
|
|
||||||
|
hide_user_profiles_from_public: "Disable user cards, user profiles and user directory for anonymous users."
|
||||||
|
|
||||||
allow_profile_backgrounds: "Allow users to upload profile backgrounds."
|
allow_profile_backgrounds: "Allow users to upload profile backgrounds."
|
||||||
|
|
||||||
sequential_replies_threshold: "Number posts a user has to make in a row in a topic before being reminded about too many sequential replies. "
|
sequential_replies_threshold: "Number posts a user has to make in a row in a topic before being reminded about too many sequential replies. "
|
||||||
|
|
|
@ -342,6 +342,9 @@ users:
|
||||||
client: true
|
client: true
|
||||||
anonymous_account_duration_minutes:
|
anonymous_account_duration_minutes:
|
||||||
default: 10080
|
default: 10080
|
||||||
|
hide_user_profiles_from_public:
|
||||||
|
default: false
|
||||||
|
client: true
|
||||||
|
|
||||||
posting:
|
posting:
|
||||||
min_post_length:
|
min_post_length:
|
||||||
|
|
|
@ -379,6 +379,8 @@ class Search
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_search
|
def user_search
|
||||||
|
return if SiteSetting.hide_user_profiles_from_public && !@guardian.user
|
||||||
|
|
||||||
users = User.includes(:user_search_data)
|
users = User.includes(:user_search_data)
|
||||||
.where("active = true AND user_search_data.search_data @@ #{ts_query("simple")}")
|
.where("active = true AND user_search_data.search_data @@ #{ts_query("simple")}")
|
||||||
.order("CASE WHEN username_lower = '#{@original_term.downcase}' THEN 0 ELSE 1 END")
|
.order("CASE WHEN username_lower = '#{@original_term.downcase}' THEN 0 ELSE 1 END")
|
||||||
|
|
|
@ -85,6 +85,21 @@ describe Search do
|
||||||
expect(result.users.length).to eq(1)
|
expect(result.users.length).to eq(1)
|
||||||
expect(result.users[0].id).to eq(user.id)
|
expect(result.users[0].id).to eq(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'hiding user profiles' do
|
||||||
|
before { SiteSetting.stubs(:hide_user_profiles_from_public).returns(true) }
|
||||||
|
|
||||||
|
it 'returns no result for anon' do
|
||||||
|
expect(result.users.length).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a result for logged in users' do
|
||||||
|
result = Search.execute('bruce', type_filter: 'user', guardian: Guardian.new(user))
|
||||||
|
expect(result.users.length).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'inactive users' do
|
context 'inactive users' do
|
||||||
|
@ -119,7 +134,6 @@ describe Search do
|
||||||
TopicAllowedUser.create!(user_id: reply.user_id, topic_id: topic.id)
|
TopicAllowedUser.create!(user_id: reply.user_id, topic_id: topic.id)
|
||||||
TopicAllowedUser.create!(user_id: post.user_id, topic_id: topic.id)
|
TopicAllowedUser.create!(user_id: post.user_id, topic_id: topic.id)
|
||||||
|
|
||||||
|
|
||||||
results = Search.execute('mars',
|
results = Search.execute('mars',
|
||||||
type_filter: 'private_messages',
|
type_filter: 'private_messages',
|
||||||
guardian: Guardian.new(reply.user))
|
guardian: Guardian.new(reply.user))
|
||||||
|
|
|
@ -3,71 +3,93 @@ require 'spec_helper'
|
||||||
describe UsersController do
|
describe UsersController do
|
||||||
|
|
||||||
describe '.show' do
|
describe '.show' do
|
||||||
let(:user) { log_in }
|
|
||||||
|
|
||||||
it 'returns success' do
|
context "anon" do
|
||||||
xhr :get, :show, username: user.username, format: :json
|
|
||||||
expect(response).to be_success
|
|
||||||
json = JSON.parse(response.body)
|
|
||||||
|
|
||||||
expect(json["user"]["has_title_badges"]).to eq(false)
|
let(:user) { Discourse.system_user }
|
||||||
|
|
||||||
end
|
it "returns success" do
|
||||||
|
xhr :get, :show, username: user.username, format: :json
|
||||||
it "returns not found when the username doesn't exist" do
|
|
||||||
xhr :get, :show, username: 'madeuppity'
|
|
||||||
expect(response).not_to be_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns not found when the user is inactive' do
|
|
||||||
inactive = Fabricate(:user, active: false)
|
|
||||||
xhr :get, :show, username: inactive.username
|
|
||||||
expect(response).not_to be_success
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises an error on invalid access" do
|
|
||||||
Guardian.any_instance.expects(:can_see?).with(user).returns(false)
|
|
||||||
xhr :get, :show, username: user.username
|
|
||||||
expect(response).to be_forbidden
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "user profile views" do
|
|
||||||
let(:other_user) { Fabricate(:user) }
|
|
||||||
|
|
||||||
it "should track a user profile view for a signed in user" do
|
|
||||||
UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, user.id)
|
|
||||||
xhr :get, :show, username: other_user.username
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should not track a user profile view for a user viewing his own profile" do
|
|
||||||
UserProfileView.expects(:add).never
|
|
||||||
xhr :get, :show, username: user.username
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should track a user profile view for an anon user" do
|
|
||||||
UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, nil)
|
|
||||||
xhr :get, :show, username: other_user.username
|
|
||||||
end
|
|
||||||
|
|
||||||
it "skips tracking" do
|
|
||||||
UserProfileView.expects(:add).never
|
|
||||||
xhr :get, :show, { username: user.username, skip_track_visit: true }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "fetching a user by external_id" do
|
|
||||||
before { user.create_single_sign_on_record(external_id: '997', last_payload: '') }
|
|
||||||
|
|
||||||
it "returns fetch for a matching external_id" do
|
|
||||||
xhr :get, :show, external_id: '997'
|
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns not found when external_id doesn't match" do
|
it "raises an error for anon when profiles are hidden" do
|
||||||
xhr :get, :show, external_id: '99'
|
SiteSetting.stubs(:hide_user_profiles_from_public).returns(true)
|
||||||
|
xhr :get, :show, username: user.username, format: :json
|
||||||
expect(response).not_to be_success
|
expect(response).not_to be_success
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "logged in" do
|
||||||
|
|
||||||
|
let(:user) { log_in }
|
||||||
|
|
||||||
|
it 'returns success' do
|
||||||
|
xhr :get, :show, username: user.username, format: :json
|
||||||
|
expect(response).to be_success
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(json["user"]["has_title_badges"]).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns not found when the username doesn't exist" do
|
||||||
|
xhr :get, :show, username: 'madeuppity'
|
||||||
|
expect(response).not_to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns not found when the user is inactive' do
|
||||||
|
inactive = Fabricate(:user, active: false)
|
||||||
|
xhr :get, :show, username: inactive.username
|
||||||
|
expect(response).not_to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error on invalid access" do
|
||||||
|
Guardian.any_instance.expects(:can_see?).with(user).returns(false)
|
||||||
|
xhr :get, :show, username: user.username
|
||||||
|
expect(response).to be_forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "user profile views" do
|
||||||
|
let(:other_user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
it "should track a user profile view for a signed in user" do
|
||||||
|
UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, user.id)
|
||||||
|
xhr :get, :show, username: other_user.username
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not track a user profile view for a user viewing his own profile" do
|
||||||
|
UserProfileView.expects(:add).never
|
||||||
|
xhr :get, :show, username: user.username
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should track a user profile view for an anon user" do
|
||||||
|
UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, nil)
|
||||||
|
xhr :get, :show, username: other_user.username
|
||||||
|
end
|
||||||
|
|
||||||
|
it "skips tracking" do
|
||||||
|
UserProfileView.expects(:add).never
|
||||||
|
xhr :get, :show, { username: user.username, skip_track_visit: true }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "fetching a user by external_id" do
|
||||||
|
before { user.create_single_sign_on_record(external_id: '997', last_payload: '') }
|
||||||
|
|
||||||
|
it "returns fetch for a matching external_id" do
|
||||||
|
xhr :get, :show, external_id: '997'
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns not found when external_id doesn't match" do
|
||||||
|
xhr :get, :show, external_id: '99'
|
||||||
|
expect(response).not_to be_success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.user_preferences_redirect' do
|
describe '.user_preferences_redirect' do
|
||||||
|
|
Loading…
Reference in a new issue