FEATURE: introduces minimum trust level for polls (#5391)

* FEATURE: introduces minimum trust level for polls

This commit makes `poll_enabled` less misleading and introduces `poll_minimum_trust_level_to_create`. If poll are enabled they will always be cooked, and if you have the required trust level you can create polls. As a side effect, it also fixes a bug where rebaking a post created by staff member when `poll_enabled=false` would end up not cooking it.

It also adds more tests to ensure settings are respected.

* admins should be whitelisted

* checks for admin in post validation

* test for >= instead of == trust level
This commit is contained in:
Joffrey JAFFEUX 2017-12-04 14:47:11 +01:00 committed by Régis Hanol
parent f466791a15
commit 63bab32816
11 changed files with 241 additions and 13 deletions

View File

@ -1,15 +1,22 @@
import { withPluginApi } from 'discourse/lib/plugin-api';
import computed from 'ember-addons/ember-computed-decorators';
import showModal from 'discourse/lib/show-modal';
function initializePollUIBuilder(api) {
const siteSettings = api.container.lookup('site-settings:main');
if (!siteSettings.poll_enabled && (api.getCurrentUser() && !api.getCurrentUser().staff)) return;
api.modifyClass('controller:composer', {
@computed('siteSettings.poll_enabled', 'siteSettings.poll_minimum_trust_level_to_create')
canBuildPoll(pollEnabled, minimumTrustLevelToCreate) {
return pollEnabled &&
this.currentUser &&
(
this.currentUser.admin ||
this.currentUser.trust_level >= minimumTrustLevelToCreate
);
},
actions: {
showPollBuilder() {
showModal("poll-ui-builder").set("toolbarEvent", this.get("toolbarEvent"));
showModal('poll-ui-builder').set('toolbarEvent', this.get('toolbarEvent'));
}
}
});
@ -18,13 +25,14 @@ function initializePollUIBuilder(api) {
return {
action: 'showPollBuilder',
icon: 'bar-chart-o',
label: 'poll.ui_builder.title'
label: 'poll.ui_builder.title',
condition: 'canBuildPoll'
};
});
}
export default {
name: "add-poll-ui-builder",
name: 'add-poll-ui-builder',
initialize() {
withPluginApi('0.8.7', initializePollUIBuilder);

View File

@ -281,10 +281,7 @@ const rule = {
function newApiInit(helper) {
helper.registerOptions((opts, siteSettings) => {
const currentUser = (opts.getCurrentUser && opts.getCurrentUser(opts.userId)) || opts.currentUser;
const staff = currentUser && currentUser.staff;
opts.features.poll = !!siteSettings.poll_enabled || staff;
opts.features.poll = !!siteSettings.poll_enabled;
opts.pollMaximumOptions = siteSettings.poll_maximum_options;
});

View File

@ -16,9 +16,10 @@
en:
site_settings:
poll_enabled: "Allow users to create polls?"
poll_enabled: "Allow polls?"
poll_maximum_options: "Maximum number of options allowed in a poll."
poll_edit_window_mins: "Number of minutes after post creation during which polls can be edited."
poll_minimum_trust_level_to_create: "Define the minimum trust level needed to create polls"
poll:
multiple_polls_without_name: "There are multiple polls without a name. Use the '<code>name</code>' attribute to uniquely identify your polls."
@ -61,5 +62,7 @@ en:
topic_must_be_open_to_toggle_status: "The topic must be open to toggle status."
only_staff_or_op_can_toggle_status: "Only a staff member or the original poster can toggle a poll status."
insufficient_trust_level_to_create: "Your trust level (%{current}) is insufficient to create polls (required: %{required})"
email:
link_to_poll: "Click to view the poll."

View File

@ -7,3 +7,7 @@ plugins:
client: true
poll_edit_window_mins:
default: 5
poll_minimum_trust_level_to_create:
default: 1
client: true
enum: 'TrustLevelSetting'

View File

@ -0,0 +1,25 @@
module DiscoursePoll
class PostValidator
def initialize(post)
@post = post
end
def validate_post
min_trust_level = SiteSetting.poll_minimum_trust_level_to_create
trusted = @post&.user&.admin ||
@post&.user&.trust_level >= TrustLevel[min_trust_level]
if !trusted
message = I18n.t("poll.insufficient_trust_level_to_create",
current: @post&.user&.trust_level,
required: min_trust_level
)
@post.errors.add(:base, message)
return false
end
true
end
end
end

View File

@ -14,12 +14,12 @@ PLUGIN_NAME ||= "discourse_poll".freeze
DATA_PREFIX ||= "data-poll-".freeze
after_initialize do
module ::DiscoursePoll
DEFAULT_POLL_NAME ||= "poll".freeze
POLLS_CUSTOM_FIELD ||= "polls".freeze
VOTES_CUSTOM_FIELD ||= "polls-votes".freeze
autoload :PostValidator, "#{Rails.root}/plugins/poll/lib/post_validator"
autoload :PollsValidator, "#{Rails.root}/plugins/poll/lib/polls_validator"
autoload :PollsUpdater, "#{Rails.root}/plugins/poll/lib/polls_updater"
@ -314,6 +314,9 @@ after_initialize do
# only care when raw has changed!
return unless self.raw_changed? || force
validator = DiscoursePoll::PostValidator.new(self)
return unless validator.validate_post
validator = DiscoursePoll::PollsValidator.new(self)
return unless (polls = validator.validate_polls)

View File

@ -336,4 +336,100 @@ describe PostsController do
end
describe "disabled polls" do
before do
SiteSetting.poll_enabled = false
end
it "doesnt cook the poll" do
post :create, params: {
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).to be_success
json = ::JSON.parse(response.body)
expect(json["cooked"]).to eq("<p>[poll]</p>\n<ul>\n<li>A</li>\n<li>B<br>\n[/poll]</li>\n</ul>")
end
end
describe "insufficient trust level" do
before do
SiteSetting.poll_minimum_trust_level_to_create = 2
end
it "invalidates the post" do
log_in_user(Fabricate(:user, trust_level: 1))
post :create, params: {
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).not_to be_success
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(
I18n.t("poll.insufficient_trust_level_to_create",
current: user.trust_level,
required: SiteSetting.poll_minimum_trust_level_to_create
)
)
end
end
describe "equal trust level" do
before do
SiteSetting.poll_minimum_trust_level_to_create = 2
end
it "validates the post" do
log_in_user(Fabricate(:user, trust_level: 2))
post :create, params: {
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).to be_success
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
end
end
describe "superior trust level" do
before do
SiteSetting.poll_minimum_trust_level_to_create = 2
end
it "validates the post" do
log_in_user(Fabricate(:user, trust_level: 3))
post :create, params: {
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).to be_success
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
end
end
describe "admin with insufficient trust level" do
before do
SiteSetting.poll_minimum_trust_level_to_create = 2
end
it "validates the post" do
log_in_user(Fabricate(:user, admin: true, trust_level: 1))
post :create, params: {
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).to be_success
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
end
end
end

View File

@ -0,0 +1,41 @@
import { acceptance } from "helpers/qunit-helpers";
import { displayPollBuilderButton } from "discourse/plugins/poll/helpers/display-poll-builder-button";
import { replaceCurrentUser } from "discourse/plugins/poll/helpers/replace-current-user";
acceptance("Poll Builder - polls are disabled", {
loggedIn: true,
settings: {
poll_enabled: false,
poll_minimum_trust_level_to_create: 2
}
});
test("sufficient trust level", (assert) => {
replaceCurrentUser({ admin: false, trust_level: 3 });
displayPollBuilderButton();
andThen(() => {
assert.ok(!exists("button[title='Build Poll']"), "it hides the builder button");
});
});
test("insufficient trust level", (assert) => {
replaceCurrentUser({ admin: false, trust_level: 1 });
displayPollBuilderButton();
andThen(() => {
assert.ok(!exists("button[title='Build Poll']"), "it hides the builder button");
});
});
test("admin", (assert) => {
replaceCurrentUser({ admin: true });
displayPollBuilderButton();
andThen(() => {
assert.ok(!exists("button[title='Build Poll']"), "it hides the builder button");
});
});

View File

@ -0,0 +1,41 @@
import { acceptance } from "helpers/qunit-helpers";
import { displayPollBuilderButton } from "discourse/plugins/poll/helpers/display-poll-builder-button";
import { replaceCurrentUser } from "discourse/plugins/poll/helpers/replace-current-user";
acceptance("Poll Builder - polls are enabled", {
loggedIn: true,
settings: {
poll_enabled: true,
poll_minimum_trust_level_to_create: 1
}
});
test("sufficient trust level", (assert) => {
replaceCurrentUser({ admin: false, trust_level: 1 });
displayPollBuilderButton();
andThen(() => {
assert.ok(exists("button[title='Build Poll']"), "it shows the builder button");
});
});
test("insufficient trust level", (assert) => {
replaceCurrentUser({ admin: false, trust_level: 0 });
displayPollBuilderButton();
andThen(() => {
assert.ok(!exists("button[title='Build Poll']"), "it hides the builder button");
});
});
test("admin with insufficient trust level", (assert) => {
replaceCurrentUser({ admin: true, trust_level: 0 });
displayPollBuilderButton();
andThen(() => {
assert.ok(exists("button[title='Build Poll']"), "it shows the builder button");
});
});

View File

@ -0,0 +1,5 @@
export function displayPollBuilderButton() {
visit("/");
click("#create-topic");
click(".d-editor-button-bar .options");
}

View File

@ -0,0 +1,5 @@
export function replaceCurrentUser(properties) {
const currentUser = Discourse.User.current();
currentUser.setProperties(properties);
Discourse.User.resetCurrent(currentUser);
}