Merge pull request #4781 from Zauberstuhl/report_feature

It is now possible to report posts and comments
This commit is contained in:
Jonne Haß 2014-05-15 19:52:26 +02:00
commit 63c44d9f6b
47 changed files with 615 additions and 276 deletions

View file

@ -49,7 +49,7 @@ Read more in [#4249](https://github.com/diaspora/diaspora/pull/4249) and [#4883]
* Redirect to the stream when switching the mobile publisher to desktop [#4806](https://github.com/diaspora/diaspora/issues/4806) * Redirect to the stream when switching the mobile publisher to desktop [#4806](https://github.com/diaspora/diaspora/issues/4806)
## Features ## Features
* You can report a single post by clicking the correct icon in the controler section [#4517](https://github.com/diaspora/diaspora/pull/4517) * You can report a single post or comment by clicking the correct icon in the controler section [#4517](https://github.com/diaspora/diaspora/pull/4517) [#4781](https://github.com/diaspora/diaspora/pull/4781)
* Add permalinks for comments [#4577](https://github.com/diaspora/diaspora/pull/4577) * Add permalinks for comments [#4577](https://github.com/diaspora/diaspora/pull/4577)
* New menu for the mobile version [#4673](https://github.com/diaspora/diaspora/pull/4673) * New menu for the mobile version [#4673](https://github.com/diaspora/diaspora/pull/4673)
* Added comment count to statistic to enable calculations of posts/comments ratios [#4799](https://github.com/diaspora/diaspora/pull/4799) * Added comment count to statistic to enable calculations of posts/comments ratios [#4799](https://github.com/diaspora/diaspora/pull/4799)

View file

Before

Width:  |  Height:  |  Size: 173 B

After

Width:  |  Height:  |  Size: 173 B

View file

@ -1,3 +0,0 @@
app.models.PostReport = Backbone.Model.extend({
urlRoot: '/post_report'
});

View file

@ -0,0 +1,4 @@
app.models.Report = Backbone.Model.extend({
urlRoot: '/report',
type: 'POST'
});

View file

@ -81,6 +81,37 @@ app.views.Base = Backbone.View.extend({
} }
}, },
report: function(evt) {
if(evt) { evt.preventDefault(); }
var msg = prompt(Diaspora.I18n.t('report.prompt'), Diaspora.I18n.t('report.prompt_default'));
if (msg == null) {
return;
}
var data = {
report: {
item_id: this.model.id,
item_type: $(evt.currentTarget).data("type"),
text: msg
}
};
var report = new app.models.Report();
report.save(data, {
success: function(model, response) {
Diaspora.page.flashMessages.render({
success: true,
notice: Diaspora.I18n.t('report.status.created')
});
},
error: function(model, response) {
Diaspora.page.flashMessages.render({
success: false,
notice: Diaspora.I18n.t('report.status.exists')
});
}
});
},
destroyModel: function(evt) { destroyModel: function(evt) {
evt && evt.preventDefault(); evt && evt.preventDefault();
var self = this; var self = this;

View file

@ -5,7 +5,8 @@ app.views.Comment = app.views.Content.extend({
events : function() { events : function() {
return _.extend({}, app.views.Content.prototype.events, { return _.extend({}, app.views.Content.prototype.events, {
"click .comment_delete": "destroyModel" "click .comment_delete": "destroyModel",
"click .comment_report": "report"
}); });
}, },

View file

@ -6,7 +6,8 @@ app.views.Feedback = app.views.Base.extend({
events: { events: {
"click *[rel='auth-required']" : "requireAuth", "click *[rel='auth-required']" : "requireAuth",
"click .like" : "toggleLike", "click .like" : "toggleLike",
"click .reshare" : "resharePost" "click .reshare" : "resharePost",
"click .post_report" : "report"
}, },
tooltipSelector : ".label", tooltipSelector : ".label",

View file

@ -20,7 +20,7 @@ app.views.StreamPost = app.views.Post.extend({
"click .remove_post": "destroyModel", "click .remove_post": "destroyModel",
"click .hide_post": "hidePost", "click .hide_post": "hidePost",
"click .post_report": "postReport", "click .post_report": "report",
"click .block_user": "blockUser" "click .block_user": "blockUser"
}, },
@ -108,21 +108,6 @@ app.views.StreamPost = app.views.Post.extend({
this.remove(); this.remove();
}, },
postReport : function(evt) {
if(evt) { evt.preventDefault(); }
var text = prompt(Diaspora.I18n.t('post_report_prompt'),
Diaspora.I18n.t('post_report_prompt_default'));
var postReport = new app.models.PostReport();
postReport.fetch({
data: {
post_id: this.model.id,
text: text
},
type: 'POST'
});
},
focusCommentTextarea: function(evt){ focusCommentTextarea: function(evt){
evt.preventDefault(); evt.preventDefault();
this.$(".new_comment_form_wrapper").removeClass("hidden"); this.$(".new_comment_form_wrapper").removeClass("hidden");

View file

@ -17,8 +17,8 @@
@import 'facebox' @import 'facebox'
@import 'aspects' @import 'aspects'
@import 'popover' @import 'popover'
@import 'post_report'
@import 'stream_element' @import 'stream_element'
@import 'report'
/* ====== media ====== */ /* ====== media ====== */
.media .media
@ -211,10 +211,10 @@ ul.as-selections
:z-index 6 :z-index 6
:float right :float right
.post_report .post_report, .comment_report
:display inline-block :display inline-block
.icons-postreport .icons-report
:height 14px :height 14px
:width 14px :width 14px

View file

@ -1,16 +1,18 @@
#post_report { #reports {
padding-top: 2em; padding-top: 2em;
.content {
float: left;
span { span {
display: block; display: block;
} }
.content {
float: left;
} }
.options { .options {
float: right; float: right;
} }
.clear { .clear {
clear: both; clear: both;
border-bottom: 1px solid #808080;
padding-bottom: 1em; padding-bottom: 1em;
margin-bottom: 1em;
} }
} }

View file

@ -61,6 +61,9 @@
i.comment:hover { i.comment:hover {
color: #424242; color: #424242;
} }
.post_report i.gray:hover {
color: $red;
}
i.heart.gray:hover { i.heart.gray:hover {
color: $red; color: $red;
} }
@ -204,6 +207,13 @@
&:hover { &:hover {
@include opacity(1); @include opacity(1);
} }
.comment_report {
display: inline-block;
.icons-report {
height: 14px;
width: 14px;
}
}
.delete { .delete {
display: inline-block; display: inline-block;
.icons-deletelabel { .icons-deletelabel {

View file

@ -133,17 +133,17 @@
padding-top: 10px; padding-top: 10px;
.controls { .controls {
.comment_delete { .comment_delete, .comment_report {
@include transition(opacity); @include transition(opacity);
@include opacity(0); @include opacity(0);
} }
} }
&:hover { &:hover {
.controls { .controls {
.comment_delete { .comment_delete, .comment_report {
@include opacity(0.3); @include opacity(0.3);
} }
.comment_delete:hover { .comment_delete:hover, .comment_report:hover {
@include opacity(1); @include opacity(1);
} }
} }

View file

@ -6,13 +6,17 @@
</div> </div>
<div class="bd"> <div class="bd">
{{#if canRemove}}
<div class="controls"> <div class="controls">
{{#if canRemove}}
<a href="#" class="delete comment_delete" title="{{t "delete"}}"> <a href="#" class="delete comment_delete" title="{{t "delete"}}">
<div alt="Deletelabel" class="icons-deletelabel" /> <div alt="Deletelabel" class="icons-deletelabel" />
<a/> <a/>
</div> {{else}}
<a href="#" data-type="comment" class="comment_report" title="{{t "report.name"}}">
<div class="icons-report"/>
</a>
{{/if}} {{/if}}
</div>
{{#with author}} {{#with author}}
<a href="/people/{{guid}}" class="author author-name {{hovercardable this}}"> <a href="/people/{{guid}}" class="author author-name {{hovercardable this}}">

View file

@ -22,5 +22,9 @@
</a> </a>
{{/if}} {{/if}}
{{/if}} {{/if}}
<a href="#" rel="auth-required" data-type="post" class="post_report" title="{{t "report.name"}}">
<i class="entypo gray large">&#x21;</i>
</a>
</div> </div>
</div> </div>

View file

@ -10,8 +10,8 @@
{{#if loggedIn}} {{#if loggedIn}}
<div class="controls"> <div class="controls">
{{#unless authorIsCurrentUser}} {{#unless authorIsCurrentUser}}
<a href="#" rel="nofollow" class="post_report" title="{{t "post_report"}}"> <a href="#" rel="nofollow" data-type="post" class="post_report" title="{{t "report.name"}}">
<div class="icons-postreport control_icon"/> <div class="icons-report control_icon"/>
</a> </a>
<a href="#" rel="nofollow" class="block_user" title="{{t "ignore"}}"> <a href="#" rel="nofollow" class="block_user" title="{{t "ignore"}}">
<div class="icons-ignoreuser control_icon"></div> <div class="icons-ignoreuser control_icon"></div>

View file

@ -1,61 +0,0 @@
class PostReportController < ApplicationController
before_filter :authenticate_user!
before_filter :redirect_unless_admin, :except => [:create]
def index
@post_report = PostReport.where(reviewed: false).all
end
def update
if PostReport.exists?(post_id: params[:id])
mark_as_reviewed
end
redirect_to :action => :index and return
end
def destroy
if Post.exists?(params[:id])
delete_post
mark_as_reviewed
end
redirect_to :action => :index and return
end
def create
username = current_user.username
unless PostReport.where(post_id: params[:post_id]).exists?(user_id: username)
post = PostReport.new(
:post_id => params[:post_id],
:user_id => username,
:text => params[:text])
result = post.save
status(( 200 if result ) || ( 422 if !result ))
else
status(409)
end
end
private
def delete_post
post = Post.find(params[:id])
current_user.retract(post)
flash[:notice] = I18n.t 'post_report.status.destroyed'
end
def mark_as_reviewed id = params[:id]
posts = PostReport.where(post_id: id)
posts.each do |post|
post.update_attributes(reviewed: true)
end
flash[:notice] = I18n.t 'post_report.status.marked'
end
def status(code)
if code == 200
flash[:notice] = I18n.t 'post_report.status.created'
else
flash[:error] = I18n.t 'post_report.status.failed'
end
render :nothing => true, :status => code
end
end

View file

@ -0,0 +1,42 @@
# Copyright (c) 2010-2011, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
class ReportController < ApplicationController
before_filter :authenticate_user!
before_filter :redirect_unless_admin, :except => [:create]
def index
@reports = Report.where(reviewed: false).all
end
def update
if report = Report.where(id: params[:id]).first
report.mark_as_reviewed
end
redirect_to :action => :index
end
def destroy
if (report = Report.where(id: params[:id]).first) && report.destroy_reported_item
flash[:notice] = I18n.t 'report.status.destroyed'
else
flash[:error] = I18n.t 'report.status.failed'
end
redirect_to :action => :index
end
def create
report = current_user.reports.new(report_params)
if report.save
render :json => true, :status => 200
else
render :nothing => true, :status => 409
end
end
private
def report_params
params.require(:report).permit(:item_id, :item_type, :text)
end
end

View file

@ -179,6 +179,7 @@ class UsersController < ApplicationController
:remember_me, :remember_me,
:getting_started, :getting_started,
email_preferences: [ email_preferences: [
:someone_reported,
:also_commented, :also_commented,
:mentioned, :mentioned,
:comment_on_post, :comment_on_post,

View file

@ -0,0 +1,16 @@
# Copyright (c) 2012, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
module ReportHelper
def report_content(id, type)
raw case type
when 'post'
t('report.post_label', title: link_to(post_page_title(Post.find_by_id(id)), post_path(id)))
when 'comment'
# comment_message is not html_safe. To prevent
# cross-site-scripting we have to escape html
t('report.comment_label', data: h(comment_message(Comment.find_by_id(id))))
end
end
end

View file

@ -1,18 +0,0 @@
class PostReportMailer < ActionMailer::Base
default :from => AppConfig.mail.sender_address
def new_report
Role.admins.each do |role|
email = User.find_by_id(role.person_id).email
format(email).deliver
end
end
private
def format(email)
mail(to: email, subject: I18n.t('notifier.post_report_email.subject')) do |format|
format.text { render 'post_report/post_report_email' }
format.html { render 'post_report/post_report_email' }
end
end
end

View file

@ -0,0 +1,26 @@
class ReportMailer < ActionMailer::Base
default :from => AppConfig.mail.sender_address
def new_report(type, id)
resource = {
:url => report_index_url,
:type => I18n.t('notifier.report_email.type.' + type),
:id => id
}
Role.admins.each do |role|
user = User.find_by_id(role.person_id)
unless user.user_preferences.exists?(:email_type => :someone_reported)
resource[:email] = user.email
format(resource).deliver
end
end
end
private
def format(resource)
mail(to: resource[:email], subject: I18n.t('notifier.report_email.subject', :type => resource[:type])) do |format|
format.html { render 'report/report_email', :locals => { :resource => resource } }
format.text { render 'report/report_email', :locals => { :resource => resource } }
end
end
end

View file

@ -1,15 +0,0 @@
class PostReport < ActiveRecord::Base
validates :user_id, presence: true
validates :post_id, presence: true
belongs_to :user
belongs_to :post
has_many :post_reports
after_create :send_report_notification
def send_report_notification
Workers::Mail::PostReportWorker.perform_async
end
end

60
app/models/report.rb Normal file
View file

@ -0,0 +1,60 @@
class Report < ActiveRecord::Base
validates :user_id, presence: true
validates :item_id, presence: true
validates :item_type, presence: true, :inclusion => { :in => %w(post comment),
:message => 'Type should match `post` or `comment`!'}
validates :text, presence: true
validate :entry_does_not_exist, :on => :create
belongs_to :user
belongs_to :post
belongs_to :comment
after_commit :send_report_notification, :on => :create
def entry_does_not_exist
if Report.where(item_id: item_id, item_type: item_type).exists?(user_id: user_id)
errors[:base] << 'You cannot report the same post twice.'
end
end
def destroy_reported_item
if item_type == 'post'
delete_post
elsif item_type == 'comment'
delete_comment
end
mark_as_reviewed
end
def delete_post
if post = Post.where(id: item_id).first
if post.author.local?
post.author.owner.retract(post)
else
post.destroy
end
end
end
def delete_comment
if comment = Comment.where(id: item_id).first
if comment.author.local?
comment.author.owner.retract(comment)
elsif comment.parent.author.local?
comment.parent.author.owner.retract(comment)
else
comment.destroy
end
end
end
def mark_as_reviewed
Report.where(item_id: item_id, item_type: item_type).update_all(reviewed: true)
end
def send_report_notification
Workers::Mail::ReportWorker.perform_async(self.item_type, self.item_id)
end
end

View file

@ -69,6 +69,8 @@ class User < ActiveRecord::Base
has_many :notifications, :foreign_key => :recipient_id has_many :notifications, :foreign_key => :recipient_id
has_many :reports
before_save :guard_unconfirmed_email, before_save :guard_unconfirmed_email,
:save_person! :save_person!

View file

@ -4,7 +4,8 @@ class UserPreference < ActiveRecord::Base
validate :must_be_valid_email_type validate :must_be_valid_email_type
VALID_EMAIL_TYPES = VALID_EMAIL_TYPES =
["mentioned", ["someone_reported",
"mentioned",
"comment_on_post", "comment_on_post",
"private_message", "private_message",
"started_sharing", "started_sharing",

View file

@ -5,7 +5,7 @@
%li= link_to t('.user_search'), user_search_path %li= link_to t('.user_search'), user_search_path
%li= link_to t('.weekly_user_stats'), weekly_user_stats_path %li= link_to t('.weekly_user_stats'), weekly_user_stats_path
%li= link_to t('.pod_stats'), pod_stats_path %li= link_to t('.pod_stats'), pod_stats_path
%li= link_to t('.post_report'), post_report_index_path %li= link_to t('.report'), report_index_path
%li= link_to t('.correlations'), correlations_path %li= link_to t('.correlations'), correlations_path
%li= link_to t('.sidekiq_monitor'), sidekiq_path %li= link_to t('.sidekiq_monitor'), sidekiq_path

View file

@ -1,21 +0,0 @@
.span-24
= render :partial => 'admins/admin_bar'
.span-24.last
%h1
= t('post_report.title')
%div#post_report
- @post_report.each do |report|
%div.content
%span
= raw t('post_report.post_label', title: link_to(post_page_title(Post.find_by_id(report.post_id)), post_path(report.post_id)))
%span
= raw t('post_report.reported_label', person: link_to(report.user_id, user_profile_path(report.user_id)))
%span
= t('post_report.reason_label', text: report.text)
%div.options
%span
= link_to t('post_report.review_link'), post_report_path(report.post_id), method: :put
%span
= link_to t('post_report.delete_link'), post_report_path(report.post_id), method: :delete
%div.clear

View file

@ -1,2 +0,0 @@
<%= t('notifier.post_report_email.body') %>

View file

@ -0,0 +1,27 @@
.span-24
= render :partial => 'admins/admin_bar'
.span-24.last
%h1
= t('report.title')
%div#reports
- @reports.each do |r|
- username = User.find_by_id(r.user_id).username
%div.content
%span
= report_content(r.item_id, r.item_type)
%span
= raw t('report.reported_label', person: link_to(username, user_profile_path(username)))
%span
= t('report.reason_label', text: r.text)
%div.options
%span
= button_to t('report.review_link'), report_path(r.id, :type => r.item_type),
:class => "button",
method: :put
%span
= button_to t('report.delete_link'), report_path(r.id, :type => r.item_type),
:data => { :confirm => t('report.confirm_deletion') },
:class => "button delete",
method: :delete
%div.clear

View file

@ -0,0 +1,2 @@
<%= t('notifier.report_email.body', url: resource[:url], type: resource[:type], id: resource[:id]) %>

View file

@ -125,11 +125,22 @@
= f.fields_for :email_preferences do |type| = f.fields_for :email_preferences do |type|
#email_prefs #email_prefs
- if current_user.admin?
%p.checkbox_select
= type.label :someone_reported, t('.someone_reported')
= type.check_box :someone_reported, {:checked => @email_prefs['someone_reported']}, false, true
%br
%p.checkbox_select %p.checkbox_select
= type.label :started_sharing, t('.started_sharing') = type.label :started_sharing, t('.started_sharing')
= type.check_box :started_sharing, {:checked => @email_prefs['started_sharing']}, false, true = type.check_box :started_sharing, {:checked => @email_prefs['started_sharing']}, false, true
%br %br
%p.checkbox_select
= type.label :also_commented, t('.also_commented')
= type.check_box :also_commented, {:checked => @email_prefs['also_commented']}, false, true
%br
%p.checkbox_select %p.checkbox_select
= type.label :mentioned, t('.mentioned') = type.label :mentioned, t('.mentioned')
= type.check_box :mentioned, {:checked => @email_prefs['mentioned']}, false, true = type.check_box :mentioned, {:checked => @email_prefs['mentioned']}, false, true

View file

@ -113,11 +113,22 @@
= f.fields_for :email_preferences do |type| = f.fields_for :email_preferences do |type|
#email_prefs #email_prefs
- if current_user.admin?
%p.checkbox_select
= type.label :someone_reported, t('.someone_reported')
= type.check_box :someone_reported, {:checked => @email_prefs['someone_reported']}, false, true
%br
%p.checkbox_select %p.checkbox_select
= type.label :started_sharing, t('.started_sharing') = type.label :started_sharing, t('.started_sharing')
= type.check_box :started_sharing, {:checked => @email_prefs['started_sharing']}, false, true = type.check_box :started_sharing, {:checked => @email_prefs['started_sharing']}, false, true
%br %br
%p.checkbox_select
= type.label :also_commented, t('.also_commented')
= type.check_box :also_commented, {:checked => @email_prefs['also_commented']}, false, true
%br
%p.checkbox_select %p.checkbox_select
= type.label :mentioned, t('.mentioned') = type.label :mentioned, t('.mentioned')
= type.check_box :mentioned, {:checked => @email_prefs['mentioned']}, false, true = type.check_box :mentioned, {:checked => @email_prefs['mentioned']}, false, true

View file

@ -1,12 +0,0 @@
module Workers
module Mail
class PostReportWorker < Base
sidekiq_options queue: :mail
def perform
PostReportMailer.new_report
end
end
end
end

View file

@ -0,0 +1,12 @@
module Workers
module Mail
class ReportWorker < Base
sidekiq_options queue: :mail
def perform(type, id)
ReportMailer.new_report(type, id)
end
end
end
end

View file

@ -99,7 +99,7 @@ en:
user_search: "User Search" user_search: "User Search"
weekly_user_stats: "Weekly User Stats" weekly_user_stats: "Weekly User Stats"
pod_stats: "Pod Stats" pod_stats: "Pod Stats"
post_report: "Reported Posts" report: "Reports"
correlations: "Correlations" correlations: "Correlations"
sidekiq_monitor: "Sidekiq monitor" sidekiq_monitor: "Sidekiq monitor"
correlations: correlations:
@ -736,9 +736,26 @@ en:
confirm_email: confirm_email:
subject: "Please activate your new email address %{unconfirmed_email}" subject: "Please activate your new email address %{unconfirmed_email}"
click_link: "To activate your new email address %{unconfirmed_email}, please follow this link:" click_link: "To activate your new email address %{unconfirmed_email}, please follow this link:"
post_report_email: report_email:
subject: "A new post was marked as offensive" type:
body: "Please review as soon as possible!" post: "post"
comment: "comment"
subject: "A new %{type} was marked as offensive"
body: |-
Hello,
the %{type} with ID %{id} was marked as offensive.
[%{url}][1]
Please review as soon as possible!
Cheers,
The diaspora* email robot!
[1]: %{url}
accept_invite: "Accept Your diaspora* invite!" accept_invite: "Accept Your diaspora* invite!"
invited_you: "%{name} invited you to diaspora*" invited_you: "%{name} invited you to diaspora*"
invite: invite:
@ -868,13 +885,15 @@ en:
other: "%{count} photos by %{author}" other: "%{count} photos by %{author}"
reshare_by: "Reshare by %{author}" reshare_by: "Reshare by %{author}"
post_report: report:
title: "Marked Reports Overview" title: "Reports Overview"
post_label: "<b>Post</b>: %{title}" post_label: "<b>Post</b>: %{title}"
comment_label: "<b>Comment</b>:<br>%{data}"
reported_label: "<b>Reported by</b> %{person}" reported_label: "<b>Reported by</b> %{person}"
reason_label: "Reason: %{text}" reason_label: "Reason: %{text}"
review_link: "Mark as reviewed" review_link: "Mark as reviewed"
delete_link: "Delete post" delete_link: "Delete item"
confirm_deletion: "Are you sure to delete the item?"
status: status:
marked: "The report was marked as reviewed" marked: "The report was marked as reviewed"
destroyed: "The post was destroyed" destroyed: "The post was destroyed"
@ -1206,6 +1225,7 @@ en:
edit_account: "Edit account" edit_account: "Edit account"
receive_email_notifications: "Receive email notifications when:" receive_email_notifications: "Receive email notifications when:"
started_sharing: "someone starts sharing with you" started_sharing: "someone starts sharing with you"
someone_reported: "someone sent a report"
mentioned: "you are mentioned in a post" mentioned: "you are mentioned in a post"
liked: "someone likes your post" liked: "someone likes your post"
reshared: "someone reshares your post" reshared: "someone reshares your post"

View file

@ -6,11 +6,15 @@
en: en:
javascripts: javascripts:
confirm_dialog: "Are you sure?" confirm_dialog: "Are you sure?"
post_report_prompt: "Please enter a reason:"
post_report_prompt_default: "offensive content"
delete: "Delete" delete: "Delete"
ignore: "Ignore" ignore: "Ignore"
post_report: "Report" report:
prompt: "Please enter a reason:"
prompt_default: "offensive content"
name: "Report"
status:
created: "The report was successfully created"
exists: "The report already exists"
ignore_user: "Ignore this user?" ignore_user: "Ignore this user?"
and: "and" and: "and"
comma: "," comma: ","

View file

@ -5,7 +5,7 @@
require 'sidekiq/web' require 'sidekiq/web'
Diaspora::Application.routes.draw do Diaspora::Application.routes.draw do
resources :post_report, :except => [:edit] resources :report, :except => [:edit, :new]
if Rails.env.production? if Rails.env.production?
mount RailsAdmin::Engine => '/admin_panel', :as => 'rails_admin' mount RailsAdmin::Engine => '/admin_panel', :as => 'rails_admin'

View file

@ -0,0 +1,6 @@
class AddPostTypeToPostReport < ActiveRecord::Migration
def change
add_column :post_reports, :post_type, :string, :null => false, :after => :post_id, :default => 'post'
change_column_default :post_reports, :post_type, nil
end
end

View file

@ -0,0 +1,8 @@
class RenamePostReportToReport < ActiveRecord::Migration
def self.up
rename_table :post_reports, :reports
end
def self.down
rename_table :reports, :post_reports
end
end

View file

@ -0,0 +1,11 @@
class RenamePostColumnsToItem < ActiveRecord::Migration
def up
rename_column :reports, :post_id, :item_id
rename_column :reports, :post_type, :item_type
end
def down
rename_column :reports, :item_id, :post_id
rename_column :reports, :item_type, :post_type
end
end

View file

@ -0,0 +1,12 @@
class ChangeUserIdTypeToInteger < ActiveRecord::Migration
def up
remove_column :reports, :user_id
add_column :reports, :user_id, :integer, :null => false, :default => 1
change_column_default :reports, :user_id, nil
end
def down
remove_column :reports, :user_id
add_column :reports, :user_id, :string
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20140308154022) do ActiveRecord::Schema.define(:version => 20140422134627) do
create_table "account_deletions", :force => true do |t| create_table "account_deletions", :force => true do |t|
t.string "diaspora_handle" t.string "diaspora_handle"
@ -316,17 +316,6 @@ ActiveRecord::Schema.define(:version => 20140308154022) do
add_index "polls", ["status_message_id"], :name => "index_polls_on_status_message_id" add_index "polls", ["status_message_id"], :name => "index_polls_on_status_message_id"
create_table "post_reports", :force => true do |t|
t.integer "post_id", :null => false
t.string "user_id"
t.boolean "reviewed", :default => false
t.text "text"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "post_reports", ["post_id"], :name => "index_post_reports_on_post_id"
create_table "posts", :force => true do |t| create_table "posts", :force => true do |t|
t.integer "author_id", :null => false t.integer "author_id", :null => false
t.boolean "public", :default => false, :null => false t.boolean "public", :default => false, :null => false
@ -410,6 +399,18 @@ ActiveRecord::Schema.define(:version => 20140308154022) do
add_index "rails_admin_histories", ["item", "table", "month", "year"], :name => "index_rails_admin_histories" add_index "rails_admin_histories", ["item", "table", "month", "year"], :name => "index_rails_admin_histories"
create_table "reports", :force => true do |t|
t.integer "item_id", :null => false
t.string "item_type", :null => false
t.boolean "reviewed", :default => false
t.text "text"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "user_id", :null => false
end
add_index "reports", ["item_id"], :name => "index_post_reports_on_post_id"
create_table "roles", :force => true do |t| create_table "roles", :force => true do |t|
t.integer "person_id" t.integer "person_id"
t.string "name" t.string "name"

View file

@ -50,7 +50,7 @@ class AccountDeleter
end end
def ignored_ar_user_associations def ignored_ar_user_associations
[:followed_tags, :invited_by, :contact_people, :aspect_memberships, :ignored_people, :conversation_visibilities, :conversations] [:followed_tags, :invited_by, :contact_people, :aspect_memberships, :ignored_people, :conversation_visibilities, :conversations, :reports]
end end
def delete_standard_user_associations def delete_standard_user_associations

View file

@ -1,79 +0,0 @@
require 'spec_helper'
describe PostReportController do
before do
sign_in alice
@message = alice.post(:status_message, :text => "hey", :to => alice.aspects.first.id)
end
describe '#index' do
context 'admin not signed in' do
it 'is behind redirect_unless_admin' do
get :index
response.should redirect_to stream_path
end
end
context 'admin signed in' do
before do
Role.add_admin(alice.person)
end
it 'succeeds and renders index' do
get :index
response.should render_template('index')
end
end
end
describe '#create' do
context 'report offensive content' do
it 'succeeds' do
put :create, :post_id => @message.id, :text => 'offensive content'
response.status.should == 200
PostReport.exists?(:post_id => @message.id).should be_true
end
end
end
describe '#update' do
context 'mark report as user' do
it 'is behind redirect_unless_admin' do
put :update, :id => @message.id
response.should redirect_to stream_path
PostReport.where(:reviewed => false, :post_id => @message.id).should be_true
end
end
context 'mark report as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
put :update, :id => @message.id
response.status.should == 302
PostReport.where(:reviewed => true, :post_id => @message.id).should be_true
end
end
end
describe '#destroy' do
context 'destroy post as user' do
it 'is behind redirect_unless_admin' do
delete :destroy, :id => @message.id
response.should redirect_to stream_path
PostReport.where(:reviewed => false, :post_id => @message.id).should be_true
end
end
context 'destroy post as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
delete :destroy, :id => @message.id
response.status.should == 302
PostReport.where(:reviewed => true, :post_id => @message.id).should be_true
end
end
end
end

View file

@ -0,0 +1,130 @@
# Copyright (c) 2010-2011, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
describe ReportController do
before do
sign_in alice
@message = alice.post(:status_message, :text => "hey", :to => alice.aspects.first.id)
@comment = alice.comment!(@message, "flying pigs, everywhere")
end
describe '#index' do
context 'admin not signed in' do
it 'is behind redirect_unless_admin' do
get :index
response.should redirect_to stream_path
end
end
context 'admin signed in' do
before do
Role.add_admin(alice.person)
end
it 'succeeds and renders index' do
get :index
response.should render_template('index')
end
end
end
describe '#create' do
let(:comment_hash) {
{:text =>"facebook, is that you?",
:item_id =>"#{@post.id}"}
}
context 'report offensive post' do
it 'succeeds' do
put :create, :report => { :item_id => @message.id, :item_type => 'post', :text => 'offensive content' }
response.status.should == 200
Report.exists?(:item_id => @message.id, :item_type => 'post').should be_true
end
end
context 'report offensive comment' do
it 'succeeds' do
put :create, :report => { :item_id => @comment.id, :item_type => 'comment', :text => 'offensive content' }
response.status.should == 200
Report.exists?(:item_id => @comment.id, :item_type => 'comment').should be_true
end
end
end
describe '#update' do
context 'mark post report as user' do
it 'is behind redirect_unless_admin' do
put :update, :id => @message.id, :type => 'post'
response.should redirect_to stream_path
Report.where(:reviewed => false, :item_id => @message.id, :item_type => 'post').should be_true
end
end
context 'mark comment report as user' do
it 'is behind redirect_unless_admin' do
put :update, :id => @comment.id, :type => 'comment'
response.should redirect_to stream_path
Report.where(:reviewed => false, :item_id => @comment.id, :item_type => 'comment').should be_true
end
end
context 'mark post report as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
put :update, :id => @message.id, :type => 'post'
response.status.should == 302
Report.where(:reviewed => true, :item_id => @message.id, :item_type => 'post').should be_true
end
end
context 'mark comment report as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
put :update, :id => @comment.id, :type => 'comment'
response.status.should == 302
Report.where(:reviewed => true, :item_id => @comment.id, :item_type => 'comment').should be_true
end
end
end
describe '#destroy' do
context 'destroy post as user' do
it 'is behind redirect_unless_admin' do
delete :destroy, :id => @message.id, :type => 'post'
response.should redirect_to stream_path
Report.where(:reviewed => false, :item_id => @message.id, :item_type => 'post').should be_true
end
end
context 'destroy comment as user' do
it 'is behind redirect_unless_admin' do
delete :destroy, :id => @comment.id, :type => 'comment'
response.should redirect_to stream_path
Report.where(:reviewed => false, :item_id => @comment.id, :item_type => 'comment').should be_true
end
end
context 'destroy post as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
delete :destroy, :id => @message.id, :type => 'post'
response.status.should == 302
Report.where(:reviewed => true, :item_id => @message.id, :item_type => 'post').should be_true
end
end
context 'destroy comment as admin' do
before do
Role.add_admin(alice.person)
end
it 'succeeds' do
delete :destroy, :id => @comment.id, :type => 'comment'
response.status.should == 302
Report.where(:reviewed => true, :item_id => @comment.id, :item_type => 'comment').should be_true
end
end
end
end

View file

@ -0,0 +1,26 @@
# Copyright (c) 2010-2011, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
describe Report do
describe '#make_notification' do
before do
@user = bob
Role.add_admin(@user)
end
it "should deliver successfully" do
expect {
ReportMailer.new_report('post', 666)
}.to_not raise_error
end
it "should be added to the delivery queue" do
expect {
ReportMailer.new_report('post', 666)
}.to change(ActionMailer::Base.deliveries, :size).by(1)
end
end
end

View file

@ -0,0 +1,79 @@
# Copyright (c) 2010-2011, Diaspora Inc. This file is
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
require 'spec_helper'
describe Report do
before do
#:report => { :item_id => @message.id, :item_type => 'post', :text => 'offensive content' }
@user = bob
@bob_post = @user.post(:status_message, :text => "hello", :to => @user.aspects.first.id)
@bob_comment = @user.comment!(@bob_post, "welcome")
@valid_post_report = {
:item_id => @bob_post.id,
:item_type => 'post',
:text => 'offensive content'
}
@valid_comment_report = {
:item_id => @bob_comment.id,
:item_type => 'comment',
:text => 'offensive content'
}
end
describe '#validation' do
it 'validates that post ID is required' do
@user.reports.build(:item_type => 'post', :text => 'blub').should_not be_valid
end
it 'validates that post type is required' do
@user.reports.build(:item_id => 666, :text => 'blub').should_not be_valid
end
it 'validates that entry does not exist' do
@user.reports.build(@valid_post_report).should be_valid
end
it 'validates that entry does exist' do
@user.reports.create(@valid_post_report)
@user.reports.build(@valid_post_report).should_not be_valid
end
end
describe '#destroy_reported_item' do
before(:each) do
@post_report = @user.reports.create(@valid_post_report)
@comment_report = @user.reports.create(@valid_comment_report)
end
describe '.post' do
it 'should destroy it' do
expect {
@post_report.destroy_reported_item
}.to change { Post.count }.by(-1)
end
it 'should be marked' do
expect {
@post_report.destroy_reported_item
}.to change { Report.where(@valid_post_report).first.reviewed }.to(true).from(false)
end
end
describe '.comment' do
it 'should destroy it' do
expect {
@comment_report.destroy_reported_item
}.to change { Comment.count }.by(-1)
end
it 'should be marked' do
expect {
@comment_report.destroy_reported_item
}.to change { Report.where(@valid_comment_report).first.reviewed }.to(true).from(false)
end
end
end
end