diff --git a/app/assets/javascripts/discourse/models/post.js b/app/assets/javascripts/discourse/models/post.js index 1baf757b5..420a11acd 100644 --- a/app/assets/javascripts/discourse/models/post.js +++ b/app/assets/javascripts/discourse/models/post.js @@ -235,7 +235,7 @@ Discourse.Post = Discourse.Model.extend({ }); } else { this.setProperties({ - cooked: Discourse.Markdown.cook(I18n.t("post.deleted_by_author")), + cooked: Discourse.Markdown.cook(I18n.t("post.deleted_by_author", {count: Discourse.SiteSettings.delete_removed_posts_after})), can_delete: false, version: this.get('version') + 1, can_recover: true, diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 897551f93..6f027adb7 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -87,6 +87,7 @@ class SiteSetting < ActiveRecord::Base setting(:apple_touch_icon_url, '/assets/default-apple-touch-icon.png') setting(:ninja_edit_window, 5.minutes.to_i) + client_setting(:delete_removed_posts_after, 24) # hours setting(:post_undo_action_window_mins, 10) setting(:system_username, '') setting(:max_mentions_per_post, 10) diff --git a/config/clock.rb b/config/clock.rb index 81fb757d6..db7907fa8 100644 --- a/config/clock.rb +++ b/config/clock.rb @@ -34,6 +34,6 @@ module Clockwork every(1.day, 'version_check') every(1.minute, 'clockwork_heartbeat') every(1.minute, 'poll_mailbox') - every(2.hours, 'destroy_old_deletion_stubs') + every(30.minutes, 'destroy_old_deletion_stubs') end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e304d3380..627c67560 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -750,7 +750,9 @@ en: reply_as_new_topic: "Reply as new Topic" continue_discussion: "Continuing the discussion from {{postLink}}:" follow_quote: "go to the quoted post" - deleted_by_author: "(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)" + deleted_by_author: + one: "(post withdrawn by author, will be automatically deleted in %{count} hour unless flagged)" + other: "(post withdrawn by author, will be automatically deleted in %{count} hours unless flagged)" deleted_by: "deleted by" expand_collapse: "expand/collapse" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index a921e4ffa..4bcd24329 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -468,6 +468,7 @@ en: queue_jobs: "DEVELOPER ONLY! WARNING! By default, queue jobs in sidekiq. If disabled, your site will be broken." crawl_images: "Enable retrieving images from third party sources to insert width and height dimensions" ninja_edit_window: "Number of seconds after posting where edits do not create a new version" + delete_removed_posts_after: "Number of hours after which a post that was removed by its author will be deleted." max_image_width: "Maximum allowed width of images in a post" category_featured_topics: "Number of topics displayed per category in the /categories page" add_rel_nofollow_to_user_content: "Add rel nofollow to all submitted user content, except for internal links (including parent domains) changing this requires you update all your baked markdown with: \"rake posts:rebake\"" diff --git a/lib/post_destroyer.rb b/lib/post_destroyer.rb index 0fdbecaec..799872b98 100644 --- a/lib/post_destroyer.rb +++ b/lib/post_destroyer.rb @@ -12,7 +12,7 @@ class PostDestroyer WHERE t.deleted_at IS NOT NULL AND t.id = posts.topic_id )") - .where("updated_at < ? AND post_number > 1", 1.day.ago) + .where("updated_at < ? AND post_number > 1", SiteSetting.delete_removed_posts_after.hours.ago) .where("NOT EXISTS ( SELECT 1 FROM post_actions pa @@ -104,7 +104,7 @@ class PostDestroyer # When a user 'deletes' their own post. We just change the text. def user_destroyed Post.transaction do - @post.revise(@user, I18n.t('js.post.deleted_by_author'), force_new_version: true) + @post.revise(@user, I18n.t('js.post.deleted_by_author', count: SiteSetting.delete_removed_posts_after), force_new_version: true) @post.update_column(:user_deleted, true) @post.update_flagged_posts_count @post.topic_links.each(&:destroy) diff --git a/spec/components/post_destroyer_spec.rb b/spec/components/post_destroyer_spec.rb index 1a90601a1..0570f41d6 100644 --- a/spec/components/post_destroyer_spec.rb +++ b/spec/components/post_destroyer_spec.rb @@ -13,6 +13,7 @@ describe PostDestroyer do describe 'destroy_old_stubs' do it 'destroys stubs for deleted by user posts' do + SiteSetting.stubs(:delete_removed_posts_after).returns(24) Fabricate(:admin) topic = post.topic reply1 = create_post(topic: topic) @@ -53,6 +54,42 @@ describe PostDestroyer do reply1.deleted_at.should == nil end + + it 'uses the delete_removed_posts_after site setting' do + Fabricate(:admin) + topic = post.topic + reply1 = create_post(topic: topic) + reply2 = create_post(topic: topic) + + PostDestroyer.new(reply1.user, reply1).destroy + PostDestroyer.new(reply2.user, reply2).destroy + + SiteSetting.stubs(:delete_removed_posts_after).returns(1) + + reply2.update_column(:updated_at, 70.minutes.ago) + + PostDestroyer.destroy_stubs + + reply1.reload + reply2.reload + + reply1.deleted_at.should == nil + reply2.deleted_at.should_not == nil + + SiteSetting.stubs(:delete_removed_posts_after).returns(72) + + reply1.update_column(:updated_at, 2.days.ago) + + PostDestroyer.destroy_stubs + + reply1.reload.deleted_at.should == nil + + SiteSetting.stubs(:delete_removed_posts_after).returns(47) + + PostDestroyer.destroy_stubs + + reply1.reload.deleted_at.should_not == nil + end end describe 'basic destroying' do @@ -65,10 +102,11 @@ describe PostDestroyer do end it "doesn't delete the post" do + SiteSetting.stubs(:delete_removed_posts_after).returns(24) post.deleted_at.should be_blank post.deleted_by.should be_blank post.user_deleted.should be_true - post.raw.should == I18n.t('js.post.deleted_by_author') + post.raw.should == I18n.t('js.post.deleted_by_author', {count: 24}) post.version.should == 2 # lets try to recover diff --git a/test/javascripts/fixtures/site_settings_fixtures.js b/test/javascripts/fixtures/site_settings_fixtures.js index f8e1e1e4b..8cfed6108 100644 --- a/test/javascripts/fixtures/site_settings_fixtures.js +++ b/test/javascripts/fixtures/site_settings_fixtures.js @@ -1,3 +1,3 @@ /*jshint maxlen:10000000 */ -Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","traditional_markdown_linebreaks":false,"top_menu":"latest|new|unread|read|favorited|categories","post_menu":"like|edit|flag|delete|share|bookmark|reply","share_links":"twitter|facebook|google+|email","track_external_right_clicks":false,"must_approve_users":false,"ga_tracking_code":"UA-33736483-2","ga_domain_name":"","enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"min_post_length":20,"max_post_length":16000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_search_term_length":3,"flush_timings_secs":5,"suppress_reply_directly_below":true,"email_domains_blacklist":"mailinator.com","email_domains_whitelist":null,"version_checks":true,"min_title_similar_length":10,"min_body_similar_length":15,"category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","max_upload_size_kb":1024,"category_featured_topics":6,"favicon_url":"/assets/favicon.ico","dynamic_favicon":false,"uncategorized_name":"uncategorized","uncategorized_color":"AB9364","uncategorized_text_color":"FFFFFF","invite_only":false,"login_required":false,"enable_local_logins":true,"enable_local_account_create":true,"enable_google_logins":true,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_cas_logins":false,"enable_github_logins":true,"enable_persona_logins":true,"educate_until_posts":2,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"min_private_message_post_length":5,"faq_url":"","tos_url":"","privacy_policy_url":"","authorized_extensions":".jpg|.jpeg|.png|.gif|.txt","relative_date_duration":14}; +Discourse.SiteSettingsOriginal = {"title":"Discourse Meta","logo_url":"/assets/logo.png","logo_small_url":"/assets/logo-single.png","traditional_markdown_linebreaks":false,"top_menu":"latest|new|unread|read|favorited|categories","post_menu":"like|edit|flag|delete|share|bookmark|reply","share_links":"twitter|facebook|google+|email","track_external_right_clicks":false,"must_approve_users":false,"ga_tracking_code":"UA-33736483-2","ga_domain_name":"","enable_long_polling":true,"polling_interval":3000,"anon_polling_interval":30000,"min_post_length":20,"max_post_length":16000,"min_topic_title_length":15,"max_topic_title_length":255,"min_private_message_title_length":2,"allow_uncategorized_topics":true,"min_search_term_length":3,"flush_timings_secs":5,"suppress_reply_directly_below":true,"email_domains_blacklist":"mailinator.com","email_domains_whitelist":null,"version_checks":true,"min_title_similar_length":10,"min_body_similar_length":15,"category_colors":"BF1E2E|F1592A|F7941D|9EB83B|3AB54A|12A89D|25AAE2|0E76BD|652D90|92278F|ED207B|8C6238|231F20|808281|B3B5B4|283890","max_upload_size_kb":1024,"category_featured_topics":6,"favicon_url":"/assets/favicon.ico","dynamic_favicon":false,"uncategorized_name":"uncategorized","uncategorized_color":"AB9364","uncategorized_text_color":"FFFFFF","invite_only":false,"login_required":false,"enable_local_logins":true,"enable_local_account_create":true,"enable_google_logins":true,"enable_yahoo_logins":true,"enable_twitter_logins":true,"enable_facebook_logins":true,"enable_cas_logins":false,"enable_github_logins":true,"enable_persona_logins":true,"educate_until_posts":2,"topic_views_heat_low":1000,"topic_views_heat_medium":2000,"topic_views_heat_high":5000,"min_private_message_post_length":5,"faq_url":"","tos_url":"","privacy_policy_url":"","authorized_extensions":".jpg|.jpeg|.png|.gif|.txt","relative_date_duration":14,"delete_removed_posts_after":24}; Discourse.SiteSettings = jQuery.extend(true, {}, Discourse.SiteSettingsOriginal);