diff --git a/Changelog.md b/Changelog.md index 9a674476a..0ebe5d65b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,10 +3,10 @@ ## Refactor ## Bug fixes - -Improve time agos by updating the plugin [#4280](https://github.com/diaspora/diaspora/issues/4280) +* Improve time agos by updating the plugin [#4280](https://github.com/diaspora/diaspora/issues/4280) ## Features +* You can report a single post by clicking the correct icon in the controler section [#4517](https://github.com/diaspora/diaspora/pull/4517) # 0.3.0.0 diff --git a/app/assets/images/icons/postreport.png b/app/assets/images/icons/postreport.png new file mode 100644 index 000000000..9c5967969 Binary files /dev/null and b/app/assets/images/icons/postreport.png differ diff --git a/app/assets/javascripts/app/models/post_report.js b/app/assets/javascripts/app/models/post_report.js new file mode 100644 index 000000000..7a9705bb5 --- /dev/null +++ b/app/assets/javascripts/app/models/post_report.js @@ -0,0 +1,3 @@ +app.models.PostReport = Backbone.Model.extend({ + urlRoot: '/post_report' +}); diff --git a/app/assets/javascripts/app/views/stream_post_views.js b/app/assets/javascripts/app/views/stream_post_views.js index 02f281b40..d8a9551e0 100644 --- a/app/assets/javascripts/app/views/stream_post_views.js +++ b/app/assets/javascripts/app/views/stream_post_views.js @@ -19,6 +19,7 @@ app.views.StreamPost = app.views.Post.extend({ "click .remove_post": "destroyModel", "click .hide_post": "hidePost", + "click .post_report": "postReport", "click .block_user": "blockUser" }, @@ -105,6 +106,21 @@ 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 fff92719b..2bc3b5965 100644 --- a/app/assets/stylesheets/application.css.sass +++ b/app/assets/stylesheets/application.css.sass @@ -16,6 +16,7 @@ @import 'facebox' @import 'aspects' @import 'popover' +@import 'post_report' /* ====== media ====== */ .media @@ -271,6 +272,13 @@ ul.as-selections :z-index 6 :float right + .post_report + :display inline-block + + .icons-postreport + :height 14px + :width 14px + .block_user :display inline-block diff --git a/app/assets/stylesheets/post_report.css.scss b/app/assets/stylesheets/post_report.css.scss new file mode 100644 index 000000000..ba865f9da --- /dev/null +++ b/app/assets/stylesheets/post_report.css.scss @@ -0,0 +1,16 @@ +#post_report { + padding-top: 2em; + span { + display: block; + } + .content { + float: left; + } + .options { + float: right; + } + .clear { + clear: both; + padding-bottom: 1em; + } +} diff --git a/app/assets/templates/stream-element_tpl.jst.hbs b/app/assets/templates/stream-element_tpl.jst.hbs index dae331937..21ed75fd4 100644 --- a/app/assets/templates/stream-element_tpl.jst.hbs +++ b/app/assets/templates/stream-element_tpl.jst.hbs @@ -10,6 +10,9 @@ {{#if loggedIn}}
{{#unless authorIsCurrentUser}} + +
+
diff --git a/app/controllers/post_report_controller.rb b/app/controllers/post_report_controller.rb new file mode 100644 index 000000000..cd8dba827 --- /dev/null +++ b/app/controllers/post_report_controller.rb @@ -0,0 +1,61 @@ +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/mailers/post_report_mailer.rb b/app/mailers/post_report_mailer.rb new file mode 100644 index 000000000..46b6737cc --- /dev/null +++ b/app/mailers/post_report_mailer.rb @@ -0,0 +1,18 @@ +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/models/post_report.rb b/app/models/post_report.rb new file mode 100644 index 000000000..a521162ad --- /dev/null +++ b/app/models/post_report.rb @@ -0,0 +1,15 @@ +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 diff --git a/app/models/role.rb b/app/models/role.rb index 965044cf4..40438a035 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -2,6 +2,8 @@ class Role < ActiveRecord::Base belongs_to :person + scope :admins, -> { where(name: 'admin') } + def self.is_admin?(person) find_by_person_id_and_name(person.id, 'admin') end diff --git a/app/views/admins/_admin_bar.haml b/app/views/admins/_admin_bar.haml index 58fc53070..dff8955fa 100644 --- a/app/views/admins/_admin_bar.haml +++ b/app/views/admins/_admin_bar.haml @@ -5,6 +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('.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 new file mode 100644 index 000000000..538ff39b0 --- /dev/null +++ b/app/views/post_report/index.html.haml @@ -0,0 +1,21 @@ +.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 new file mode 100644 index 000000000..fb4b688f1 --- /dev/null +++ b/app/views/post_report/post_report_email.markerb @@ -0,0 +1,2 @@ +<%= t('notifier.post_report_email.body') %> + diff --git a/app/workers/mail/post_report_worker.rb b/app/workers/mail/post_report_worker.rb new file mode 100644 index 000000000..a3f841942 --- /dev/null +++ b/app/workers/mail/post_report_worker.rb @@ -0,0 +1,12 @@ +module Workers + module Mail + class PostReportWorker < Base + sidekiq_options queue: :mail + + def perform + PostReportMailer.new_report + end + end + end +end + diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 8c2c64f5b..0df1e38a9 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -91,6 +91,7 @@ en: user_search: "User Search" weekly_user_stats: "Weekly User Stats" pod_stats: "Pod Stats" + post_report: "Reported Posts" correlations: "Correlations" sidekiq_monitor: "Sidekiq monitor" correlations: @@ -721,6 +722,9 @@ 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!" accept_invite: "Accept Your diaspora* invite!" invited_you: "%{name} invited you to diaspora*" invite: @@ -849,6 +853,19 @@ en: other: "%{count} photos by %{author}" reshare_by: "Reshare by %{author}" + post_report: + title: "Marked Reports Overview" + post_label: "Post: %{title}" + reported_label: "Reported by %{person}" + reason_label: "Reason: %{text}" + review_link: "Mark as reviewed" + delete_link: "Delete post" + status: + marked: "The report was marked as reviewed" + destroyed: "The post was destroyed" + created: "A report was created" + failed: "Something went wrong" + share_visibilites: update: post_hidden_and_muted: "%{name}'s post has been hidden, and notifications have been muted." diff --git a/config/locales/javascript/javascript.en.yml b/config/locales/javascript/javascript.en.yml index 3504d75e2..336fd609c 100644 --- a/config/locales/javascript/javascript.en.yml +++ b/config/locales/javascript/javascript.en.yml @@ -6,8 +6,11 @@ en: javascripts: confirm_dialog: "Are you sure?" + post_report_prompt: "Please enter a reason:" + post_report_prompt_default: "offensive content" delete: "Delete" ignore: "Ignore" + post_report: "Report" ignore_user: "Ignore this user?" and: "and" comma: "," diff --git a/config/routes.rb b/config/routes.rb index 471a708de..70e376620 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,6 +5,8 @@ require 'sidekiq/web' Diaspora::Application.routes.draw do + resources :post_report, :except => [:edit] + if Rails.env.production? mount RailsAdmin::Engine => '/admin_panel', :as => 'rails_admin' end diff --git a/db/migrate/20131017093025_create_post_reports.rb b/db/migrate/20131017093025_create_post_reports.rb new file mode 100644 index 000000000..bb68ff896 --- /dev/null +++ b/db/migrate/20131017093025_create_post_reports.rb @@ -0,0 +1,13 @@ +class CreatePostReports < ActiveRecord::Migration + def change + create_table :post_reports do |t| + t.integer :post_id, :null => false + t.string :user_id + t.boolean :reviewed, :default => false + t.text :text + + t.timestamps + end + add_index :post_reports, :post_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 5326609a5..b4c80620d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -283,6 +283,17 @@ ActiveRecord::Schema.define(:version => 20131213171804) do t.datetime "updated_at", :null => false end + 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| t.integer "author_id", :null => false t.boolean "public", :default => false, :null => false @@ -316,8 +327,8 @@ ActiveRecord::Schema.define(:version => 20131213171804) do t.boolean "favorite", :default => false t.string "facebook_id" t.string "tweet_id" - t.text "tumblr_ids" t.integer "open_graph_cache_id" + t.text "tumblr_ids" end add_index "posts", ["author_id", "root_guid"], :name => "index_posts_on_author_id_and_root_guid", :unique => true diff --git a/spec/controllers/post_report_controller_spec.rb b/spec/controllers/post_report_controller_spec.rb new file mode 100644 index 000000000..513e19164 --- /dev/null +++ b/spec/controllers/post_report_controller_spec.rb @@ -0,0 +1,79 @@ +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