FEATURE: Allow users to customize bonuses for reviewable types
A new settings section in the review queue allows admins to specify that certain types of flags should be weighted higher than others.
This commit is contained in:
parent
da2f659635
commit
62956003c3
|
@ -0,0 +1,7 @@
|
||||||
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
|
export default RestAdapter.extend({
|
||||||
|
pathFor() {
|
||||||
|
return "/review/settings";
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,4 +1,5 @@
|
||||||
/* You might be looking for navigation-item. */
|
/* You might be looking for navigation-item. */
|
||||||
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
import computed from "ember-addons/ember-computed-decorators";
|
import computed from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
|
@ -6,9 +7,13 @@ export default Ember.Component.extend({
|
||||||
classNameBindings: ["active"],
|
classNameBindings: ["active"],
|
||||||
router: Ember.inject.service(),
|
router: Ember.inject.service(),
|
||||||
|
|
||||||
@computed("path")
|
@computed("label", "i18nLabel", "icon")
|
||||||
fullPath(path) {
|
contents(label, i18nLabel, icon) {
|
||||||
return Discourse.getURL(path);
|
let text = i18nLabel || I18n.t(label);
|
||||||
|
if (icon) {
|
||||||
|
return `${iconHTML(icon)} ${text}`.htmlSafe();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed("route", "router.currentRoute")
|
@computed("route", "router.currentRoute")
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
saving: false,
|
||||||
|
saved: false,
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
save() {
|
||||||
|
let bonuses = {};
|
||||||
|
this.get("settings.reviewable_score_types").forEach(st => {
|
||||||
|
bonuses[st.id] = parseFloat(st.score_bonus);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set("saving", true);
|
||||||
|
ajax("/review/settings", { method: "PUT", data: { bonuses } })
|
||||||
|
.then(() => {
|
||||||
|
this.set("saved", true);
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.finally(() => this.set("saving", false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -173,6 +173,7 @@ export default function() {
|
||||||
this.route("show", { path: "/:reviewable_id" });
|
this.route("show", { path: "/:reviewable_id" });
|
||||||
this.route("index", { path: "/" });
|
this.route("index", { path: "/" });
|
||||||
this.route("topics", { path: "/topics" });
|
this.route("topics", { path: "/topics" });
|
||||||
|
this.route("settings", { path: "/settings" });
|
||||||
});
|
});
|
||||||
this.route("signup", { path: "/signup" });
|
this.route("signup", { path: "/signup" });
|
||||||
this.route("login", { path: "/login" });
|
this.route("login", { path: "/login" });
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default Discourse.Route.extend({
|
||||||
|
model() {
|
||||||
|
return this.store.find("reviewable-settings");
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
controller.set("settings", model);
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,17 +1,7 @@
|
||||||
{{#if routeParam}}
|
{{#if routeParam}}
|
||||||
{{#if i18nLabel}}
|
{{#link-to route routeParam}}{{contents}}{{/link-to}}
|
||||||
{{#link-to route routeParam}}{{i18nLabel}}{{/link-to}}
|
{{else if route}}
|
||||||
{{else}}
|
{{#link-to route}}{{contents}}{{/link-to}}
|
||||||
{{#link-to route routeParam}}{{i18n label}}{{/link-to}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if route}}
|
<a href="{{get-url path}}" data-auto-route="true">{{contents}}</a>
|
||||||
{{#link-to route}}{{i18n label}}{{/link-to}}
|
|
||||||
{{else}}
|
|
||||||
{{#if path}}
|
|
||||||
<a href="{{unbound fullPath}}" data-auto-route="true">{{i18n label}}</a>
|
|
||||||
{{else}}
|
|
||||||
<a href="{{unbound href}}" data-auto-route="true">{{i18n label}}</a>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,83 +1,76 @@
|
||||||
<div class="reviewable">
|
<div class="reviewable-container">
|
||||||
<ul class="nav nav-pills reviewable-title">
|
<div class="reviewable-list">
|
||||||
{{nav-item route='review.index' label='review.view_all'}}
|
{{#if reviewables}}
|
||||||
{{nav-item route='review.topics' label='review.grouped_by_topic'}}
|
{{#load-more selector=".reviewable-item" action=(action "loadMore")}}
|
||||||
</ul>
|
<div class='reviewables'>
|
||||||
|
{{#each reviewables as |r|}}
|
||||||
<div class="reviewable-container">
|
{{reviewable-item reviewable=r remove=(action "remove")}}
|
||||||
<div class="reviewable-list">
|
{{/each}}
|
||||||
{{#if reviewables}}
|
|
||||||
{{#load-more selector=".reviewable-item" action=(action "loadMore")}}
|
|
||||||
<div class='reviewables'>
|
|
||||||
{{#each reviewables as |r|}}
|
|
||||||
{{reviewable-item reviewable=r remove=(action "remove")}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/load-more}}
|
|
||||||
{{conditional-loading-spinner condition=reviewables.loadingMore}}
|
|
||||||
{{else}}
|
|
||||||
<div class="no-review">
|
|
||||||
{{i18n "review.none"}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/load-more}}
|
||||||
|
{{conditional-loading-spinner condition=reviewables.loadingMore}}
|
||||||
|
{{else}}
|
||||||
|
<div class="no-review">
|
||||||
|
{{i18n "review.none"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='reviewable-filters'>
|
||||||
|
<div class='reviewable-filter'>
|
||||||
|
<label class='filter-label'>{{i18n "review.filters.status"}}</label>
|
||||||
|
{{combo-box value=filterStatus content=statuses}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='reviewable-filters'>
|
{{#if filtersExpanded}}
|
||||||
<div class='reviewable-filter'>
|
<div class='reviewable-filter'>
|
||||||
<label class='filter-label'>{{i18n "review.filters.status"}}</label>
|
<label class='filter-label'>{{i18n "review.filters.type.title"}}</label>
|
||||||
{{combo-box value=filterStatus content=statuses}}
|
{{combo-box value=filterType content=allTypes none="review.filters.type.all"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if filtersExpanded}}
|
<div class='reviewable-filter'>
|
||||||
<div class='reviewable-filter'>
|
<label class='filter-label'>{{i18n "review.filters.minimum_score"}}</label>
|
||||||
<label class='filter-label'>{{i18n "review.filters.type.title"}}</label>
|
{{input value=filterScore class="score-filter"}}
|
||||||
{{combo-box value=filterType content=allTypes none="review.filters.type.all"}}
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='reviewable-filter'>
|
<div class='reviewable-filter'>
|
||||||
<label class='filter-label'>{{i18n "review.filters.minimum_score"}}</label>
|
<label class='filter-label'>{{i18n "review.filters.category"}}</label>
|
||||||
{{input value=filterScore class="score-filter"}}
|
{{category-chooser none="category.all" value=filterCategoryId}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='reviewable-filter'>
|
<div class='reviewable-filter topic-filter'>
|
||||||
<label class='filter-label'>{{i18n "review.filters.category"}}</label>
|
{{i18n "review.filtered_user"}}
|
||||||
{{category-chooser none="category.all" value=filterCategoryId}}
|
{{user-selector
|
||||||
</div>
|
excludeCurrentUser=false
|
||||||
|
usernames=filterUsername
|
||||||
|
fullWidthWrap="true"
|
||||||
|
class="user-selector"
|
||||||
|
single="true"
|
||||||
|
canReceiveUpdates="true"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if filterTopic}}
|
||||||
<div class='reviewable-filter topic-filter'>
|
<div class='reviewable-filter topic-filter'>
|
||||||
{{i18n "review.filtered_user"}}
|
{{i18n "review.filtered_topic"}}
|
||||||
{{user-selector
|
{{d-button label="review.show_all_topics" icon="times" action=(action "resetTopic")}}
|
||||||
excludeCurrentUser=false
|
|
||||||
usernames=filterUsername
|
|
||||||
fullWidthWrap="true"
|
|
||||||
class="user-selector"
|
|
||||||
single="true"
|
|
||||||
canReceiveUpdates="true"}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if filterTopic}}
|
|
||||||
<div class='reviewable-filter topic-filter'>
|
|
||||||
{{i18n "review.filtered_topic"}}
|
|
||||||
{{d-button label="review.show_all_topics" icon="times" action=(action "resetTopic")}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class='reviewable-filters-actions'>
|
<div class='reviewable-filters-actions'>
|
||||||
|
{{d-button
|
||||||
|
icon="sync"
|
||||||
|
label="review.filters.refresh"
|
||||||
|
|
||||||
|
class="btn-primary refresh" action=(action "refresh")}}
|
||||||
|
|
||||||
|
{{#if site.mobileView}}
|
||||||
{{d-button
|
{{d-button
|
||||||
icon="sync"
|
label="show_help"
|
||||||
label="review.filters.refresh"
|
icon=toggleFiltersIcon
|
||||||
|
class="btn-default expand-secondary-filters"
|
||||||
class="btn-primary refresh" action=(action "refresh")}}
|
action=(action "toggleFilters")}}
|
||||||
|
{{/if}}
|
||||||
{{#if site.mobileView}}
|
|
||||||
{{d-button
|
|
||||||
label="show_help"
|
|
||||||
icon=toggleFiltersIcon
|
|
||||||
class="btn-default expand-secondary-filters"
|
|
||||||
action=(action "toggleFilters")}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class='reviewable-settings'>
|
||||||
|
<h4>{{i18n "review.settings.score_bonuses.title"}}</h4>
|
||||||
|
<p class='description'>{{i18n "review.settings.score_bonuses.description"}}</p>
|
||||||
|
|
||||||
|
{{#each settings.reviewable_score_types as |rst|}}
|
||||||
|
<div class='reviewable-score-type'>
|
||||||
|
<div class='title'>{{rst.title}}</div>
|
||||||
|
<div class='field'>
|
||||||
|
{{input value=rst.score_bonus}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<div class='reviewable-score-type'>
|
||||||
|
<div class='title'></div>
|
||||||
|
<div class='field'>
|
||||||
|
{{d-button
|
||||||
|
icon="check"
|
||||||
|
label="review.settings.save_changes"
|
||||||
|
class="btn-primary save-settings"
|
||||||
|
action=(action "save")
|
||||||
|
disabled=saving}}
|
||||||
|
|
||||||
|
{{#if saved}}
|
||||||
|
<span class='saved'>{{i18n "review.settings.saved"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,45 +1,38 @@
|
||||||
<div class="reviewable">
|
{{#if reviewableTopics}}
|
||||||
<ul class="nav nav-pills reviewable-title">
|
<table class='reviewable-topics'>
|
||||||
{{nav-item route='review.index' routeParam=(query-params topic_id=null) label='review.view_all'}}
|
<thead>
|
||||||
{{nav-item route='review.topics' label='review.grouped_by_topic'}}
|
<th>{{i18n "review.topics.topic"}} </th>
|
||||||
</ul>
|
<th>{{i18n "review.topics.reviewable_count"}}</th>
|
||||||
|
<th>{{i18n "review.topics.reported_by"}}</th>
|
||||||
{{#if reviewableTopics}}
|
<th></th>
|
||||||
<table class='reviewable-topics'>
|
</thead>
|
||||||
<thead>
|
<tbody>
|
||||||
<th>{{i18n "review.topics.topic"}} </th>
|
{{#each reviewableTopics as |rt|}}
|
||||||
<th>{{i18n "review.topics.reviewable_count"}}</th>
|
<tr class='reviewable-topic'>
|
||||||
<th>{{i18n "review.topics.reported_by"}}</th>
|
<td class="topic-title">
|
||||||
<th></th>
|
<div class='combined-title'>
|
||||||
</thead>
|
{{topic-status topic=rt}}
|
||||||
<tbody>
|
<a href={{rt.relative_url}} target="_blank">{{replace-emoji rt.fancy_title}}</a>
|
||||||
{{#each reviewableTopics as |rt|}}
|
</div>
|
||||||
<tr class='reviewable-topic'>
|
</td>
|
||||||
<td class="topic-title">
|
<td class="reviewable-count">
|
||||||
<div class='combined-title'>
|
{{rt.stats.count}}
|
||||||
{{topic-status topic=rt}}
|
</td>
|
||||||
<a href={{rt.relative_url}} target="_blank">{{replace-emoji rt.fancy_title}}</a>
|
<td class="reported-by">
|
||||||
</div>
|
{{i18n "review.topics.unique_users" count=rt.stats.unique_users}}
|
||||||
</td>
|
</td>
|
||||||
<td class="reviewable-count">
|
<td class="reviewable-details">
|
||||||
{{rt.stats.count}}
|
{{#link-to "review.index" (query-params topic_id=rt.id) class="btn btn-primary btn-small"}}
|
||||||
</td>
|
{{d-icon "list"}}
|
||||||
<td class="reported-by">
|
<span>{{i18n "review.topics.details"}}</span>
|
||||||
{{i18n "review.topics.unique_users" count=rt.stats.unique_users}}
|
{{/link-to}}
|
||||||
</td>
|
</td>
|
||||||
<td class="reviewable-details">
|
</tr>
|
||||||
{{#link-to "review.index" (query-params topic_id=rt.id) class="btn btn-primary btn-small"}}
|
{{/each}}
|
||||||
{{d-icon "list"}}
|
</tbody>
|
||||||
<span>{{i18n "review.topics.details"}}</span>
|
</table>
|
||||||
{{/link-to}}
|
{{else}}
|
||||||
</td>
|
<div class="no-review">
|
||||||
</tr>
|
{{i18n "review.none"}}
|
||||||
{{/each}}
|
</div>
|
||||||
</tbody>
|
{{/if}}
|
||||||
</table>
|
|
||||||
{{else}}
|
|
||||||
<div class="no-review">
|
|
||||||
{{i18n "review.none"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
{{outlet}}
|
<div class="reviewable">
|
||||||
|
<ul class="nav nav-pills reviewable-title">
|
||||||
|
{{nav-item route='review.index' label='review.view_all'}}
|
||||||
|
{{nav-item route='review.topics' label='review.grouped_by_topic'}}
|
||||||
|
{{#if currentUser.admin}}
|
||||||
|
{{nav-item route='review.settings' label='review.settings.title' icon='wrench'}}
|
||||||
|
{{/if}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
.reviewable {
|
.reviewable {
|
||||||
|
.nav-pills {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.reviewable-container {
|
.reviewable-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -23,6 +27,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reviewable-settings {
|
||||||
|
p.description {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.saved {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
.reviewable-score-type {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.reviewable-user-details {
|
.reviewable-user-details {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,21 @@ class ReviewablesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def settings
|
||||||
|
raise Discourse::InvalidAccess.new unless current_user.admin?
|
||||||
|
|
||||||
|
post_action_types = PostActionType.where(id: PostActionType.flag_types.values).order('id')
|
||||||
|
data = { reviewable_score_types: post_action_types }
|
||||||
|
|
||||||
|
if request.put?
|
||||||
|
params[:bonuses].each do |id, bonus|
|
||||||
|
PostActionType.where(id: id).update_all(score_bonus: bonus.to_f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
render_serialized(data, ReviewableSettingsSerializer, rest_serializer: true)
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def find_reviewable
|
def find_reviewable
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
class ReviewableScoreBonusSerializer < ApplicationSerializer
|
||||||
|
attributes :id, :name, :score_bonus
|
||||||
|
|
||||||
|
def name
|
||||||
|
I18n.t("post_action_types.#{object.name_key}.title")
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,5 @@
|
||||||
class ReviewableScoreTypeSerializer < ApplicationSerializer
|
class ReviewableScoreTypeSerializer < ApplicationSerializer
|
||||||
attributes :id, :title
|
attributes :id, :title, :score_bonus
|
||||||
|
|
||||||
# Allow us to share post action type translations for backwards compatibility
|
# Allow us to share post action type translations for backwards compatibility
|
||||||
def title
|
def title
|
||||||
|
@ -7,4 +7,12 @@ class ReviewableScoreTypeSerializer < ApplicationSerializer
|
||||||
I18n.t("reviewable_score_types.#{ReviewableScore.types[id]}.title")
|
I18n.t("reviewable_score_types.#{ReviewableScore.types[id]}.title")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def score_bonus
|
||||||
|
object.score_bonus.to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_score_bonus?
|
||||||
|
object.respond_to?(:score_bonus)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
class ReviewableSettingsSerializer < ApplicationSerializer
|
||||||
|
attributes :id
|
||||||
|
|
||||||
|
has_many :reviewable_score_types, serializer: ReviewableScoreTypeSerializer
|
||||||
|
|
||||||
|
def id
|
||||||
|
scope.user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def reviewable_score_types
|
||||||
|
object[:reviewable_score_types]
|
||||||
|
end
|
||||||
|
end
|
|
@ -359,9 +359,17 @@ en:
|
||||||
placeholder: "type the message title here"
|
placeholder: "type the message title here"
|
||||||
|
|
||||||
review:
|
review:
|
||||||
|
settings:
|
||||||
|
saved: "Saved"
|
||||||
|
save_changes: "Save Changes"
|
||||||
|
title: "Settings"
|
||||||
|
score_bonuses:
|
||||||
|
title: "Score Bonuses"
|
||||||
|
description: "Bonuses allow certain types to be scored higher than others so they can be prioritized. Note: changing these values will not apply to previously scored items."
|
||||||
|
|
||||||
moderation_history: "Moderation History"
|
moderation_history: "Moderation History"
|
||||||
view_all: "view all"
|
view_all: "View All"
|
||||||
grouped_by_topic: "grouped by topic"
|
grouped_by_topic: "Grouped by Topic"
|
||||||
none: "There are no items to review."
|
none: "There are no items to review."
|
||||||
view_pending: "view pending"
|
view_pending: "view pending"
|
||||||
topic_has_pending:
|
topic_has_pending:
|
||||||
|
|
|
@ -318,6 +318,8 @@ Discourse::Application.routes.draw do
|
||||||
get "review" => "reviewables#index" # For ember app
|
get "review" => "reviewables#index" # For ember app
|
||||||
get "review/:reviewable_id" => "reviewables#show", constraints: { reviewable_id: /\d+/ }
|
get "review/:reviewable_id" => "reviewables#show", constraints: { reviewable_id: /\d+/ }
|
||||||
get "review/topics" => "reviewables#topics"
|
get "review/topics" => "reviewables#topics"
|
||||||
|
get "review/settings" => "reviewables#settings"
|
||||||
|
put "review/settings" => "reviewables#settings"
|
||||||
put "review/:reviewable_id/perform/:action_id" => "reviewables#perform", constraints: {
|
put "review/:reviewable_id/perform/:action_id" => "reviewables#perform", constraints: {
|
||||||
reviewable_id: /\d+/,
|
reviewable_id: /\d+/,
|
||||||
action_id: /[a-z\_]+/
|
action_id: /[a-z\_]+/
|
||||||
|
|
|
@ -12,6 +12,22 @@ describe ReviewablesController do
|
||||||
put "/review/123/perform/approve.json"
|
put "/review/123/perform/approve.json"
|
||||||
expect(response.code).to eq("403")
|
expect(response.code).to eq("403")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "denies settings" do
|
||||||
|
get "/review/settings.json"
|
||||||
|
expect(response.code).to eq("403")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "regular user" do
|
||||||
|
before do
|
||||||
|
sign_in(Fabricate(:user))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not allow settings" do
|
||||||
|
get "/review/settings.json"
|
||||||
|
expect(response.code).to eq("403")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when logged in" do
|
context "when logged in" do
|
||||||
|
@ -307,6 +323,22 @@ describe ReviewablesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "#settings" do
|
||||||
|
it "renders the settings as JSON" do
|
||||||
|
get "/review/settings.json"
|
||||||
|
expect(response.code).to eq("200")
|
||||||
|
json = ::JSON.parse(response.body)
|
||||||
|
expect(json['reviewable_settings']).to be_present
|
||||||
|
expect(json['reviewable_score_types']).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows the settings to be updated" do
|
||||||
|
put "/review/settings.json", params: { bonuses: { 8 => 3.45 } }
|
||||||
|
expect(response.code).to eq("200")
|
||||||
|
expect(PostActionType.find_by(id: 8).score_bonus).to eq(3.45)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "#update" do
|
context "#update" do
|
||||||
let(:reviewable) { Fabricate(:reviewable) }
|
let(:reviewable) { Fabricate(:reviewable) }
|
||||||
let(:reviewable_post) { Fabricate(:reviewable_queued_post) }
|
let(:reviewable_post) { Fabricate(:reviewable_queued_post) }
|
||||||
|
|
|
@ -33,6 +33,16 @@ QUnit.test("Grouped by topic", async assert => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test("Settings", async assert => {
|
||||||
|
await visit("/review/settings");
|
||||||
|
|
||||||
|
assert.ok(find(".reviewable-score-type").length, "has a list of bonuses");
|
||||||
|
|
||||||
|
await fillIn(".reviewable-score-type:eq(0) .field input ", "0.5");
|
||||||
|
await click(".save-settings");
|
||||||
|
assert.ok(find(".reviewable-settings .saved").length, "it saved");
|
||||||
|
});
|
||||||
|
|
||||||
QUnit.test("Flag related", async assert => {
|
QUnit.test("Flag related", async assert => {
|
||||||
await visit("/review");
|
await visit("/review");
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,30 @@ export default function(helpers) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.get("/review/settings", () => {
|
||||||
|
return response(200, {
|
||||||
|
reviewable_score_types: [
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Off-Topic",
|
||||||
|
score_bonus: 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Inappropriate",
|
||||||
|
score_bonus: 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
reviewable_settings: {
|
||||||
|
id: 13870,
|
||||||
|
reviewable_score_type_ids: [3, 4]
|
||||||
|
},
|
||||||
|
__rest_serializer: "1"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.put("/review/settings", () => response(200, {}));
|
||||||
|
|
||||||
this.get("/review/:id", () => {
|
this.get("/review/:id", () => {
|
||||||
return response(200, {
|
return response(200, {
|
||||||
reviewable: flag
|
reviewable: flag
|
||||||
|
|
Loading…
Reference in New Issue