# Offense count: 41
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
- Max: 247
+ Max: 257
# Offense count: 12
# Configuration parameters: CountBlocks.
# Markdown formatting support
gem "redcarpet"
+# For status transitions of Issues
+gem "aasm"
+
# Load libxml support for XML parsing and generation
gem "libxml-ruby", ">= 2.0.5", :require => "libxml"
remote: https://rubygems.org/
specs:
SystemTimer (1.2.3)
+ aasm (4.1.0)
actioncable (5.1.5)
actionpack (= 5.1.5)
nio4r (~> 2.0)
DEPENDENCIES
SystemTimer (>= 1.1.3)
+ aasm
actionpack-page_caching
annotate
autoprefixer-rails
display: none;
}
}
+
+.read-reports {
+ background: #eee;
+ opacity: 0.7;
+}
+
+.report-related-block {
+ display:inline-block;
+}
+
+.report-block {
+ width:475px;
+ float:left;
+ margin-right:100px;
+}
+
+.related-reports {
+ width: 280px;
+ float: right;
+
+ ul {
+ padding-left: $lineheight;
+ margin-bottom: 0;
+
+ li {
+ list-style: disc;
+ }
+ }
+}
+
+.issue-comments {
+ width:475px;
+}
+
+.issues-list {
+ td:nth-child(2) {
+ white-space: nowrap;
+ }
+}
+
+.report-disclaimer {
+ background: #fff1f0;
+ color: #d85030;
+ border-color: rgba(216, 80, 48, 0.3);
+ padding: 10px 20px;
+ margin-bottom: $lineheight;
+
+ ul {
+ padding-left: $lineheight;
+ margin-bottom: 0;
+
+ li {
+ list-style: disc;
+ }
+ }
+}
--- /dev/null
+class IssueCommentsController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :require_user
+ before_action :check_permission
+
+ def create
+ @issue = Issue.find(params[:issue_id])
+ comment = @issue.comments.build(issue_comment_params)
+ comment.user = current_user
+ comment.save!
+ notice = t(".comment_created")
+ reassign_issue(@issue) if params[:reassign]
+ redirect_to @issue, :notice => notice
+ end
+
+ private
+
+ def issue_comment_params
+ params.require(:issue_comment).permit(:body)
+ end
+
+ def check_permission
+ unless current_user.administrator? || current_user.moderator?
+ flash[:error] = t("application.require_moderator_or_admin.not_a_moderator_or_admin")
+ redirect_to root_path
+ end
+ end
+
+ # This sort of assumes there are only two roles
+ def reassign_issue(issue)
+ role = (Issue::ASSIGNED_ROLES - [issue.assigned_role]).first
+ issue.assigned_role = role
+ issue.save!
+ end
+end
--- /dev/null
+class IssuesController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :require_user
+ before_action :check_permission
+ before_action :find_issue, :only => [:show, :resolve, :reopen, :ignore]
+
+ def index
+ @title = t ".title"
+
+ @issue_types = []
+ @issue_types.concat %w[Note] if current_user.moderator?
+ @issue_types.concat %w[DiaryEntry DiaryComment User] if current_user.administrator?
+
+ @users = User.joins(:roles).where(:user_roles => { :role => current_user.roles.map(&:role) }).distinct
+ @issues = Issue.where(:assigned_role => current_user.roles.map(&:role))
+
+ # If search
+ if params[:search_by_user] && params[:search_by_user].present?
+ @find_user = User.find_by(:display_name => params[:search_by_user])
+ if @find_user
+ @issues = @issues.where(:reported_user_id => @find_user.id)
+ else
+ @issues = @issues.none
+ flash.now[:warning] = t(".user_not_found")
+ end
+ end
+
+ @issues = @issues.where(:status => params[:status]) if params[:status] && params[:status].present?
+
+ @issues = @issues.where(:reportable_type => params[:issue_type]) if params[:issue_type] && params[:issue_type].present?
+
+ if params[:last_updated_by] && params[:last_updated_by].present?
+ last_updated_by = params[:last_updated_by].to_s == "nil" ? nil : params[:last_updated_by].to_i
+ @issues = @issues.where(:updated_by => last_updated_by)
+ end
+ end
+
+ def show
+ @read_reports = @issue.read_reports
+ @unread_reports = @issue.unread_reports
+ @comments = @issue.comments
+ @related_issues = @issue.reported_user.issues.where(:assigned_role => current_user.roles.map(&:role)) if @issue.reported_user
+ @new_comment = IssueComment.new(:issue => @issue)
+ end
+
+ # Status Transistions
+ def resolve
+ if @issue.resolve
+ @issue.save!
+ redirect_to @issue, :notice => t(".resolved")
+ else
+ render :show
+ end
+ end
+
+ def ignore
+ if @issue.ignore
+ @issue.updated_by = current_user.id
+ @issue.save!
+ redirect_to @issue, :notice => t(".ignored")
+ else
+ render :show
+ end
+ end
+
+ def reopen
+ if @issue.reopen
+ @issue.updated_by = current_user.id
+ @issue.save!
+ redirect_to @issue, :notice => t(".reopened")
+ else
+ render :show
+ end
+ end
+
+ private
+
+ def find_issue
+ @issue = Issue.find(params[:id])
+ end
+
+ def check_permission
+ unless current_user.administrator? || current_user.moderator?
+ flash[:error] = t("application.require_moderator_or_admin.not_a_moderator_or_admin")
+ redirect_to root_path
+ end
+ end
+end
--- /dev/null
+class ReportsController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :require_user
+
+ def new
+ if required_new_report_params_present?
+ @report = Report.new
+ @report.issue = Issue.find_or_initialize_by(create_new_report_params)
+ else
+ redirect_to root_path, :notice => t(".missing_params")
+ end
+ end
+
+ def create
+ @report = current_user.reports.new(report_params)
+ @report.issue = Issue.find_or_initialize_by(:reportable_id => params[:report][:issue][:reportable_id], :reportable_type => params[:report][:issue][:reportable_type])
+
+ if @report.save
+ @report.issue.save
+ @report.issue.reopen! unless @report.issue.open?
+ redirect_to helpers.reportable_url(@report.issue.reportable), :notice => t(".successful_report")
+ else
+ redirect_to new_report_path(:reportable_type => @report.issue.reportable_type, :reportable_id => @report.issue.reportable_id), :notice => t(".provide_details")
+ end
+ end
+
+ private
+
+ def required_new_report_params_present?
+ create_new_report_params["reportable_id"].present? && create_new_report_params["reportable_type"].present?
+ end
+
+ def create_new_report_params
+ params.permit(:reportable_id, :reportable_type)
+ end
+
+ def report_params
+ params[:report].permit(:details, :category)
+ end
+end
--- /dev/null
+module IssuesHelper
+ def reportable_url(reportable)
+ case reportable
+ when DiaryEntry
+ url_for(:controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.user.display_name, :id => reportable.id)
+ when User
+ url_for(:controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.display_name)
+ when DiaryComment
+ url_for(:controller => reportable.diary_entry.class.name.underscore, :action => :view, :display_name => reportable.diary_entry.user.display_name, :id => reportable.diary_entry.id, :anchor => "comment#{reportable.id}")
+ when Note
+ url_for(:controller => :browse, :action => :note, :id => reportable.id)
+ end
+ end
+
+ def reportable_title(reportable)
+ case reportable
+ when DiaryEntry
+ reportable.title
+ when User
+ reportable.display_name
+ when DiaryComment
+ "#{reportable.diary_entry.title}, Comment id ##{reportable.id}"
+ when Note
+ "Note ##{reportable.id}"
+ end
+ end
+end
--- /dev/null
+module ReportsHelper
+ def report_link(name, reportable)
+ link_to name, new_report_url(:reportable_id => reportable.id, :reportable_type => reportable.class.name)
+ end
+end
--- /dev/null
+# == Schema Information
+#
+# Table name: issues
+#
+# id :integer not null, primary key
+# reportable_type :string not null
+# reportable_id :integer not null
+# reported_user_id :integer
+# status :enum default("open"), not null
+# assigned_role :enum not null
+# resolved_at :datetime
+# resolved_by :integer
+# updated_by :integer
+# reports_count :integer default(0)
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_issues_on_assigned_role (assigned_role)
+# index_issues_on_reportable_type_and_reportable_id (reportable_type,reportable_id)
+# index_issues_on_reported_user_id (reported_user_id)
+# index_issues_on_status (status)
+# index_issues_on_updated_by (updated_by)
+#
+# Foreign Keys
+#
+# issues_reported_user_id_fkey (reported_user_id => users.id)
+# issues_resolved_by_fkey (resolved_by => users.id)
+# issues_updated_by_fkey (updated_by => users.id)
+#
+
+class Issue < ActiveRecord::Base
+ belongs_to :reportable, :polymorphic => true
+ belongs_to :reported_user, :class_name => "User", :foreign_key => :reported_user_id
+ belongs_to :user_resolved, :class_name => "User", :foreign_key => :resolved_by
+ belongs_to :user_updated, :class_name => "User", :foreign_key => :updated_by
+
+ has_many :reports, :dependent => :destroy
+ has_many :comments, :class_name => "IssueComment", :dependent => :destroy
+
+ validates :reportable_id, :uniqueness => { :scope => [:reportable_type] }
+
+ ASSIGNED_ROLES = %w[administrator moderator].freeze
+ validates :assigned_role, :presence => true, :inclusion => ASSIGNED_ROLES
+
+ before_validation :set_default_assigned_role
+ before_validation :set_reported_user
+
+ scope :with_status, ->(issue_status) { where(:status => statuses[issue_status]) }
+
+ def read_reports
+ resolved_at.present? ? reports.where("updated_at < ?", resolved_at) : nil
+ end
+
+ def unread_reports
+ resolved_at.present? ? reports.where("updated_at >= ?", resolved_at) : reports
+ end
+
+ include AASM
+ aasm :column => :status, :no_direct_assignment => true do
+ state :open, :initial => true
+ state :ignored
+ state :resolved
+
+ event :ignore do
+ transitions :from => :open, :to => :ignored
+ end
+
+ event :resolve do
+ transitions :from => :open, :to => :resolved
+ after do
+ self.resolved_at = Time.now.getutc
+ end
+ end
+
+ event :reopen do
+ transitions :from => :resolved, :to => :open
+ transitions :from => :ignored, :to => :open
+ end
+ end
+
+ private
+
+ def set_reported_user
+ self.reported_user = case reportable.class.name
+ when "User"
+ reportable
+ when "Note"
+ reportable.author
+ else
+ reportable.user
+ end
+ end
+
+ def set_default_assigned_role
+ if assigned_role.blank?
+ self.assigned_role = case reportable
+ when Note then "moderator"
+ else "administrator"
+ end
+ end
+ end
+end
--- /dev/null
+# == Schema Information
+#
+# Table name: issue_comments
+#
+# id :integer not null, primary key
+# issue_id :integer not null
+# user_id :integer not null
+# body :text not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_issue_comments_on_issue_id (issue_id)
+# index_issue_comments_on_user_id (user_id)
+#
+# Foreign Keys
+#
+# issue_comments_issue_id_fkey (issue_id => issues.id)
+# issue_comments_user_id_fkey (user_id => users.id)
+#
+
+class IssueComment < ActiveRecord::Base
+ belongs_to :issue
+ belongs_to :user
+
+ validates :body, :presence => true
+ validates :user, :presence => true
+ validates :issue, :presence => true
+end
--- /dev/null
+# == Schema Information
+#
+# Table name: reports
+#
+# id :integer not null, primary key
+# issue_id :integer not null
+# user_id :integer not null
+# details :text not null
+# category :string not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+# Indexes
+#
+# index_reports_on_issue_id (issue_id)
+# index_reports_on_user_id (user_id)
+#
+# Foreign Keys
+#
+# reports_issue_id_fkey (issue_id => issues.id)
+# reports_user_id_fkey (user_id => users.id)
+#
+
+class Report < ActiveRecord::Base
+ belongs_to :issue, :counter_cache => true
+ belongs_to :user
+
+ validates :issue, :presence => true
+ validates :user, :presence => true
+ validates :details, :presence => true
+ validates :category, :presence => true
+
+ def self.categories_for(reportable)
+ case reportable.class.name
+ when "DiaryEntry" then %w[spam offensive threat other]
+ when "DiaryComment" then %w[spam offensive threat other]
+ when "User" then %w[spam offensive threat vandal other]
+ when "Note" then %w[spam personal abusive other]
+ else %w[other]
+ end
+ end
+end
has_many :roles, :class_name => "UserRole"
+ has_many :issues, :class_name => "Issue", :foreign_key => :reported_user_id
+ has_many :issue_comments
+
+ has_many :reports
+
scope :visible, -> { where(:status => %w[pending active confirmed]) }
scope :active, -> { where(:status => %w[active confirmed]) }
scope :identifiable, -> { where(:data_public => true) }
<p class='warning'><%= t "javascripts.notes.show.anonymous_warning" %></p>
<% end -%>
+ <% if current_user && current_user != @note.author %>
+ <p class="deemphasize"><%= report_link(t(".report"), @note) %></p>
+ <% end %>
+
<% if @note_comments.length > 1 %>
<div class='note-comments'>
<ul>
<div class="clearfix diary-comment">
<%= user_thumbnail diary_comment.user %>
- <p class="deemphasize comment-heading" id="comment<%= diary_comment.id %>"><%= raw(t('.comment_from', :link_user => (link_to h(diary_comment.user.display_name), user_path(diary_comment.user)), :comment_created_at => link_to(l(diary_comment.created_at, :format => :friendly), :anchor => "comment#{diary_comment.id}"))) %></p>
+ <p class="deemphasize comment-heading" id="comment<%= diary_comment.id %>"><%= raw(t('.comment_from', :link_user => (link_to h(diary_comment.user.display_name), user_path(diary_comment.user)), :comment_created_at => link_to(l(diary_comment.created_at, :format => :friendly), :anchor => "comment#{diary_comment.id}"))) %>
+ <% if current_user and diary_comment.user.id != current_user.id %>
+ | <%= report_link(t(".report"), diary_comment) %>
+ <% end %>
+ </p>
+
<div class="richtext"><%= diary_comment.body.to_html %></div>
<% if current_user && current_user.administrator? %>
<span>
<li><%= link_to t('.edit_link'), :action => 'edit', :display_name => diary_entry.user.display_name, :id => diary_entry.id %></li>
<% end %>
+ <% if current_user and diary_entry.user != current_user %>
+ <li>
+ <%= report_link(t(".report"), diary_entry) %>
+ </li>
+ <% end %>
+
<% if current_user && current_user.administrator? %>
- <li><%= link_to t('.hide_link'), hide_diary_entry_path(:display_name => diary_entry.user.display_name, :id => diary_entry.id), :method => :post, :data => { :confirm => t('.confirm') } %></li>
+ <li>
+ <%= link_to t('.hide_link'), hide_diary_entry_path(:display_name => diary_entry.user.display_name, :id => diary_entry.id), :method => :post, :data => { :confirm => t('.confirm') } %>
+ </li>
<% end %>
</ul>
</div>
--- /dev/null
+<div class="issue-comments">
+ <% comments.each do |comment| %>
+ <div class="comment">
+ <div style="float:left">
+ <%= link_to user_thumbnail(comment.user), user_path(comment.user.display_name) %>
+ </div>
+ <b> <%= link_to comment.user.display_name, user_path(comment.user.display_name) %> </b> <br/>
+ <%= comment.body %>
+ </div>
+ <span class="deemphasize">
+ <%= t(".created_at", :datetime => l(comment.created_at.to_datetime, :format => :friendly)) %>
+ </span>
+ <hr>
+ <% end %>
+</div>
+<br/>
+<div class="comment">
+ <%= form_for @new_comment, url: issue_comments_path(@issue) do |f| %>
+ <%= richtext_area :issue_comment, :body, :cols => 10, :rows => 8, :required => true %>
+ <%= label_tag :reassign, t('.reassign_param') %> <%= check_box_tag :reassign, true %>
+ <br/>
+ <br/>
+ <%= submit_tag 'Submit' %>
+ <% end %>
+</div>
--- /dev/null
+<% reports.each do |report| %>
+ <div class="report">
+ <div style="float:left">
+ <%= link_to user_thumbnail(report.user), user_path(report.user.display_name) %>
+ </div>
+ <%= t ".reported_by_html", :category => report.category, :user => link_to(report.user.display_name, user_path(report.user.display_name)) %>
+ <br/>
+ <span class="deemphasize">
+ <%= t(".updated_at", :datetime => l(report.updated_at.to_datetime, :format => :friendly)) %>
+ </span>
+ <br/>
+ <%= report.details %>
+ <br/>
+ </div>
+ <hr>
+<% end %>
--- /dev/null
+<% content_for :heading do %>
+ <h1><%= t ".title" %></h1>
+<% end %>
+
+<%= form_tag(issues_path, :method => :get) do %>
+<p><%= t ".search_guidance" %></p>
+<%= select_tag :status, options_for_select(Issue.aasm.states.map(&:name).map{|state| [t(".states.#{state}"), state]}, params[:status]), :include_blank => t(".select_status"), :data => { :behavior => 'category_dropdown' } %>
+<%= select_tag :issue_type, options_for_select(@issue_types, params[:issue_type]), :include_blank => t(".select_type"), :data => { :behavior => 'category_dropdown' } %>
+<%= text_field_tag :search_by_user, params[:search_by_user], placeholder: t(".reported_user") %>
+<%= select_tag :last_updated_by, options_for_select(@users.all.collect{|f| [f.display_name, f.id]} << [ t(".not_updated"), "nil"], params[:last_updated_by]), :include_blank => t(".select_last_updated_by"), :data => { :behavior => 'category_dropdown' } %>
+<%= submit_tag t(".search"), :name => nil %>
+<% end %>
+<br/>
+
+<% if @issues.length == 0 %>
+ <p><%= t ".issues_not_found" %></p>
+<% end %>
+
+<br/>
+
+<table class="issues-list">
+ <thead>
+ <tr>
+ <th><%= t ".status" %></th>
+ <th><%= t ".reports" %></th>
+ <th><%= t ".reported_item" %></th>
+ <th><%= t ".reported_user" %></th>
+ <th><%= t ".last_updated" %></th>
+ </tr>
+ </thead>
+ <tbody>
+ <% @issues.each do |issue| %>
+ <tr>
+ <td><%= t ".states.#{issue.status}" %></td>
+ <td><%= link_to t(".reports_count", :count => issue.reports_count), issue %></td>
+ <td><%= link_to reportable_title(issue.reportable), reportable_url(issue.reportable) %></td>
+ <td><%= link_to issue.reported_user.display_name, user_path(issue.reported_user.display_name) if issue.reported_user %></td>
+ <td>
+ <% if issue.user_updated %>
+ <%= t ".last_updated_time_user_html", :user => link_to(issue.user_updated.display_name, user_path(issue.user_updated.display_name)),
+ :time => distance_of_time_in_words_to_now(issue.updated_at),
+ :title => l(issue.updated_at) %>
+ <% else %>
+ <%= t ".last_updated_time_html", :time => distance_of_time_in_words_to_now(issue.updated_at),
+ :title => l(issue.updated_at) %>
+ <% end %>
+ </td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
--- /dev/null
+<% content_for :heading do %>
+<h2><%= t ".title", :status => @issue.status.humanize, :issue_id => @issue.id %></h2>
+<p><%= @issue.reportable.model_name.human %> : <%= link_to reportable_title(@issue.reportable), reportable_url(@issue.reportable) %></p>
+<p class="deemphasize">
+ <small>
+ <%= @issue.assigned_role %>
+ | <%= t ".reports", :count => @issue.reports.count %>
+ | <%= t ".report_created_at", :datetime => l(@issue.created_at.to_datetime, :format => :friendly) %>
+ <%= " | " + t(".last_resolved_at", :datetime => l(@issue.resolved_at.to_datetime, :format =>:friendly)) if @issue.resolved_at? %>
+ <%= " | " + t(".last_updated_at", :datetime => l(@issue.updated_at.to_datetime, :format => :friendly), :displayname => @issue.user_updated.display_name ) if @issue.user_updated %>
+ </small>
+</p>
+<p>
+ <%= link_to t('.resolve'), resolve_issue_url(@issue), :method => :post if @issue.may_resolve? %>
+ <% if @issue.may_ignore? %>
+ | <%= link_to t('.ignore'), ignore_issue_url(@issue), :method => :post %>
+ <% end %>
+</p>
+<p><%= link_to t('.reopen'), reopen_issue_url(@issue), :method => :post if @issue.may_reopen? %></p>
+<% end %>
+
+<div class="report-related-block">
+
+ <div class="report-block">
+ <h3><%= t ".reports_of_this_issue" %></h3>
+
+ <% if @read_reports.present? %>
+ <div class="read-reports">
+ <h4><%= t ".read_reports" %></h4>
+ <%= render 'reports', reports: @read_reports %>
+ </div>
+ <% end %>
+
+ <% if @unread_reports.any? %>
+ <div class="unread-reports">
+ <h4><%= t ".new_reports" %></h4>
+ <%= render 'reports', reports: @unread_reports %>
+ </div>
+ <% end %>
+ <br/>
+ </div>
+
+ <% if @issue.reported_user %>
+ <div class="related-reports">
+ <h3><%= t ".other_issues_against_this_user" %></h3>
+ <% if @related_issues.count > 1 %>
+ <ul>
+ <% @related_issues.each do |issue| %>
+ <% if issue.id != @issue.id %>
+ <li><%= link_to reportable_title(issue.reportable), issue %></li>
+ <% end %>
+ <% end %>
+ </ul>
+ <% else %>
+ <p><%= t ".no_other_issues" %></p>
+ <% end %>
+ </div>
+ <% end %>
+</div>
+
+<h3><%= t ".comments_on_this_issue" %></h3>
+<%= render 'comments', comments: @comments %>
</nav>
<nav class='secondary'>
<ul>
+ <% if current_user and ( current_user.administrator? or current_user.moderator? ) %>
+ <li class="compact-hide <%= current_page_class(issues_path) %>"><%= link_to t('layouts.issues'), issues_path %></li>
+ <% end %>
<li class="compact-hide <%= current_page_class(traces_path) %>"><%= link_to t('layouts.gps_traces'), traces_path %></li>
<li class="compact-hide <%= current_page_class(diary_path) %>"><%= link_to t('layouts.user_diaries'), diary_path %></li>
<li class="compact-hide <%= current_page_class(copyright_path) %>"><%= link_to t('layouts.copyright'), copyright_path %></li>
--- /dev/null
+<% content_for :heading do %>
+ <h1><%= t ".title_html", :link => link_to(reportable_title(@report.issue.reportable), reportable_url(@report.issue.reportable)) %></h1>
+<% end %>
+
+<div class="report-disclaimer">
+ <%= t('.disclaimer.intro') %>
+ <ul>
+ <li> <%= t('.disclaimer.not_just_mistake') %> </li>
+ <li> <%= t('.disclaimer.unable_to_fix') %> </li>
+ <li> <%= t('.disclaimer.resolve_with_user') %> </li>
+ </ul>
+</div>
+
+<%= form_for(@report) do |f| %>
+ <%= f.error_messages %>
+ <fieldset>
+ <%= f.fields_for @report.issue do |issue_form| %>
+ <%= issue_form.hidden_field :reportable_id %>
+ <%= issue_form.hidden_field :reportable_type %>
+ <% end %>
+
+ <div class='form-row'>
+ <p><%= t('.select') %></p>
+ <ul>
+ <% Report.categories_for(@report.issue.reportable).each do |c| %>
+ <li>
+ <%= radio_button :report, :category, c %>
+ <%= label_tag "report_category_#{c}", t(".categories.#{@report.issue.reportable.class.name.underscore}.#{c}") %> <br/>
+ </li>
+ <% end %>
+ </ul>
+ </div>
+
+ <div class='form-row'>
+ <%= text_area :report, :details, :cols => 20, :rows => 5, placeholder: t('.details'), required: true %>
+ </div>
+
+ <div class='buttons'>
+ <%= f.submit %>
+ </div>
+ </fieldset>
+<% end %>
</li>
<% end %>
+ <% if current_user and @user.id != current_user.id %>
+ <li>
+ <%= report_link(t(".report"), @user) %>
+ </li>
+ <% end %>
</ul>
<% end %>
reopened_by: "Reactivated by %{user} <abbr title='%{exact_time}'>%{when} ago</abbr>"
reopened_by_anonymous: "Reactivated by anonymous <abbr title='%{exact_time}'>%{when} ago</abbr>"
hidden_by: "Hidden by %{user} <abbr title='%{exact_time}'>%{when} ago</abbr>"
+ report: Report this note
query:
title: "Query Features"
introduction: "Click on the map to find nearby features."
edit_link: Edit this entry
hide_link: Hide this entry
confirm: Confirm
+ report: Report this entry
diary_comment:
comment_from: "Comment from %{link_user} on %{comment_created_at}"
hide_link: Hide this comment
confirm: Confirm
+ report: Report this comment
location:
location: "Location:"
view: "View"
results:
no_results: "No results found"
more_results: "More results"
+ issues:
+ index:
+ title: Issues
+ select_status: Select Status
+ select_type: Select Type
+ select_last_updated_by: Select Last Updated By
+ reported_user: Reported User
+ not_updated: Not Updated
+ search: Search
+ search_guidance: "Search Issues:"
+ user_not_found: User does not exist
+ issues_not_found: No such issues found
+ status: Status
+ reports: Reports
+ last_updated: Last Updated
+ last_updated_time_html: "<abbr title='%{title}'>%{time} ago</abbr>"
+ last_updated_time_user_html: "<abbr title='%{title}'>%{time} ago</abbr> by %{user}"
+ link_to_reports: View Reports
+ reported_user: Reported User
+ reports_count:
+ one: "1 Report"
+ other: "%{count} Reports"
+ reported_item: Reported Item
+ states:
+ ignored: Ignored
+ open: Open
+ resolved: Resolved
+ update:
+ new_report: Your report has been registered sucessfully
+ successful_update: Your report has been updated successfully
+ provide_details: Please provide the required details
+ show:
+ title: "%{status} Issue #%{issue_id}"
+ reports:
+ zero: No reports
+ one: 1 report
+ other: "%{count} reports"
+ report_created_at: "First reported at %{datetime}"
+ last_resolved_at: "Last resolved at %{datetime}"
+ last_updated_at: "Last updated at %{datetime} by %{displayname}"
+ resolve: Resolve
+ ignore: Ignore
+ reopen: Reopen
+ reports_of_this_issue: Reports of this Issue
+ read_reports: Read Reports
+ new_reports: New Reports
+ other_issues_against_this_user: Other issues against this user
+ no_other_issues: No other issues against this user.
+ comments_on_this_issue: Comments on this issue
+ resolve:
+ resolved: Issue status has been set to 'Resolved'
+ ignore:
+ ignored: Issue status has been set to 'Ignored'
+ reopen:
+ reopened: Issue status has been set to 'Open'
+ comments:
+ created_at: "On %{datetime}"
+ reassign_param: Reassign Issue?
+ reports:
+ updated_at: "On %{datetime}"
+ reported_by_html: "Reported as %{category} by %{user}"
+ issue_comments:
+ create:
+ comment_created: Your comment was successfully created
+ reports:
+ new:
+ title_html: "Report %{link}"
+ missing_params: "Cannot create a new report"
+ details: Please provide some more details about the problem (required).
+ select: "Select a reason for your report:"
+ disclaimer:
+ intro: "Before sending your report to the site moderators, please ensure that:"
+ not_just_mistake: You are certain that the problem is not just a mistake
+ unable_to_fix: You are unable to fix the problem yourself or with the help of your fellow community members
+ resolve_with_user: You have already tried to resolve the problem with the user concerned
+ categories:
+ diary_entry:
+ spam: This diary entry is/contains spam
+ offensive: This diary entry is obscene/offensive
+ threat: This diary entry contains a threat
+ other: Other
+ diary_comment:
+ spam: This diary comment is/contains spam
+ offensive: This diary comment is obscene/offensive
+ threat: This diary comment contains a threat
+ other: Other
+ user:
+ spam: This user profile is/contains spam
+ offensive: This user profile is obscene/offensive
+ threat: This user profile contains a threat
+ vandal: This user is a vandal
+ other: Other
+ note:
+ spam: This note is spam
+ personal: This note contains personal data
+ abusive: This note is abusive
+ other: Other
+ create:
+ successful_report: Your report has been registered sucessfully
+ provide_details: Please provide the required details
layouts:
project_name:
# in <title>
edit: Edit
history: History
export: Export
+ issues: Issues
data: Data
export_data: Export Data
gps_traces: GPS Traces
application:
require_cookies:
cookies_needed: "You appear to have cookies disabled - please enable cookies in your browser before continuing."
+ require_admin:
+ not_an_admin: You need to be an admin to perform that action.
require_moderator:
not_a_moderator: "You need to be a moderator to perform that action."
+ require_moderator_or_admin:
+ not_a_moderator_or_admin: You need to be a moderator or an admin to perform that action
setup_user_auth:
blocked_zero_hour: "You have an urgent message on the OpenStreetMap web site. You need to read the message before you will be able to save your edits."
blocked: "Your access to the API has been blocked. Please log-in to the web interface to find out more."
friends_diaries: "friends' diary entries"
nearby_changesets: "nearby user changesets"
nearby_diaries: "nearby user diary entries"
+ report: Report this User
popup:
your location: "Your location"
nearby mapper: "Nearby mapper"
resources :user_blocks
match "/blocks/:id/revoke" => "user_blocks#revoke", :via => [:get, :post], :as => "revoke_user_block"
+ # issues and reports
+ resources :issues do
+ resources :comments, :controller => :issue_comments
+ member do
+ post "resolve"
+ post "assign"
+ post "ignore"
+ post "reopen"
+ end
+ end
+
+ resources :reports
+
# redactions
resources :redactions
end
--- /dev/null
+require "migrate"
+
+class CreateIssuesAndReports < ActiveRecord::Migration[5.0]
+ def up
+ create_enumeration :issue_status_enum, %w[open ignored resolved]
+
+ create_table :issues do |t|
+ t.string :reportable_type, :null => false
+ t.integer :reportable_id, :null => false
+ t.integer :reported_user_id
+ t.column :status, :issue_status_enum, :null => false, :default => "open"
+ t.column :assigned_role, :user_role_enum, :null => false
+ t.datetime :resolved_at
+ t.integer :resolved_by
+ t.integer :updated_by
+ t.integer :reports_count, :default => 0
+ t.timestamps :null => false
+ end
+
+ add_foreign_key :issues, :users, :column => :reported_user_id, :name => "issues_reported_user_id_fkey"
+ add_foreign_key :issues, :users, :column => :resolved_by, :name => "issues_resolved_by_fkey"
+ add_foreign_key :issues, :users, :column => :updated_by, :name => "issues_updated_by_fkey"
+
+ add_index :issues, [:reportable_type, :reportable_id]
+ add_index :issues, [:reported_user_id]
+ add_index :issues, [:status]
+ add_index :issues, [:assigned_role]
+ add_index :issues, [:updated_by]
+
+ create_table :reports do |t|
+ t.integer :issue_id, :null => false
+ t.integer :user_id, :null => false
+ t.text :details, :null => false
+ t.string :category, :null => false
+ t.timestamps :null => false
+ end
+
+ add_foreign_key :reports, :issues, :name => "reports_issue_id_fkey"
+ add_foreign_key :reports, :users, :column => :user_id, :name => "reports_user_id_fkey"
+
+ add_index :reports, :issue_id
+ add_index :reports, :user_id
+
+ create_table :issue_comments do |t|
+ t.integer :issue_id, :null => false
+ t.integer :user_id, :null => false
+ t.text :body, :null => false
+ t.timestamps :null => false
+ end
+
+ add_foreign_key :issue_comments, :issues, :name => "issue_comments_issue_id_fkey"
+ add_foreign_key :issue_comments, :users, :column => :user_id, :name => "issue_comments_user_id_fkey"
+
+ add_index :issue_comments, :issue_id
+ add_index :issue_comments, :user_id
+ end
+
+ def down
+ drop_table :issue_comments
+ drop_table :reports
+ drop_table :issues
+ drop_enumeration :issue_status_enum
+ end
+end
);
+--
+-- Name: issue_status_enum; Type: TYPE; Schema: public; Owner: -
+--
+
+CREATE TYPE issue_status_enum AS ENUM (
+ 'open',
+ 'ignored',
+ 'resolved'
+);
+
+
--
-- Name: note_event_enum; Type: TYPE; Schema: public; Owner: -
--
ALTER SEQUENCE gpx_files_id_seq OWNED BY gpx_files.id;
+--
+-- Name: issue_comments; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE issue_comments (
+ id integer NOT NULL,
+ issue_id integer NOT NULL,
+ user_id integer NOT NULL,
+ body text NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: issue_comments_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE issue_comments_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: issue_comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE issue_comments_id_seq OWNED BY issue_comments.id;
+
+
+--
+-- Name: issues; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE issues (
+ id integer NOT NULL,
+ reportable_type character varying NOT NULL,
+ reportable_id integer NOT NULL,
+ reported_user_id integer,
+ status issue_status_enum DEFAULT 'open'::public.issue_status_enum NOT NULL,
+ assigned_role user_role_enum NOT NULL,
+ resolved_at timestamp without time zone,
+ resolved_by integer,
+ updated_by integer,
+ reports_count integer DEFAULT 0,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: issues_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE issues_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: issues_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE issues_id_seq OWNED BY issues.id;
+
+
--
-- Name: languages; Type: TABLE; Schema: public; Owner: -
--
);
+--
+-- Name: reports; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE reports (
+ id integer NOT NULL,
+ issue_id integer NOT NULL,
+ user_id integer NOT NULL,
+ details text NOT NULL,
+ category character varying NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: reports_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE reports_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: reports_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE reports_id_seq OWNED BY reports.id;
+
+
--
-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -
--
ALTER TABLE ONLY gpx_files ALTER COLUMN id SET DEFAULT nextval('gpx_files_id_seq'::regclass);
+--
+-- Name: issue_comments id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments ALTER COLUMN id SET DEFAULT nextval('issue_comments_id_seq'::regclass);
+
+
+--
+-- Name: issues id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues ALTER COLUMN id SET DEFAULT nextval('issues_id_seq'::regclass);
+
+
--
-- Name: messages id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY redactions ALTER COLUMN id SET DEFAULT nextval('redactions_id_seq'::regclass);
+--
+-- Name: reports id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports ALTER COLUMN id SET DEFAULT nextval('reports_id_seq'::regclass);
+
+
--
-- Name: user_blocks id; Type: DEFAULT; Schema: public; Owner: -
--
ADD CONSTRAINT gpx_files_pkey PRIMARY KEY (id);
+--
+-- Name: issue_comments issue_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: issues issues_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT issues_pkey PRIMARY KEY (id);
+
+
--
-- Name: languages languages_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ADD CONSTRAINT relations_pkey PRIMARY KEY (relation_id, version);
+--
+-- Name: reports reports_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports
+ ADD CONSTRAINT reports_pkey PRIMARY KEY (id);
+
+
--
-- Name: user_blocks user_blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
CREATE INDEX index_diary_entry_subscriptions_on_diary_entry_id ON diary_entry_subscriptions USING btree (diary_entry_id);
+--
+-- Name: index_issue_comments_on_issue_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issue_comments_on_issue_id ON issue_comments USING btree (issue_id);
+
+
+--
+-- Name: index_issue_comments_on_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issue_comments_on_user_id ON issue_comments USING btree (user_id);
+
+
+--
+-- Name: index_issues_on_assigned_role; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_assigned_role ON issues USING btree (assigned_role);
+
+
+--
+-- Name: index_issues_on_reportable_type_and_reportable_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_reportable_type_and_reportable_id ON issues USING btree (reportable_type, reportable_id);
+
+
+--
+-- Name: index_issues_on_reported_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_reported_user_id ON issues USING btree (reported_user_id);
+
+
+--
+-- Name: index_issues_on_status; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_status ON issues USING btree (status);
+
+
+--
+-- Name: index_issues_on_updated_by; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_updated_by ON issues USING btree (updated_by);
+
+
--
-- Name: index_note_comments_on_body; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_oauth_tokens_on_user_id ON oauth_tokens USING btree (user_id);
+--
+-- Name: index_reports_on_issue_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_reports_on_issue_id ON reports USING btree (issue_id);
+
+
+--
+-- Name: index_reports_on_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_reports_on_user_id ON reports USING btree (user_id);
+
+
--
-- Name: index_user_blocks_on_user_id; Type: INDEX; Schema: public; Owner: -
--
ADD CONSTRAINT gpx_files_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
+--
+-- Name: issue_comments issue_comments_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id);
+
+
+--
+-- Name: issue_comments issue_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
+
+
+--
+-- Name: issues issues_reported_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT issues_reported_user_id_fkey FOREIGN KEY (reported_user_id) REFERENCES users(id);
+
+
+--
+-- Name: issues issues_resolved_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT issues_resolved_by_fkey FOREIGN KEY (resolved_by) REFERENCES users(id);
+
+
+--
+-- Name: issues issues_updated_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT issues_updated_by_fkey FOREIGN KEY (updated_by) REFERENCES users(id);
+
+
--
-- Name: messages messages_from_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ADD CONSTRAINT relations_redaction_id_fkey FOREIGN KEY (redaction_id) REFERENCES redactions(id);
+--
+-- Name: reports reports_issue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports
+ ADD CONSTRAINT reports_issue_id_fkey FOREIGN KEY (issue_id) REFERENCES issues(id);
+
+
+--
+-- Name: reports reports_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports
+ ADD CONSTRAINT reports_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
+
+
--
-- Name: user_blocks user_blocks_moderator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
('20150111192335'),
('20150222101847'),
('20150818224516'),
+('20160822153055'),
('20161002153425'),
('20161011010929'),
('20170222134109'),
('7'),
('8'),
('9');
-
-
--- /dev/null
+require "test_helper"
+
+class IssueCommentsControllerTest < ActionController::TestCase
+ def test_comment_by_normal_user
+ issue = create(:issue)
+
+ # Login as normal user
+ session[:user] = create(:user).id
+
+ post :create, :params => { :issue_id => issue.id }
+ assert_response :redirect
+ assert_redirected_to root_path
+ end
+
+ def test_comment
+ issue = create(:issue)
+
+ # Login as administrator
+ session[:user] = create(:administrator_user).id
+
+ post :create, :params => { :issue_id => issue.id, :issue_comment => { :body => "test comment" } }
+ assert_response :redirect
+ assert_redirected_to issue
+ assert_equal 1, issue.comments.length
+ end
+end
--- /dev/null
+require "test_helper"
+
+class IssuesControllerTest < ActionController::TestCase
+ teardown do
+ # cleanup any emails set off by the test
+ ActionMailer::Base.deliveries.clear
+ end
+
+ def test_view_dashboard_without_auth
+ # Access issues_path without login
+ get :index
+ assert_response :redirect
+ assert_redirected_to login_path(:referer => issues_path)
+
+ # Access issues_path as normal user
+ session[:user] = create(:user).id
+ get :index
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ # Access issues_path by admin
+ session[:user] = create(:administrator_user).id
+ get :index
+ assert_response :success
+
+ # Access issues_path by moderator
+ session[:user] = create(:moderator_user).id
+ get :index
+ assert_response :success
+ end
+
+ def test_change_status_by_normal_user
+ target_user = create(:user)
+ issue = create(:issue, :reportable => target_user, :reported_user => target_user)
+
+ # Login as normal user
+ session[:user] = create(:user).id
+
+ assert_equal 1, Issue.count
+
+ get :resolve, :params => { :id => issue.id }
+
+ assert_response :redirect
+ assert_redirected_to root_path
+ end
+
+ def test_change_status_by_admin
+ target_user = create(:user)
+ issue = create(:issue, :reportable => target_user, :reported_user => target_user)
+
+ # Login as administrator
+ session[:user] = create(:administrator_user).id
+
+ # Test 'Resolved'
+ get :resolve, :params => { :id => issue.id }
+ assert_equal true, Issue.find_by(:reportable_id => target_user.id, :reportable_type => "User").resolved?
+ assert_response :redirect
+
+ # Test 'Reopen'
+ get :reopen, :params => { :id => issue.id }
+ assert_equal true, Issue.find_by(:reportable_id => target_user.id, :reportable_type => "User").open?
+ assert_response :redirect
+
+ # Test 'Ignored'
+ get :ignore, :params => { :id => issue.id }
+ assert_equal true, Issue.find_by(:reportable_id => target_user, :reportable_type => "User").ignored?
+ assert_response :redirect
+ end
+end
--- /dev/null
+require "test_helper"
+
+class ReportsControllerTest < ActionController::TestCase
+ def test_new_report_without_login
+ target_user = create(:user)
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :redirect
+ assert_redirected_to login_path(:referer => new_report_path(:reportable_id => target_user.id, :reportable_type => "User"))
+ end
+
+ def test_new_report_after_login
+ target_user = create(:user)
+
+ session[:user] = create(:user).id
+
+ assert_equal 0, Issue.count
+
+ # Create an Issue and a report
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ category = "other"
+ post :create,
+ :params => {
+ :report => {
+ :details => details,
+ :category => category,
+ :issue => { :reportable_id => target_user.id, :reportable_type => "User" }
+ }
+ }
+ end
+ assert_equal 1, Issue.count
+ assert_response :redirect
+ assert_redirected_to user_path(target_user.display_name)
+ end
+
+ def test_new_report_with_incomplete_details
+ # Test creation of a new issue and a new report
+ target_user = create(:user)
+
+ # Login
+ session[:user] = create(:user).id
+
+ assert_equal 0, Issue.count
+
+ # Create an Issue and a report
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ category = "other"
+ post :create,
+ :params => {
+ :report => {
+ :details => details,
+ :category => category,
+ :issue => { :reportable_id => target_user.id, :reportable_type => "User" }
+ }
+ }
+ end
+ assert_equal 1, Issue.count
+ assert_response :redirect
+ assert_redirected_to user_path(target_user.display_name)
+
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :success
+
+ # Report without details
+ assert_no_difference "Issue.count" do
+ category = "other"
+ post :create,
+ :params => {
+ :report => {
+ :category => category,
+ :issue => { :reportable_id => 1, :reportable_type => "User" }
+ }
+ }
+ end
+ assert_response :redirect
+ assert_equal 1, Issue.find_by(:reportable_id => target_user.id, :reportable_type => "User").reports.count
+ end
+
+ def test_new_report_with_complete_details
+ # Test creation of a new issue and a new report
+ target_user = create(:user)
+
+ # Login
+ session[:user] = create(:user).id
+
+ assert_equal 0, Issue.count
+
+ # Create an Issue and a report
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ category = "other"
+ post :create,
+ :params => {
+ :report => {
+ :details => details,
+ :category => category,
+ :issue => { :reportable_id => target_user.id, :reportable_type => "User" }
+ }
+ }
+ end
+ assert_equal 1, Issue.count
+ assert_response :redirect
+ assert_redirected_to user_path(target_user.display_name)
+
+ # Create a report for an existing Issue
+ get :new, :params => { :reportable_id => target_user.id, :reportable_type => "User" }
+ assert_response :success
+ assert_no_difference "Issue.count" do
+ details = "Details of another report under the same issue"
+ category = "other"
+ post :create,
+ :params => {
+ :report => {
+ :details => details,
+ :category => category,
+ :issue => { :reportable_id => target_user.id, :reportable_type => "User" }
+ }
+ }
+ end
+ assert_response :redirect
+ report_count = Issue.find_by(:reportable_id => target_user.id, :reportable_type => "User").reports.count
+ assert_equal 2, report_count
+ end
+end
--- /dev/null
+FactoryBot.define do
+ factory :issue do
+ # Default to reporting users
+ association :reportable, :factory => :user
+ association :reported_user, :factory => :user
+ end
+end
--- /dev/null
+FactoryBot.define do
+ factory :report do
+ sequence(:details) { |n| "Report details #{n}" }
+ category "other"
+ issue
+ user
+ end
+end
--- /dev/null
+require "test_helper"
+
+class IssueCommentTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
--- /dev/null
+require "test_helper"
+
+class IssueTest < ActiveSupport::TestCase
+ def test_assigned_role
+ issue = create(:issue)
+
+ assert issue.valid?
+ issue.assigned_role = "bogus"
+ assert_not issue.valid?
+ end
+
+ def test_reported_user
+ note = create(:note_comment, :author => create(:user)).note
+ anonymous_note = create(:note_comment, :author => nil).note
+ user = create(:user)
+ create(:language, :code => "en")
+ diary_entry = create(:diary_entry)
+ issue = Issue.new
+
+ issue.reportable = user
+ issue.save!
+ assert_equal issue.reported_user, user
+
+ issue.reportable = note
+ issue.save!
+ assert_equal issue.reported_user, note.author
+
+ issue.reportable = anonymous_note
+ issue.save!
+ assert_nil issue.reported_user
+
+ issue.reportable = diary_entry
+ issue.save!
+ assert_equal issue.reported_user, diary_entry.user
+ end
+
+ def test_default_assigned_role
+ create(:language, :code => "en")
+ diary_entry = create(:diary_entry)
+ note = create(:note_with_comments)
+
+ issue = Issue.new
+ issue.reportable = diary_entry
+ issue.save!
+ assert_equal "administrator", issue.assigned_role
+
+ issue = Issue.new
+ issue.reportable = note
+ issue.save!
+ assert_equal "moderator", issue.assigned_role
+
+ # check the callback doesn't override an explicitly set role
+ issue.assigned_role = "administrator"
+ issue.save!
+ issue.reload
+ assert_equal "administrator", issue.assigned_role
+ end
+end
--- /dev/null
+require "test_helper"
+
+class ReportTest < ActiveSupport::TestCase
+ def test_issue_required
+ report = create(:report)
+
+ assert report.valid?
+ report.issue = nil
+ assert_not report.valid?
+ end
+
+ def test_user_required
+ report = create(:report)
+
+ assert report.valid?
+ report.user = nil
+ assert_not report.valid?
+ end
+
+ def test_details_required
+ report = create(:report)
+
+ assert report.valid?
+ report.details = ""
+ assert_not report.valid?
+ end
+
+ def test_category_required
+ report = create(:report)
+
+ assert report.valid?
+ report.category = ""
+ assert_not report.valid?
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class IssuesTest < ApplicationSystemTestCase
+ include IssuesHelper
+
+ def test_view_issues_not_logged_in
+ visit issues_path
+ assert page.has_content?(I18n.t("user.login.title"))
+ end
+
+ def test_view_issues_normal_user
+ sign_in_as(create(:user))
+
+ visit issues_path
+ assert page.has_content?(I18n.t("application.require_moderator_or_admin.not_a_moderator_or_admin"))
+ end
+
+ def test_view_no_issues
+ sign_in_as(create(:moderator_user))
+
+ visit issues_path
+ assert page.has_content?(I18n.t("issues.index.issues_not_found"))
+ end
+
+ def test_view_issues
+ sign_in_as(create(:moderator_user))
+ issues = create_list(:issue, 3, :assigned_role => "moderator")
+
+ visit issues_path
+ assert page.has_content?(issues.first.reported_user.display_name)
+ end
+
+ def test_view_issues_with_no_reported_user
+ sign_in_as(create(:moderator_user))
+ anonymous_note = create(:note_with_comments)
+ issue = create(:issue, :reportable => anonymous_note)
+
+ visit issues_path
+ assert page.has_content?(reportable_title(anonymous_note))
+
+ visit issue_path(issue)
+ assert page.has_content?(reportable_title(anonymous_note))
+ end
+
+ def test_search_issues_by_user
+ good_user = create(:user)
+ bad_user = create(:user)
+ create(:issue, :reportable => bad_user, :reported_user => bad_user, :assigned_role => "administrator")
+
+ sign_in_as(create(:administrator_user))
+
+ # No issues against the user
+ visit issues_path
+ fill_in "search_by_user", :with => good_user.display_name
+ click_on "Search"
+ assert page.has_content?(I18n.t("issues.index.issues_not_found"))
+
+ # User doesn't exist
+ visit issues_path
+ fill_in "search_by_user", :with => "Nonexistant User"
+ click_on "Search"
+ assert page.has_content?(I18n.t("issues.index.user_not_found"))
+
+ # Find Issue against bad_user
+ visit issues_path
+ fill_in "search_by_user", :with => bad_user.display_name
+ click_on "Search"
+ assert_not page.has_content?(I18n.t("issues.index.issues_not_found"))
+ end
+
+ def test_commenting
+ issue = create(:issue)
+ sign_in_as(create(:moderator_user))
+
+ visit issue_path(issue)
+
+ fill_in :issue_comment_body, :with => "test comment"
+ click_on "Submit"
+ assert page.has_content?(I18n.t("issue_comments.create.comment_created"))
+ assert page.has_content?("test comment")
+
+ issue.reload
+ assert_equal issue.comments.first.body, "test comment"
+ end
+
+ def test_reassign_issue
+ issue = create(:issue)
+ assert_equal "administrator", issue.assigned_role
+ sign_in_as(create(:administrator_user))
+
+ visit issue_path(issue)
+
+ fill_in :issue_comment_body, :with => "reassigning to moderators"
+ check :reassign
+ click_on "Submit"
+
+ issue.reload
+ assert_equal "moderator", issue.assigned_role
+ end
+
+ def test_issue_index_with_multiple_roles
+ user1 = create(:user)
+ user2 = create(:user)
+ issue1 = create(:issue, :reportable => user1, :assigned_role => "administrator")
+ issue2 = create(:issue, :reportable => user2, :assigned_role => "moderator")
+
+ user = create(:administrator_user)
+ create(:user_role, :user => user, :role => "moderator")
+ sign_in_as(user)
+
+ visit issues_path
+
+ assert page.has_link?(I18n.t("issues.index.reports_count", :count => issue1.reports_count), :href => issue_path(issue1))
+ assert page.has_link?(I18n.t("issues.index.reports_count", :count => issue2.reports_count), :href => issue_path(issue2))
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class ReportDiaryCommentTest < ApplicationSystemTestCase
+ def setup
+ create(:language, :code => "en")
+ @diary_entry = create(:diary_entry)
+ @comment = create(:diary_comment, :diary_entry => @diary_entry)
+ end
+
+ def test_no_link_when_not_logged_in
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content?(@comment.body)
+
+ assert_not page.has_content?(I18n.t("diary_entry.diary_comment.report"))
+ end
+
+ def test_it_works
+ sign_in_as(create(:user))
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content? @diary_entry.title
+
+ click_on I18n.t("diary_entry.diary_comment.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.diary_comment.spam")
+ fill_in "report_details", :with => "This comment is spam"
+ click_on "Create Report"
+
+ assert page.has_content? "Your report has been registered sucessfully"
+
+ assert_equal 1, Issue.count
+ assert Issue.last.reportable == @comment
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class ReportDiaryEntryTest < ApplicationSystemTestCase
+ def setup
+ create(:language, :code => "en")
+ @diary_entry = create(:diary_entry)
+ end
+
+ def test_no_link_when_not_logged_in
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content?(@diary_entry.title)
+
+ assert_not page.has_content?(I18n.t("diary_entry.diary_entry.report"))
+ end
+
+ def test_it_works
+ sign_in_as(create(:user))
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content? @diary_entry.title
+
+ click_on I18n.t("diary_entry.diary_entry.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.diary_entry.spam")
+ fill_in "report_details", :with => "This is advertising"
+ click_on "Create Report"
+
+ assert page.has_content? "Your report has been registered sucessfully"
+ end
+
+ def test_it_reopens_issue
+ issue = create(:issue, :reportable => @diary_entry)
+ issue.resolve!
+
+ sign_in_as(create(:user))
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content? @diary_entry.title
+
+ click_on I18n.t("diary_entry.diary_entry.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.diary_entry.spam")
+ fill_in "report_details", :with => "This is advertising"
+ click_on "Create Report"
+
+ issue.reload
+ assert_not issue.resolved?
+ assert issue.open?
+ end
+
+ def test_missing_report_params
+ sign_in_as(create(:user))
+ visit new_report_path
+ assert page.has_content? I18n.t("reports.new.missing_params")
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class ReportNoteTest < ApplicationSystemTestCase
+ def test_no_link_when_not_logged_in
+ note = create(:note_with_comments)
+ visit browse_note_path(note)
+ assert page.has_content?(note.comments.first.body)
+
+ assert_not page.has_content?(I18n.t("browse.note.report"))
+ end
+
+ def test_can_report_anonymous_notes
+ note = create(:note_with_comments)
+ sign_in_as(create(:user))
+ visit browse_note_path(note)
+
+ click_on I18n.t("browse.note.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.note.spam")
+ fill_in "report_details", :with => "This is spam"
+ click_on "Create Report"
+
+ assert page.has_content? "Your report has been registered sucessfully"
+
+ assert_equal 1, Issue.count
+ assert Issue.last.reportable == note
+ end
+
+ def test_can_report_notes_with_author
+ note = create(:note_comment, :author => create(:user)).note
+ sign_in_as(create(:user))
+ visit browse_note_path(note)
+
+ click_on I18n.t("browse.note.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.note.spam")
+ fill_in "report_details", :with => "This is spam"
+ click_on "Create Report"
+
+ assert page.has_content? "Your report has been registered sucessfully"
+
+ assert_equal 1, Issue.count
+ assert Issue.last.reportable == note
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class ReportUserTest < ApplicationSystemTestCase
+ def test_no_link_when_not_logged_in
+ note = create(:note_with_comments)
+ visit browse_note_path(note)
+ assert page.has_content?(note.comments.first.body)
+
+ assert_not page.has_content?(I18n.t("user.view.report"))
+ end
+
+ def test_can_report_user
+ user = create(:user)
+ sign_in_as(create(:user))
+ visit user_path(user.display_name)
+
+ click_on I18n.t("user.view.report")
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("reports.new.disclaimer.intro")
+
+ choose I18n.t("reports.new.categories.user.vandal")
+ fill_in "report_details", :with => "This user is a vandal"
+ click_on "Create Report"
+
+ assert page.has_content? "Your report has been registered sucessfully"
+
+ assert_equal 1, Issue.count
+ assert Issue.last.reportable == user
+ end
+end