From 1748d3b9402c6b04b4a1e1bc9b71c7f7d3a3c62c Mon Sep 17 00:00:00 2001
From: Lukas Matt <lukas@zauberstuhl.de>
Date: Tue, 21 Jan 2014 09:09:45 -0500
Subject: [PATCH] It is now possible to report comments

* Renamed PostReport to Report
* Added report button to SPV
* Updated rspec

refs diaspora/diaspora#4732
refs diaspora/diaspora#4710
refs diaspora/diaspora#4711
refs diaspora/diaspora#4517
---
 .../icons/{postreport.png => report.png}      | Bin
 .../javascripts/app/models/post_report.js     |   3 -
 app/assets/javascripts/app/models/report.js   |   8 ++
 .../javascripts/app/views/comment_view.js     |  17 ++-
 .../javascripts/app/views/feedback_view.js    |  17 ++-
 app/assets/javascripts/app/views/post_view.js |  15 +++
 .../app/views/stream_post_views.js            |  17 +--
 app/assets/stylesheets/application.css.sass   |   6 +-
 app/assets/stylesheets/entypo.css.scss        |   1 +
 .../{post_report.css.scss => report.css.scss} |   4 +-
 .../stylesheets/single-post-view.css.scss     |   6 +
 app/assets/templates/comment_tpl.jst.hbs      |  14 +-
 .../single-post-actions_tpl.jst.hbs           |   4 +
 .../templates/stream-element_tpl.jst.hbs      |   4 +-
 app/controllers/post_report_controller.rb     |  61 ---------
 app/controllers/report_controller.rb          |  67 ++++++++++
 app/helpers/report_helper.rb                  |  13 ++
 app/mailers/post_report_mailer.rb             |  18 ---
 app/mailers/report_mailer.rb                  |  26 ++++
 app/models/{post_report.rb => report.rb}      |   7 +-
 app/views/admins/_admin_bar.haml              |   2 +-
 app/views/post_report/index.html.haml         |  21 ---
 .../post_report/post_report_email.markerb     |   2 -
 app/views/report/index.html.haml              |  21 +++
 app/views/report/report_email.markerb         |   2 +
 app/workers/mail/post_report_worker.rb        |  12 --
 app/workers/mail/report_worker.rb             |  12 ++
 config/locales/diaspora/en.yml                |  27 +++-
 config/locales/javascript/javascript.en.yml   |   6 +-
 config/routes.rb                              |   2 +-
 ...0121132816_add_post_type_to_post_report.rb |   5 +
 ...0214104217_rename_post_report_to_report.rb |   8 ++
 db/schema.rb                                  |  12 ++
 .../post_report_controller_spec.rb            |  79 -----------
 spec/controllers/report_controller_spec.rb    | 126 ++++++++++++++++++
 35 files changed, 406 insertions(+), 239 deletions(-)
 rename app/assets/images/icons/{postreport.png => report.png} (100%)
 delete mode 100644 app/assets/javascripts/app/models/post_report.js
 create mode 100644 app/assets/javascripts/app/models/report.js
 rename app/assets/stylesheets/{post_report.css.scss => report.css.scss} (71%)
 delete mode 100644 app/controllers/post_report_controller.rb
 create mode 100644 app/controllers/report_controller.rb
 create mode 100644 app/helpers/report_helper.rb
 delete mode 100644 app/mailers/post_report_mailer.rb
 create mode 100644 app/mailers/report_mailer.rb
 rename app/models/{post_report.rb => report.rb} (53%)
 delete mode 100644 app/views/post_report/index.html.haml
 delete mode 100644 app/views/post_report/post_report_email.markerb
 create mode 100644 app/views/report/index.html.haml
 create mode 100644 app/views/report/report_email.markerb
 delete mode 100644 app/workers/mail/post_report_worker.rb
 create mode 100644 app/workers/mail/report_worker.rb
 create mode 100644 db/migrate/20140121132816_add_post_type_to_post_report.rb
 create mode 100644 db/migrate/20140214104217_rename_post_report_to_report.rb
 delete mode 100644 spec/controllers/post_report_controller_spec.rb
 create mode 100644 spec/controllers/report_controller_spec.rb

