Merge branch 'master' of github.com:discourse/discourse

This commit is contained in:
Robin Ward 2013-05-07 10:27:09 -04:00
commit 4200f4107b
29 changed files with 175 additions and 78 deletions

View File

@ -15,7 +15,6 @@ gem 'simple_handlebars_rails', path: 'vendor/gems/simple_handlebars_rails'
gem 'redcarpet', require: false
gem 'activerecord-postgres-hstore'
gem 'acts_as_paranoid'
gem 'active_attr' # until we get ActiveModel::Model with Rails 4
gem 'airbrake', '3.1.2', require: false # errbit is broken with 3.1.3 for now
gem 'clockwork', require: false
@ -135,7 +134,9 @@ gem 'lru_redux'
gem 'rack-mini-profiler', require: false # require: false #, git: 'git://github.com/SamSaffron/MiniProfiler'
# used for caching, optional
gem 'redis-rack-cache', require: false
# redis-rack-cache is missing a sane expiry policy, it hogs redis
# https://github.com/jodosha/redis-store/pull/183
gem 'redis-rack-cache', :git => 'git://github.com/SamSaffron/redis-rack-cache.git', require: false
gem 'rack-cache', require: false
gem 'rack-cors', require: false

View File

@ -6,6 +6,14 @@ GIT
rake
rake-compiler
GIT
remote: git://github.com/SamSaffron/redis-rack-cache.git
revision: 379ef30e31d4e185cb1d7f8badca0cc06403eba2
specs:
redis-rack-cache (1.2.1)
rack-cache (~> 1.2)
redis-store (~> 1.1.0)
GIT
remote: git://github.com/SamSaffron/sprockets.git
revision: bacf2ec4d4d10cd8d1ab25a6360740314c512237
@ -120,8 +128,6 @@ GEM
activesupport (3.2.12)
i18n (~> 0.6)
multi_json (~> 1.0)
acts_as_paranoid (0.4.1)
activerecord (~> 3.2)
airbrake (3.1.2)
activesupport
builder
@ -352,9 +358,6 @@ GEM
redis-rack (1.4.2)
rack (~> 1.4.1)
redis-store (~> 1.1.0)
redis-rack-cache (1.2.1)
rack-cache (~> 1.2)
redis-store (~> 1.1.0)
redis-rails (3.2.3)
redis-actionpack (~> 3.2.3)
redis-activesupport (~> 3.2.3)
@ -448,7 +451,6 @@ DEPENDENCIES
active_attr
active_model_serializers!
activerecord-postgres-hstore
acts_as_paranoid
airbrake (= 3.1.2)
barber (= 0.3.0)
better_errors
@ -507,7 +509,7 @@ DEPENDENCIES
rb-inotify (~> 0.9)
redcarpet
redis
redis-rack-cache
redis-rack-cache!
redis-rails
rest-client
rinku

View File

