Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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: 104 additions & 108 deletions app/views/curator/users.html.erb
Original file line number Diff line number Diff line change
@@ -1,132 +1,128 @@
<% 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 %>
<h2><%= t('curation.users.title') %></h2>

<div class="form-inline">
<% if current_user.is_admin? %>
<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 %>

<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>
<% end %>
Curate Users
</h2>

<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