# Offense count: 41
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
- Max: 240
+ Max: 250
# 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.4)
actionpack (= 5.1.4)
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-block{
+ width:280px;
+ float:right;
+}
+
+.issue-comments {
+ width:475px;
+}
+
+.report-disclaimer {
+ background: #fff1f0;
+ color: #d85030;
+ border-color: rgba(216, 80, 48, 0.3);
+ padding: 5px;
+ 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("issues.comment.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_admin.not_an_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 :set_issues
+ before_action :check_permission
+ before_action :find_issue, :only => [:show, :resolve, :reopen, :ignore]
+
+ def index
+ @title = t ".title"
+
+ if current_user.moderator?
+ @issue_types = @moderator_issues
+ @users = User.joins(:roles).where(:user_roles => { :role => "moderator" })
+ else
+ @issue_types = @admin_issues
+ @users = User.joins(:roles).where(:user_roles => { :role => "administrator" })
+ end
+
+ @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
+ notice = t("issues.index.user_not_found")
+ end
+ end
+
+ if params[:status] && params[:status][0].present?
+ @issues = @issues.where(:status => params[:status][0])
+ end
+
+ if params[:issue_type] && params[:issue_type][0].present?
+ @issues = @issues.where(:reportable_type => params[:issue_type][0])
+ end
+
+ if params[:last_updated_by] && params[:last_updated_by][0].present?
+ last_updated_by = params[:last_updated_by][0].to_s == "nil" ? nil : params[:last_updated_by][0].to_i
+ @issues = @issues.where(:updated_by => last_updated_by)
+ end
+
+ redirect_to issues_path, :notice => notice if notice
+ 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("issues.resolved")
+ else
+ render :show
+ end
+ end
+
+ def ignore
+ if @issue.ignore
+ @issue.updated_by = current_user.id
+ @issue.save!
+ redirect_to @issue, :notice => t("issues.ignored")
+ else
+ render :show
+ end
+ end
+
+ def reopen
+ if @issue.reopen
+ @issue.updated_by = current_user.id
+ @issue.save!
+ redirect_to @issue, :notice => t("issues.reopened")
+ else
+ render :show
+ end
+ end
+
+ private
+
+ def set_issues
+ @admin_issues = %w[DiaryEntry DiaryComment User]
+ @moderator_issues = %w[Note]
+ end
+
+ def check_if_updated
+ if @issue.reportable && (@issue.ignored? || @issue.resolved?) && @issue.reportable.has_attribute?(:updated_by) && @issue.reportable.updated_at > @last_report.updated_at
+ true
+ else
+ false
+ end
+ end
+
+ def find_issue
+ @issue = Issue.find(params[:id])
+ end
+
+ def check_permission
+ unless current_user.administrator? || current_user.moderator?
+ flash[:error] = t("application.require_admin.not_an_admin")
+ redirect_to root_path
+ end
+ end
+
+ def issue_params
+ params[:issue].permit(:reportable_id, :reportable_type)
+ end
+
+ def report_params
+ params[:report].permit(:details)
+ end
+
+ def issue_comment_params
+ params.require(:issue_comment).permit(:body)
+ end
+
+ def sort_column
+ Issue.column_names.include?(params[:sort]) ? params[:sort] : "status"
+ end
+
+ def sort_direction
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
+ end
+end
--- /dev/null
+class ReportsController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :require_user
+
+ def new
+ if create_new_report_params.present?
+ @report = Report.new
+ @report.issue = Issue.find_or_initialize_by(create_new_report_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 root_path, :notice => t("issues.create.successful_report")
+ else
+ redirect_to new_report_path(:reportable_type => @report.issue.reportable_type, :reportable_id => @report.issue.reportable_id), :notice => t("issues.create.provide_details")
+ end
+ end
+
+ private
+
+ 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
+# == 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 :integer
+# 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_reportable_id_and_reportable_type (reportable_id,reportable_type)
+# index_issues_on_reported_user_id (reported_user_id)
+# index_issues_on_updated_by (updated_by)
+#
+# Foreign Keys
+#
+# issues_reported_user_id_fkey (reported_user_id => users.id) ON DELETE => cascade
+# issues_updated_by_fkey (updated_by => users.id) ON DELETE => cascade
+#
+
+class Issue < ActiveRecord::Base
+ belongs_to :reportable, :polymorphic => true
+ belongs_to :reported_user, :class_name => "User", :foreign_key => :reported_user_id
+ 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
+
+ # Check if more statuses are needed
+ enum :status => %w[open ignored resolved]
+
+ 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
+ role = %w[Note].include?(reportable.class.name) ? "moderator" : "administrator"
+ self.assigned_role = role if assigned_role.blank?
+ 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) ON DELETE => cascade
+# issue_comments_user_id (user_id => users.id) ON DELETE => cascade
+#
+
+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
+# user_id :integer
+# 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) ON DELETE => cascade
+# reports_user_id_fkey (user_id => users.id) ON DELETE => cascade
+#
+
+class Report < ActiveRecord::Base
+ belongs_to :issue, :counter_cache => true
+ belongs_to :user
+
+ 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 vandalism 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_one :issue, :class_name => "Issue", :foreign_key => :updated_by
+ 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) }
<br/>
<%= note_event(@note.status, @note.closed_at, @note_comments.last.author) %>
<% end %>
+ <% if current_user && current_user != @note.author %>
+ <%= link_to new_report_url(reportable_id: @note.id, reportable_type: @note.class.name), :title => t('browse.note.report') do %>
+ ⚐
+ <% end %>
+ <% end %>
</div>
<% if @note_comments.find { |comment| comment.author.nil? } -%>
<div class="clearfix diary-comment">
<%= user_thumbnail diary_comment.user %>
- <p class="deemphasize comment-heading" id="comment<%= diary_comment.id %>"><%= raw(t('diary_entry.diary_comment.comment_from', :link_user => (link_to h(diary_comment.user.display_name), :controller => 'user', :action => 'view', :display_name => diary_comment.user.display_name), :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('diary_entry.diary_comment.comment_from', :link_user => (link_to h(diary_comment.user.display_name), :controller => 'user', :action => 'view', :display_name => diary_comment.user.display_name), :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 %>
+ <%= link_to new_report_url(reportable_id: diary_comment.id, reportable_type: diary_comment.class.name), :title => t('diary_entry.diary_comment.report') do %>
+ ⚐
+ <% end %>
+ <% end %>
+ </p>
+
<div class="richtext"><%= diary_comment.body.to_html %></div>
<%= if_administrator(:span) do %>
<%= link_to t('diary_entry.diary_comment.hide_link'), hide_diary_comment_path(:display_name => diary_comment.diary_entry.user.display_name, :id => diary_comment.diary_entry.id, :comment => diary_comment.id), :method => :post, :data=> { :confirm => t('diary_entry.diary_comment.confirm') } %>
<h2><%= link_to h(diary_entry.title), :action => 'view', :display_name => diary_entry.user.display_name, :id => diary_entry.id %></h2>
+ <% if current_user and diary_entry.user != current_user %>
+ <%= link_to new_report_url(reportable_id: diary_entry.id, reportable_type: diary_entry.class.name), :title => t('diary_entry.diary_entry.report') do %>
+ ⚐
+ <% end %>
+ <% end %>
+
<small class='deemphasize'>
<%= raw(t 'diary_entry.diary_entry.posted_by', :link_user => (link_to h(diary_entry.user.display_name), :controller => 'user', :action => 'view', :display_name => diary_entry.user.display_name), :created => l(diary_entry.created_at, :format => :blog), :language_link => (link_to h(diary_entry.language.name), :controller => 'diary_entry', :action => 'list', :display_name => nil, :language => diary_entry.language_code)) %>
</small>
--- /dev/null
+<div class="issue-comments">
+ <% comments.each do |comment| %>
+ <div class="comment">
+ <div style="float:left">
+ <%= link_to user_thumbnail(comment.user), :controller => :user, :action =>:view, :display_name => comment.user.display_name %>
+ </div>
+ <b> <%= link_to comment.user.display_name, :controller => :user, :action =>:view, :display_name => 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('issues.show.comments.reassign_param') %> <%= check_box_tag :reassign, true %>
+ <br/>
+ <br/>
+ <%= submit_tag 'Submit' %>
+ <% end %>
+</div>
--- /dev/null
+<% reports.each do |report| %>
+ <div class="reports">
+ <div style="float:left">
+ <%= link_to user_thumbnail(report.user), :controller => :user, :action =>:view, :display_name => report.user.display_name %>
+ </div>
+ <%= t(".reported_by_html", :user_name => report.user.display_name, :user_url => url_for(:controller => :user, :action => :view, :display_name => report.user.display_name)) %> <br/>
+ <span class="deemphasize">
+ <%= t(".updated_at", :datetime => l(report.updated_at.to_datetime, :format => :friendly)) %>
+ </span>
+ <br/>
+ <span class="deemphasize">
+ <%= t ".category", category: report.category %>
+ </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 :status, nil, Issue.aasm.states.map(&:name).map{|state| [t("issues.states.#{state}"), state]}, { :include_blank => t(".select_status")}, data: { behavior: 'category_dropdown' } %>
+<%= select :issue_type, nil, @issue_types, { :include_blank => t(".select_type")}, data: { behavior: 'category_dropdown' } %>
+<%= text_field_tag :search_by_user, params[:search_by_user], placeholder: t(".reported_user") %>
+<%= select :last_updated_by, nil, @users.all.collect {|f| [f.display_name, f.id]} << [ t(".not_updated"), "nil"], { :include_blank => t(".select_last_updated_by")}, data: { behavior: 'category_dropdown' } %>
+<%= submit_tag t(".search") %>
+<% end %>
+<br/>
+
+<% if @issues.length == 0 %>
+ <p><%= t ".issues_not_found" %></p>
+<% end %>
+
+<br/>
+
+<table>
+ <thead>
+ <tr>
+ <td><b><%= t ".status" %></b></td>
+ <td><b><%= t ".reports" %></b></td>
+ <td><b><%= t ".reported_item" %></b></td>
+ <td><b><%= t ".reported_user" %></b></td>
+ <td><b><%= t ".last_updated_by" %></b></td>
+ <td><b><%= t ".last_updated_at" %></b></td>
+ </tr>
+ </thead>
+ <tbody>
+ <% @issues.each do |issue| %>
+ <tr>
+ <td><%= t "issues.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, :controller => :user, :action => :view, :display_name => issue.reported_user.display_name if issue.reported_user %></td>
+ <td><% if issue.user_updated %> <%= issue.user_updated.display_name %> <% else %> - <% end %></td>
+ <td><%= l(issue.updated_at.to_datetime, :format => :friendly) %></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('issues.resolve'), resolve_issue_url(@issue), :method => :post if @issue.may_resolve? %>
+ <% if @issue.may_ignore? %>
+ | <%= link_to t('issues.ignore'), ignore_issue_url(@issue), :method => :post %>
+ <% end %>
+</p>
+<p><%= link_to t('issues.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-block">
+ <h3><%= t ".other_issues_against_this_user" %></h3>
+ <div class="unread-reports">
+ <% if @related_issues.count > 1 %>
+ <% @related_issues.each do |issue| %>
+ <% if issue.id != @issue.id %>
+ <%= link_to reportable_title(issue.reportable), issue %> <br/>
+ <% end %>
+ <% end %>
+ <% else %>
+ <p><%= t ".no_other_issues" %></p>
+ <% end %>
+ </div>
+ </div>
+ <% end %>
+</div>
+
+<h3><%= t ".comments_on_this_issue" %></h3>
+<div class="unread-reports">
+ <%= render 'comments', comments: @comments %>
+</div>
</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('issues.new.disclaimer.intro') %>
+ <ul>
+ <li> <%= t('issues.new.disclaimer.not_just_mistake') %> </li>
+ <li> <%= t('issues.new.disclaimer.unable_to_fix') %> </li>
+ <li> <%= t('issues.new.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('issues.new.select') %></p>
+ <ul>
+ <% Report.categories_for(@report.issue.reportable).each do |c| %>
+ <li>
+ <%= radio_button :report, :category, c %>
+ <%= label_tag "report_category_#{c}", t("reports.categories.#{@report.issue.reportable.class.name}.#{c}") %> <br/>
+ </li>
+ <% end %>
+ </ul>
+ </div>
+
+ <div class='form-row'>
+ <%= text_area :report, :details, :cols => 20, :rows => 5, placeholder: t('issues.new.details'), required: true %>
+ </div>
+
+ <div class='buttons'>
+ <%= f.submit %>
+ </div>
+ </fieldset>
+<% end %>
</li>
<% end %>
+ <% if current_user and @this_user.id != current_user.id %>
+ <li>
+ <%= link_to new_report_url(reportable_id: @this_user.id, reportable_type: @this_user.class.name), :title => t('user.view.report') do %>
+ ⚐
+ <% end %>
+ </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:
+ report: Report
+ resolve: Resolve
+ ignore: Ignore
+ reopen: Reopen
+ 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_at: Last Updated At
+ last_updated_by: Last Updated By
+ link_to_reports: View Reports
+ reported_user: Reported User
+ reports_count:
+ one: "1 Report"
+ other: "%{count} Reports"
+ reported_item: Reported Item
+ create:
+ successful_report: Your report has been registered sucessfully
+ provide_details: Please provide the required details
+ update:
+ new_report: Your report been registered sucessfully
+ successful_update: Your report has been updated successfully
+ provide_details: Please provide the required details
+ new:
+ 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
+ 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}"
+ 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
+ comments:
+ reassign_param: Reassign Issue?
+ comment:
+ provide_details: Please provide the required details
+ comment_created: Your comment was successfully created
+ comments:
+ created_at: "On %{datetime}"
+ reports:
+ category: "Category: %{category}"
+ updated_at: "On %{datetime}"
+ reported_by_html: "Reported by <a href=\"%{user_url}\">%{user_name}</a>"
+ resolved: Issue status has been set to 'Resolved'
+ ignored: Issue status has been set to 'Ignored'
+ reopened: Issue status has been set to 'Open'
+ states:
+ ignored: Ignored
+ open: Open
+ resolved: Resolved
+ reports:
+ new:
+ title_html: "Report %{link}"
+ categories:
+ DiaryEntry:
+ spam: This Diary Entry is/contains spam
+ offensive: This Diary Entry is obscene/offensive
+ threat: This Diary Entry contains a threat
+ other: Other
+ DiaryComment:
+ 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
+ vandalism: This note is vandalism
+ personal: This note contains personal data
+ abusive: This note is abusive
+ other: Other
layouts:
project_name:
# in <title>
edit: Edit
history: History
export: Export
+ issues: Issues
data: Data
export_data: Export Data
gps_traces: GPS Traces
paragraph_1_html: |
OpenStreetMap has few formal rules but we expect all participants to collaborate
with, and communicate with, the community. If you are considering
- any activities other than editing by hand, please read and follow the guidelines on
- <a href='https://wiki.openstreetmap.org/wiki/Import/Guidelines'>Imports</a> and
+ any activities other than editing by hand, please read and follow the guidelines on
+ <a href='https://wiki.openstreetmap.org/wiki/Import/Guidelines'>Imports</a> and
<a href='https://wiki.openstreetmap.org/wiki/Automated_Edits_code_of_conduct'>Automated Edits</a>.
questions:
title: Any questions?
title: Join the community
explanation_html: |
If you have noticed a problem with our map data, for example a road is missing or your address, the best way to
- proceed is to join the OpenStreetMap community and add or repair the data yourself.
+ proceed is to join the OpenStreetMap community and add or repair the data yourself.
add_a_note:
instructions_html: |
Just click <a class='icon note'></a> or the same icon on the map display.
title: Other concerns
explanation_html: |
If you have concerns about how our data is being used or about the contents please consult our
- <a href='/copyright'>copyright page</a> for more legal information, or contact the appropriate
- <a href='https://wiki.osmfoundation.org/wiki/Working_Groups'>OSMF working group</a>.
+ <a href='/copyright'>copyright page</a> for more legal information, or contact the appropriate
+ <a href='https://wiki.osmfoundation.org/wiki/Working_Groups'>OSMF working group</a>.
help_page:
title: Getting Help
introduction: |
License page</a> for details.
legal_title: Legal
legal_html: |
- This site and many other related services are formally operated by the
- <a href='https://osmfoundation.org/'>OpenStreetMap Foundation</a> (OSMF)
- on behalf of the community. Use of all OSMF operated services is subject
+ This site and many other related services are formally operated by the
+ <a href='https://osmfoundation.org/'>OpenStreetMap Foundation</a> (OSMF)
+ on behalf of the community. Use of all OSMF operated services is subject
to our <a href="https://wiki.openstreetmap.org/wiki/Acceptable_Use_Policy">
Acceptable Use Policies</a> and our <a href="https://wiki.osmfoundation.org/wiki/Privacy_Policy">Privacy Policy</a>
- <br>
- Please <a href='https://osmfoundation.org/Contact'>contact the OSMF</a>
- if you have licensing, copyright or other legal questions.
+ <br>
+ Please <a href='https://osmfoundation.org/Contact'>contact the OSMF</a>
+ if you have licensing, copyright or other legal questions and issues.
<br>
OpenStreetMap, the magnifying glass logo and State of the Map are <a href="https://wiki.osmfoundation.org/wiki/Trademark_Policy">registered trademarks of the OSMF</a>.
partners_title: Partners
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."
setup_user_auth:
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"
match "/user/:display_name/diary" => "diary_entry#list", :via => :get
match "/diary/:language" => "diary_entry#list", :via => :get
match "/diary" => "diary_entry#list", :via => :get
- match "/user/:display_name/diary/:id" => "diary_entry#view", :via => :get, :id => /\d+/
+ match "/user/:display_name/diary/:id" => "diary_entry#view", :via => :get, :id => /\d+/, :as => :diary_entry
match "/user/:display_name/diary/:id/newcomment" => "diary_entry#comment", :via => :post, :id => /\d+/
match "/user/:display_name/diary/:id/edit" => "diary_entry#edit", :via => [:get, :post], :id => /\d+/
match "/user/:display_name/diary/:id/hide" => "diary_entry#hide", :via => :post, :id => /\d+/, :as => :hide_diary_entry
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
+class CreateIssuesAndReports < ActiveRecord::Migration[5.0]
+ def change
+ create_table :issues do |t|
+ t.string :reportable_type, :null => false
+ t.integer :reportable_id, :null => false
+ t.integer :reported_user_id
+ t.integer :status
+ 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", :on_delete => :cascade
+ add_foreign_key :issues, :users, :column => :updated_by, :name => "issues_updated_by_fkey", :on_delete => :cascade
+
+ add_index :issues, :reported_user_id
+ add_index :issues, [:reportable_id, :reportable_type]
+ add_index :issues, :updated_by
+
+ create_table :reports do |t|
+ t.integer :issue_id
+ t.integer :user_id
+ 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", :on_delete => :cascade
+ add_foreign_key :reports, :users, :column => :user_id, :name => "reports_user_id_fkey", :on_delete => :cascade
+
+ add_index :reports, :user_id
+ add_index :reports, :issue_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", :on_delete => :cascade
+ add_foreign_key :issue_comments, :users, :column => :user_id, :name => "issue_comments_user_id", :on_delete => :cascade
+
+ add_index :issue_comments, :user_id
+ add_index :issue_comments, :issue_id
+ end
+end
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 integer,
+ 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,
+ user_id integer,
+ 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: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments ALTER COLUMN id SET DEFAULT nextval('issue_comments_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues ALTER COLUMN id SET DEFAULT nextval('issues_id_seq'::regclass);
+
+
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY redactions ALTER COLUMN id SET DEFAULT nextval('redactions_id_seq'::regclass);
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports ALTER COLUMN id SET DEFAULT nextval('reports_id_seq'::regclass);
+
+
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ADD CONSTRAINT gpx_files_pkey PRIMARY KEY (id);
+--
+-- Name: issue_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: issues_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issues
+ ADD CONSTRAINT issues_pkey PRIMARY KEY (id);
+
+
--
-- Name: languages_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ADD CONSTRAINT relations_pkey PRIMARY KEY (relation_id, version);
+--
+-- Name: reports_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports
+ ADD CONSTRAINT reports_pkey PRIMARY KEY (id);
+
+
--
-- Name: 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_reportable_id_and_reportable_type; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issues_on_reportable_id_and_reportable_type ON issues USING btree (reportable_id, reportable_type);
+
+
+--
+-- 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_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_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) ON DELETE CASCADE;
+
+
+--
+-- Name: issue_comments_user_id; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: 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) ON DELETE CASCADE;
+
+
+--
+-- Name: 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) ON DELETE CASCADE;
+
+
--
-- Name: 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_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) ON DELETE CASCADE;
+
+
+--
+-- Name: 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) ON DELETE CASCADE;
+
+
--
-- Name: user_blocks_moderator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
('20150111192335'),
('20150222101847'),
('20150818224516'),
+('20160822153055'),
('20161002153425'),
('20161011010929'),
('20170222134109'),
--- /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 root_path
+ 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 root_path
+
+ 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 root_path
+
+ # 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_reported_user
+ note = create(:note_comment, :author => create(:user)).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
+
+ # FIXME: doesn't handle anonymous notes
+ issue.reportable = note
+ issue.save!
+ assert_equal issue.reported_user, note.author
+
+ 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_details_required
+ report = create(:report)
+
+ assert report.valid?
+ report.details = ""
+ assert !report.valid?
+ end
+
+ def test_category_required
+ report = create(:report)
+
+ assert report.valid?
+ report.category = ""
+ assert !report.valid?
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class IssuesTest < ApplicationSystemTestCase
+ include IssuesHelper
+
+ def test_view_issues_normal_user
+ sign_in_as(create(:user))
+
+ visit issues_path
+ assert page.has_content?(I18n.t("application.require_admin.not_an_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 !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(".issues.comment.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?(user1.display_name, :href => issue_path(issue1))
+ assert page.has_link?(user2.display_name, :href => issue_path(issue2))
+ end
+end
--- /dev/null
+require "application_system_test_case"
+
+class ReportAnonymousNoteTest < ApplicationSystemTestCase
+ def test_no_flag_when_not_logged_in
+ note = create(:note_with_comments)
+ visit browse_note_path(note)
+ assert page.has_content?(note.comments.first.body)
+
+ assert !page.has_content?("\u2690")
+ end
+
+ def test_can_report_anonymous_notes
+ note = create(:note_with_comments)
+ sign_in_as(create(:user))
+ visit browse_note_path(note)
+
+ click_on "\u2690"
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("issues.new.disclaimer.intro")
+
+ choose I18n.t("reports.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 ReportDiaryEntryTest < ApplicationSystemTestCase
+ def setup
+ create(:language, :code => "en")
+ @diary_entry = create(:diary_entry)
+ end
+
+ def test_no_flag_when_not_logged_in
+ visit diary_entry_path(@diary_entry.user.display_name, @diary_entry)
+ assert page.has_content?(@diary_entry.title)
+
+ assert !page.has_content?("\u2690")
+ 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 "\u2690"
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("issues.new.disclaimer.intro")
+
+ choose I18n.t("reports.categories.DiaryEntry.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 "\u2690"
+ assert page.has_content? "Report"
+ assert page.has_content? I18n.t("issues.new.disclaimer.intro")
+
+ choose I18n.t("reports.categories.DiaryEntry.spam")
+ fill_in "report_details", :with => "This is advertising"
+ click_on "Create Report"
+
+ issue.reload
+ assert !issue.resolved?
+ assert issue.open?
+ end
+end
end
end
end
+
+ def sign_in_as(user)
+ stub_hostip_requests
+ visit login_path
+ fill_in "username", :with => user.email
+ fill_in "password", :with => "test"
+ click_on "Login", :match => :first
+ end
end
end