# 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.0.4)
actionpack (= 5.0.4)
nio4r (>= 1.2, < 3.0)
DEPENDENCIES
SystemTimer (>= 1.1.3)
+ aasm
actionpack-page_caching
autoprefixer-rails
bigdecimal (~> 1.1.0)
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;
+}
+
+.new-report-form {
+ border:1px solid #ccc;
+ border-radius:4px;
+ display:block-inline;
+}
+
+.new-report-checkbox{
+ float:left;
+ margin-left:10px;
+ margin-top:3px;
+}
+
+.new-report-string {
+ font-size:15px;
+}
+
+.report-button {
+ float:right;
+}
+
+.disclaimer {
+ width: 600px;
+ background: #fff1f0;
+ color: #d85030;
+ border-color: rgba(216, 80, 48, 0.3);
+}
@entry = @this_user.diary_entries.visible.where(:id => params[:id]).first
if @entry
@title = t "diary_entry.view.title", :user => params[:display_name], :title => @entry.title
+ if params[:comment_id]
+ @reported_comment = DiaryComment.where(:id => params[:comment_id])
+ end
else
@title = t "diary_entry.no_such_entry.title", :id => params[:id]
render :action => "no_such_entry", :status => :not_found
--- /dev/null
+class IssuesController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :require_user
+ before_action :set_issues
+ before_action :check_permission, :only => [:index, :show, :resolve, :open, :ignore, :comment]
+ before_action :find_issue, :only => [:show, :resolve, :reopen, :ignore]
+ before_action :setup_user_role, :only => [:show, :index]
+
+ helper_method :sort_column, :sort_direction
+
+ def index
+ if @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(:issue_type => @user_role).order(sort_column + " " + sort_direction)
+
+ # If search
+ if params[:search_by_user] && !params[:search_by_user].blank?
+ @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.search.user_not_found")
+ end
+ end
+
+ if params[:status] && !params[:status][0].blank?
+ @issues = @issues.where(:status => params[:status][0].to_i)
+ end
+
+ if params[:issue_type] && !params[:issue_type][0].blank?
+ @issues = @issues.where(:reportable_type => params[:issue_type][0])
+ end
+
+ # If last_updated_by
+ if params[:last_updated_by] && !params[:last_updated_by][0].blank?
+ 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
+
+ notice = t("issues.index.search.issues_not_found") if @issues.first.nil?
+
+ if params[:last_reported_by] && !params[:last_reported_by][0].blank?
+ last_reported_by = params[:last_reported_by][0].to_s == "nil" ? nil : params[:last_reported_by][0].to_i
+ @issues = @issues.where(:updated_by => last_reported_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.user.issues.where(:issue_type => @user_role)
+
+ @updated_by_admin = User.find(@issue.updated_by) if @issue.updated_by
+ end
+
+ def new
+ unless create_new_issue_params.blank?
+ @issue = Issue.find_or_initialize_by(create_new_issue_params)
+ path = "issues.report_strings." + @issue.reportable.class.name.to_s
+ @report_strings_yaml = t(path)
+ end
+ end
+
+ def create
+ @issue = Issue.find_by_reportable_id_and_reportable_type(params[:reportable_id], params[:reportable_type])
+ # Check if Issue already exists
+ unless @issue
+ @issue = Issue.find_or_initialize_by(issue_params)
+ @issue.updated_by = nil
+
+ # Reassign to moderators if it is a moderator issue
+ @issue.issue_type = "administrator"
+ reassign_issue if @moderator_issues.include? @issue.reportable.class.name
+ end
+
+ # Check if details provided are sufficient
+ if check_report_params
+ @report = @issue.reports.build(report_params)
+ details = report_details
+ @report.reporter_user_id = @user.id
+ @report.details = details
+ # Checking if instance has been updated since last report
+ @last_report = @issue.reports.order(:updated_at => :desc).last
+ if check_if_updated
+ @issue.save! if @issue.reopen
+ end
+
+ if @issue.save!
+ @issue.report_count = @issue.reports.count
+ @issue.save!
+
+ @admins_or_mods = UserRole.where(:role => @issue.issue_type)
+ @admins_or_mods.each do |user|
+ Notifier.new_issue_notification(@issue.id, User.find(user.user_id)).deliver_now
+ end
+
+ redirect_back "/", :notice => t("issues.create.successful_report")
+ end
+ else
+ redirect_to new_issue_path(:reportable_type => @issue.reportable_type, :reportable_id => @issue.reportable_id, :reported_user_id => @issue.reported_user_id), :notice => t("issues.create.provide_details")
+ end
+ end
+
+ def update
+ @issue = Issue.find_by(issue_params)
+ # Check if details provided are sufficient
+ if check_report_params
+ @report = @issue.reports.where(:reporter_user_id => @user.id).first
+
+ if @report.nil?
+ @report = @issue.reports.build(report_params)
+ @report.reporter_user_id = @user.id
+ notice = t("issues.update.new_report")
+ end
+
+ details = report_details
+ @report.details = details
+
+ # Checking if instance has been updated since last report
+ @last_report = @issue.reports.order(:updated_at => :desc).last
+ if check_if_updated
+ @issue.reopen
+ @issue.save!
+ end
+
+ notice = t("issues.update.successful_update") if notice.nil?
+
+ if @report.save!
+ @issue.report_count = @issue.reports.count
+ @issue.save!
+ redirect_back "/", :notice => notice
+ end
+ else
+ redirect_to new_issue_path(:reportable_type => @issue.reportable_type, :reportable_id => @issue.reportable_id, :reported_user_id => @issue.reported_user_id), :notice => t("issues.update.provide_details")
+ end
+ end
+
+ def comment
+ @issue = Issue.find(params[:id])
+ if issue_comment_params.blank?
+ notice = t("issues.comment.provide_details")
+ else
+ @issue_comment = @issue.comments.build(issue_comment_params)
+ @issue_comment.commenter_user_id = @user.id
+ if params[:reassign]
+ reassign_issue
+ @issue_comment.reassign = true
+ end
+ @issue_comment.save!
+ @issue.updated_by = @user.id
+ @issue.save!
+ notice = t("issues.comment.comment_created")
+ end
+ redirect_to @issue, :notice => notice
+ 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 = @user.id
+ @issue.save!
+ redirect_to @issue, :notice => t("issues.ignored")
+ else
+ render :show
+ end
+ end
+
+ def reopen
+ if @issue.reopen
+ @issue.updated_by = @user.id
+ @issue.save!
+ redirect_to @issue, :notice => t("issues.reopened")
+ else
+ render :show
+ end
+ end
+
+ # Reassign Issues between Administrators and Moderators
+ def reassign_issue
+ @issue.issue_type = upgrade_issue(@issue.issue_type)
+ @issue.save!
+ end
+
+ private
+
+ def upgrade_issue(type)
+ if type == "moderator"
+ "administrator"
+ else
+ "moderator"
+ end
+ end
+
+ def set_issues
+ @admin_issues = %w(DiaryEntry DiaryComment User)
+ @moderator_issues = %w(Changeset Note)
+ end
+
+ def setup_user_role
+ # Get user role
+ @user_role = @user.administrator? ? "administrator" : "moderator"
+ 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
+ return true
+ else
+ return false
+ end
+ end
+
+ def report_details
+ params[:report][:details] + "--||--" + params[:report_type].to_s + "--||--"
+ end
+
+ def check_report_params
+ params[:report] && params[:report][:details] && params[:report_type]
+ end
+
+ def find_issue
+ @issue = Issue.find(params[:id])
+ end
+
+ def check_permission
+ unless @user.administrator? || @user.moderator?
+ flash[:error] = t("application.require_admin.not_an_admin")
+ redirect_to root_path
+ end
+ end
+
+ def create_new_issue_params
+ params.permit(:reportable_id, :reportable_type, :reported_user_id)
+ end
+
+ def issue_params
+ params[:issue].permit(:reportable_id, :reportable_type, :reported_user_id)
+ 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
+
+ # back-port of ActionController#redirect_back from rails 5
+ def redirect_back(fallback_location, **args)
+ if referer = request.headers["Referer"]
+ redirect_to referer, **args
+ else
+ redirect_to fallback_location, **args
+ end
+ end
+end
--- /dev/null
+module IssuesHelper
+ def reportable_url(reportable)
+ class_name = reportable.class.name
+ case class_name
+ when "DiaryEntry"
+ link_to reportable.title, :controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.user.display_name, :id => reportable.id
+ when "User"
+ link_to reportable.display_name.to_s, :controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.display_name
+ when "DiaryComment"
+ link_to "#{reportable.diary_entry.title}, Comment id ##{reportable.id}", :controller => reportable.diary_entry.class.name.underscore, :action => :view, :display_name => reportable.diary_entry.user.display_name, :id => reportable.diary_entry.id, :comment_id => reportable.id
+ when "Changeset"
+ link_to "Changeset ##{reportable.id}", :controller => :browse, :action => :changeset, :id => reportable.id
+ when "Note"
+ link_to "Note ##{reportable.id}", :controller => :browse, :action => :note, :id => reportable.id
+ end
+ end
+
+ def reports_url(issue)
+ class_name = issue.reportable.class.name
+ case class_name
+ when "DiaryEntry"
+ link_to issue.reportable.title, issue
+ when "User"
+ link_to issue.reportable.display_name.to_s, issue
+ when "DiaryComment"
+ link_to "#{issue.reportable.diary_entry.title}, Comment id ##{issue.reportable.id}", issue
+ when "Changeset"
+ link_to "Changeset ##{issue.reportable.id}", issue
+ when "Note"
+ link_to "Note ##{issue.reportable.id}", issue
+ end
+ end
+
+ def instance_url(reportable)
+ class_name = reportable.class.name
+ case class_name
+ when "DiaryEntry"
+ link_to "Show Instance", :controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.user.display_name, :id => reportable.id
+ when "User"
+ link_to "Show Instance", :controller => reportable.class.name.underscore, :action => :view, :display_name => reportable.display_name
+ when "DiaryComment"
+ link_to "Show Instance", :controller => reportable.diary_entry.class.name.underscore, :action => :view, :display_name => reportable.diary_entry.user.display_name, :id => reportable.diary_entry.id, :comment_id => reportable.id
+ when "Changeset"
+ link_to "Show Instance", :controller => :browse, :action => :changeset, :id => reportable.id
+ when "Note"
+ link_to "Show Instance", :controller => :browse, :action => :note, :id => reportable.id
+ end
+ end
+
+ def sortable(column, title = nil)
+ title ||= column.titleize
+ direction = column == sort_column && sort_direction == "asc" ? "desc" : "asc"
+ if column == sort_column
+ arrow = direction == "desc" ? ["25B2".hex].pack("U") : ["25BC".hex].pack("U")
+ title += arrow
+ end
+ link_to title, params.merge(:sort => column, :direction => direction)
+ end
+
+ def report_type(report_class)
+ case report_class
+ when "DiaryEntry"
+ t("activerecord.models.diary_entry")
+ when "User"
+ t("activerecord.models.user")
+ when "DiaryComment"
+ t("activerecord.models.diary_comment")
+ when "Changeset"
+ t("activerecord.models.changeset")
+ when "Note"
+ t("activerecord.models.note")
+ end
+ end
+end
--- /dev/null
+class Issue < ActiveRecord::Base
+ belongs_to :reportable, :polymorphic => true
+ belongs_to :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] }
+ validates :reported_user_id, :presence => true
+
+ # Check if more statuses are needed
+ enum :status => %w(open ignored resolved)
+ enum :type => %w(administrator moderator)
+
+ 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
+end
--- /dev/null
+class IssueComment < ActiveRecord::Base
+ belongs_to :issue
+ belongs_to :user, :class_name => "User", :foreign_key => :commenter_user_id
+
+ validates :body, :presence => true
+end
end
end
+ def new_issue_notification(issue_id, recipient)
+ with_recipient_locale recipient do
+ @url = url_for(:host => SERVER_URL,
+ :controller => "issues",
+ :action => "show",
+ :id => issue_id)
+ subject = I18n.t("notifier.new_issue_notification.subject")
+ mail :to => recipient.email, :subject => subject
+ end
+ end
+
private
def set_shared_template_vars
--- /dev/null
+class Report < ActiveRecord::Base
+ belongs_to :issue
+ belongs_to :user, :class_name => "User", :foreign_key => :reporter_user_id
+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) }
<h2>
<a class="geolink" href="<%= root_path %>"><span class="icon close"></span></a>
<%= t('browse.changeset.title', :id => @changeset.id) %>
+ <% if @user and @user.id != @changeset.user.id %>
+ <%= link_to new_issue_url(reportable_id: @changeset.id, reportable_type: @changeset.class.name, reported_user_id: @changeset.user.id,referer: request.fullpath), :title => t('browse.changeset.report') do %>
+ ⚐
+ <% end %>
+ <% end %>
</h2>
<div class="browse-section">
<%= render :partial => "tag_details", :object => @changeset.tags.except('comment') %>
<h4 class="comments-header"><%= t('browse.changeset.discussion') %></h4>
-
+
<div class="buttons clearfix subscribe-buttons">
<form action="#" class="hide_unless_logged_in">
<% if @user and @changeset.subscribers.exists?(@user.id) %>
<h2>
<a class="geolink" href="<%= root_path %>"><span class="icon close"></span></a>
<%= t "browse.note.#{@note.status}_title", :note_name => @note.id %>
+ <% if @user && @note.author && @user.id != @note.author.id %>
+ <%= link_to new_issue_url(reportable_id: @note.id, reportable_type: @note.class.name, reported_user_id: @note.author.id,referer: request.fullpath), :title => t('browse.note.report') do %>
+ ⚐
+ <% end %>
+ <% end %>
</h2>
<div class="browse-section">
<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 @user and diary_comment.user.id != @user.id %>
+ <%= link_to new_issue_url(reportable_id: diary_comment.id, reportable_type: diary_comment.class.name, reported_user_id: diary_comment.user.id, referer: request.fullpath), :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 @user and diary_entry.user.id != @user.id %>
+ <%= link_to new_issue_url(reportable_id: diary_entry.id, reportable_type: diary_entry.class.name, reported_user_id: diary_entry.user.id,referer: request.fullpath), :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>
<a id="comments"></a>
<div class='comments'>
-<%= render :partial => 'diary_comment', :collection => @entry.visible_comments %>
+ <% if @reported_comment %>
+ <%= render :partial => 'diary_comment', :collection => @reported_comment %>
+ <% else %>
+ <%= render :partial => 'diary_comment', :collection => @entry.visible_comments %>
+ <% end %>
</div>
<%= if_logged_in(:div) do %>
<h3 id="newcomment"><%= t 'diary_entry.view.leave_a_comment' %></h3>
--- /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 %>
+
+ <% if comment.reassign %>
+ <br/>
+ <i><%= t('issues.show.comments.reassign') %></i>
+ <% end %>
+ </div>
+ <span class="deemphasize">
+ On <%= l comment.created_at.to_datetime, :format => :friendly %>
+ </span>
+ <hr>
+ <% end %>
+</div>
+<br/>
+<div class="comment">
+ <%= form_for :issue_comment, :url => { :action => 'comment', :id => @issue.id, :user_id => @user.id } do |f| %>
+ <%= richtext_area :issue_comment, :body, :cols => 10, :rows => 8, :required => true %>
+ <%= label_tag t('issues.show.comments.reassign_param') %> <%= check_box_tag :reassign, true %>
+ <br/>
+ <br/>
+ <%= submit_tag 'Submit' %>
+ <% end %>
+</div>
--- /dev/null
+<% reports.each do |report| %>
+ <% details = report.details.split("--||--") %>
+ <div class="reports">
+ <div style="float:left">
+ <%= link_to user_thumbnail(report.user), :controller => :user,:action =>:view, :display_name => report.user.display_name %>
+ </div>
+ Reported as <b><%= details[1].delete('[').delete(']').downcase %></b> by <b><%= link_to report.user.display_name, :controller => :user,:action =>:view, :display_name => report.user.display_name %></b> <br/>
+ <span class="deemphasize">
+ On <%= l report.updated_at.to_datetime, :format => :friendly %>
+ </span>
+ <br/>
+ <%= details[0] %>
+ <br/>
+ </div>
+ <hr>
+<% end %>
--- /dev/null
+<p id= "notice"><%= notice %></p>
+
+<% content_for :heading do %>
+ <h1>List of <%= @user_role %> issues:</h1>
+<% end %>
+
+<%= form_tag(issues_path, :method => :get) do %>
+Search for a particular issue(s): <br/>
+<%= select :status, nil, [['open', 0],['resolved',2],['ignored',1]],{:include_blank => "Select status"},data: { behavior: 'category_dropdown' } %>
+<%= select :issue_type, nil, @issue_types,{:include_blank => "Select type"}, data: { behavior: 'category_dropdown' } %>
+<%= text_field_tag :search_by_user, params[:search_by_user], placeholder: "Reported User" %>
+<%= select :last_reported_by, nil, @users.all.collect {|f| [f.display_name, f.id]} << ['Not updated',"nil"], {:include_blank => "Select last updated by"}, data: { behavior: 'category_dropdown' } %>
+<%= submit_tag "Search" %>
+<% end %>
+<br/>
+<br/>
+
+<table>
+ <thead>
+ <tr>
+ <td style="width:40px;"><b> <%= sortable("status") %></b></td>
+ <td style="width:160px;"><b> <%= sortable("report_count", "Number of Reports") %></b></td>
+ <td style="width:141px;"><b> <%= sortable("updated_at","Last updated at") %></b></td>
+ <td style="width:140px;"><b> <%= sortable("updated_by","Last updated by") %></b></td>
+ <td style="width:203px;"><b> Link to reports </b></td>
+ <td style="width:128px;"><b> <%= sortable("reported_user_id","Reported User") %> </b></td>
+ <td style="width:67px;"><b> Link to reported instance</b></td>
+ </tr>
+ </thead>
+ <tbody>
+ <% @issues.each do |issue| %>
+ <tr>
+ <td><%= issue.status.humanize %></td>
+ <td style="text-align:center;"><%= issue.report_count %></td>
+ <td><%= l(issue.updated_at.to_datetime, :format => :friendly) %></td>
+ <td><% if issue.user_updated %> <%= issue.user_updated.display_name %> <% else %> - <% end %></td>
+ <td><%= reports_url(issue) %></td>
+ <td><%= link_to issue.user.display_name , :controller => :user, :action => :view,:display_name => issue.user.display_name %></td>
+ <td><%= instance_url(issue.reportable) %></td>
+ </tr>
+ <% end %>
+ </tbody>
+</table>
--- /dev/null
+<% content_for :heading do %>
+ <h1>Report <%= reportable_url(@issue.reportable) %></h1>
+<% end %>
+
+<div class="disclaimer">
+ <ul>
+ <%= t('issues.new.disclaimer.intro') %>:
+ <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(@issue) do |f| %>
+ <%= f.error_messages %>
+ <fieldset>
+ <div class='form-row'>
+ <%= f.hidden_field :reportable_id %>
+ <%= f.hidden_field :reportable_type %>
+ <%= f.hidden_field :reported_user_id %>
+ </div>
+
+ <div class='form-row' style='width:600px'>
+ <p><%= t('issues.new.select') %>:</p>
+
+ <div class="new-report-form">
+ <% @report_strings_yaml.each do |k,v| %>
+ <div style="padding-left:5px">
+ <%= radio_button_tag :report_type, v[:type].to_s %>
+ <%= label_tag v[:details].to_s %> <br/>
+ </div>
+ <% end %>
+ </div>
+
+ <br/>
+ <%= text_area :report, :details, :cols => 20, :rows => 3, placeholder: t('issues.new.details'), required: true %>
+ </div>
+
+ <div class='buttons'>
+ <%= submit_tag %>
+ </div>
+ </fieldset>
+<% end %>
--- /dev/null
+<% content_for :heading do %>
+<h2> <%= @issue.status.humanize %> Issue #<%= @issue.id %> <br/></h2>
+<p><%= report_type(@issue.reportable_type) %> : <%= reportable_url(@issue.reportable) %></p>
+<p class="deemphasize">
+ <small>
+ <%= @issue.reports.count %> reports | First reported: <%= l @issue.created_at.to_datetime, :format => :friendly %> <%= "| Last resolved at #{l(@issue.resolved_at.to_datetime, :format =>:friendly)}" if @issue.resolved_at? %> <%= "| Last updated at #{l(@issue.updated_at.to_datetime, :format => :friendly)} by #{@updated_by_admin.display_name}" if @updated_by_admin %>
+ </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>Reports under this issue:</h3>
+
+ <% if @read_reports.present? %>
+ <div class="read-reports">
+ <h4>Read Reports:</h4>
+ <br/>
+ <%= render 'reports',reports: @read_reports %>
+ </div>
+ <% end %>
+
+ <% if @unread_reports.any? %>
+ <div class="unread-reports">
+ <h4>New Reports:</h4>
+ <br/>
+ <%= render 'reports',reports: @unread_reports %>
+ </div>
+ <% end %>
+ <br/>
+ </div>
+
+ <div class="related-block">
+ <h3> 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 %>
+ <%= reports_url(issue) %> <br/>
+ <% end %>
+ <% end %>
+ <% else %>
+ <p>No other reports against this user!</p>
+ <% end %>
+ </div>
+ </div>
+</div>
+
+<h3>Comments on this issue:</h3>
+<div class="unread-reports">
+ <%= render 'comments', comments: @comments %>
+</div>
</nav>
<nav class='secondary'>
<ul>
+ <% if @user and ( @user.administrator? or @user.moderator? ) %>
+ <li class="compact-hide <%= current_page_class(issues_path) %>"><b><%= link_to t('layouts.reports'), issues_path %></b></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
+<p><%= t("notifier.new_issue_notification.greeting") %></p>
+
+<p><%= t("notifier.new_issue_notification.new_issue") %></p>
+
+<p><%= link_to t("notifier.new_issue_notification.url"), @url %></p>
\ No newline at end of file
</p>
</div>
+ <% if @user and @this_user.id != @user.id %>
+ <div class="report-button">
+ <%= link_to new_issue_url(reportable_id: @this_user.id, reportable_type: @this_user.class.name, reported_user_id: @this_user.id,referer: request.fullpath), :title => t('user.view.report') do%>
+ ⚐
+ <% end %>
+ </div>
+ <% end %>
+
<div class="user-description richtext"><%= @this_user.description.to_html %></div>
</div>
title_comment: Changeset %{id} - %{comment}
join_discussion: Log in to join the discussion
discussion: Discussion
+ report: Report this changeset?
node:
title: 'Node: %{name}'
history_title: 'Node History: %{name}'
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:
+ search:
+ user_not_found: User does not exist
+ issues_not_found: No such issues found
+ 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 into the problem. (This field cannot be left blank!)
+ select: Select a reason for your report
+ disclaimer:
+ placeholder: Before sending in a report for official action, be sure that
+ placeholder1: You are sure that the problem is not just a mistake
+ placeholder2: You are unable to fix the problem yourself
+ placeholder3: You have tried to resolve the problem with the user
+ show:
+ comments:
+ reassign: The Issue was reassigned
+ reassign_param: Reassign Issue?
+ comment:
+ provide_details: Please provide the required details
+ comment_created: Your comment was successfully created
+ resolved: Issue status has been set to 'Resolved'
+ ignored: Issue status has been set to 'Ignored'
+ reopened: Issue status has been set to 'Open'
+ report_strings:
+ DiaryEntry:
+ spam:
+ type: "[SPAM]"
+ details: This Diary Entry is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This Diary Entry is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This Diary Entry contains a threat
+ other:
+ type: "[OTHER]"
+ details: Other
+ DiaryComment:
+ spam:
+ type: "[SPAM]"
+ details: This Diary Comment is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This Diary Comment is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This Diary Comment contains a threat
+ other:
+ type: "[OTHER]"
+ details: Other
+ User:
+ spam:
+ type: "[SPAM]"
+ details: This User profile is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This User profile is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This User profile contains a threat
+ vandal:
+ type: "[VANDAL]"
+ details: This User is a vandal
+ other:
+ type: "[OTHER]"
+ details: Other
+ Changeset:
+ undiscussed_import:
+ type: "[UNDISCUSSED-IMPORT]"
+ details: This changeset is an undiscussed import
+ mechanical_edit:
+ type: "[MECH-EDIT]"
+ details: This changeset is a mechanical edit
+ edit_error:
+ type: "[EDIT-ERROR]"
+ details: This changeset contains a newbie or an editor error
+ spam:
+ type: "[SPAM]"
+ details: This changeset is/contains spam
+ vandalism:
+ type: "[VANDALISM]"
+ details: This changeset is/contains vandalism
+ other:
+ type: "[OTHER]"
+ details: Other
+ Note:
+ spam:
+ type: "[SPAM]"
+ details: This note is spam
+ vandalism:
+ type: "[VANDALISM]"
+ details: This note is vandalism
+ personal:
+ type: "[PERSONAL]"
+ details: This note contains personal data
+ abusive:
+ type: "[ABUSIVE]"
+ details: This note is abusive
+ other:
+ type: "[OTHER]"
+ details: Other
layouts:
project_name:
title: OpenStreetMap
edit: Edit
history: History
export: Export
+ reports: Reports
data: Data
export_data: Export Data
gps_traces: GPS Traces
details: More details about the changeset can be found at %{url}.
unsubscribe: To unsubscribe from updates to this changeset, visit %{url} and
click "Unsubscribe".
+ new_issue_notification:
+ subject: "[OpenStreetMap] New Issue"
+ greeting: "Hi,"
+ new_issue: "A new issue has been created"
+ url: You can view the issue here
message:
inbox:
title: Inbox
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
title_comment: "Changeset %{id} - %{comment}"
join_discussion: "Log in to join the discussion"
discussion: Discussion
+ report: Report this changeset?
node:
title: "Node: %{name}"
history_title: "Node History: %{name}"
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:
+ search:
+ user_not_found: User does not exist
+ issues_not_found: No such issues found
+ 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 into the problem. (This field cannot be left blank!)
+ select: Select a reason for your report
+ disclaimer:
+ intro: Before sending in a report for official action, be sure that
+ not_just_mistake: You are sure that the problem is not just a mistake
+ unable_to_fix: You are unable to fix the problem yourself
+ resolve_with_user: You have tried to resolve the problem with the user
+ show:
+ comments:
+ reassign: The Issue was reassigned
+ reassign_param: Reassign Issue?
+ comment:
+ provide_details: Please provide the required details
+ comment_created: Your comment was successfully created
+ resolved: Issue status has been set to 'Resolved'
+ ignored: Issue status has been set to 'Ignored'
+ reopened: Issue status has been set to 'Open'
+ report_strings:
+ DiaryEntry:
+ spam:
+ type: "[SPAM]"
+ details: This Diary Entry is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This Diary Entry is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This Diary Entry contains a threat
+ other:
+ type: "[OTHER]"
+ details: Other
+ DiaryComment:
+ spam:
+ type: "[SPAM]"
+ details: This Diary Comment is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This Diary Comment is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This Diary Comment contains a threat
+ other:
+ type: "[OTHER]"
+ details: Other
+ User:
+ spam:
+ type: "[SPAM]"
+ details: This User profile is/contains spam
+ offensive:
+ type: "[OFFENSIVE]"
+ details: This User profile is obscene/offensive
+ threat:
+ type: "[THREAT]"
+ details: This User profile contains a threat
+ vandal:
+ type: "[VANDAL]"
+ details: This User is a vandal
+ other:
+ type: "[OTHER]"
+ details: Other
+ Changeset:
+ undiscussed_import:
+ type: "[UNDISCUSSED-IMPORT]"
+ details: This changeset is an undiscussed import
+ mechanical_edit:
+ type: "[MECH-EDIT]"
+ details: This changeset is a mechanical edit
+ edit_error:
+ type: "[EDIT-ERROR]"
+ details: This changeset contains a newbie or an editor error
+ spam:
+ type: "[SPAM]"
+ details: This changeset is/contains spam
+ vandalism:
+ type: "[VANDALISM]"
+ details: This changeset is/contains vandalism
+ other:
+ type: "[OTHER]"
+ details: Other
+ Note:
+ spam:
+ type: "[SPAM]"
+ details: This note is spam
+ vandalism:
+ type: "[VANDALISM]"
+ details: This note is vandalism
+ personal:
+ type: "[PERSONAL]"
+ details: This note contains personal data
+ abusive:
+ type: "[ABUSIVE]"
+ details: This note is abusive
+ other:
+ type: "[OTHER]"
+ details: Other
layouts:
project_name:
# in <title>
edit: Edit
history: History
export: Export
+ reports: Reports
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='http://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='http://wiki.openstreetmap.org/wiki/Import/Guidelines'>Imports</a> and
<a href='http://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='http://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='http://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='http://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='http://osmfoundation.org/'>OpenStreetMap Foundation</a> (OSMF)
+ on behalf of the community. Use of all OSMF operated services is subject
to our <a href="http://wiki.openstreetmap.org/wiki/Acceptable_Use_Policy">
Acceptable Use Policies</a> and our <a href="http://wiki.osmfoundation.org/wiki/Privacy_Policy">Privacy Policy</a>
- <br>
- Please <a href='http://osmfoundation.org/Contact'>contact the OSMF</a>
+ <br>
+ Please <a href='http://osmfoundation.org/Contact'>contact the OSMF</a>
if you have licensing, copyright or other legal questions and issues.
partners_title: Partners
notifier:
partial_changeset_without_comment: "without comment"
details: "More details about the changeset can be found at %{url}."
unsubscribe: 'To unsubscribe from updates to this changeset, visit %{url} and click "Unsubscribe".'
+ new_issue_notification:
+ subject: "[OpenStreetMap] New Issue"
+ greeting: "Hi,"
+ new_issue: "A new issue has been created"
+ url: You can view the issue here
message:
inbox:
title: "Inbox"
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"
resources :user_blocks
match "/blocks/:id/revoke" => "user_blocks#revoke", :via => [:get, :post], :as => "revoke_user_block"
+ # issues and reports
+ resources :issues do
+ member do
+ post "resolve"
+ post "assign"
+ post "ignore"
+ post "reopen"
+ end
+ end
+
+ post "/comment" => "issues#comment"
+
# redactions
resources :redactions
end
--- /dev/null
+require "migrate"
+
+class CreateIssuesAndReports < ActiveRecord::Migration
+ def change
+ create_table :issues do |t|
+ t.string :reportable_type, :null => false
+ t.integer :reportable_id, :null => false
+ t.integer :reported_user_id, :null => false
+ t.integer :status
+ t.string :issue_type
+ t.datetime :resolved_at
+ t.integer :resolved_by
+ t.datetime :created_at
+ t.datetime :updated_at
+ t.integer :updated_by
+
+ t.timestamps :null => false
+ end
+
+ add_foreign_key :issues, :users, :column => :reported_user_id, :name => "issues_reported_user_id_fkey", :on_delete => :cascade
+
+ add_index :issues, :reported_user_id
+ add_index :issues, [:reportable_id, :reportable_type]
+
+ create_table :reports do |t|
+ t.integer :issue_id
+ t.integer :reporter_user_id
+ t.text :details
+ t.datetime :created_at
+ t.datetime :updated_at
+
+ t.timestamps :null => false
+ end
+
+ add_foreign_key :reports, :issues, :name => "reports_issue_id_fkey", :on_delete => :cascade
+ add_foreign_key :reports, :users, :column => :reporter_user_id, :name => "reports_reporter_user_id_fkey", :on_delete => :cascade
+
+ add_index :reports, :reporter_user_id
+ add_index :reports, :issue_id
+ end
+end
--- /dev/null
+class CreateIssueComments < ActiveRecord::Migration
+ def change
+ create_table :issue_comments do |t|
+ t.integer :issue_id
+ t.integer :commenter_user_id
+ t.text :body
+ t.datetime :created_at
+ t.boolean :reassign
+ 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 => :commenter_user_id, :name => "issue_comments_commenter_user_id", :on_delete => :cascade
+
+ add_index :issue_comments, :commenter_user_id
+ add_index :issue_comments, :issue_id
+ end
+end
--- /dev/null
+class AddReportCountToIssues < ActiveRecord::Migration
+ def change
+ add_column :issues, :report_count, :integer, :default => 0
+ add_foreign_key :issues, :users, :column => :updated_by, :name => "issues_updated_by_fkey", :on_delete => :cascade
+ add_index :issues, :updated_by
+ 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,
+ commenter_user_id integer,
+ body text,
+ created_at timestamp without time zone NOT NULL,
+ reassign boolean,
+ 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 NOT NULL,
+ status integer,
+ issue_type character varying,
+ resolved_at timestamp without time zone,
+ resolved_by integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ updated_by integer,
+ report_count integer DEFAULT 0
+);
+
+
+--
+-- 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,
+ reporter_user_id integer,
+ details text,
+ 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_commenter_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_issue_comments_on_commenter_user_id ON issue_comments USING btree (commenter_user_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_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_reporter_user_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_reports_on_reporter_user_id ON reports USING btree (reporter_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_commenter_user_id; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY issue_comments
+ ADD CONSTRAINT issue_comments_commenter_user_id FOREIGN KEY (commenter_user_id) REFERENCES users(id) ON DELETE CASCADE;
+
+
+--
+-- 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: 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_reporter_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY reports
+ ADD CONSTRAINT reports_reporter_user_id_fkey FOREIGN KEY (reporter_user_id) REFERENCES users(id) ON DELETE CASCADE;
+
+
--
-- Name: user_blocks_moderator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
('20150111192335'),
('20150222101847'),
('20150818224516'),
+('20160822153055'),
+('20160822153115'),
+('20160822153153'),
('20161002153425'),
('20161011010929'),
('20170222134109'),
('7'),
('8'),
('9');
-
-
--- /dev/null
+require "test_helper"
+
+class IssuesControllerTest < ActionController::TestCase
+ fixtures :users, :user_roles, :issues
+
+ 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] = users(:normal_user).id
+ get :index
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ # Access issues_path by admin
+ session[:user] = users(:administrator_user).id
+ get :index
+ # this is redirected because there are no issues?!
+ assert_response :redirect
+ assert_redirected_to issues_path
+
+ # Access issues_path by moderator
+ session[:user] = users(:moderator_user).id
+ get :index
+ # this is redirected because there are no issues?!
+ assert_response :redirect
+ assert_redirected_to issues_path
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_new_issue_without_login
+ # Test creation of a new issue and a new report without logging in
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 1
+ assert_response :redirect
+ assert_redirected_to login_path(:referer => new_issue_path(:reportable_id => 1, :reportable_type => "User", :reported_user_id => 1))
+ end
+
+ def test_new_issue_after_login
+ # Test creation of a new issue and a new report
+
+ # Login
+ session[:user] = users(:normal_user).id
+
+ assert_equal Issue.count, 0
+
+ # Create an Issue and a report
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ post :create,
+ :report => { :details => details },
+ :report_type => "[OFFENSIVE]",
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_equal Issue.count, 1
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_new_report_with_incomplete_details
+ # Test creation of a new issue and a new report
+
+ # Login
+ session[:user] = users(:normal_user).id
+
+ assert_equal Issue.count, 0
+
+ # Create an Issue and a report
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ post :create,
+ :report => { :details => details },
+ :report_type => "[OFFENSIVE]",
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_equal Issue.count, 1
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2
+ assert_response :success
+
+ # Report without report_type
+ assert_no_difference "Issue.count" do
+ details = "Details of another report under the same issue"
+ post :create,
+ :report => { :details => details },
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_response :redirect
+ assert_equal Issue.find_by_reportable_id_and_reportable_type(1, "User").reports.count, 1
+
+ # Report without details
+ assert_no_difference "Issue.count" do
+ post :create,
+ :report_type => "[OFFENSIVE]",
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_response :redirect
+ assert_equal Issue.find_by_reportable_id_and_reportable_type(1, "User").reports.count, 1
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_new_report_with_complete_details
+ # Test creation of a new issue and a new report
+
+ # Login
+ session[:user] = users(:normal_user).id
+
+ assert_equal Issue.count, 0
+
+ # Create an Issue and a report
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2
+ assert_response :success
+ assert_difference "Issue.count", 1 do
+ details = "Details of a report"
+ post :create,
+ :report => { :details => details },
+ :report_type => "[OFFENSIVE]",
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_equal Issue.count, 1
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ # Create a report for an existing Issue
+ get :new, :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2
+ assert_response :success
+ assert_no_difference "Issue.count" do
+ details = "Details of another report under the same issue"
+ post :create,
+ :report => { :details => details },
+ :report_type => "[OFFENSIVE]",
+ :issue => { :reportable_id => 1, :reportable_type => "User", :reported_user_id => 2 }
+ end
+ assert_response :redirect
+ report_count = Issue.find_by_reportable_id_and_reportable_type(1, "User").reports.count
+ assert_equal report_count, 2
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_change_status_by_normal_user
+ # Login as normal user
+ session[:user] = users(:normal_user).id
+
+ # Create Issue
+ test_new_issue_after_login
+ assert_equal Issue.count, 1
+
+ get :resolve, :id => Issue.find_by_reportable_id_and_reportable_type(1, "User").id
+
+ assert_response :redirect
+ assert_redirected_to root_path
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_change_status_by_admin
+ # Login as normal user
+ session[:user] = users(:normal_user).id
+
+ # Create Issue
+ test_new_issue_after_login
+ assert_equal Issue.count, 1
+ assert_response :redirect
+
+ # Login as administrator
+ session[:user] = users(:administrator_user).id
+
+ # Test 'Resolved'
+ get :resolve, :id => Issue.find_by_reportable_id_and_reportable_type(1, "User").id
+ assert_equal Issue.find_by_reportable_id_and_reportable_type(1, "User").resolved?, true
+ assert_response :redirect
+
+ # Test 'Reopen'
+ get :reopen, :id => Issue.find_by_reportable_id_and_reportable_type(1, "User").id
+ assert_equal Issue.find_by_reportable_id_and_reportable_type(1, "User").open?, true
+ assert_response :redirect
+
+ # Test 'Ignored'
+ get :ignore, :id => Issue.find_by_reportable_id_and_reportable_type(1, "User").id
+ assert_equal Issue.find_by_reportable_id_and_reportable_type(1, "User").ignored?, true
+ assert_response :redirect
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_search_issues
+ # Login as administrator
+ session[:user] = users(:administrator_user).id
+
+ # No issues against the user
+ get :index, :search_by_user => "test1"
+ assert_response :redirect
+ assert_redirected_to issues_path
+
+ # User doesn't exist
+ get :index, :search_by_user => "test1000"
+ assert_response :redirect
+ assert_redirected_to issues_path
+
+ # Create Issue against user_id:2
+ test_new_issue_after_login
+ assert_equal Issue.count, 1
+ assert_equal Issue.first.reported_user_id, 2
+
+ session[:user] = users(:administrator_user).id
+
+ # Find Issue against user_id:2
+ get :index, :search_by_user => "test2"
+ assert_response :success
+
+ # clear session
+ session.delete(:user)
+ end
+
+ def test_comment_by_normal_user
+ # Create Issue
+ test_new_issue_after_login
+ assert_equal Issue.count, 1
+
+ get :comment, :id => 1
+ assert_response :redirect
+ assert_redirected_to root_path
+ end
+
+ def test_comment
+ # Create Issue
+ test_new_issue_after_login
+ assert_equal Issue.count, 1
+ @issue = Issue.all.first
+
+ # Login as administrator
+ session[:user] = users(:administrator_user).id
+
+ get :comment, :id => @issue.id, :issue_comment => { :body => "test comment" }
+ assert_response :redirect
+ assert_redirected_to @issue
+
+ # clear session
+ session.delete(:user)
+ end
+end
--- /dev/null
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ issue_id: 1
+ user_id: 1
+ body: MyText
+ created_at: 2015-05-26 18:30:32
+
+two:
+ issue_id: 1
+ user_id: 1
+ body: MyText
+ created_at: 2015-05-26 18:30:32
--- /dev/null
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# one:
+# reportable_type: User
+# reportable_id: 1
+# reported_user_id: 1
+# status: 1
+# issue_type: administrator
+# resolved_at: 2015-05-16 13:06:16
+# resolved_by: 1
+# created_at: 2015-05-16 13:06:16
+# updated_at: 2015-05-16 13:06:16
+
+# two:
+# reportable_type: User
+# reportable_id: 1
+# reported_user_id: 1
+# status: 1
+# issue_type: moderator
+# resolved_at: 2015-05-16 13:06:16
+# resolved_by: 1
+# created_at: 2015-05-16 13:06:16
+# updated_at: 2015-05-16 13:06:16
--- /dev/null
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ issue_id: 1
+ user_id: 1
+ details: MyText
+ created_at: 2015-05-16 13:26:20
+ updated_at: 2015-05-16 13:26:20
+
+two:
+ issue_id: 1
+ user_id: 1
+ details: MyText
+ created_at: 2015-05-16 13:26:20
+ updated_at: 2015-05-16 13:26:20
--- /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
+ # test "the truth" do
+ # assert true
+ # end
+end
--- /dev/null
+require "test_helper"
+
+class ReportTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end