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 'redcarpet', require: false
gem 'activerecord-postgres-hstore' gem 'activerecord-postgres-hstore'
gem 'acts_as_paranoid'
gem 'active_attr' # until we get ActiveModel::Model with Rails 4 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 'airbrake', '3.1.2', require: false # errbit is broken with 3.1.3 for now
gem 'clockwork', require: false 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' gem 'rack-mini-profiler', require: false # require: false #, git: 'git://github.com/SamSaffron/MiniProfiler'
# used for caching, optional # 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-cache', require: false
gem 'rack-cors', require: false gem 'rack-cors', require: false

View File

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

View File

@ -53,9 +53,12 @@ Discourse.ClickTrack = {
if (!ownLink) { if (!ownLink) {
var $badge = $('span.badge', $link); var $badge = $('span.badge', $link);
if ($badge.length === 1) { 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(".onebox-result").length === 0 || $link.hasClass("track-link")) { if ($link.closest('.badge-category').length === 0) {
$badge.html(parseInt($badge.html(), 10) + 1); // 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);
}
} }
} }
} }

View File

@ -432,7 +432,7 @@ Discourse.TopicController = Discourse.ObjectController.extend({
deletePost: function(post) { deletePost: function(post) {
// Moderators can delete posts. Regular users can only create a deleted at message. // 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()); post.set('deleted_at', new Date());
} else { } else {
post.set('cooked', Discourse.Markdown.cook(Em.String.i18n("post.deleted_by_author"))); 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() { togglePreview: function() {
this.toggleProperty('showPreview'); 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 // Import a quote from the post

View File

@ -171,9 +171,12 @@ Discourse.PostView = Discourse.View.extend({
postView.$(".cooked a[href]").each(function() { postView.$(".cooked a[href]").each(function() {
var link = $(this); var link = $(this);
if (link.attr('href') === lc.url) { 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(".onebox-result").length === 0 || link.hasClass("track-link")) { if (link.closest('.badge-category').length === 0) {
link.append("<span class='badge badge-notification clicks' title='" + Em.String.i18n("topic_summary.clicks") + "'>" + lc.clicks + "</span>"); // 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 invite = Invite.where(invited_by_id: current_user.id, email: params[:email]).first
raise Discourse::InvalidParameters.new(:email) if invite.blank? raise Discourse::InvalidParameters.new(:email) if invite.blank?
invite.destroy invite.trash!
render nothing: true render nothing: true
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,10 +4,12 @@ require_dependency 'topic_view'
require_dependency 'rate_limiter' require_dependency 'rate_limiter'
require_dependency 'text_sentinel' require_dependency 'text_sentinel'
require_dependency 'text_cleaner' require_dependency 'text_cleaner'
require_dependency 'trashable'
class Topic < ActiveRecord::Base class Topic < ActiveRecord::Base
include ActionView::Helpers include ActionView::Helpers
include RateLimiter::OnCreateRecord include RateLimiter::OnCreateRecord
include Trashable
def self.max_sort_order def self.max_sort_order
2**31 - 1 2**31 - 1
@ -18,9 +20,17 @@ class Topic < ActiveRecord::Base
end end
versioned if: :new_version_required? 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 :default_rate_limiter
rate_limit :limit_topics_per_day rate_limit :limit_topics_per_day

View File

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

View File

@ -21,11 +21,11 @@ class ListableTopicSerializer < BasicTopicSerializer
def age def age
AgeWords.age_words(Time.now - (object.created_at || Time.now)) AgeWords.age_words(Time.now - (object.created_at || Time.now))
end end
def bumped def bumped
object.created_at < object.bumped_at object.created_at < object.bumped_at
end end
def bumped_age def bumped_age
return nil if object.bumped_at.blank? return nil if object.bumped_at.blank?
AgeWords.age_words(Time.now - object.bumped_at) AgeWords.age_words(Time.now - object.bumped_at)

View File

@ -2,6 +2,10 @@ if Rails.configuration.respond_to?(:enable_rack_cache) && Rails.configuration.en
require 'rack-cache' require 'rack-cache'
require 'redis-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 url = DiscourseRedis.url
class Rack::Cache::Discourse < Rack::Cache::Context class Rack::Cache::Discourse < Rack::Cache::Context

View File

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

View File

@ -242,7 +242,7 @@ nl:
email_body: "%{link}\n\n%{message}" email_body: "%{link}\n\n%{message}"
notify_moderators: notify_moderators:
title: "Licht moderatoren in" 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 long_form: moderatoren ingelicht
email_title: "Graag aandacht voor een bericht in '%{title}'" email_title: "Graag aandacht voor een bericht in '%{title}'"
email_body: "%{link}\n\n%{message}" email_body: "%{link}\n\n%{message}"
@ -340,6 +340,21 @@ nl:
title: Licht persoon in title: Licht persoon in
xaxis: Dag xaxis: Dag
yaxis: Aantal privé-berichten 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: dashboard:
rails_env_warning: "Je server draait in %{env} modus." 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" 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" 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." 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 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." 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" 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" imgur_endpoint: "End point voor het uploaden van imgur.com-afbeeldingen"
max_image_width: Maximale breedte voor een afbeelding in een bericht max_image_width: Maximale breedte voor een afbeelding in een bericht
category_featured_topics: Aantal topics dat wordt weergegeven in de categorielijst 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)" 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" 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')" 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." 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_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" 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" 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+" 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_id: "app_id (registreer op https://developers.facebook.com/apps)"
facebook_app_secret: "app_secret (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_id: "github_client_id (registreer op https://github.com/settings/applications)"
github_client_secret: "github_client_secret (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." 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_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_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_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_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 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." 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_enabled: "Deze topic is nu gesloten. Nieuwe reacties worden niet langer geaccepteerd."
closed_disabled: "Deze topic is nu geopend. Nieuwe reacties worden weer 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." 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_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." 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." not_allowed: "is niet toegestaan vanaf die e-mailprovider. Gebruik een ander e-mailadres."
invite_mailer: 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: | 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: 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) * [rack-mini-profiler](https://rubygems.org/gems/rack-mini-profiler)
* [sass](https://rubygems.org/gems/sass) * [sass](https://rubygems.org/gems/sass)
* [rest-client](https://rubygems.org/gems/rest-client) * [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) * [activerecord-postgres-hstore](https://rubygems.org/gems/activerecord-postgres-hstore)
* [fastimage](https://rubygems.org/gems/fastimage) * [fastimage](https://rubygems.org/gems/fastimage)
* [seed-fu](https://rubygems.org/gems/seed-fu) * [seed-fu](https://rubygems.org/gems/seed-fu)

View File

@ -37,8 +37,7 @@ class PostDestroyer
# Feature users in the topic # Feature users in the topic
Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id) Jobs.enqueue(:feature_topic_users, topic_id: @post.topic_id, except_post_id: @post.id)
# Actually soft-delete the post :) @post.trash!
@post.destroy
Topic.reset_highest(@post.topic_id) Topic.reset_highest(@post.topic_id)
@post.update_flagged_posts_count @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 it 'correctly handles post visibility' do
Guardian.new(user).can_see?(post).should be_true Guardian.new(user).can_see?(post).should be_true
post.destroy post.trash!
post.reload post.reload
Guardian.new(user).can_see?(post).should be_false Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true Guardian.new(admin).can_see?(post).should be_true
post.recover post.recover!
post.reload post.reload
topic.destroy topic.trash!
topic.reload topic.reload
Guardian.new(user).can_see?(post).should be_false Guardian.new(user).can_see?(post).should be_false
Guardian.new(admin).can_see?(post).should be_true Guardian.new(admin).can_see?(post).should be_true

View File

@ -196,7 +196,7 @@ describe TopicView do
describe "filter_posts_after" do describe "filter_posts_after" do
it "returns undeleted posts after a post" 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.should_not be_initial_load
topic_view.index_offset.should == 1 topic_view.index_offset.should == 1
topic_view.index_reverse.should be_false topic_view.index_reverse.should be_false

View File

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

View File

@ -120,7 +120,7 @@ describe PostActionsController do
end end
it "works with a deleted post" do 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] xhr :post, :clear_flags, id: flagged_post.id, post_action_type_id: PostActionType.types[:spam]
response.should be_success response.should be_success
end end

View File

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

View File

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

View File

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

View File

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