diff --git a/app/assets/images/icons/postreport.png b/app/assets/images/icons/report.png
similarity index 100%
rename from app/assets/images/icons/postreport.png
rename to app/assets/images/icons/report.png
diff --git a/app/assets/javascripts/app/models/post_report.js b/app/assets/javascripts/app/models/post_report.js
deleted file mode 100644
index 7a9705bb56..0000000000
--- a/app/assets/javascripts/app/models/post_report.js
+++ /dev/null
@@ -1,3 +0,0 @@
-app.models.PostReport = Backbone.Model.extend({
-  urlRoot: '/post_report'
-});
diff --git a/app/assets/javascripts/app/models/report.js b/app/assets/javascripts/app/models/report.js
new file mode 100644
index 0000000000..594c7565b3
--- /dev/null
+++ b/app/assets/javascripts/app/models/report.js
@@ -0,0 +1,8 @@
+app.models.Report = Backbone.Model.extend({
+  urlRoot: '/report',
+
+  getReason: function() {
+    return prompt(Diaspora.I18n.t('report_prompt'), Diaspora.I18n.t('report_prompt_default'));
+  }
+
+});
diff --git a/app/assets/javascripts/app/views/comment_view.js b/app/assets/javascripts/app/views/comment_view.js
index 8ed7ff48a5..a02676ffff 100644
--- a/app/assets/javascripts/app/views/comment_view.js
+++ b/app/assets/javascripts/app/views/comment_view.js
@@ -5,7 +5,8 @@ app.views.Comment = app.views.Content.extend({
 
   events : function() {
     return _.extend({}, app.views.Content.prototype.events, {
-      "click .comment_delete": "destroyModel"
+      "click .comment_delete": "destroyModel",
+      "click .comment_report": "report"
     });
   },
 
@@ -31,6 +32,20 @@ app.views.Comment = app.views.Content.extend({
 
   canRemove : function() {
     return app.currentUser.authenticated() && (this.ownComment() || this.postOwner())
+  },
+
+  report: function(evt) {
+    if(evt) { evt.preventDefault(); }
+    var report = new app.models.Report();
+    var msg = report.getReason();
+    if (msg !== null) {
+      var id = this.model.id;
+      var type = $(evt.currentTarget).data("type");
+      report.fetch({
+        data: { id: id, type: type, text: msg },
+        type: 'POST'
+      });
+    }
   }
 });
 
diff --git a/app/assets/javascripts/app/views/feedback_view.js b/app/assets/javascripts/app/views/feedback_view.js
index d7f84a6538..62e898f049 100644
--- a/app/assets/javascripts/app/views/feedback_view.js
+++ b/app/assets/javascripts/app/views/feedback_view.js
@@ -6,7 +6,8 @@ app.views.Feedback = app.views.Base.extend({
   events: {
     "click *[rel='auth-required']" : "requireAuth",
     "click .like" : "toggleLike",
-    "click .reshare" : "resharePost"
+    "click .reshare" : "resharePost",
+    "click .post_report" : "report"
   },
 
   tooltipSelector : ".label",
@@ -44,5 +45,19 @@ app.views.Feedback = app.views.Base.extend({
     if( app.currentUser.authenticated() ) { return }
     alert("you must be logged in to do that!")
     return false;
+  },
+
+  report: function(evt) {
+    if(evt) { evt.preventDefault(); }
+    var report = new app.models.Report();
+    var msg = report.getReason();
+    if (msg !== null) {
+      var id = this.model.id;
+      var type = $(evt.currentTarget).data("type");
+      report.fetch({
+        data: { id: id, type: type, text: msg },
+        type: 'POST'
+      });
+    }
   }
 });
diff --git a/app/assets/javascripts/app/views/post_view.js b/app/assets/javascripts/app/views/post_view.js
index 7d45e6bdfa..9aef827287 100644
--- a/app/assets/javascripts/app/views/post_view.js
+++ b/app/assets/javascripts/app/views/post_view.js
@@ -13,5 +13,20 @@ app.views.Post = app.views.Base.extend({
 
   showPost : function() {
     return (app.currentUser.get("showNsfw")) || !this.model.get("nsfw")
+  },
+
+  report: function(evt) {
+    if(evt) { evt.preventDefault(); }
+    var report = new app.models.Report();
+    var msg = report.getReason();
+    if (msg !== null) {
+      var id = this.model.id;
+      var type = $(evt.currentTarget).data("type");
+      report.fetch({
+        data: { id: id, type: type, text: msg },
+        type: 'POST'
+      });
+    }
   }
+
 });
diff --git a/app/assets/javascripts/app/views/stream_post_views.js b/app/assets/javascripts/app/views/stream_post_views.js
index fb1d846d46..ef557362d0 100644
--- a/app/assets/javascripts/app/views/stream_post_views.js
+++ b/app/assets/javascripts/app/views/stream_post_views.js
@@ -20,7 +20,7 @@ app.views.StreamPost = app.views.Post.extend({
 
     "click .remove_post": "destroyModel",
     "click .hide_post": "hidePost",
-    "click .post_report": "postReport",
+    "click .post_report": "report",
     "click .block_user": "blockUser"
   },
 
@@ -108,21 +108,6 @@ app.views.StreamPost = app.views.Post.extend({
     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){
     evt.preventDefault();
     this.$(".new_comment_form_wrapper").removeClass("hidden");
diff --git a/app/assets/stylesheets/application.css.sass b/app/assets/stylesheets/application.css.sass
index 96184c3158..52199d0470 100644
--- a/app/assets/stylesheets/application.css.sass
+++ b/app/assets/stylesheets/application.css.sass
@@ -17,8 +17,8 @@
 @import 'facebox'
 @import 'aspects'
 @import 'popover'
-@import 'post_report'
 @import 'stream_element'
+@import 'report'
 
 /* ====== media ====== */
 .media
@@ -211,10 +211,10 @@ ul.as-selections
   :z-index 6
   :float right
 
-  .post_report
+  .post_report, .comment_report
     :display inline-block
 
-    .icons-postreport
+    .icons-report
       :height 14px
       :width 14px
 
diff --git a/app/assets/stylesheets/entypo.css.scss b/app/assets/stylesheets/entypo.css.scss
index a2d2a37745..615ed4e28c 100644
--- a/app/assets/stylesheets/entypo.css.scss
+++ b/app/assets/stylesheets/entypo.css.scss
@@ -141,6 +141,7 @@
   &.globe:before { content: '\1f30e'; } /* 1f30e */
   &.graduation-cap:before { content: '\1f393 '; } /* 1f393 */
   &.heart-empty:before { content: '\2661'; } /* 2661 */
+  &.report:before { content: '\21'; } /* 21 */
   &.heart:before { content: '\2665'; } /* 2665 */
   &.help:before { content: '\2753'; } /* 2753 */
   &.home:before { content: '\2302'; } /* 2302 */
diff --git a/app/assets/stylesheets/post_report.css.scss b/app/assets/stylesheets/report.css.scss
similarity index 71%
rename from app/assets/stylesheets/post_report.css.scss
rename to app/assets/stylesheets/report.css.scss
index ba865f9da0..6fa29ce075 100644
--- a/app/assets/stylesheets/post_report.css.scss
+++ b/app/assets/stylesheets/report.css.scss
@@ -1,4 +1,4 @@
-#post_report {
+#report {
   padding-top: 2em;
   span {
     display: block;
@@ -11,6 +11,8 @@
   }
   .clear {
     clear: both;
+    border-bottom: 1px solid #808080;
     padding-bottom: 1em;
+    margin-bottom: 1em;
   }
 }
diff --git a/app/assets/stylesheets/single-post-view.css.scss b/app/assets/stylesheets/single-post-view.css.scss
index 2fd30c8882..0568634071 100644
--- a/app/assets/stylesheets/single-post-view.css.scss
+++ b/app/assets/stylesheets/single-post-view.css.scss
@@ -61,6 +61,12 @@
       i.comment:hover {
         color: #424242;
       }
+      i.report.gray:hover {
+        color: $red;
+      }
+      i.report.red:hover {
+        color: #f55f5a;
+      }
       i.heart.gray:hover {
         color: $red;
       }
diff --git a/app/assets/templates/comment_tpl.jst.hbs b/app/assets/templates/comment_tpl.jst.hbs
index e87eb2eeb3..71be0bd4e4 100644
--- a/app/assets/templates/comment_tpl.jst.hbs
+++ b/app/assets/templates/comment_tpl.jst.hbs
@@ -6,13 +6,17 @@
   </div>
 
   <div class="bd">
+    <div class="controls">
     {{#if canRemove}}
-      <div class="controls">
-        <a href="#" class="delete comment_delete" title="{{t "delete"}}">
-          <div alt="Deletelabel" class="icons-deletelabel" />
-        <a/>
-      </div>
+      <a href="#" class="delete comment_delete" title="{{t "delete"}}">
+        <div alt="Deletelabel" class="icons-deletelabel" />
+      <a/>
+    {{else}}
+      <a href="#" data-type="comment" class="comment_report" title="{{t "report"}}">
+        <div class="icons-report control_icon"/>
+      </a>
     {{/if}}
+    </div>
 
     {{#with author}}
       <a href="/people/{{guid}}" class="author author-name {{hovercardable this}}">
diff --git a/app/assets/templates/single-post-viewer/single-post-actions_tpl.jst.hbs b/app/assets/templates/single-post-viewer/single-post-actions_tpl.jst.hbs
index 66a5101aab..26fcd0822e 100644
--- a/app/assets/templates/single-post-viewer/single-post-actions_tpl.jst.hbs
+++ b/app/assets/templates/single-post-viewer/single-post-actions_tpl.jst.hbs
@@ -22,5 +22,9 @@
             </a>
         {{/if}}
     {{/if}}
+
+    <a href="#" rel="auth-required" data-type="post" class="post_report" title="{{t "report"}}">
+      <i class="entypo report gray large"/>
+    </a>
   </div>
 </div>
diff --git a/app/assets/templates/stream-element_tpl.jst.hbs b/app/assets/templates/stream-element_tpl.jst.hbs
index 21ed75fd4c..45a73f9a04 100644
--- a/app/assets/templates/stream-element_tpl.jst.hbs
+++ b/app/assets/templates/stream-element_tpl.jst.hbs
@@ -10,8 +10,8 @@
     {{#if loggedIn}}
       <div class="controls">
         {{#unless authorIsCurrentUser}}
-          <a href="#" rel="nofollow" class="post_report" title="{{t "post_report"}}">
-            <div class="icons-postreport control_icon"/>
+          <a href="#" rel="nofollow" data-type="post" class="post_report" title="{{t "report"}}">
+            <div class="icons-report control_icon"/>
           </a>
           <a href="#" rel="nofollow" class="block_user" title="{{t "ignore"}}">
             <div class="icons-ignoreuser control_icon"></div>
diff --git a/app/controllers/post_report_controller.rb b/app/controllers/post_report_controller.rb
deleted file mode 100644
index cd8dba827d..0000000000
--- a/app/controllers/post_report_controller.rb
+++ /dev/null
@@ -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
diff --git a/app/controllers/report_controller.rb b/app/controllers/report_controller.rb
new file mode 100644
index 0000000000..a5dcef21d7
--- /dev/null
+++ b/app/controllers/report_controller.rb
@@ -0,0 +1,67 @@
+class ReportController < ApplicationController
+  before_filter :authenticate_user!
+  before_filter :redirect_unless_admin, :except => [:create]
+
+  def index
+    @report = Report.where(reviewed: false).all
+  end
+
+  def update
+    if Report.where(post_type: params[:type]).exists?(post_id: params[:id])
+      mark_as_reviewed
+    end
+    redirect_to :action => :index and return
+  end
+
+  def destroy
+    if (params[:type].eql? "post")
+      if Post.exists?(params[:id])
+        delete_post
+      end
+    elsif (params[:type].eql? "comment")
+      if Comment.exists?(params[:id])
+        delete_comment
+      end
+    end
+    redirect_to :action => :index and return
+  end
+
+  def create
+    code = 400
+    username = current_user.username
+    post = Report.new(
+      :post_id => params[:id],
+      :post_type => params[:type],
+      :user_id => username,
+      :text => params[:text])
+    unless Report.where("post_id = ? AND post_type = ?", params[:id], params[:type]).exists?(user_id: username)
+      result = post.save
+      code = 200 if result
+    end
+    render :nothing => true, :status => code
+  end
+
+  private
+    def delete_post
+      post = Post.find(params[:id])
+      current_user.retract(post)
+      mark_as_reviewed
+      flash[:notice] = I18n.t 'report.status.destroyed'
+    end
+
+    def delete_comment
+      comment = Comment.find(params[:id])
+      #current_user.retract(comment)
+      comment.destroy
+      mark_as_reviewed
+      flash[:notice] = I18n.t 'report.status.destroyed'
+    end
+
+    def mark_as_reviewed
+      posts = Report.where("post_id = ? AND post_type = ?", params[:id], params[:type])
+      posts.each do |post|
+        post.update_attributes(reviewed: true)
+      end
+      flash[:notice] = I18n.t 'report.status.marked'
+    end
+end
diff --git a/app/helpers/report_helper.rb b/app/helpers/report_helper.rb
new file mode 100644
index 0000000000..91b94e49f5
--- /dev/null
+++ b/app/helpers/report_helper.rb
@@ -0,0 +1,13 @@
+#   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)
+    if type.eql? "post"
+      raw t('report.post_label', title: link_to(post_page_title(Post.find_by_id(id)), post_path(id)))
+    elsif type.eql? "comment"
+      raw t('report.comment_label', data: comment_message(Comment.find_by_id(id)))
+    end
+  end
+end
diff --git a/app/mailers/post_report_mailer.rb b/app/mailers/post_report_mailer.rb
deleted file mode 100644
index 46b6737ccf..0000000000
--- a/app/mailers/post_report_mailer.rb
+++ /dev/null
@@ -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
diff --git a/app/mailers/report_mailer.rb b/app/mailers/report_mailer.rb
new file mode 100644
index 0000000000..e7dbb911c3
--- /dev/null
+++ b/app/mailers/report_mailer.rb
@@ -0,0 +1,26 @@
+class ReportMailer < ActionMailer::Base
+  default :from => AppConfig.mail.sender_address
+
+  def new_report(type, id)
+    url = AppConfig.pod_uri.to_s
+    url << report_index_path
+    resource = Hash[
+      :subject => I18n.t('notifier.report_email.subject', :type => type),
+      :url => url,
+      :type => type,
+      :id => id
+    ]
+    Role.admins.each do |role|
+      resource[:email] = User.find_by_id(role.person_id).email
+      format(resource).deliver
+    end
+  end
+
+  private
+    def format(resource)
+      mail(to: resource[:email], subject: resource[:subject]) do |format|
+        format.html { render 'report/report_email', :locals => { :resource => resource } }
+        format.text { render 'report/report_email', :locals => { :resource => resource } }
+      end
+    end
+end
diff --git a/app/models/post_report.rb b/app/models/report.rb
similarity index 53%
rename from app/models/post_report.rb
rename to app/models/report.rb
index a521162ada..b47a006dcf 100644
--- a/app/models/post_report.rb
+++ b/app/models/report.rb
@@ -1,15 +1,16 @@
-class PostReport < ActiveRecord::Base
+class Report < ActiveRecord::Base
   validates :user_id, presence: true
   validates :post_id, presence: true
+  validates :post_type, presence: true
 
   belongs_to :user
   belongs_to :post
 
-  has_many :post_reports  
+  has_many :reports  
 
   after_create :send_report_notification
 
   def send_report_notification
-    Workers::Mail::PostReportWorker.perform_async
+    Workers::Mail::ReportWorker.perform_async(self.post_type, self.post_id)
   end
 end
diff --git a/app/views/admins/_admin_bar.haml b/app/views/admins/_admin_bar.haml
index dff8955fad..cb6ac9b34a 100644
--- a/app/views/admins/_admin_bar.haml
+++ b/app/views/admins/_admin_bar.haml
@@ -5,7 +5,7 @@
       %li= link_to t('.user_search'), user_search_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('.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('.sidekiq_monitor'), sidekiq_path
 	
diff --git a/app/views/post_report/index.html.haml b/app/views/post_report/index.html.haml
deleted file mode 100644
index 538ff39b0e..0000000000
--- a/app/views/post_report/index.html.haml
+++ /dev/null
@@ -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
diff --git a/app/views/post_report/post_report_email.markerb b/app/views/post_report/post_report_email.markerb
deleted file mode 100644
index fb4b688f1d..0000000000
--- a/app/views/post_report/post_report_email.markerb
+++ /dev/null
@@ -1,2 +0,0 @@
-<%= t('notifier.post_report_email.body') %>
-
diff --git a/app/views/report/index.html.haml b/app/views/report/index.html.haml
new file mode 100644
index 0000000000..8da7c73bdf
--- /dev/null
+++ b/app/views/report/index.html.haml
@@ -0,0 +1,21 @@
+.span-24
+  = render :partial => 'admins/admin_bar'
+
+.span-24.last
+  %h1
+    = t('report.title')
+  %div#report
+    - @report.each do |r|
+      %div.content
+        %span
+          = report_content(r.post_id, r.post_type)
+        %span
+          = raw t('report.reported_label', person: link_to(r.user_id, user_profile_path(r.user_id)))
+        %span
+          = t('report.reason_label', text: r.text)
+      %div.options
+        %span
+          = link_to t('report.review_link'), report_path(r.post_id, :type => r.post_type), method: :put
+        %span
+          = link_to t('report.delete_link'), report_path(r.post_id, :type => r.post_type), method: :delete
+      %div.clear
diff --git a/app/views/report/report_email.markerb b/app/views/report/report_email.markerb
new file mode 100644
index 0000000000..ba787d56a3
--- /dev/null
+++ b/app/views/report/report_email.markerb
@@ -0,0 +1,2 @@
+<%= t('notifier.report_email.body', url: resource[:url], type: resource[:type], id: resource[:id]) %>
+
diff --git a/app/workers/mail/post_report_worker.rb b/app/workers/mail/post_report_worker.rb
deleted file mode 100644
index a3f8419426..0000000000
--- a/app/workers/mail/post_report_worker.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Workers
-  module Mail
-    class PostReportWorker < Base
-      sidekiq_options queue: :mail
-
-      def perform
-        PostReportMailer.new_report
-      end
-    end
-  end
-end
-
diff --git a/app/workers/mail/report_worker.rb b/app/workers/mail/report_worker.rb
new file mode 100644
index 0000000000..92c201dcbb
--- /dev/null
+++ b/app/workers/mail/report_worker.rb
@@ -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
+
diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml
index 60dc8c116d..e5a440f766 100644
--- a/config/locales/diaspora/en.yml
+++ b/config/locales/diaspora/en.yml
@@ -99,7 +99,7 @@ en:
       user_search: "User Search"
       weekly_user_stats: "Weekly User Stats"
       pod_stats: "Pod Stats"
-      post_report: "Reported Posts"
+      report: "Reports"
       correlations: "Correlations"
       sidekiq_monitor: "Sidekiq monitor"
     correlations:
@@ -736,9 +736,23 @@ en:
     confirm_email:
         subject: "Please activate your new email address %{unconfirmed_email}"
         click_link: "To activate your new email address %{unconfirmed_email}, please follow this link:"
-    post_report_email:
-        subject: "A new post was marked as offensive"
-        body: "Please review as soon as possible!"
+    report_email:
+        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!"
     invited_you: "%{name} invited you to diaspora*"
     invite:
@@ -868,9 +882,10 @@ en:
         other: "%{count} photos by %{author}"
       reshare_by: "Reshare by %{author}"
 
-  post_report:
-    title: "Marked Reports Overview"
+  report:
+    title: "Reports Overview"
     post_label: "<b>Post</b>: %{title}"
+    comment_label: "<b>Comment</b>:<br>%{data}"
     reported_label: "<b>Reported by</b> %{person}"
     reason_label: "Reason: %{text}"
     review_link: "Mark as reviewed"
diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml
index 5a619206ed..e10959db64 100644
--- a/config/locales/javascript/javascript.en.yml
+++ b/config/locales/javascript/javascript.en.yml
@@ -6,11 +6,11 @@
 en:
   javascripts:
     confirm_dialog: "Are you sure?"
-    post_report_prompt: "Please enter a reason:"
-    post_report_prompt_default: "offensive content"
+    report_prompt: "Please enter a reason:"
+    report_prompt_default: "offensive content"
     delete: "Delete"
     ignore: "Ignore"
-    post_report: "Report"
+    report: "Report"
     ignore_user: "Ignore this user?"
     and: "and"
     comma: ","
diff --git a/config/routes.rb b/config/routes.rb
index 39b66432e5..dc659bbbbd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -5,7 +5,7 @@
 require 'sidekiq/web'
 
 Diaspora::Application.routes.draw do
-  resources :post_report, :except => [:edit]
+  resources :report, :except => [:edit, :new]
 
   if Rails.env.production?
     mount RailsAdmin::Engine => '/admin_panel', :as => 'rails_admin'
diff --git a/db/migrate/20140121132816_add_post_type_to_post_report.rb b/db/migrate/20140121132816_add_post_type_to_post_report.rb
new file mode 100644
index 0000000000..be50eca0af
--- /dev/null
+++ b/db/migrate/20140121132816_add_post_type_to_post_report.rb
@@ -0,0 +1,5 @@
+class AddPostTypeToPostReport < ActiveRecord::Migration
+  def change
+    add_column :post_reports, :post_type, :string, :null => false, :after => :post_id
+  end
+end
diff --git a/db/migrate/20140214104217_rename_post_report_to_report.rb b/db/migrate/20140214104217_rename_post_report_to_report.rb
new file mode 100644
index 0000000000..2d477491e3
--- /dev/null
+++ b/db/migrate/20140214104217_rename_post_report_to_report.rb
@@ -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
diff --git a/db/schema.rb b/db/schema.rb
index 97f7cb6c28..b483d9b033 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -410,6 +410,18 @@ ActiveRecord::Schema.define(:version => 20140308154022) do
 
   add_index "rails_admin_histories", ["item", "table", "month", "year"], :name => "index_rails_admin_histories"
 
+  create_table "reports", :force => true do |t|
+    t.integer  "post_id",                       :null => false
+    t.string   "post_type",                     :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 "reports", ["post_id"], :name => "index_post_reports_on_post_id"
+
   create_table "roles", :force => true do |t|
     t.integer  "person_id"
     t.string   "name"
diff --git a/spec/controllers/post_report_controller_spec.rb b/spec/controllers/post_report_controller_spec.rb
deleted file mode 100644
index 513e191642..0000000000
--- a/spec/controllers/post_report_controller_spec.rb
+++ /dev/null
@@ -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
diff --git a/spec/controllers/report_controller_spec.rb b/spec/controllers/report_controller_spec.rb
new file mode 100644
index 0000000000..614dc1b451
--- /dev/null
+++ b/spec/controllers/report_controller_spec.rb
@@ -0,0 +1,126 @@
+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?",
+       :post_id =>"#{@post.id}"}
+    }
+
+    context 'report offensive post' do
+      it 'succeeds' do
+        put :create, :id => @message.id, :type => 'post', :text => 'offensive content'
+        response.status.should == 200
+        Report.exists?(:post_id => @message.id, :post_type => 'post').should be_true
+      end
+    end
+    context 'report offensive comment' do
+      it 'succeeds' do
+        put :create, :id => @comment.id, :type => 'comment', :text => 'offensive content'
+        response.status.should == 200
+        Report.exists?(:post_id => @comment.id, :post_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, :post_id => @message.id, :post_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, :post_id => @comment.id, :post_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, :post_id => @message.id, :post_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, :post_id => @comment.id, :post_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, :post_id => @message.id, :post_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, :post_id => @comment.id, :post_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, :post_id => @message.id, :post_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, :post_id => @comment.id, :post_type => 'comment').should be_true
+      end
+    end
+  end
+end
-- 
GitLab