@ -53,12 +53,15 @@ Discourse.ClickTrack = {
if (!ownLink) {
var $badge = $('span.badge', $link);
if ($badge.length === 1) {
// don't update counts in oneboxes (except when we force it)
// don't update counts in category badge
if ($link.closest('.badge-category').length === 0) {
// nor in oneboxes (except when we force it)
if ($link.closest(".onebox-result").length === 0 || $link.hasClass("track-link")) {
$badge.html(parseInt($badge.html(), 10) + 1);
}
}
}
}
// If they right clicked, change the destination href
if (e.which === 3) {

View File

@ -432,7 +432,7 @@ Discourse.TopicController = Discourse.ObjectController.extend({
deletePost: function(post) {
// Moderators can delete posts. Regular users can only create a deleted at message.
if (Discourse.get('currentUser.moderator')) {
if (Discourse.get('currentUser.staff')) {
post.set('deleted_at', new Date());
} else {
post.set('cooked', Discourse.Markdown.cook(Em.String.i18n("post.deleted_by_author")));

View File

@ -67,7 +67,7 @@ Discourse.Composer = Discourse.Model.extend({
togglePreview: function() {
this.toggleProperty('showPreview');
Discourse.KeyValueStore.set({ key: 'showPreview', value: this.get('showPreview') });
Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
},
// Import a quote from the post

View File

@ -171,11 +171,14 @@ Discourse.PostView = Discourse.View.extend({
postView.$(".cooked a[href]").each(function() {
var link = $(this);
if (link.attr('href') === lc.url) {
// don't display badge counts in oneboxes (except when we force it)
// don't display badge counts on category badge
if (link.closest('.badge-category').length === 0) {
// nor in oneboxes (except when we force it)
if (link.closest(".onebox-result").length === 0 || link.hasClass("track-link")) {
link.append("<span class='badge badge-notification clicks' title='" + Em.String.i18n("topic_summary.clicks") + "'>" + lc.clicks + "</span>");
}
}
}
});
}
});

View File

@ -33,7 +33,7 @@ class InvitesController < ApplicationController
invite = Invite.where(invited_by_id: current_user.id, email: params[:email]).first
raise Discourse::InvalidParameters.new(:email) if invite.blank?
invite.destroy
invite.trash!
render nothing: true
end

View File

@ -128,7 +128,7 @@ class PostsController < ApplicationController
def recover
post = find_post_from_params
guardian.ensure_can_recover_post!(post)
post.recover
post.recover!
render nothing: true
end
@ -186,8 +186,8 @@ class PostsController < ApplicationController
def find_post_from_params
finder = Post.where(id: params[:id] || params[:post_id])
# Include deleted posts if the user is a moderator
finder = finder.with_deleted if current_user.try(:moderator?)
# Include deleted posts if the user is staff
finder = finder.with_deleted if current_user.try(:staff?)
post = finder.first
guardian.ensure_can_see!(post)

View File

@ -100,7 +100,7 @@ class TopicsController < ApplicationController
def destroy
topic = Topic.where(id: params[:id]).first
guardian.ensure_can_delete!(topic)
topic.destroy
topic.trash!
render nothing: true
end

View File

@ -1,4 +1,7 @@
require_dependency 'trashable'
class Invite < ActiveRecord::Base
include Trashable
belongs_to :user
belongs_to :topic
@ -9,8 +12,6 @@ class Invite < ActiveRecord::Base
validates_presence_of :email
validates_presence_of :invited_by_id
acts_as_paranoid
before_create do
self.invite_key ||= SecureRandom.hex
end

View File

@ -3,19 +3,19 @@ require_dependency 'pretty_text'
require_dependency 'rate_limiter'
require_dependency 'post_revisor'
require_dependency 'enum'
require_dependency 'trashable'
require 'archetype'
require 'digest/sha1'
class Post < ActiveRecord::Base
include RateLimiter::OnCreateRecord
include Trashable
versioned if: :raw_changed?
rate_limit
acts_as_paranoid
after_recover :update_flagged_posts_count
belongs_to :user
belongs_to :topic, counter_cache: :posts_count
@ -52,6 +52,11 @@ class Post < ActiveRecord::Base
@types ||= Enum.new(:regular, :moderator_action)
end
def recover!
super
update_flagged_posts_count
end
def raw_quality
sentinel = TextSentinel.body_sentinel(raw)
errors.add(:raw, I18n.t(:is_invalid)) unless sentinel.valid?

View File

@ -1,10 +1,12 @@
require_dependency 'rate_limiter'
require_dependency 'system_message'
require_dependency 'trashable'
class PostAction < ActiveRecord::Base
class AlreadyActed < StandardError; end
include RateLimiter::OnCreateRecord
include Trashable
attr_accessible :post_action_type_id, :post_id, :user_id, :post, :user, :post_action_type, :message, :related_post_id
@ -12,8 +14,6 @@ class PostAction < ActiveRecord::Base
belongs_to :user
belongs_to :post_action_type
acts_as_paranoid
rate_limit :post_action_rate_limiter
validate :message_quality
@ -114,8 +114,7 @@ class PostAction < ActiveRecord::Base
def self.remove_act(user, post, post_action_type_id)
if action = where(post_id: post.id, user_id: user.id, post_action_type_id: post_action_type_id).first
action.destroy
action.deleted_at = Time.zone.now
action.trash!
action.run_callbacks(:save)
end
end

View File

@ -4,10 +4,12 @@ require_dependency 'topic_view'
require_dependency 'rate_limiter'
require_dependency 'text_sentinel'
require_dependency 'text_cleaner'
require_dependency 'trashable'
class Topic < ActiveRecord::Base
include ActionView::Helpers
include RateLimiter::OnCreateRecord
include Trashable
def self.max_sort_order
2**31 - 1
@ -18,9 +20,17 @@ class Topic < ActiveRecord::Base
end
versioned if: :new_version_required?
acts_as_paranoid
after_recover :update_flagged_posts_count
after_destroy :update_flagged_posts_count
def trash!
super
update_flagged_posts_count
end
def recover!
super
update_flagged_posts_count
end
rate_limit :default_rate_limiter
rate_limit :limit_topics_per_day

View File

@ -442,11 +442,11 @@ class User < ActiveRecord::Base
posts.order("post_number desc").each do |p|
if p.post_number == 1
p.topic.destroy
p.topic.trash!
# TODO: But the post is not destroyed. Why?
else
# TODO: This should be using the PostDestroyer!
p.destroy
p.trash!
end
end
end

View File

@ -2,6 +2,10 @@ if Rails.configuration.respond_to?(:enable_rack_cache) && Rails.configuration.en
require 'rack-cache'
require 'redis-rack-cache'
# by default we will cache up to 3 minutes in redis, if you want to cut down on redis usage
# cut down this number
RedisRackCache.max_cache_seconds = 60 * 3
url = DiscourseRedis.url
class Rack::Cache::Discourse < Rack::Cache::Context

View File

@ -34,6 +34,7 @@ nl:
you: Jij
or: of
now: zonet
read_more: lees verder
suggested_topics:
title: Aanbevolen topics
@ -75,8 +76,8 @@ nl:
"2": "Likes ontvangen"
"3": "Bladwijzers"
"4": "Topics"
"5": "Berichten"
"6": "Responses"
"5": "Antwoorden"
"6": "Reacties"
"7": "Genoemd"
"9": "Citaten"
"10": "Favorieten"
@ -100,6 +101,9 @@ nl:
external_links_in_new_tab: Open alle externe links in een nieuw tabblad
enable_quoting: Activeer antwoord-met-citaat voor geselecteerde tekst
moderator: "{{user}} is een moderator"
admin: "{{user}} is een admin"
change_password:
action: wijzig
success: (e-mail verzonden)
@ -248,7 +252,7 @@ nl:
disable: "Schakel naar normale weergave"
private_message_info:
title: 'Privé-conversatie'
title: 'Privé-bericht'
invite: Nodig anderen uit...
email: E-mail
@ -375,9 +379,9 @@ nl:
edited: "<i title='gewijzigd' class='icon icon-pencil'></i> {{username}} {{link}}"
liked: "<i title='leuk gevonden' class='icon icon-heart'></i> {{username}} {{link}}"
private_message: "<i class='icon icon-envelope-alt' title='privé-bericht'></i> {{username}} {{link}}"
invited_to_private_message: "{{username}} heeft je uitgenodigd voor een privé-conversatie: {{link}}"
invited_to_private_message: "<i class='icon icon-envelope-alt' title='privé-bericht'></i> {{username}} {{link}}"
invitee_accepted: "<i title='heeft je uitnodiging geaccepteerd' class='icon icon-signin'></i> {{username}} heeft je uitnodiging geaccepteerd en heeft zich ingeschreven om deel te nemen."
moved_post: "<i title='bericht verplaatst' class='icon icon-arrow-right'></i> {{username}} heeft je post verplaast naar {{link}}"
moved_post: "<i title='bericht verplaatst' class='icon icon-arrow-right'></i> {{username}} verplaatst naar {{link}}"
total_flagged: aantal gemarkeerde berichten
image_selector:
@ -437,7 +441,7 @@ nl:
create_in: "Maak een {{categoryName}} topic"
create: Maak topic
create_long: Maak een nieuw topic
private_message: Start een privé-gesprek
private_message: Stuur een privé-bericht
list: Topics
new: nieuw topic
title: Topic
@ -535,11 +539,11 @@ nl:
inviting: Uitnodigen...
invite_private:
title: Nodig uit voor privé-conversatie
title: Stuur een privé-bericht
email_or_username: E-mail of gebruikersnaam van genodigde
email_or_username_placeholder: e-mailadres of gebruikersnaam
action: Uitnodigen
success: "Bedankt! We hebben deze persoon uitgenodigd om deel te nemen aan deze privé-conversatie."
success: "Bedankt! We hebben deze persoon dit privé-bericht gestuurd."
error: "Sorry, er is iets misgegaan bij het uitnodigen van deze persoon"
invite_reply:
@ -656,8 +660,8 @@ nl:
inappropriate: "{{icons}} markeerden dit als ongepast"
notify_moderators: "{{icons}} lichtte moderators in"
notify_moderators_with_url: "{{icons}} <a href='{{postUrl}}'>lichtte moderators in</a>"
notify_user: "{{icons}} begonnen een privé-conversatie"
notify_user_with_url: "{{icons}} begonnen een <a href='{{postUrl}}'>privé-conversatie</a>"
notify_user: "{{icons}} verstuurde een privé-bericht"
notify_user_with_url: "{{icons}} verstuurde een <a href='{{postUrl}}'>privé-bericht</a>"
bookmark: "{{icons}} voegden dit toe aan hun favorieten"
like: "{{icons}} vinden dit leuk"
vote: "{{icons}} hebben hier op gestemd"
@ -666,7 +670,7 @@ nl:
spam: Jij markeerde dit als spam
inappropriate: Jij markeerde dit als ongepast
notify_moderators: Jij markeerde dit voor moderatie
notify_user: Jij begon een privé-conversatie met deze persoon
notify_user: Jij stuurde een privé-bericht naar deze persoon
bookmark: Jij voegde dit bericht toe aan je favorieten
like: Jij vindt dit leuk
vote: Jij hebt op dit bericht gestemd
@ -684,8 +688,8 @@ nl:
one: "Jij en iemand anders markeerden dit voor moderatie"
other: "Jij en {{count}} anderen markeerden dit voor moderatie"
notify_user:
one: "Jij en iemand anders begonnen een privé-conversatie met deze persoon"
other: "Jij en {{count}} anderen begonnen een privé-conversatie met deze persoon"
one: "Jij en iemand anders stuurden een privé-bericht naar deze persoon"
other: "Jij en {{count}} anderen stuurden een privé-bericht naar deze persoon"
bookmark:
one: "Jij en iemand anders voegden dit bericht toe aan de favorieten"
other: "Jij en {{count}} anderen voegden dit bericht toe aan de favorieten"
@ -709,8 +713,8 @@ nl:
one: "Iemand heeft dit bericht gemarkeerd voor moderatie"
other: "{{count}} Mensen hebben dit bericht gemarkeerd voor moderatie"
notify_user:
one: "Iemand is een privé-conversatie begonnen met deze persoon"
other: "{{count}} Mensen zijn een privé-conversatie begonnen met deze persoon"
one: "Iemand stuurde een privé-bericht naar deze persoon"
other: "{{count}} Mensen stuurden een privé-bericht naar deze persoon"
bookmark:
one: "Iemand heeft dit bericht toegevoegd aan zijn favorieten"
other: "{{count}} Mensen hebben dit bericht toegevoegd aan hun favorieten"
@ -735,11 +739,13 @@ nl:
none: (geen categorie)
edit: bewerk
edit_long: Bewerk categorie
edit_uncategorized: "Wijzig ongecategoriseerd"
view: Bekijk topics in categorie
delete: Verwijder categorie
create: Maak categorie
save: Bewaar categorie
creation_error: Er ging bij het maken van de categorie iets mis.
save_error: Er ging iets mis bij het opslaan van de categorie.
more_posts: "bekijk alle {{posts}}..."
name: Naam categorie
description: Omschrijving

View File

@ -242,7 +242,7 @@ nl:
email_body: "%{link}\n\n%{message}"
notify_moderators:
title: "Licht moderatoren in"
description: "Dit bericht moet door een moderator bekeken worden. Dit vanwege de <a href='/faq'>FAQ</a>, <a href='/tos'>TOS</a> of een andere reden hier nog niet genoemd."
description: "Dit bericht moet door een moderator bekeken worden. Dit vanwege de <a href='/faq'>FAQ</a>, <a href='%{tos_url}'>TOS</a> of een andere reden hier nog niet genoemd."
long_form: moderatoren ingelicht
email_title: "Graag aandacht voor een bericht in '%{title}'"
email_body: "%{link}\n\n%{message}"
@ -340,6 +340,21 @@ nl:
title: Licht persoon in
xaxis: Dag
yaxis: Aantal privé-berichten
top_referrers:
title: Top verwijzingen
xaxis: Lid
num_clicks: Clicks
num_topics: Topics
top_traffic_sources:
title: Top trafficbronnen
xaxis: Domein
num_clicks: Clicks
num_topics: Topics
num_users: Leden
top_referred_topics:
title: Top doorverwezen topics
xaxis: Topic
num_clicks: Clicks
dashboard:
rails_env_warning: "Je server draait in %{env} modus."
@ -400,11 +415,12 @@ nl:
company_domain: "De domeinnaam van het bedrijf dat deze site draait. Wordt gebruikt in juridische delen van de site, zoals /tos"
api_key: "De beveiligde API-sleutel wordt gebruikt om topics te maken en bij te werken. Gebruik /admin/api om deze in te stellen"
access_password: "Wanneer beperkte toegang aan staat, moet dit wachtwoord worden opgegeven."
queue_jobs: "Zet verschillende taken in een queue binnen sidekiq, als ongeldige queues zich op dezelfde lijn bevinden"
queue_jobs: "Zet verschillende taken in een queue binnen sidekiq, bij 'false' worden taken ineens uitgevoerd"
crawl_images: Zet het ophalen van afbeeldingen van externe bronnen aan
ninja_edit_window: "Hoe snel je een aanpassing kan maken zonder dat er een nieuwe versie wordt opgeslagen, in seconden."
enable_imgur: "Gebruik de imgur API voor uploads en sla afbeeldingen niet lokaal op"
imgur_api_key: "imgur.com API-sleutel - nodig om afbeeldingen te kunnen uploaden naar imgur"
imgur_client_id: "Je imgur.com client ID, nodig om afbeeldingen te kunnen uploaden naar imgur"
imgur_client_secret: "Je imgur.com client secret. Is nog niet nodig voor het uploaden van afbeeldingen, maar dat zou in de toekomst kunnen veranderen."
imgur_endpoint: "End point voor het uploaden van imgur.com-afbeeldingen"
max_image_width: Maximale breedte voor een afbeelding in een bericht
category_featured_topics: Aantal topics dat wordt weergegeven in de categorielijst
@ -439,10 +455,11 @@ nl:
flags_required_to_hide_post: "Berichten zullen automatisch worden verborgen zodra het aantal meldingen maximaal dit aantal is (0 voor nooit)"
cooldown_minutes_after_hiding_posts: "Hoeveel minuten moet iemand wachten voordat zij hun bericht kunnen wijzigen nadat het is verborgen door meldingen"
traditional_markdown_linebreaks: "Gebruik traditionele regeleinden in Markdown, omloop is impliciet tenzij er 2 spaties aan vooraf gaan"
traditional_markdown_linebreaks: "Gebruik traditionele regeleinden in Markdown, gebruik 2 spaties voor een nieuw regeleinde"
post_undo_action_window_mins: "Het tijdsbestek waarin iemand een actie binnen een bericht kan terugdraaien (zoals 'vind ik leuk')"
must_approve_users: "De eigenaars van een forum moeten de leden accepteren voordat zij toegang krijgen."
ga_tracking_code: "Google analytics trackingcode, zie: http://google.com/analytics"
ga_domain_name: "Google analytics domeinnaam, bijv. mijnsite.nl; zie http://google.com/analytics"
top_menu: "De volgorde en selectie van items in het hoofdmenu. Bijvoorbeeld latest|hot|read|favorited|unread|new|posted|categories"
post_menu: "De volgorde en selectie van items in het berichtmenu. Bijvoorbeeld like|edit|flag|delete|share|bookmark|reply"
share_links: "De volgorde en selectie van items in het deelmenu. Bijvoorbeeld twitter|facebook|google+"
@ -475,7 +492,7 @@ nl:
facebook_app_id: "app_id (registreer op https://developers.facebook.com/apps)"
facebook_app_secret: "app_secret (registreer op https://developers.facebook.com/apps)"
enable_github_logins: Zet inloggen met Facebook aan. Hiervoor heb je een facebook_app_id en facebook_app_secret nodig.
enable_github_logins: Zet inloggen met Github aan. Hiervoor heb je een github_client_id en github_client_secret nodig.
github_client_id: "github_client_id (registreer op https://github.com/settings/applications)"
github_client_secret: "github_client_secret (registreer op https://github.com/settings/applications)"
@ -487,9 +504,11 @@ nl:
previous_visit_timeout_hours: "Hoe lang een bezoek duurt voordat we het als het 'vorige' bezoek beschouwen, in uren."
uncategorized_name: De naam voor ongecategoriseerde topics in de categorielijst
uncategorized_color: De achtergrondkleur van de badge voor de categorie met topics zonder categorie
uncategorized_text_color: De tekstkleur van de badge voor de categorie met topics zonder categorie
rate_limit_create_topic: "Hoeveel seconden voordat je een ander topic kan aanmaken"
rate_limit_create_post: "Hoeveel seconden voordat je een ander bericht kan aanmaken"
rate_limit_create_topic: Hoeveel seconden voordat je een ander topic kan aanmaken
rate_limit_create_post: Hoeveel seconden voordat je een ander bericht kan aanmaken
max_likes_per_day: Het maximaal aantal keren per dag dat een lid berichten leuk kan vinden
max_flags_per_day: Het maximaal aantal moderatie-meldingen dat een lid per dag kan geven
@ -583,7 +602,7 @@ nl:
archived_disabled: "Deze topic is niet langer gearchiveerd en kan weer veranderd worden."
closed_enabled: "Deze topic is nu gesloten. Nieuwe reacties worden niet langer geaccepteerd."
closed_disabled: "Deze topic is nu geopend. Nieuwe reacties worden weer geaccepteerd."
pinned_enabled: "Deze topic is nu gepind en zal bovenaan de lijst van topics in zijn categorie staan."
pinned_enabled: "Deze topic is nu gepind en zal bovenaan de lijst van topics in zijn categorie staan. Wanneer iemand op 'Verwijder pin' drukt, zal de topic voor diegene niet meer bovenaan staan."
pinned_disabled: "Deze topic is niet langer gepind en zal niet meer bovenaan de lijst van topics in zijn categorie staan."
visible_enabled: "Deze topic is nu zichtbaar en zal weer worden weergegeven in topiclijsten."
visible_disabled: "Deze topic is nu onzichtbaar en zal niet worden weergegeven in topiclijsten, en kan alleen via een directe link worden bezocht."
@ -613,9 +632,9 @@ nl:
not_allowed: "is niet toegestaan vanaf die e-mailprovider. Gebruik een ander e-mailadres."
invite_mailer:
subject_template: "[%{site_name}] %{invitee_name} heeft je uitgenodigd om aan een discussie deel te nemen op %{site_name}"
subject_template: "[%{site_name}] %{invitee_name} heeft je uitgenodigd om op een privé-bericht te reageren op %{site_name}"
text_body_template: |
%{invitee_name} heeft je uitgenodigd voor deze topic: "%{topic_title}" op %{site_name}.
%{invitee_name} heeft je uitgenodigd voor dit bericht: "%{topic_title}" op %{site_name}.
Mocht je geïnteresseerd zijn, klik dan op de link hieronder om naar de discussie te gaan:

View File

@ -14,7 +14,6 @@ The following Ruby Gems are used in Discourse:
* [rack-mini-profiler](https://rubygems.org/gems/rack-mini-profiler)
* [sass](https://rubygems.org/gems/sass)
* [rest-client](https://rubygems.org/gems/rest-client)
* [rails3_acts_as_paranoid](https://rubygems.org/gems/rails3_acts_as_paranoid)
* [activerecord-postgres-hstore](https://rubygems.org/gems/activerecord-postgres-hstore)
* [fastimage](https://rubygems.org/gems/fastimage)
* [seed-fu](https://rubygems.org/gems/seed-fu)

View File

@ -37,8 +37,7 @@ class PostDestroyer
# Feature users in the topic
Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id)
# Actually soft-delete the post :)
@post.destroy
@post.trash!
Topic.reset_highest(@post.topic_id)
@post.update_flagged_posts_count

46
lib/trashable.rb Normal file
View File

@ -0,0 +1,46 @@
module Trashable
extend ActiveSupport::Concern
included do
default_scope where(with_deleted_scope_sql)
# scope unscoped does not work
end
module ClassMethods
def with_deleted
# lifted from acts_as_paranoid, works around https://github.com/rails/rails/issues/4306
#
# with this in place Post.limit(10).with_deleted, will work as expected
#
scope = self.scoped.with_default_scope
scope.where_values.delete(with_deleted_scope_sql)
scope
end
def with_deleted_scope_sql
scoped.table[:deleted_at].eq(nil).to_sql
end
end
def trash!
# note, an argument could be made that the column should probably called trashed_at
# however, deleted_at is the terminology used in the UI
#
# we could hijack use a delete! and delete - redirecting the originals elsewhere, but that is
# confusing as well. So for now, we go with trash!
#
update_column(:deleted_at, DateTime.now)
end
def recover!
# see: https://github.com/rails/rails/issues/8436
#
# Fixed in Rails 4
#
self.class.unscoped.update_all({deleted_at: nil}, id: self.id)
raw_write_attribute :deleted_at, nil
end
end

View File

@ -222,14 +222,14 @@ describe Guardian do
it 'correctly handles post visibility' do
Guardian.new(user).can_see?(post).should be_true
post.destroy
post.trash!
post.reload
Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true
post.recover
post.recover!
post.reload
topic.destroy
topic.trash!
topic.reload
Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true

View File

@ -196,7 +196,7 @@ describe TopicView do
describe "filter_posts_after" do
it "returns undeleted posts after a post" do
topic_view.filter_posts_after(p1.post_number).should == [p2, p3, p5]
topic_view.filter_posts_after(p1.post_number).map(&:id).should == [p2.id, p3.id, p5.id]
topic_view.should_not be_initial_load
topic_view.index_offset.should == 1
topic_view.index_reverse.should be_false

View File

@ -29,7 +29,7 @@ describe InvitesController do
end
it "destroys the invite" do
Invite.any_instance.expects(:destroy)
Invite.any_instance.expects(:trash!)
delete :destroy, email: invite.email
end

View File

@ -120,7 +120,7 @@ describe PostActionsController do
end
it "works with a deleted post" do
flagged_post.destroy
flagged_post.trash!
xhr :post, :clear_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
response.should be_success
end

View File

@ -30,7 +30,7 @@ describe PostsController do
context "deleted post" do
before do
post.destroy
post.trash!
end
it "can't find deleted posts as an anonymous user" do
@ -121,7 +121,7 @@ describe PostsController do
end
it "calls recover" do
Post.any_instance.expects(:recover)
Post.any_instance.expects(:recover!)
xhr :put, :recover, post_id: post.id
end

View File

@ -78,7 +78,7 @@ describe PostAction do
it "should reset counts when a topic is deleted" do
PostAction.act(codinghorror, post, PostActionType.types[:off_topic])
post.topic.destroy
post.topic.trash!
PostAction.flagged_posts_count.should == 0
end

View File

@ -50,7 +50,7 @@ describe Post do
let(:post) { Fabricate(:post, post_args) }
before do
post.destroy
post.trash!
post.reload
end
@ -60,7 +60,7 @@ describe Post do
describe "recovery" do
before do
post.recover
post.recover!
post.reload
end

View File

@ -58,7 +58,7 @@ describe UserAction do
other_stats.should == expecting
public_topic.destroy
public_topic.trash!
stats_for_user.should == []
stream_count.should == 0
@ -66,7 +66,7 @@ describe UserAction do
category = Fabricate(:category, secure: true)
public_topic.recover
public_topic.recover!
public_topic.category = category
public_topic.save