Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ document.addEventListener("turbolinks:load", function(e) {
// Testing section on source page
Sources.init();

// Approve/reject user curation buttons
Curation.init();

var setStarButtonState = function (button) {
if (button.data('starred')) {
button.html("<i class='icon icon-h3 star-fill-icon'> </i>");
Expand Down
36 changes: 36 additions & 0 deletions app/assets/javascripts/curation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Curation = {
applyParam: function (select, param) {
const value = select.value;
const url = new URL(window.location.href);
if (!value) {
url.searchParams.delete(param);
} else {
url.searchParams.set(param, value);
}
window.location.replace(url.toString());
},

curateUser: function (e) {
e.preventDefault();
const url = $(this).parents('.curate-user-buttons').data('actionUrl');
const panel = $(this).parents('.curate-user');
panel.fadeOut('fast');

$.ajax({
url: url,
method: 'PUT',
dataType: 'script',
data: { user: { role_id: $(this).data('roleId') } }
}).fail(function (e) {
panel.show();
console.error(e);
alert('An error occurred while attempting to curate the user.');
});

return false;
},

init: function () {
$('.curate-user-buttons .btn').click(Curation.curateUser);
}
}
11 changes: 10 additions & 1 deletion app/controllers/curator_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ def topic_suggestions
def users
@role = Role.fetch(params[:role]) if current_user.is_admin?
@role ||= Role.fetch('unverified_user')
@users = User.with_role(@role).order('created_at DESC')
@users = User.with_role(@role)
max_age = nil
if params[:max_age]
begin
max_age = ActiveSupport::Duration.parse(params[:max_age])
rescue ArgumentError
end
end
@users = @users.where('users.created_at > ?', max_age.ago) if max_age
@users = @users.order('created_at DESC')
if params[:with_content]
@users = @users.includes(*User::CREATED_RESOURCE_TYPES).with_created_resources
end
Expand Down
19 changes: 17 additions & 2 deletions app/helpers/curators_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# The controller for actions related to the Ban model
module CuratorsHelper

MAX_AGE_OPTIONS = [
{ period: nil, key: 'any' }.with_indifferent_access,
{ period: 1.week, key: 'week' }.with_indifferent_access,
{ period: 1.month, key: 'month' }.with_indifferent_access,
{ period: 1.year, key: 'year' }.with_indifferent_access
].freeze

def print_curation_action(action)
resource, action = action.split('.')
if action
Expand All @@ -20,6 +27,14 @@ def role_options(selected_role, scope: User)
options_for_select(array, selected_role.name)
end

def max_age_options(selected_age = nil)
array = MAX_AGE_OPTIONS.map do |age|
[t("curation.users.filters.max_age.options.#{age[:key]}"), age[:period]&.iso8601]
end

options_for_select(array, selected_age)
end

def recent_approvals
PublicActivity::Activity.where(key: 'user.change_role').where('created_at > ?', 3.months.ago).order('created_at DESC').select do |activity|
[Role.rejected.id, Role.approved.id].include?(activity.parameters[:new])
Expand All @@ -28,10 +43,10 @@ def recent_approvals

def approval_message(role_id)
if role_id == Role.approved.id
text = 'approved'
text = t('curation.users.activity.approved')
css_class = 'text-success'
else
text = 'rejected'
text = t('curation.users.activity.rejected')
css_class = 'text-danger'
end

Expand Down
2 changes: 1 addition & 1 deletion app/views/curation_mailer/user_requires_approval.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@
<li><b>Reject</b> - Mark this user as a spammer, preventing them from creating further content on <%= TeSS::Config.site['title_short'] %>.</li>
</ul>

<%= link_to('Click here to approve or reject this user', curate_users_url(with_content: true)) %>
<%= link_to('Click here to approve or reject this user', curate_users_url(with_content: true, max_age: 1.month.iso8601)) %>
2 changes: 1 addition & 1 deletion app/views/curation_mailer/user_requires_approval.text.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ This user's resources are currently hidden. As a curator, you can decide whether
"Reject" - Mark this user as a spammer, preventing them from creating further content on <%= TeSS::Config.site['title_short'] %>.

To approve or reject this user, please visit:
<%= curate_users_url(with_content: true) %>
<%= curate_users_url(with_content: true, max_age: 1.month.iso8601) %>
212 changes: 103 additions & 109 deletions app/views/curator/users.html.erb
Original file line number Diff line number Diff line change
@@ -1,132 +1,126 @@
<% filter_params = {} %>
<% filter_params[:role] = params[:role] if params.key?(:role) %>
<% filter_params[:with_content] = params[:with_content] if params.key?(:with_content) %>
<% filter_params[:max_age] = params[:max_age] if params.key?(:max_age) %>

<div class="page-header">
<h2>
<% if current_user.is_admin? %>
<div class="pull-right form-inline">
<select id="role_id" onchange="window.location = '<%= url_for(filter_params.merge(role: '_ROLE_')) -%>'.replace('_ROLE_', $(this).val());" autocomplete="off" class="form-control" style="width: 15em">
<%= role_options(@role) %>
</select>
<div class="checkbox btn btn-default<%= ' active' if params[:with_content] -%>" title="Only show users who have created content.">
<%= label_tag do %>
<%= check_box_tag :with_content, true, params[:with_content], autocomplete: 'off',
data: { url: url_for(params[:with_content] ? filter_params.except(:with_content) : filter_params.merge({ with_content: true })) },
onchange: "window.location = $(this).data('url');" %>
with content?
<% end %>
</div>
<h2><%= t('curation.users.title') %></h2>

<div class="form-inline">
<div class="form-group">
<%= label_tag('role_id', t('curation.users.filters.with_role.title')) %>
<%= select_tag('role_id', role_options(@role),
onchange: "Curation.applyParam(this, 'role')",
title: t('curation.users.filters.with_role.hint'),
autocomplete: 'off', class: 'form-control') %>
</div>
<% end %>
Curate Users
</h2>

<div class="form-group">
<%= label_tag('max_age', t('curation.users.filters.max_age.title')) %>
<%= select_tag('max_age', max_age_options(filter_params[:max_age]),
onchange: "Curation.applyParam(this, 'max_age')",
title: t('curation.users.filters.max_age.hint'),
autocomplete: 'off', class: 'form-control') %>
</div>

<div class="checkbox btn btn-default<%= ' active' if params[:with_content] -%>" title="<%= t('curation.users.filters.with_content.hint') %>">
<%= label_tag do %>
<%= check_box_tag :with_content, true, params[:with_content], autocomplete: 'off',
data: { url: url_for(params[:with_content] ? filter_params.except(:with_content) : filter_params.merge({ with_content: true })) },
onchange: "window.location = $(this).data('url');" %>
<%= t('curation.users.filters.with_content.title') %>
<% end %>
</div>
</div>
</div>

<div class="col-sm-4 col-sm-push-8">
<div class="panel panel-default" id="recent-user-curation-activity">
<div class="panel-heading">Recent Curation Activity</div>
<div class="panel-body">
<% activities = recent_approvals %>
<% if activities.any? %>
<ul class="recent-approvals">
<% activities.each do |activity| %>
<li>
<strong><%= link_to activity.trackable.name, activity.trackable, target: '_blank' -%></strong> was
<%= approval_message(activity.parameters[:new]) -%> by <%= activity.owner.try(:username) -%>
<%= time_ago_in_words(activity.created_at) -%> ago.
</li>
<% end %>
</ul>
<% else %>
<span class="muted">No recent approvals/rejections.</span>
<% end %>
<div class="row">
<div class="col-sm-4 col-sm-push-8">
<div class="panel panel-default" id="recent-user-curation-activity">
<div class="panel-heading"><%= t('curation.users.recent_activity.title') %></div>
<div class="panel-body">
<% activities = recent_approvals %>
<% if activities.any? %>
<ul class="recent-approvals">
<% activities.each do |activity| %>
<li>
<strong><%= link_to activity.trackable.name, activity.trackable, target: '_blank' -%></strong> -
<%= approval_message(activity.parameters[:new]) -%>
<%= t('curation.users.activity.info',
curator: activity.owner.try(:username),
time: time_ago_in_words(activity.created_at)) -%>
</li>
<% end %>
</ul>
<% else %>
<span class="muted"><%= t('curation.users.recent_activity.empty') %></span>
<% end %>
</div>
</div>
</div>
</div>

<div class="col-sm-8 col-sm-pull-4">
<% if @users.any? %>
<% @users.each do |user| %>
<div class="panel panel-default curate-user">
<div class="panel-heading">
<%= link_to user.name, user, target: '_blank' %>
<div class="col-sm-8 col-sm-pull-4">
<% if @users.any? %>
<% @users.each do |user| %>
<div class="panel panel-default curate-user">
<div class="panel-heading">
<%= link_to user.name, user, target: '_blank' %>

<% if user.banned? %>
<span class="text-danger">(<%= user.shadowbanned? ? 'shadowbanned' : 'banned'-%>)</span>
<% end %>
<% if user.banned? %>
<span class="text-danger">(<%= user.shadowbanned? ? 'shadowbanned' : 'banned'-%>)</span>
<% end %>

<div class="pull-right curate-user-buttons" data-action-url="<%= user_path(user) -%>">
<%= link_to('Approve', '#', class: 'btn btn-xs btn-success', 'data-role-id' => Role.approved.id ) %>
<%= link_to('Reject', '#', class: 'btn btn-xs btn-danger', 'data-role-id' => Role.rejected.id ) %>
<div class="pull-right curate-user-buttons" data-action-url="<%= user_path(user) -%>">
<%= link_to('Approve', '#', class: 'btn btn-xs btn-success', 'data-role-id' => Role.approved.id ) %>
<%= link_to('Reject', '#', class: 'btn btn-xs btn-danger', 'data-role-id' => Role.rejected.id ) %>
</div>
</div>
</div>
<div class="panel-body">
<i>Registered <%= time_ago_in_words(user.created_at) -%> ago</i><br/>
<div class="panel-body">
<i><%= t('curation.users.list.registered_time', time: time_ago_in_words(user.created_at)) -%></i><br/>

<strong>Public email:</strong>
<% if user.profile.email.blank? %>
<span class="empty">None specified</span>
<% else %>
<%= mail_to user.profile.email %>
<% end %><br/>
<strong><%= Profile.human_attribute_name(:email) %>:</strong>
<% if user.profile.email.blank? %>
<span class="empty"><%= t('curation.users.list.blank_attribute') %></span>
<% else %>
<%= mail_to user.profile.email %>
<% end %><br/>

<strong><%= User.human_attribute_name(:website) %>:</strong>
<% if user.profile.website.blank? %>
<span class="empty">None specified</span>
<% else %>
<%= link_to user.profile.website, user.profile.website, rel: 'nofollow' %>
<% end %><br/>
<strong><%= User.human_attribute_name(:website) %>:</strong>
<% if user.profile.website.blank? %>
<span class="empty"><%= t('curation.users.list.blank_attribute') %></span>
<% else %>
<%= link_to user.profile.website, user.profile.website, rel: 'nofollow' %>
<% end %><br/>

<% User::CREATED_RESOURCE_TYPES.each do |type| %>
<% count = user.send(type).count %>
<% if count > 0 %>
<% resources = user.send(type).order(created_at: :desc).first(3) %>
<strong><%= resources.first.class.model_name.human -%></strong>
<ul>
<% resources.each do |resource| %>
<li>
<%= link_to resource.title, resource, target: '_blank' %>
<% if resource.respond_to?(:url) %>
<br/>
URL: <%= link_to resource.url, resource.url, target: '_blank', rel: 'nofollow noreferrer noopener' -%>
<% end %>
</li>
<% User::CREATED_RESOURCE_TYPES.each do |type| %>
<% count = user.send(type).count %>
<% if count > 0 %>
<% resources = user.send(type).order(created_at: :desc).first(3) %>
<strong><%= resources.first.class.model_name.human -%></strong>
<ul>
<% resources.each do |resource| %>
<li>
<%= link_to resource.title, resource, target: '_blank' %>
<% if resource.respond_to?(:url) %>
<br/>
URL: <%= link_to resource.url, resource.url, target: '_blank', rel: 'nofollow noreferrer noopener' -%>
<% end %>
</li>
<% end %>
</ul>
<% if count > 3 %>
<%= link_to "See all #{pluralize(count, resources.first.class.model_name.human)}",
polymorphic_path(type, user: user.username), target: '_blank' -%><br/>
<% end %>
</ul>
<% if count > 3 %>
<%= link_to "See all #{pluralize(count, resources.first.class.model_name.human)}",
polymorphic_path(type, user: user.username), target: '_blank' -%><br/>
<% end %>
<% end %>
<% end %>
</div>
</div>
</div>
<% end %>
<% end %>

<%= render partial: 'search/common/pagination_bar', locals: { resources: @users } %>
<% else %>
Could not find any <%= @role.title.downcase.pluralize-%> requiring approval. Another curator may have already taken action.
<% end %>
<%= render partial: 'search/common/pagination_bar', locals: { resources: @users } %>
<% else %>
<%= t('curation.users.list.empty', role: @role.title.downcase.pluralize)-%>
<% end %>
</div>
</div>

<script>
$('.curate-user-buttons .btn').click(function () {
var url = $(this).parents('.curate-user-buttons').data('actionUrl');
var panel = $(this).parents('.curate-user');

$.ajax({
url: url,
method: 'PUT',
dataType: 'script',
data: { user: { role_id: $(this).data('roleId') } }
}).done(function () {
panel.fadeOut();
}).fail(function (e) {
console.log(e);
alert('An error occurred while attempting to curate the user.');
});

return false;
});
</script>
2 changes: 1 addition & 1 deletion app/views/layouts/_user_menu.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

<% if is_admin || is_curator %>
<li class="dropdown-item">
<%= link_to curate_users_path(with_content: true) do %>
<%= link_to curate_users_path(with_content: true, max_age: 1.month.iso8601) do %>
<i class="fa fa-user-times"></i> <%= t('menu.user.curate_users') %>
<% end %>
</li>
Expand Down
Loading
Loading