diff --git a/Gemfile b/Gemfile
index 0c1ade10891..fac8f5ac73e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,4 +1,4 @@
-source 'https://rubygems.org'
+source 'http://rubygems.org'
gem 'active_model_serializers', git: 'git://github.com/rails-api/active_model_serializers.git'
gem 'ember-rails', git: 'git://github.com/emberjs/ember-rails.git' # so we get the pre version
diff --git a/Gemfile.lock b/Gemfile.lock
index 3684482f265..15802713ac2 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -71,7 +71,7 @@ PATH
rails (~> 3.1)
GEM
- remote: https://rubygems.org/
+ remote: http://rubygems.org/
specs:
actionmailer (3.2.12)
actionpack (= 3.2.12)
diff --git a/app/assets/javascripts/discourse/controllers/topic_controller.js b/app/assets/javascripts/discourse/controllers/topic_controller.js
index f1c503b0d85..cd0e48f4b66 100644
--- a/app/assets/javascripts/discourse/controllers/topic_controller.js
+++ b/app/assets/javascripts/discourse/controllers/topic_controller.js
@@ -255,6 +255,15 @@ Discourse.TopicController = Discourse.ObjectController.extend({
this.get('content').toggleStar();
},
+ /**
+ Clears the pin from a topic for the currentUser
+
+ @method clearPin
+ **/
+ clearPin: function() {
+ this.get('content').clearPin();
+ },
+
// Receive notifications for this topic
subscribe: function() {
var bus,
diff --git a/app/assets/javascripts/discourse/models/topic.js b/app/assets/javascripts/discourse/models/topic.js
index 5dc912a8e8a..937ab5824e0 100644
--- a/app/assets/javascripts/discourse/models/topic.js
+++ b/app/assets/javascripts/discourse/models/topic.js
@@ -329,6 +329,27 @@ Discourse.Topic = Discourse.Model.extend({
});
},
+ /**
+ Clears the pin from a topic for the currentUser
+
+ @method clearPin
+ **/
+ clearPin: function() {
+
+ var topic = this;
+
+ // Clear the pin optimistically from the object
+ topic.set('pinned', false);
+
+ $.ajax("/t/" + this.get('id') + "/clear-pin", {
+ type: 'PUT',
+ error: function() {
+ // On error, put the pin back
+ topic.set('pinned', true);
+ }
+ });
+ },
+
// Is the reply to a post directly below it?
isReplyDirectlyBelow: function(post) {
var postBelow, posts;
diff --git a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
index c2fe4e777d1..a68ee7ca4c1 100644
--- a/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
+++ b/app/assets/javascripts/discourse/views/topic_footer_buttons_view.js
@@ -68,6 +68,30 @@ Discourse.TopicFooterButtonsView = Ember.ContainerView.extend({
buffer.push("");
}
}));
+
+ // Add our clear pin button
+ this.addObject(Discourse.ButtonView.createWithMixins({
+ textKey: 'topic.clear_pin.title',
+ helpKey: 'topic.clear_pin.help',
+ classNameBindings: ['unpinned'],
+
+ // Hide the button if it becomes unpinned
+ unpinned: function() {
+ // When not logged in don't show the button
+ if (!Discourse.get('currentUser')) return 'hidden'
+
+ return this.get('controller.pinned') ? null : 'hidden';
+ }.property('controller.pinned'),
+
+ click: function(buffer) {
+ this.get('controller').clearPin();
+ },
+
+ renderIcon: function(buffer) {
+ buffer.push("");
+ }
+ }));
+
}
this.addObject(Discourse.ButtonView.createWithMixins({
diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb
index 8381ed13d23..2ddb7868da4 100644
--- a/app/controllers/topics_controller.rb
+++ b/app/controllers/topics_controller.rb
@@ -14,7 +14,9 @@ class TopicsController < ApplicationController
:mute,
:unmute,
:set_notifications,
- :move_posts]
+ :move_posts,
+ :clear_pin]
+
before_filter :consider_user_for_promotion, only: :show
skip_before_filter :check_xhr, only: [:avatar, :show, :feed]
@@ -127,16 +129,21 @@ class TopicsController < ApplicationController
end
end
+ def clear_pin
+ topic = Topic.where(id: params[:topic_id].to_i).first
+ guardian.ensure_can_see!(topic)
+ topic.clear_pin_for(current_user)
+ render nothing: true
+ end
+
def timings
-
PostTiming.process_timings(
- current_user,
- params[:topic_id].to_i,
- params[:highest_seen].to_i,
- params[:topic_time].to_i,
- (params[:timings] || []).map{|post_number, t| [post_number.to_i, t.to_i]}
+ current_user,
+ params[:topic_id].to_i,
+ params[:highest_seen].to_i,
+ params[:topic_time].to_i,
+ (params[:timings] || []).map{|post_number, t| [post_number.to_i, t.to_i]}
)
-
render nothing: true
end
diff --git a/app/models/post.rb b/app/models/post.rb
index fc42d8d2157..82cf60d74e7 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -54,7 +54,7 @@ class Post < ActiveRecord::Base
after_commit :store_unique_post_key, on: :create
after_create do
- TopicUser.auto_track(user_id, topic_id, TopicUser::NotificationReasons::CREATED_POST)
+ TopicUser.auto_track(user_id, topic_id, TopicUser.notification_reasons[:created_post])
end
scope :by_newest, order('created_at desc, id desc')
diff --git a/app/models/post_alert_observer.rb b/app/models/post_alert_observer.rb
index c421122d881..88c7bca9071 100644
--- a/app/models/post_alert_observer.rb
+++ b/app/models/post_alert_observer.rb
@@ -80,7 +80,7 @@ class PostAlertObserver < ActiveRecord::Observer
return unless Guardian.new(user).can_see?(post)
# skip if muted on the topic
- return if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser::NotificationLevel::MUTED
+ return if TopicUser.get(post.topic, user).try(:notification_level) == TopicUser.notification_levels[:muted]
# Don't notify the same user about the same notification on the same post
return if user.notifications.exists?(notification_type: type, topic_id: post.topic_id, post_number: post.post_number)
@@ -132,7 +132,7 @@ class PostAlertObserver < ActiveRecord::Observer
exclude_user_ids << extract_mentioned_users(post).map(&:id)
exclude_user_ids << extract_quoted_users(post).map(&:id)
exclude_user_ids.flatten!
- TopicUser.where(topic_id: post.topic_id, notification_level: TopicUser::NotificationLevel::WATCHING).includes(:user).each do |tu|
+ TopicUser.where(topic_id: post.topic_id, notification_level: TopicUser.notification_levels[:watching]).includes(:user).each do |tu|
create_notification(tu.user, Notification.types[:posted], post) unless exclude_user_ids.include?(tu.user_id)
end
end
diff --git a/app/models/topic.rb b/app/models/topic.rb
index 9c9f85cc6cd..4b0f2ce42c6 100644
--- a/app/models/topic.rb
+++ b/app/models/topic.rb
@@ -83,11 +83,9 @@ class Topic < ActiveRecord::Base
after_create do
changed_to_category(category)
- TopicUser.change(
- user_id, id,
- notification_level: TopicUser::NotificationLevel::WATCHING,
- notifications_reason_id: TopicUser::NotificationReasons::CREATED_TOPIC
- )
+ TopicUser.change(user_id, id,
+ notification_level: TopicUser.notification_levels[:watching],
+ notifications_reason_id: TopicUser.notification_reasons[:created_topic])
if archetype == Archetype.private_message
DraftSequence.next!(user, Draft::NEW_PRIVATE_MESSAGE)
else
@@ -206,8 +204,17 @@ class Topic < ActiveRecord::Base
end
def update_status(property, status, user)
+
Topic.transaction do
- update_column(property, status)
+
+ # Special case: if it's pinned, update that
+ if property.to_sym == :pinned
+ update_pinned(status)
+ else
+ # otherwise update the column
+ update_column(property, status)
+ end
+
key = "topic_statuses.#{property}_"
key << (status ? 'enabled' : 'disabled')
@@ -506,7 +513,7 @@ class Topic < ActiveRecord::Base
# Enable/disable the mute on the topic
def toggle_mute(user, muted)
- TopicUser.change(user, self.id, notification_level: muted?(user) ? TopicUser::NotificationLevel::REGULAR : TopicUser::NotificationLevel::MUTED )
+ TopicUser.change(user, self.id, notification_level: muted?(user) ? TopicUser.notification_levels[:regular] : TopicUser.notification_levels[:muted] )
end
def slug
@@ -526,7 +533,17 @@ class Topic < ActiveRecord::Base
def muted?(user)
return false unless user && user.id
tu = topic_users.where(user_id: user.id).first
- tu && tu.notification_level == TopicUser::NotificationLevel::MUTED
+ tu && tu.notification_level == TopicUser.notification_levels[:muted]
+ end
+
+ def clear_pin_for(user)
+ return unless user.present?
+
+ TopicUser.change(user.id, id, cleared_pinned_at: Time.now)
+ end
+
+ def update_pinned(status)
+ update_column(:pinned_at, status ? Time.now : nil)
end
def draft_key
@@ -535,18 +552,18 @@ class Topic < ActiveRecord::Base
# notification stuff
def notify_watch!(user)
- TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::WATCHING)
+ TopicUser.change(user, id, notification_level: TopicUser.notification_levels[:watching])
end
def notify_tracking!(user)
- TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::TRACKING)
+ TopicUser.change(user, id, notification_level: TopicUser.notification_levels[:tracking])
end
def notify_regular!(user)
- TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::REGULAR)
+ TopicUser.change(user, id, notification_level: TopicUser.notification_levels[:regular])
end
def notify_muted!(user)
- TopicUser.change(user, id, notification_level: TopicUser::NotificationLevel::MUTED)
+ TopicUser.change(user, id, notification_level: TopicUser.notification_levels[:muted])
end
end
diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb
index b92e7bec122..e3d29d0a819 100644
--- a/app/models/topic_user.rb
+++ b/app/models/topic_user.rb
@@ -2,185 +2,173 @@ class TopicUser < ActiveRecord::Base
belongs_to :user
belongs_to :topic
- module NotificationLevel
- WATCHING = 3
- TRACKING = 2
- REGULAR = 1
- MUTED = 0
- end
+ # Class methods
+ class << self
- module NotificationReasons
- CREATED_TOPIC = 1
- USER_CHANGED = 2
- USER_INTERACTED = 3
- CREATED_POST = 4
- end
+ # Enums
+ def notification_levels
+ @notification_levels ||= Enum.new(:muted, :regular, :tracking, :watching, start: 0)
+ end
- def self.auto_track(user_id, topic_id, reason)
- if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists?
- change(user_id, topic_id,
- notification_level: NotificationLevel::TRACKING,
+ def notification_reasons
+ @notification_reasons ||= Enum.new(:created_topic, :user_changed, :user_interacted, :created_post)
+ end
+
+ def auto_track(user_id, topic_id, reason)
+ if TopicUser.where(user_id: user_id, topic_id: topic_id, notifications_reason_id: nil).exists?
+ change(user_id, topic_id,
+ notification_level: notification_levels[:tracking],
notifications_reason_id: reason
- )
+ )
- MessageBus.publish("/topic/#{topic_id}", {
- notification_level_change: NotificationLevel::TRACKING,
- notifications_reason_id: reason
- }, user_ids: [user_id])
- end
- end
-
-
- # Find the information specific to a user in a forum topic
- def self.lookup_for(user, topics)
- # If the user isn't logged in, there's no last read posts
- return {} if user.blank? || topics.blank?
-
- topic_ids = topics.map(&:id)
- create_lookup(TopicUser.where(topic_id: topic_ids, user_id: user.id))
- end
-
- def self.create_lookup(topic_users)
- topic_users = topic_users.to_a
-
- result = {}
- return result if topic_users.blank?
-
- topic_users.each do |ftu|
- result[ftu.topic_id] = ftu
- end
- result
- end
-
- def self.get(topic,user)
- if Topic === topic
- topic = topic.id
- end
- if User === user
- user = user.id
- end
-
- TopicUser.where('topic_id = ? and user_id = ?', topic, user).first
- end
-
- # Change attributes for a user (creates a record when none is present). First it tries an update
- # since there's more likely to be an existing record than not. If the update returns 0 rows affected
- # it then creates the row instead.
- def self.change(user_id, topic_id, attrs)
- # Sometimes people pass objs instead of the ids. We can handle that.
- topic_id = topic_id.id if topic_id.is_a?(Topic)
- user_id = user_id.id if user_id.is_a?(User)
-
- TopicUser.transaction do
- attrs = attrs.dup
- attrs[:starred_at] = DateTime.now if attrs[:starred_at].nil? && attrs[:starred]
-
- if attrs[:notification_level]
- attrs[:notifications_changed_at] ||= DateTime.now
- attrs[:notifications_reason_id] ||= TopicUser::NotificationReasons::USER_CHANGED
+ MessageBus.publish("/topic/#{topic_id}", {
+ notification_level_change: notification_levels[:tracking],
+ notifications_reason_id: reason
+ }, user_ids: [user_id])
end
- attrs_array = attrs.to_a
+ end
- attrs_sql = attrs_array.map { |t| "#{t[0]} = ?" }.join(", ")
- vals = attrs_array.map { |t| t[1] }
- rows = TopicUser.update_all([attrs_sql, *vals], topic_id: topic_id.to_i, user_id: user_id)
+ # Find the information specific to a user in a forum topic
+ def lookup_for(user, topics)
+ # If the user isn't logged in, there's no last read posts
+ return {} if user.blank? || topics.blank?
- if rows == 0
- now = DateTime.now
- auto_track_after = self.exec_sql("select auto_track_topics_after_msecs from users where id = ?", user_id).values[0][0]
- auto_track_after ||= SiteSetting.auto_track_topics_after
- auto_track_after = auto_track_after.to_i
+ topic_ids = topics.map(&:id)
+ create_lookup(TopicUser.where(topic_id: topic_ids, user_id: user.id))
+ end
- if auto_track_after >= 0 && auto_track_after <= (attrs[:total_msecs_viewed] || 0)
- attrs[:notification_level] ||= TopicUser::NotificationLevel::TRACKING
+ def create_lookup(topic_users)
+ topic_users = topic_users.to_a
+
+ result = {}
+ return result if topic_users.blank?
+
+ topic_users.each do |ftu|
+ result[ftu.topic_id] = ftu
+ end
+ result
+ end
+
+ def get(topic,user)
+ topic = topic.id if Topic === topic
+ user = user.id if User === user
+ TopicUser.where('topic_id = ? and user_id = ?', topic, user).first
+ end
+
+ # Change attributes for a user (creates a record when none is present). First it tries an update
+ # since there's more likely to be an existing record than not. If the update returns 0 rows affected
+ # it then creates the row instead.
+ def change(user_id, topic_id, attrs)
+ # Sometimes people pass objs instead of the ids. We can handle that.
+ topic_id = topic_id.id if topic_id.is_a?(Topic)
+ user_id = user_id.id if user_id.is_a?(User)
+
+ TopicUser.transaction do
+ attrs = attrs.dup
+ attrs[:starred_at] = DateTime.now if attrs[:starred_at].nil? && attrs[:starred]
+
+ if attrs[:notification_level]
+ attrs[:notifications_changed_at] ||= DateTime.now
+ attrs[:notifications_reason_id] ||= TopicUser.notification_reasons[:user_changed]
end
+ attrs_array = attrs.to_a
- TopicUser.create(attrs.merge!(user_id: user_id, topic_id: topic_id.to_i, first_visited_at: now ,last_visited_at: now))
+ attrs_sql = attrs_array.map { |t| "#{t[0]} = ?" }.join(", ")
+ vals = attrs_array.map { |t| t[1] }
+ rows = TopicUser.update_all([attrs_sql, *vals], topic_id: topic_id.to_i, user_id: user_id)
+
+ if rows == 0
+ now = DateTime.now
+ auto_track_after = User.select(:auto_track_topics_after_msecs).where(id: user_id).first.auto_track_topics_after_msecs
+ auto_track_after ||= SiteSetting.auto_track_topics_after
+
+ if auto_track_after >= 0 && auto_track_after <= (attrs[:total_msecs_viewed] || 0)
+ attrs[:notification_level] ||= notification_levels[:tracking]
+ end
+
+ TopicUser.create(attrs.merge!(user_id: user_id, topic_id: topic_id.to_i, first_visited_at: now ,last_visited_at: now))
+ end
end
+ rescue ActiveRecord::RecordNotUnique
+ # In case of a race condition to insert, do nothing
end
- rescue ActiveRecord::RecordNotUnique
- # In case of a race condition to insert, do nothing
- end
- def self.track_visit!(topic,user)
- now = DateTime.now
- rows = exec_sql_row_count(
- "update topic_users set last_visited_at=? where topic_id=? and user_id=?",
- now, topic.id, user.id
- )
-
- if rows == 0
- exec_sql('insert into topic_users(topic_id, user_id, last_visited_at, first_visited_at)
- values(?,?,?,?)',
- topic.id, user.id, now, now)
- end
- end
-
- # Update the last read and the last seen post count, but only if it doesn't exist.
- # This would be a lot easier if psql supported some kind of upsert
- def self.update_last_read(user, topic_id, post_number, msecs)
- return if post_number.blank?
- msecs = 0 if msecs.to_i < 0
-
- args = {
- user_id: user.id,
- topic_id: topic_id,
- post_number: post_number,
- now: DateTime.now,
- msecs: msecs,
- tracking: TopicUser::NotificationLevel::TRACKING,
- threshold: SiteSetting.auto_track_topics_after
- }
-
- rows = exec_sql("UPDATE topic_users
- SET
- last_read_post_number = greatest(:post_number, tu.last_read_post_number),
- seen_post_count = t.highest_post_number,
- total_msecs_viewed = tu.total_msecs_viewed + :msecs,
- notification_level =
- case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
- coalesce(u.auto_track_topics_after_msecs,:threshold) and
- coalesce(u.auto_track_topics_after_msecs, :threshold) >= 0 then
- :tracking
- else
- tu.notification_level
- end
- FROM topic_users tu
- join topics t on t.id = tu.topic_id
- join users u on u.id = :user_id
- WHERE
- tu.topic_id = topic_users.topic_id AND
- tu.user_id = topic_users.user_id AND
- tu.topic_id = :topic_id AND
- tu.user_id = :user_id
- RETURNING
- topic_users.notification_level, tu.notification_level old_level
- ",
- args).values
-
- if rows.length == 1
- before = rows[0][1].to_i
- after = rows[0][0].to_i
-
- if before != after
- MessageBus.publish("/topic/#{topic_id}", {notification_level_change: after}, user_ids: [user.id])
+ def track_visit!(topic,user)
+ now = DateTime.now
+ rows = TopicUser.update_all({last_visited_at: now}, {topic_id: topic.id, user_id: user.id})
+ if rows == 0
+ TopicUser.create(topic_id: topic.id, user_id: user.id, last_visited_at: now, first_visited_at: now)
end
end
- if rows.length == 0
- args[:tracking] = TopicUser::NotificationLevel::TRACKING
- args[:regular] = TopicUser::NotificationLevel::REGULAR
- args[:site_setting] = SiteSetting.auto_track_topics_after
- exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, seen_post_count, last_visited_at, first_visited_at, notification_level)
- SELECT :user_id, :topic_id, :post_number, ft.highest_post_number, :now, :now,
- case when coalesce(u.auto_track_topics_after_msecs, :site_setting) = 0 then :tracking else :regular end
- FROM topics AS ft
- JOIN users u on u.id = :user_id
- WHERE ft.id = :topic_id
- AND NOT EXISTS(SELECT 1
- FROM topic_users AS ftu
- WHERE ftu.user_id = :user_id and ftu.topic_id = :topic_id)",
- args)
+ # Update the last read and the last seen post count, but only if it doesn't exist.
+ # This would be a lot easier if psql supported some kind of upsert
+ def update_last_read(user, topic_id, post_number, msecs)
+ return if post_number.blank?
+ msecs = 0 if msecs.to_i < 0
+
+ args = {
+ user_id: user.id,
+ topic_id: topic_id,
+ post_number: post_number,
+ now: DateTime.now,
+ msecs: msecs,
+ tracking: notification_levels[:tracking],
+ threshold: SiteSetting.auto_track_topics_after
+ }
+
+ rows = exec_sql("UPDATE topic_users
+ SET
+ last_read_post_number = greatest(:post_number, tu.last_read_post_number),
+ seen_post_count = t.highest_post_number,
+ total_msecs_viewed = tu.total_msecs_viewed + :msecs,
+ notification_level =
+ case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
+ coalesce(u.auto_track_topics_after_msecs,:threshold) and
+ coalesce(u.auto_track_topics_after_msecs, :threshold) >= 0 then
+ :tracking
+ else
+ tu.notification_level
+ end
+ FROM topic_users tu
+ join topics t on t.id = tu.topic_id
+ join users u on u.id = :user_id
+ WHERE
+ tu.topic_id = topic_users.topic_id AND
+ tu.user_id = topic_users.user_id AND
+ tu.topic_id = :topic_id AND
+ tu.user_id = :user_id
+ RETURNING
+ topic_users.notification_level, tu.notification_level old_level
+ ",
+ args).values
+
+ if rows.length == 1
+ before = rows[0][1].to_i
+ after = rows[0][0].to_i
+
+ if before != after
+ MessageBus.publish("/topic/#{topic_id}", {notification_level_change: after}, user_ids: [user.id])
+ end
+ end
+
+ if rows.length == 0
+ args[:tracking] = notification_levels[:tracking]
+ args[:regular] = notification_levels[:regular]
+ args[:site_setting] = SiteSetting.auto_track_topics_after
+ exec_sql("INSERT INTO topic_users (user_id, topic_id, last_read_post_number, seen_post_count, last_visited_at, first_visited_at, notification_level)
+ SELECT :user_id, :topic_id, :post_number, ft.highest_post_number, :now, :now,
+ case when coalesce(u.auto_track_topics_after_msecs, :site_setting) = 0 then :tracking else :regular end
+ FROM topics AS ft
+ JOIN users u on u.id = :user_id
+ WHERE ft.id = :topic_id
+ AND NOT EXISTS(SELECT 1
+ FROM topic_users AS ftu
+ WHERE ftu.user_id = :user_id and ftu.topic_id = :topic_id)",
+ args)
+ end
end
+
end
+
end
diff --git a/app/models/user.rb b/app/models/user.rb
index e14b0916bf8..33b703a1c55 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -443,11 +443,8 @@ class User < ActiveRecord::Base
end
def readable_name
- if name.present? && name != username
- "#{name} (#{username})"
- else
- username
- end
+ return "#{name} (#{username})" if name.present? && name != username
+ username
end
protected
@@ -461,25 +458,14 @@ class User < ActiveRecord::Base
end
def update_tracked_topics
- if auto_track_topics_after_msecs_changed?
+ return unless auto_track_topics_after_msecs_changed?
- if auto_track_topics_after_msecs < 0
-
- User.exec_sql('update topic_users set notification_level = ?
- where notifications_reason_id is null and
- user_id = ?' , TopicUser::NotificationLevel::REGULAR , id)
- else
-
- User.exec_sql('update topic_users set notification_level = ?
- where notifications_reason_id is null and
- user_id = ? and
- total_msecs_viewed < ?' , TopicUser::NotificationLevel::REGULAR , id, auto_track_topics_after_msecs)
-
- User.exec_sql('update topic_users set notification_level = ?
- where notifications_reason_id is null and
- user_id = ? and
- total_msecs_viewed >= ?' , TopicUser::NotificationLevel::TRACKING , id, auto_track_topics_after_msecs)
- end
+ where_conditions = {notifications_reason_id: nil, user_id: id}
+ if auto_track_topics_after_msecs < 0
+ TopicUser.update_all({notification_level: TopicUser.notification_levels[:regular]}, where_conditions)
+ else
+ TopicUser.update_all(["notification_level = CASE WHEN total_msecs_viewed < ? THEN ? ELSE ? END",
+ auto_track_topics_after_msecs, TopicUser.notification_levels[:regular], TopicUser.notification_levels[:tracking]], where_conditions)
end
end
diff --git a/app/serializers/category_topic_serializer.rb b/app/serializers/category_topic_serializer.rb
index d17fc548a5d..0df31095c20 100644
--- a/app/serializers/category_topic_serializer.rb
+++ b/app/serializers/category_topic_serializer.rb
@@ -2,7 +2,6 @@ class CategoryTopicSerializer < BasicTopicSerializer
attributes :slug,
:visible,
- :pinned,
:closed,
:archived
diff --git a/app/serializers/topic_list_item_serializer.rb b/app/serializers/topic_list_item_serializer.rb
index 49ea8ebafe5..e5466abf0ab 100644
--- a/app/serializers/topic_list_item_serializer.rb
+++ b/app/serializers/topic_list_item_serializer.rb
@@ -1,3 +1,5 @@
+require_dependency 'pinned_check'
+
class TopicListItemSerializer < BasicTopicSerializer
attributes :views,
@@ -29,4 +31,8 @@ class TopicListItemSerializer < BasicTopicSerializer
object.posters || []
end
+ def pinned
+ PinnedCheck.new(object, object.user_data).pinned?
+ end
+
end
diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb
index 337dd11ffb0..9e7fa407862 100644
--- a/app/serializers/topic_view_serializer.rb
+++ b/app/serializers/topic_view_serializer.rb
@@ -1,3 +1,5 @@
+require_dependency 'pinned_check'
+
class TopicViewSerializer < ApplicationSerializer
# These attributes will be delegated to the topic
@@ -12,7 +14,6 @@ class TopicViewSerializer < ApplicationSerializer
:last_posted_at,
:visible,
:closed,
- :pinned,
:archived,
:moderator_posts_count,
:has_best_of,
@@ -42,7 +43,8 @@ class TopicViewSerializer < ApplicationSerializer
:notifications_reason_id,
:posts,
:at_bottom,
- :highest_post_number
+ :highest_post_number,
+ :pinned
has_one :created_by, serializer: BasicUserSerializer, embed: :objects
has_one :last_poster, serializer: BasicUserSerializer, embed: :objects
@@ -193,6 +195,10 @@ class TopicViewSerializer < ApplicationSerializer
object.highest_post_number
end
+ def pinned
+ PinnedCheck.new(object.topic, object.topic_user).pinned?
+ end
+
def posts
return @posts if @posts.present?
@posts = []
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index ff27b03c3aa..102d0970a87 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -452,6 +452,10 @@ en:
title: 'Reply'
help: 'begin composing a reply to this topic'
+ clear_pin:
+ title: "Clear pin"
+ help: "Clear the pinned status of this topic so it no longer appears at the top of your topic list"
+
share:
title: 'Share'
help: 'share a link to this topic'
diff --git a/config/routes.rb b/config/routes.rb
index 3693bf64423..eb6271a448f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -175,6 +175,7 @@ Discourse::Application.routes.draw do
put 't/:topic_id/star' => 'topics#star', :constraints => {:topic_id => /\d+/}
put 't/:slug/:topic_id/status' => 'topics#status', :constraints => {:topic_id => /\d+/}
put 't/:topic_id/status' => 'topics#status', :constraints => {:topic_id => /\d+/}
+ put 't/:topic_id/clear-pin' => 'topics#clear_pin', :constraints => {:topic_id => /\d+/}
put 't/:topic_id/mute' => 'topics#mute', :constraints => {:topic_id => /\d+/}
put 't/:topic_id/unmute' => 'topics#unmute', :constraints => {:topic_id => /\d+/}
diff --git a/db/migrate/20130306180148_add_cleared_pinned_to_topic_users.rb b/db/migrate/20130306180148_add_cleared_pinned_to_topic_users.rb
new file mode 100644
index 00000000000..9cc4af5d914
--- /dev/null
+++ b/db/migrate/20130306180148_add_cleared_pinned_to_topic_users.rb
@@ -0,0 +1,9 @@
+class AddClearedPinnedToTopicUsers < ActiveRecord::Migration
+ def change
+ add_column :topic_users, :cleared_pinned_at, :datetime, null: true
+
+ add_column :topics, :pinned_at, :datetime, null: true
+ execute "UPDATE topics SET pinned_at = created_at WHERE pinned"
+ remove_column :topics, :pinned
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index fa70f2c1037..09a4e77d22a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -8,6 +8,13 @@ SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
+--
+-- Name: backup; Type: SCHEMA; Schema: -; Owner: -
+--
+
+CREATE SCHEMA backup;
+
+
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: -
--
@@ -36,12 +43,1176 @@ CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA public;
COMMENT ON EXTENSION hstore IS 'data type for storing sets of (key, value) pairs';
-SET search_path = public, pg_catalog;
+SET search_path = backup, pg_catalog;
SET default_tablespace = '';
SET default_with_oids = false;
+--
+-- Name: categories; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE categories (
+ id integer NOT NULL,
+ name character varying(50) NOT NULL,
+ color character varying(6) DEFAULT 'AB9364'::character varying NOT NULL,
+ topic_id integer,
+ top1_topic_id integer,
+ top2_topic_id integer,
+ top1_user_id integer,
+ top2_user_id integer,
+ topic_count integer DEFAULT 0 NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ user_id integer NOT NULL,
+ topics_year integer,
+ topics_month integer,
+ topics_week integer,
+ slug character varying(255) NOT NULL
+);
+
+
+--
+-- Name: categories_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE categories_id_seq
+ START WITH 5
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: categories_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE categories_id_seq OWNED BY categories.id;
+
+
+--
+-- Name: category_featured_topics; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE category_featured_topics (
+ category_id integer NOT NULL,
+ topic_id integer NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: category_featured_users; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE category_featured_users (
+ id integer NOT NULL,
+ category_id integer,
+ user_id integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: category_featured_users_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE category_featured_users_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: category_featured_users_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE category_featured_users_id_seq OWNED BY category_featured_users.id;
+
+
+--
+-- Name: draft_sequences; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE draft_sequences (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ draft_key character varying(255) NOT NULL,
+ sequence integer NOT NULL
+);
+
+
+--
+-- Name: draft_sequences_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE draft_sequences_id_seq
+ START WITH 20
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: draft_sequences_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE draft_sequences_id_seq OWNED BY draft_sequences.id;
+
+
+--
+-- Name: drafts; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE drafts (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ draft_key character varying(255) NOT NULL,
+ data text NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ sequence integer DEFAULT 0 NOT NULL
+);
+
+
+--
+-- Name: drafts_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE drafts_id_seq
+ START WITH 2
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: drafts_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE drafts_id_seq OWNED BY drafts.id;
+
+
+--
+-- Name: email_logs; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE email_logs (
+ id integer NOT NULL,
+ to_address character varying(255) NOT NULL,
+ email_type character varying(255) NOT NULL,
+ user_id integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: email_logs_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE email_logs_id_seq
+ START WITH 3
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: email_logs_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE email_logs_id_seq OWNED BY email_logs.id;
+
+
+--
+-- Name: email_tokens; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE email_tokens (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ email character varying(255) NOT NULL,
+ token character varying(255) NOT NULL,
+ confirmed boolean DEFAULT false NOT NULL,
+ expired boolean DEFAULT false NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: email_tokens_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE email_tokens_id_seq
+ START WITH 3
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: email_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE email_tokens_id_seq OWNED BY email_tokens.id;
+
+
+--
+-- Name: facebook_user_infos; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE facebook_user_infos (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ facebook_user_id integer NOT NULL,
+ username character varying(255) NOT NULL,
+ first_name character varying(255),
+ last_name character varying(255),
+ email character varying(255),
+ gender character varying(255),
+ name character varying(255),
+ link character varying(255),
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: facebook_user_infos_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE facebook_user_infos_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: facebook_user_infos_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE facebook_user_infos_id_seq OWNED BY facebook_user_infos.id;
+
+
+--
+-- Name: incoming_links; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE incoming_links (
+ id integer NOT NULL,
+ url character varying(1000) NOT NULL,
+ referer character varying(1000) NOT NULL,
+ domain character varying(100) NOT NULL,
+ topic_id integer,
+ post_number integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: incoming_links_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE incoming_links_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: incoming_links_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE incoming_links_id_seq OWNED BY incoming_links.id;
+
+
+--
+-- Name: invites; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE invites (
+ id integer NOT NULL,
+ invite_key character varying(32) NOT NULL,
+ email character varying(255) NOT NULL,
+ invited_by_id integer NOT NULL,
+ user_id integer,
+ redeemed_at timestamp without time zone,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ deleted_at timestamp without time zone
+);
+
+
+--
+-- Name: invites_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE invites_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: invites_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE invites_id_seq OWNED BY invites.id;
+
+
+--
+-- Name: notifications; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE notifications (
+ id integer NOT NULL,
+ notification_type integer NOT NULL,
+ user_id integer NOT NULL,
+ data character varying(255) NOT NULL,
+ read boolean DEFAULT false NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ topic_id integer,
+ post_number integer,
+ post_action_id integer
+);
+
+
+--
+-- Name: notifications_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE notifications_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: notifications_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE notifications_id_seq OWNED BY notifications.id;
+
+
+--
+-- Name: onebox_renders; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE onebox_renders (
+ id integer NOT NULL,
+ url character varying(255) NOT NULL,
+ cooked text NOT NULL,
+ expires_at timestamp without time zone NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ preview text
+);
+
+
+--
+-- Name: onebox_renders_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE onebox_renders_id_seq
+ START WITH 2
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: onebox_renders_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE onebox_renders_id_seq OWNED BY onebox_renders.id;
+
+
+--
+-- Name: post_action_types; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE post_action_types (
+ name_key character varying(50) NOT NULL,
+ is_flag boolean DEFAULT false NOT NULL,
+ icon character varying(20),
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ id integer NOT NULL
+);
+
+
+--
+-- Name: post_action_types_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE post_action_types_id_seq
+ START WITH 6
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: post_action_types_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE post_action_types_id_seq OWNED BY post_action_types.id;
+
+
+--
+-- Name: post_actions; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE post_actions (
+ id integer NOT NULL,
+ post_id integer NOT NULL,
+ user_id integer NOT NULL,
+ post_action_type_id integer NOT NULL,
+ deleted_at timestamp without time zone,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: post_actions_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE post_actions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: post_actions_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE post_actions_id_seq OWNED BY post_actions.id;
+
+
+--
+-- Name: post_onebox_renders; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE post_onebox_renders (
+ post_id integer NOT NULL,
+ onebox_render_id integer NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: post_replies; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE post_replies (
+ post_id integer,
+ reply_id integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: post_timings; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE post_timings (
+ topic_id integer NOT NULL,
+ post_number integer NOT NULL,
+ user_id integer NOT NULL,
+ msecs integer NOT NULL
+);
+
+
+--
+-- Name: posts; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE posts (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ topic_id integer NOT NULL,
+ post_number integer NOT NULL,
+ raw text NOT NULL,
+ cooked text NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ reply_to_post_number integer,
+ cached_version integer DEFAULT 1 NOT NULL,
+ reply_count integer DEFAULT 0 NOT NULL,
+ quote_count integer DEFAULT 0 NOT NULL,
+ reply_below_post_number integer,
+ deleted_at timestamp without time zone,
+ off_topic_count integer DEFAULT 0 NOT NULL,
+ offensive_count integer DEFAULT 0 NOT NULL,
+ like_count integer DEFAULT 0 NOT NULL,
+ incoming_link_count integer DEFAULT 0 NOT NULL,
+ bookmark_count integer DEFAULT 0 NOT NULL,
+ avg_time integer,
+ score double precision,
+ reads integer DEFAULT 0 NOT NULL,
+ post_type integer DEFAULT 1 NOT NULL,
+ vote_count integer DEFAULT 0 NOT NULL,
+ sort_order integer,
+ last_editor_id integer
+);
+
+
+--
+-- Name: posts_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE posts_id_seq
+ START WITH 16
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: posts_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE posts_id_seq OWNED BY posts.id;
+
+
+--
+-- Name: site_customizations; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE site_customizations (
+ id integer NOT NULL,
+ name character varying(255) NOT NULL,
+ stylesheet text,
+ header text,
+ "position" integer NOT NULL,
+ user_id integer NOT NULL,
+ enabled boolean NOT NULL,
+ key character varying(255) NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ override_default_style boolean DEFAULT false NOT NULL,
+ stylesheet_baked text DEFAULT ''::text NOT NULL
+);
+
+
+--
+-- Name: site_customizations_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE site_customizations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: site_customizations_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE site_customizations_id_seq OWNED BY site_customizations.id;
+
+
+--
+-- Name: site_settings; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE site_settings (
+ id integer NOT NULL,
+ name character varying(255) NOT NULL,
+ data_type integer NOT NULL,
+ value text,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: site_settings_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE site_settings_id_seq
+ START WITH 4
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: site_settings_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE site_settings_id_seq OWNED BY site_settings.id;
+
+
+--
+-- Name: topic_allowed_users; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topic_allowed_users (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ topic_id integer NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: topic_allowed_users_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE topic_allowed_users_id_seq
+ START WITH 3
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: topic_allowed_users_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE topic_allowed_users_id_seq OWNED BY topic_allowed_users.id;
+
+
+--
+-- Name: topic_invites; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topic_invites (
+ id integer NOT NULL,
+ topic_id integer NOT NULL,
+ invite_id integer NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: topic_invites_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE topic_invites_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: topic_invites_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE topic_invites_id_seq OWNED BY topic_invites.id;
+
+
+--
+-- Name: topic_link_clicks; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topic_link_clicks (
+ id integer NOT NULL,
+ topic_link_id integer NOT NULL,
+ user_id integer,
+ ip bigint NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: topic_link_clicks_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE topic_link_clicks_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: topic_link_clicks_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE topic_link_clicks_id_seq OWNED BY topic_link_clicks.id;
+
+
+--
+-- Name: topic_links; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topic_links (
+ id integer NOT NULL,
+ topic_id integer NOT NULL,
+ post_id integer,
+ user_id integer NOT NULL,
+ url character varying(500) NOT NULL,
+ domain character varying(100) NOT NULL,
+ internal boolean DEFAULT false NOT NULL,
+ link_topic_id integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ reflection boolean DEFAULT false,
+ clicks integer DEFAULT 0 NOT NULL,
+ link_post_id integer
+);
+
+
+--
+-- Name: topic_links_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE topic_links_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: topic_links_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE topic_links_id_seq OWNED BY topic_links.id;
+
+
+--
+-- Name: topic_users; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topic_users (
+ user_id integer NOT NULL,
+ topic_id integer NOT NULL,
+ starred boolean DEFAULT false NOT NULL,
+ posted boolean DEFAULT false NOT NULL,
+ last_read_post_number integer,
+ seen_post_count integer,
+ starred_at timestamp without time zone,
+ muted_at timestamp without time zone,
+ last_visited_at timestamp without time zone,
+ first_visited_at timestamp without time zone,
+ notifications integer DEFAULT 2,
+ notifications_changed_at timestamp without time zone,
+ notifications_reason_id integer,
+ CONSTRAINT test_starred_at CHECK (((starred = false) OR (starred_at IS NOT NULL)))
+);
+
+
+--
+-- Name: topics; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE topics (
+ id integer NOT NULL,
+ title character varying(255) NOT NULL,
+ last_posted_at timestamp without time zone,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ views integer DEFAULT 0 NOT NULL,
+ posts_count integer DEFAULT 0 NOT NULL,
+ user_id integer NOT NULL,
+ last_post_user_id integer NOT NULL,
+ reply_count integer DEFAULT 0 NOT NULL,
+ featured_user1_id integer,
+ featured_user2_id integer,
+ featured_user3_id integer,
+ avg_time integer,
+ deleted_at timestamp without time zone,
+ highest_post_number integer DEFAULT 0 NOT NULL,
+ image_url character varying(255),
+ off_topic_count integer DEFAULT 0 NOT NULL,
+ offensive_count integer DEFAULT 0 NOT NULL,
+ like_count integer DEFAULT 0 NOT NULL,
+ incoming_link_count integer DEFAULT 0 NOT NULL,
+ bookmark_count integer DEFAULT 0 NOT NULL,
+ star_count integer DEFAULT 0 NOT NULL,
+ category_id integer,
+ visible boolean DEFAULT true NOT NULL,
+ moderator_posts_count integer DEFAULT 0 NOT NULL,
+ closed boolean DEFAULT false NOT NULL,
+ pinned boolean DEFAULT false NOT NULL,
+ archived boolean DEFAULT false NOT NULL,
+ bumped_at timestamp without time zone NOT NULL,
+ sub_tag character varying(255),
+ has_best_of boolean DEFAULT false NOT NULL,
+ meta_data public.hstore,
+ vote_count integer DEFAULT 0 NOT NULL,
+ archetype character varying(255) DEFAULT 'regular'::character varying NOT NULL,
+ featured_user4_id integer
+);
+
+
+--
+-- Name: topics_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE topics_id_seq
+ START WITH 16
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: topics_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE topics_id_seq OWNED BY topics.id;
+
+
+--
+-- Name: trust_levels; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE trust_levels (
+ id integer NOT NULL,
+ name_key character varying(255) NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: trust_levels_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE trust_levels_id_seq
+ START WITH 3
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: trust_levels_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE trust_levels_id_seq OWNED BY trust_levels.id;
+
+
+--
+-- Name: twitter_user_infos; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE twitter_user_infos (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ screen_name character varying(255) NOT NULL,
+ twitter_user_id integer NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: twitter_user_infos_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE twitter_user_infos_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: twitter_user_infos_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE twitter_user_infos_id_seq OWNED BY twitter_user_infos.id;
+
+
+--
+-- Name: uploads; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE uploads (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ topic_id integer NOT NULL,
+ original_filename character varying(255) NOT NULL,
+ filesize integer NOT NULL,
+ width integer,
+ height integer,
+ url character varying(255) NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: uploads_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE uploads_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: uploads_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE uploads_id_seq OWNED BY uploads.id;
+
+
+--
+-- Name: user_actions; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE user_actions (
+ id integer NOT NULL,
+ action_type integer NOT NULL,
+ user_id integer NOT NULL,
+ target_topic_id integer,
+ target_post_id integer,
+ target_user_id integer,
+ acting_user_id integer,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: user_actions_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE user_actions_id_seq
+ START WITH 40
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: user_actions_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE user_actions_id_seq OWNED BY user_actions.id;
+
+
+--
+-- Name: user_open_ids; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE user_open_ids (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ email character varying(255) NOT NULL,
+ url character varying(255) NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ active boolean NOT NULL
+);
+
+
+--
+-- Name: user_open_ids_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE user_open_ids_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: user_open_ids_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE user_open_ids_id_seq OWNED BY user_open_ids.id;
+
+
+--
+-- Name: user_visits; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE user_visits (
+ id integer NOT NULL,
+ user_id integer NOT NULL,
+ visited_at date NOT NULL
+);
+
+
+--
+-- Name: user_visits_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE user_visits_id_seq
+ START WITH 4
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: user_visits_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE user_visits_id_seq OWNED BY user_visits.id;
+
+
+--
+-- Name: users; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE users (
+ id integer NOT NULL,
+ username character varying(20) NOT NULL,
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL,
+ name character varying(255),
+ bio_raw text,
+ seen_notification_id integer DEFAULT 0 NOT NULL,
+ last_posted_at timestamp without time zone,
+ email character varying(256) NOT NULL,
+ password_hash character varying(64),
+ salt character varying(32),
+ active boolean,
+ username_lower character varying(20) NOT NULL,
+ auth_token character varying(32),
+ last_seen_at timestamp without time zone,
+ website character varying(255),
+ admin boolean DEFAULT false NOT NULL,
+ moderator boolean DEFAULT false NOT NULL,
+ last_emailed_at timestamp without time zone,
+ email_digests boolean DEFAULT true NOT NULL,
+ trust_level_id integer DEFAULT 1 NOT NULL,
+ bio_cooked text,
+ email_private_messages boolean DEFAULT true,
+ email_direct boolean DEFAULT true NOT NULL,
+ approved boolean DEFAULT false NOT NULL,
+ approved_by_id integer,
+ approved_at timestamp without time zone,
+ topics_entered integer DEFAULT 0 NOT NULL,
+ posts_read_count integer DEFAULT 0 NOT NULL,
+ digest_after_days integer DEFAULT 7 NOT NULL,
+ previous_visit_at timestamp without time zone
+);
+
+
+--
+-- Name: users_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE users_id_seq
+ START WITH 3
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE users_id_seq OWNED BY users.id;
+
+
+--
+-- Name: versions; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE versions (
+ id integer NOT NULL,
+ versioned_id integer,
+ versioned_type character varying(255),
+ user_id integer,
+ user_type character varying(255),
+ user_name character varying(255),
+ modifications text,
+ number integer,
+ reverted_from integer,
+ tag character varying(255),
+ created_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: versions_id_seq; Type: SEQUENCE; Schema: backup; Owner: -
+--
+
+CREATE SEQUENCE versions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: versions_id_seq; Type: SEQUENCE OWNED BY; Schema: backup; Owner: -
+--
+
+ALTER SEQUENCE versions_id_seq OWNED BY versions.id;
+
+
+--
+-- Name: views; Type: TABLE; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE TABLE views (
+ parent_id integer NOT NULL,
+ parent_type character varying(50) NOT NULL,
+ ip bigint NOT NULL,
+ viewed_at timestamp without time zone NOT NULL,
+ user_id integer
+);
+
+
+SET search_path = public, pg_catalog;
+
--
-- Name: categories; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
@@ -926,6 +2097,7 @@ CREATE TABLE topic_users (
notifications_changed_at timestamp without time zone,
notifications_reason_id integer,
total_msecs_viewed integer DEFAULT 0 NOT NULL,
+ cleared_pinned_at timestamp without time zone,
CONSTRAINT test_starred_at CHECK (((starred = false) OR (starred_at IS NOT NULL)))
);
@@ -961,7 +2133,6 @@ CREATE TABLE topics (
visible boolean DEFAULT true NOT NULL,
moderator_posts_count integer DEFAULT 0 NOT NULL,
closed boolean DEFAULT false NOT NULL,
- pinned boolean DEFAULT false NOT NULL,
archived boolean DEFAULT false NOT NULL,
bumped_at timestamp without time zone NOT NULL,
has_best_of boolean DEFAULT false NOT NULL,
@@ -972,7 +2143,8 @@ CREATE TABLE topics (
custom_flag_count integer DEFAULT 0 NOT NULL,
spam_count integer DEFAULT 0 NOT NULL,
illegal_count integer DEFAULT 0 NOT NULL,
- inappropriate_count integer DEFAULT 0 NOT NULL
+ inappropriate_count integer DEFAULT 0 NOT NULL,
+ pinned_at timestamp without time zone
);
@@ -1294,6 +2466,213 @@ CREATE TABLE views (
);
+SET search_path = backup, pg_catalog;
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY categories ALTER COLUMN id SET DEFAULT nextval('categories_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY category_featured_users ALTER COLUMN id SET DEFAULT nextval('category_featured_users_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY draft_sequences ALTER COLUMN id SET DEFAULT nextval('draft_sequences_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY drafts ALTER COLUMN id SET DEFAULT nextval('drafts_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY email_logs ALTER COLUMN id SET DEFAULT nextval('email_logs_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY email_tokens ALTER COLUMN id SET DEFAULT nextval('email_tokens_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY facebook_user_infos ALTER COLUMN id SET DEFAULT nextval('facebook_user_infos_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY incoming_links ALTER COLUMN id SET DEFAULT nextval('incoming_links_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY invites ALTER COLUMN id SET DEFAULT nextval('invites_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY notifications ALTER COLUMN id SET DEFAULT nextval('notifications_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY onebox_renders ALTER COLUMN id SET DEFAULT nextval('onebox_renders_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY post_action_types ALTER COLUMN id SET DEFAULT nextval('post_action_types_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY post_actions ALTER COLUMN id SET DEFAULT nextval('post_actions_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY posts ALTER COLUMN id SET DEFAULT nextval('posts_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY site_customizations ALTER COLUMN id SET DEFAULT nextval('site_customizations_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY site_settings ALTER COLUMN id SET DEFAULT nextval('site_settings_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY topic_allowed_users ALTER COLUMN id SET DEFAULT nextval('topic_allowed_users_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY topic_invites ALTER COLUMN id SET DEFAULT nextval('topic_invites_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY topic_link_clicks ALTER COLUMN id SET DEFAULT nextval('topic_link_clicks_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY topic_links ALTER COLUMN id SET DEFAULT nextval('topic_links_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY topics ALTER COLUMN id SET DEFAULT nextval('topics_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY trust_levels ALTER COLUMN id SET DEFAULT nextval('trust_levels_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY twitter_user_infos ALTER COLUMN id SET DEFAULT nextval('twitter_user_infos_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY uploads ALTER COLUMN id SET DEFAULT nextval('uploads_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY user_actions ALTER COLUMN id SET DEFAULT nextval('user_actions_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY user_open_ids ALTER COLUMN id SET DEFAULT nextval('user_open_ids_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY user_visits ALTER COLUMN id SET DEFAULT nextval('user_visits_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass);
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: backup; Owner: -
+--
+
+ALTER TABLE ONLY versions ALTER COLUMN id SET DEFAULT nextval('versions_id_seq'::regclass);
+
+
+SET search_path = public, pg_catalog;
+
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -1504,6 +2883,242 @@ ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regcl
ALTER TABLE ONLY versions ALTER COLUMN id SET DEFAULT nextval('versions_id_seq'::regclass);
+SET search_path = backup, pg_catalog;
+
+--
+-- Name: actions_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY user_actions
+ ADD CONSTRAINT actions_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: categories_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY categories
+ ADD CONSTRAINT categories_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: category_featured_users_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY category_featured_users
+ ADD CONSTRAINT category_featured_users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: draft_sequences_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY draft_sequences
+ ADD CONSTRAINT draft_sequences_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: drafts_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY drafts
+ ADD CONSTRAINT drafts_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: email_logs_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY email_logs
+ ADD CONSTRAINT email_logs_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: email_tokens_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY email_tokens
+ ADD CONSTRAINT email_tokens_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: facebook_user_infos_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY facebook_user_infos
+ ADD CONSTRAINT facebook_user_infos_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: forum_thread_link_clicks_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY topic_link_clicks
+ ADD CONSTRAINT forum_thread_link_clicks_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: forum_thread_links_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY topic_links
+ ADD CONSTRAINT forum_thread_links_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: forum_threads_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY topics
+ ADD CONSTRAINT forum_threads_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: incoming_links_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY incoming_links
+ ADD CONSTRAINT incoming_links_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: invites_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY invites
+ ADD CONSTRAINT invites_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: notifications_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY notifications
+ ADD CONSTRAINT notifications_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: onebox_renders_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY onebox_renders
+ ADD CONSTRAINT onebox_renders_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: post_action_types_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY post_action_types
+ ADD CONSTRAINT post_action_types_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: post_actions_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY post_actions
+ ADD CONSTRAINT post_actions_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: posts_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY posts
+ ADD CONSTRAINT posts_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: site_customizations_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY site_customizations
+ ADD CONSTRAINT site_customizations_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: site_settings_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY site_settings
+ ADD CONSTRAINT site_settings_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: topic_allowed_users_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY topic_allowed_users
+ ADD CONSTRAINT topic_allowed_users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: topic_invites_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY topic_invites
+ ADD CONSTRAINT topic_invites_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: trust_levels_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY trust_levels
+ ADD CONSTRAINT trust_levels_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: twitter_user_infos_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY twitter_user_infos
+ ADD CONSTRAINT twitter_user_infos_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: uploads_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY uploads
+ ADD CONSTRAINT uploads_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: user_open_ids_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY user_open_ids
+ ADD CONSTRAINT user_open_ids_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: user_visits_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY user_visits
+ ADD CONSTRAINT user_visits_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY users
+ ADD CONSTRAINT users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: versions_pkey; Type: CONSTRAINT; Schema: backup; Owner: -; Tablespace:
+--
+
+ALTER TABLE ONLY versions
+ ADD CONSTRAINT versions_pkey PRIMARY KEY (id);
+
+
+SET search_path = public, pg_catalog;
+
--
-- Name: actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@@ -1768,6 +3383,430 @@ ALTER TABLE ONLY versions
ADD CONSTRAINT versions_pkey PRIMARY KEY (id);
+SET search_path = backup, pg_catalog;
+
+--
+-- Name: cat_featured_threads; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX cat_featured_threads ON category_featured_topics USING btree (category_id, topic_id);
+
+
+--
+-- Name: idx_search_thread; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX idx_search_thread ON topics USING gin (to_tsvector('english'::regconfig, (title)::text));
+
+
+--
+-- Name: idx_search_user; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX idx_search_user ON users USING gin (to_tsvector('english'::regconfig, (username)::text));
+
+
+--
+-- Name: idx_unique_actions; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX idx_unique_actions ON post_actions USING btree (user_id, post_action_type_id, post_id) WHERE (deleted_at IS NULL);
+
+
+--
+-- Name: idx_unique_rows; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX idx_unique_rows ON user_actions USING btree (action_type, user_id, target_topic_id, target_post_id, acting_user_id);
+
+
+--
+-- Name: incoming_index; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX incoming_index ON incoming_links USING btree (topic_id, post_number);
+
+
+--
+-- Name: index_actions_on_acting_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_actions_on_acting_user_id ON user_actions USING btree (acting_user_id);
+
+
+--
+-- Name: index_actions_on_user_id_and_action_type; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_actions_on_user_id_and_action_type ON user_actions USING btree (user_id, action_type);
+
+
+--
+-- Name: index_categories_on_forum_thread_count; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_categories_on_forum_thread_count ON categories USING btree (topic_count);
+
+
+--
+-- Name: index_categories_on_name; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_categories_on_name ON categories USING btree (name);
+
+
+--
+-- Name: index_category_featured_users_on_category_id_and_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_category_featured_users_on_category_id_and_user_id ON category_featured_users USING btree (category_id, user_id);
+
+
+--
+-- Name: index_draft_sequences_on_user_id_and_draft_key; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_draft_sequences_on_user_id_and_draft_key ON draft_sequences USING btree (user_id, draft_key);
+
+
+--
+-- Name: index_drafts_on_user_id_and_draft_key; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_drafts_on_user_id_and_draft_key ON drafts USING btree (user_id, draft_key);
+
+
+--
+-- Name: index_email_logs_on_created_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_email_logs_on_created_at ON email_logs USING btree (created_at DESC);
+
+
+--
+-- Name: index_email_logs_on_user_id_and_created_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_email_logs_on_user_id_and_created_at ON email_logs USING btree (user_id, created_at DESC);
+
+
+--
+-- Name: index_email_tokens_on_token; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_email_tokens_on_token ON email_tokens USING btree (token);
+
+
+--
+-- Name: index_facebook_user_infos_on_facebook_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_facebook_user_infos_on_facebook_user_id ON facebook_user_infos USING btree (facebook_user_id);
+
+
+--
+-- Name: index_facebook_user_infos_on_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_facebook_user_infos_on_user_id ON facebook_user_infos USING btree (user_id);
+
+
+--
+-- Name: index_forum_thread_link_clicks_on_forum_thread_link_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_forum_thread_link_clicks_on_forum_thread_link_id ON topic_link_clicks USING btree (topic_link_id);
+
+
+--
+-- Name: index_forum_thread_links_on_forum_thread_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_forum_thread_links_on_forum_thread_id ON topic_links USING btree (topic_id);
+
+
+--
+-- Name: index_forum_thread_links_on_forum_thread_id_and_post_id_and_url; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_forum_thread_links_on_forum_thread_id_and_post_id_and_url ON topic_links USING btree (topic_id, post_id, url);
+
+
+--
+-- Name: index_forum_thread_users_on_forum_thread_id_and_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_forum_thread_users_on_forum_thread_id_and_user_id ON topic_users USING btree (topic_id, user_id);
+
+
+--
+-- Name: index_forum_threads_on_bumped_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_forum_threads_on_bumped_at ON topics USING btree (bumped_at DESC);
+
+
+--
+-- Name: index_forum_threads_on_category_id_and_sub_tag_and_bumped_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_forum_threads_on_category_id_and_sub_tag_and_bumped_at ON topics USING btree (category_id, sub_tag, bumped_at);
+
+
+--
+-- Name: index_invites_on_email_and_invited_by_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_invites_on_email_and_invited_by_id ON invites USING btree (email, invited_by_id);
+
+
+--
+-- Name: index_invites_on_invite_key; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_invites_on_invite_key ON invites USING btree (invite_key);
+
+
+--
+-- Name: index_notifications_on_post_action_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_notifications_on_post_action_id ON notifications USING btree (post_action_id);
+
+
+--
+-- Name: index_notifications_on_user_id_and_created_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_notifications_on_user_id_and_created_at ON notifications USING btree (user_id, created_at);
+
+
+--
+-- Name: index_onebox_renders_on_url; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_onebox_renders_on_url ON onebox_renders USING btree (url);
+
+
+--
+-- Name: index_post_actions_on_post_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_post_actions_on_post_id ON post_actions USING btree (post_id);
+
+
+--
+-- Name: index_post_onebox_renders_on_post_id_and_onebox_render_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_post_onebox_renders_on_post_id_and_onebox_render_id ON post_onebox_renders USING btree (post_id, onebox_render_id);
+
+
+--
+-- Name: index_post_replies_on_post_id_and_reply_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_post_replies_on_post_id_and_reply_id ON post_replies USING btree (post_id, reply_id);
+
+
+--
+-- Name: index_posts_on_reply_to_post_number; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_posts_on_reply_to_post_number ON posts USING btree (reply_to_post_number);
+
+
+--
+-- Name: index_posts_on_topic_id_and_post_number; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_posts_on_topic_id_and_post_number ON posts USING btree (topic_id, post_number);
+
+
+--
+-- Name: index_site_customizations_on_key; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_site_customizations_on_key ON site_customizations USING btree (key);
+
+
+--
+-- Name: index_topic_allowed_users_on_topic_id_and_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_topic_allowed_users_on_topic_id_and_user_id ON topic_allowed_users USING btree (topic_id, user_id);
+
+
+--
+-- Name: index_topic_allowed_users_on_user_id_and_topic_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_topic_allowed_users_on_user_id_and_topic_id ON topic_allowed_users USING btree (user_id, topic_id);
+
+
+--
+-- Name: index_topic_invites_on_invite_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_topic_invites_on_invite_id ON topic_invites USING btree (invite_id);
+
+
+--
+-- Name: index_topic_invites_on_topic_id_and_invite_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_topic_invites_on_topic_id_and_invite_id ON topic_invites USING btree (topic_id, invite_id);
+
+
+--
+-- Name: index_twitter_user_infos_on_twitter_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_twitter_user_infos_on_twitter_user_id ON twitter_user_infos USING btree (twitter_user_id);
+
+
+--
+-- Name: index_twitter_user_infos_on_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_twitter_user_infos_on_user_id ON twitter_user_infos USING btree (user_id);
+
+
+--
+-- Name: index_uploads_on_forum_thread_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_uploads_on_forum_thread_id ON uploads USING btree (topic_id);
+
+
+--
+-- Name: index_uploads_on_user_id; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_uploads_on_user_id ON uploads USING btree (user_id);
+
+
+--
+-- Name: index_user_open_ids_on_url; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_user_open_ids_on_url ON user_open_ids USING btree (url);
+
+
+--
+-- Name: index_user_visits_on_user_id_and_visited_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_user_visits_on_user_id_and_visited_at ON user_visits USING btree (user_id, visited_at);
+
+
+--
+-- Name: index_users_on_auth_token; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_users_on_auth_token ON users USING btree (auth_token);
+
+
+--
+-- Name: index_users_on_email; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_users_on_email ON users USING btree (email);
+
+
+--
+-- Name: index_users_on_last_posted_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_users_on_last_posted_at ON users USING btree (last_posted_at);
+
+
+--
+-- Name: index_users_on_username; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_users_on_username ON users USING btree (username);
+
+
+--
+-- Name: index_users_on_username_lower; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX index_users_on_username_lower ON users USING btree (username_lower);
+
+
+--
+-- Name: index_versions_on_created_at; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_created_at ON versions USING btree (created_at);
+
+
+--
+-- Name: index_versions_on_number; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_number ON versions USING btree (number);
+
+
+--
+-- Name: index_versions_on_tag; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_tag ON versions USING btree (tag);
+
+
+--
+-- Name: index_versions_on_user_id_and_user_type; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_user_id_and_user_type ON versions USING btree (user_id, user_type);
+
+
+--
+-- Name: index_versions_on_user_name; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_user_name ON versions USING btree (user_name);
+
+
+--
+-- Name: index_versions_on_versioned_id_and_versioned_type; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_versions_on_versioned_id_and_versioned_type ON versions USING btree (versioned_id, versioned_type);
+
+
+--
+-- Name: index_views_on_parent_id_and_parent_type; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX index_views_on_parent_id_and_parent_type ON views USING btree (parent_id, parent_type);
+
+
+--
+-- Name: post_timings_summary; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE INDEX post_timings_summary ON post_timings USING btree (topic_id, post_number);
+
+
+--
+-- Name: post_timings_unique; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX post_timings_unique ON post_timings USING btree (topic_id, post_number, user_id);
+
+
+--
+-- Name: unique_views; Type: INDEX; Schema: backup; Owner: -; Tablespace:
+--
+
+CREATE UNIQUE INDEX unique_views ON views USING btree (parent_id, parent_type, ip, viewed_at);
+
+
+SET search_path = public, pg_catalog;
+
--
-- Name: cat_featured_threads; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
@@ -2603,4 +4642,6 @@ INSERT INTO schema_migrations (version) VALUES ('20130213203300');
INSERT INTO schema_migrations (version) VALUES ('20130221215017');
-INSERT INTO schema_migrations (version) VALUES ('20130226015336');
\ No newline at end of file
+INSERT INTO schema_migrations (version) VALUES ('20130226015336');
+
+INSERT INTO schema_migrations (version) VALUES ('20130306180148');
\ No newline at end of file
diff --git a/lib/pinned_check.rb b/lib/pinned_check.rb
new file mode 100644
index 00000000000..d1890c58b37
--- /dev/null
+++ b/lib/pinned_check.rb
@@ -0,0 +1,24 @@
+# Helps us determine whether a topic should be displayed as pinned or not,
+# taking into account anonymous users and users who have dismissed it
+class PinnedCheck
+
+ def initialize(topic, topic_user=nil)
+ @topic, @topic_user = topic, topic_user
+ end
+
+ def pinned?
+
+ # If the topic isn't pinned the answer is false
+ return false if @topic.pinned_at.blank?
+
+ # If the user is anonymous or hasn't entered the topic, the value is always true
+ return true if @topic_user.blank?
+
+ # If the user hasn't cleared the pin, it's true
+ return true if @topic_user.cleared_pinned_at.blank?
+
+ # The final check is to see whether the cleared the pin before or after it was last pinned
+ @topic_user.cleared_pinned_at < @topic.pinned_at
+ end
+
+end
\ No newline at end of file
diff --git a/lib/topic_query.rb b/lib/topic_query.rb
index 468f975ee18..f6a75dcaaf6 100644
--- a/lib/topic_query.rb
+++ b/lib/topic_query.rb
@@ -6,6 +6,47 @@ require_dependency 'topic_list'
class TopicQuery
+ class << self
+ # use the constants in conjuction with COALESCE to determine the order with regard to pinned
+ # topics that have been cleared by the user. There
+ # might be a cleaner way to do this.
+ def lowest_date
+ "2010-01-01"
+ end
+
+ def highest_date
+ "3000-01-01"
+ end
+
+ # If you've clearned the pin, use bumped_at, otherwise put it at the top
+ def order_with_pinned_sql
+ "CASE
+ WHEN (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}'))
+ THEN '#{highest_date}'
+ ELSE topics.bumped_at
+ END DESC"
+ end
+
+ # If you've clearned the pin, use bumped_at, otherwise put it at the top
+ def order_nocategory_with_pinned_sql
+ "CASE
+ WHEN topics.category_id IS NULL and (COALESCE(topics.pinned_at, '#{lowest_date}') > COALESCE(tu.cleared_pinned_at, '#{lowest_date}'))
+ THEN '#{highest_date}'
+ ELSE topics.bumped_at
+ END DESC"
+ end
+
+ # For anonymous users
+ def order_nocategory_basic_bumped
+ "CASE WHEN topics.category_id IS NULL and (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC"
+ end
+
+ def order_basic_bumped
+ "CASE WHEN (topics.pinned_at IS NOT NULL) THEN 0 ELSE 1 END, topics.bumped_at DESC"
+ end
+
+ end
+
def initialize(user=nil, opts={})
@user = user
@@ -64,23 +105,19 @@ class TopicQuery
# The popular view of topics
def list_popular
- return_list(unordered: true) do |list|
- list.order('CASE WHEN topics.category_id IS NULL and topics.pinned THEN 0 ELSE 1 END, topics.bumped_at DESC')
- end
+ TopicList.new(@user, default_list)
end
# The favorited topics
def list_favorited
return_list do |list|
- list.joins("INNER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.starred AND tu.user_id = #{@user_id})")
+ list.where('tu.starred')
end
end
def list_read
return_list(unordered: true) do |list|
- list
- .joins("INNER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
- .order('COALESCE(tu.last_visited_at, topics.bumped_at) DESC')
+ list.order('COALESCE(tu.last_visited_at, topics.bumped_at) DESC')
end
end
@@ -93,17 +130,30 @@ class TopicQuery
end
def list_posted
- return_list do |list|
- list.joins("INNER JOIN topic_users AS tu ON (tu.topic_id = topics.id AND tu.posted AND tu.user_id = #{@user_id})")
- end
+ return_list {|l| l.where('tu.user_id IS NOT NULL') }
end
def list_uncategorized
- return_list {|l| l.where(category_id: nil).order('topics.pinned desc')}
+ return_list(unordered: true) do |list|
+ list = list.where(category_id: nil)
+
+ if @user_id.present?
+ list.order(TopicQuery.order_with_pinned_sql)
+ else
+ list.order(TopicQuery.order_nocategory_basic_bumped)
+ end
+ end
end
def list_category(category)
- return_list {|l| l.where(category_id: category.id).order('topics.pinned desc')}
+ return_list(unordered: true) do |list|
+ list = list.where(category_id: category.id)
+ if @user_id.present?
+ list.order(TopicQuery.order_with_pinned_sql)
+ else
+ list.order(TopicQuery.order_basic_bumped)
+ end
+ end
end
def unread_count
@@ -130,8 +180,22 @@ class TopicQuery
query_opts = @opts.merge(list_opts)
page_size = query_opts[:per_page] || SiteSetting.topics_per_page
+ # Start with a list of all topics
result = Topic
- result = result.topic_list_order unless query_opts[:unordered]
+
+ if @user_id.present?
+ result = result.joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
+ end
+
+ unless query_opts[:unordered]
+ # If we're logged in, we have to pay attention to our pinned settings
+ if @user_id.present?
+ result = result.order(TopicQuery.order_nocategory_with_pinned_sql)
+ else
+ result = result.order(TopicQuery.order_basic_bumped)
+ end
+ end
+
result = result.listable_topics.includes(:category)
result = result.where('categories.name is null or categories.name <> ?', query_opts[:exclude_category]) if query_opts[:exclude_category]
result = result.where('categories.name = ?', query_opts[:only_category]) if query_opts[:only_category]
@@ -145,16 +209,15 @@ class TopicQuery
def new_results(list_opts={})
default_list(list_opts)
- .joins("LEFT OUTER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id})")
.where("topics.created_at >= :created_at", created_at: @user.treat_as_new_topic_start_date)
.where("tu.last_read_post_number IS NULL")
- .where("COALESCE(tu.notification_level, :tracking) >= :tracking", tracking: TopicUser::NotificationLevel::TRACKING)
+ .where("COALESCE(tu.notification_level, :tracking) >= :tracking", tracking: TopicUser.notification_levels[:tracking])
end
def unread_results(list_opts={})
default_list(list_opts)
- .joins("INNER JOIN topic_users AS tu ON (topics.id = tu.topic_id AND tu.user_id = #{@user_id} AND tu.last_read_post_number < topics.highest_post_number)")
- .where("COALESCE(tu.notification_level, :regular) >= :tracking", regular: TopicUser::NotificationLevel::REGULAR, tracking: TopicUser::NotificationLevel::TRACKING)
+ .where("tu.last_read_post_number < topics.highest_post_number")
+ .where("COALESCE(tu.notification_level, :regular) >= :tracking", regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
end
def random_suggested_results_for(topic, count, exclude_topic_ids)
diff --git a/lib/unread.rb b/lib/unread.rb
index 5aed0f2516c..e41d37e4e29 100644
--- a/lib/unread.rb
+++ b/lib/unread.rb
@@ -27,7 +27,7 @@ class Unread
protected
def do_not_notify?(notification_level)
- [TopicUser::NotificationLevel::MUTED, TopicUser::NotificationLevel::REGULAR].include?(notification_level)
+ [TopicUser.notification_levels[:muted], TopicUser.notification_levels[:regular]].include?(notification_level)
end
end
diff --git a/spec/components/pinned_check_spec.rb b/spec/components/pinned_check_spec.rb
new file mode 100644
index 00000000000..2cab45ae9f6
--- /dev/null
+++ b/spec/components/pinned_check_spec.rb
@@ -0,0 +1,58 @@
+require 'pinned_check'
+
+describe PinnedCheck do
+
+ #let(:topic) { Fabricate.build(:topic) }
+
+ let(:pinned_at) { 12.hours.ago }
+ let(:unpinned_topic) { Fabricate.build(:topic) }
+ let(:pinned_topic) { Fabricate.build(:topic, pinned_at: pinned_at) }
+
+ context "without a topic_user record (either anonymous or never been in the topic)" do
+
+ it "returns false if the topic is not pinned" do
+ PinnedCheck.new(unpinned_topic).should_not be_pinned
+ end
+
+ it "returns true if the topic is pinned" do
+ PinnedCheck.new(unpinned_topic).should_not be_pinned
+ end
+
+ end
+
+ context "with a topic_user record" do
+ let(:user) { Fabricate.build(:user) }
+ let(:unpinned_topic_user) { Fabricate.build(:topic_user, user: user, topic: unpinned_topic) }
+
+ describe "unpinned topic" do
+ let(:topic_user) { TopicUser.new(topic: unpinned_topic, user: user) }
+
+ it "returns false" do
+ PinnedCheck.new(unpinned_topic, topic_user).should_not be_pinned
+ end
+
+ end
+
+ describe "pinned topic" do
+ let(:topic_user) { TopicUser.new(topic: pinned_topic, user: user) }
+
+ it "is pinned if the topic_user's cleared_pinned_at is blank" do
+ PinnedCheck.new(pinned_topic, topic_user).should be_pinned
+ end
+
+ it "is not pinned if the topic_user's cleared_pinned_at is later than when it was pinned_at" do
+ topic_user.cleared_pinned_at = (pinned_at + 1.hour)
+ PinnedCheck.new(pinned_topic, topic_user).should_not be_pinned
+ end
+
+ it "is pinned if the topic_user's cleared_pinned_at is earlier than when it was pinned_at" do
+ topic_user.cleared_pinned_at = (pinned_at - 3.hours)
+ PinnedCheck.new(pinned_topic, topic_user).should be_pinned
+ end
+ end
+
+
+ end
+
+end
+
diff --git a/spec/components/topic_query_spec.rb b/spec/components/topic_query_spec.rb
index 2f5ea43e1d7..4536cec3607 100644
--- a/spec/components/topic_query_spec.rb
+++ b/spec/components/topic_query_spec.rb
@@ -12,14 +12,13 @@ describe TopicQuery do
context 'a bunch of topics' do
let!(:regular_topic) { Fabricate(:topic, title: 'this is a regular topic', user: creator, bumped_at: 15.minutes.ago) }
- let!(:pinned_topic) { Fabricate(:topic, title: 'this is a pinned topic', user: creator, pinned: true, bumped_at: 10.minutes.ago) }
+ let!(:pinned_topic) { Fabricate(:topic, title: 'this is a pinned topic', user: creator, pinned_at: 10.minutes.ago, bumped_at: 10.minutes.ago) }
let!(:archived_topic) { Fabricate(:topic, title: 'this is an archived topic', user: creator, archived: true, bumped_at: 6.minutes.ago) }
let!(:invisible_topic) { Fabricate(:topic, title: 'this is an invisible topic', user: creator, visible: false, bumped_at: 5.minutes.ago) }
let!(:closed_topic) { Fabricate(:topic, title: 'this is a closed topic', user: creator, closed: true, bumped_at: 1.minute.ago) }
+ let(:topics) { topic_query.list_popular.topics }
context 'list_popular' do
- let(:topics) { topic_query.list_popular.topics }
-
it "returns the topics in the correct order" do
topics.should == [pinned_topic, closed_topic, archived_topic, regular_topic]
end
@@ -33,6 +32,17 @@ describe TopicQuery do
end
end
+ context 'after clearring a pinned topic' do
+ before do
+ pinned_topic.clear_pin_for(user)
+ end
+
+ it "no longer shows the pinned topic at the top" do
+ topics.should == [closed_topic, archived_topic, pinned_topic, regular_topic]
+ end
+
+ end
+
end
context 'categorized' do
diff --git a/spec/components/unread_spec.rb b/spec/components/unread_spec.rb
index c567ee9bd00..e40b9d2a2bf 100644
--- a/spec/components/unread_spec.rb
+++ b/spec/components/unread_spec.rb
@@ -7,8 +7,8 @@ describe Unread do
before do
@topic = Fabricate(:topic, posts_count: 13, highest_post_number: 13)
@topic_user = TopicUser.get(@topic, @topic.user)
- @topic_user.stubs(:notification_level).returns(TopicUser::NotificationLevel::TRACKING)
- @topic_user.notification_level = TopicUser::NotificationLevel::TRACKING
+ @topic_user.stubs(:notification_level).returns(TopicUser.notification_levels[:tracking])
+ @topic_user.notification_level = TopicUser.notification_levels[:tracking]
@unread = Unread.new(@topic, @topic_user)
end
@@ -51,7 +51,7 @@ describe Unread do
it 'has 0 new posts if the user has read 10 posts but is not tracking' do
@topic_user.stubs(:seen_post_count).returns(10)
- @topic_user.stubs(:notification_level).returns(TopicUser::NotificationLevel::REGULAR)
+ @topic_user.stubs(:notification_level).returns(TopicUser.notification_levels[:regular])
@unread.new_posts.should == 0
end
diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb
index d7a463ff29a..5c78aa7acfa 100644
--- a/spec/controllers/topics_controller_spec.rb
+++ b/spec/controllers/topics_controller_spec.rb
@@ -74,6 +74,37 @@ describe TopicsController do
end
+ context 'clear_pin' do
+ it 'needs you to be logged in' do
+ lambda { xhr :put, :clear_pin, topic_id: 1 }.should raise_error(Discourse::NotLoggedIn)
+ end
+
+ context 'when logged in' do
+ let(:topic) { Fabricate(:topic) }
+ let!(:user) { log_in }
+
+ it "fails when the user can't see the topic" do
+ Guardian.any_instance.expects(:can_see?).with(topic).returns(false)
+ xhr :put, :clear_pin, topic_id: topic.id
+ response.should_not be_success
+ end
+
+ describe 'when the user can see the topic' do
+ it "calls clear_pin_for if the user can see the topic" do
+ Topic.any_instance.expects(:clear_pin_for).with(user).once
+ xhr :put, :clear_pin, topic_id: topic.id
+ end
+
+ it "succeeds" do
+ xhr :put, :clear_pin, topic_id: topic.id
+ response.should be_success
+ end
+ end
+
+ end
+
+ end
+
context 'status' do
it 'needs you to be logged in' do
lambda { xhr :put, :status, topic_id: 1, status: 'visible', enabled: true }.should raise_error(Discourse::NotLoggedIn)
diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb
index 1991ff7124b..f8f669e0852 100644
--- a/spec/models/topic_spec.rb
+++ b/spec/models/topic_spec.rb
@@ -547,8 +547,12 @@ describe Topic do
@topic.reload
end
+ it "doesn't have a pinned_at" do
+ @topic.pinned_at.should be_blank
+ end
+
it 'should not be pinned' do
- @topic.should_not be_pinned
+ @topic.pinned_at.should be_blank
end
it 'adds a moderator post' do
@@ -562,13 +566,13 @@ describe Topic do
context 'enable' do
before do
- @topic.update_attribute :pinned, false
+ @topic.update_attribute :pinned_at, nil
@topic.update_status('pinned', true, @user)
@topic.reload
end
it 'should be pinned' do
- @topic.should be_pinned
+ @topic.pinned_at.should be_present
end
it 'adds a moderator post' do
@@ -588,7 +592,7 @@ describe Topic do
@topic.reload
end
- it 'should not be pinned' do
+ it 'should not be archived' do
@topic.should_not be_archived
end
@@ -866,8 +870,12 @@ describe Topic do
topic.should be_visible
end
+ it "has an empty pinned_at" do
+ topic.pinned_at.should be_blank
+ end
+
it 'is not pinned' do
- topic.should_not be_pinned
+ topic.pinned_at.should be_blank
end
it 'is not closed' do
diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb
index 41daf770e01..60a7e61a37e 100644
--- a/spec/models/topic_user_spec.rb
+++ b/spec/models/topic_user_spec.rb
@@ -5,155 +5,165 @@ describe TopicUser do
it { should belong_to :user }
it { should belong_to :topic }
+ let!(:yesterday) { DateTime.now.yesterday }
+
before do
- #mock time so we can test dates
- @now = DateTime.now.yesterday
- DateTime.expects(:now).at_least_once.returns(@now)
- @topic = Fabricate(:topic)
- @user = Fabricate(:coding_horror)
+ DateTime.expects(:now).at_least_once.returns(yesterday)
+ end
+
+ let!(:topic) { Fabricate(:topic) }
+ let!(:user) { Fabricate(:coding_horror) }
+ let(:topic_user) { TopicUser.get(topic,user) }
+ let(:topic_creator_user) { TopicUser.get(topic, topic.user) }
+
+ let(:post) { Fabricate(:post, topic: topic, user: user) }
+ let(:new_user) { Fabricate(:user, auto_track_topics_after_msecs: 1000) }
+ let(:topic_new_user) { TopicUser.get(topic, new_user)}
+
+
+ describe "unpinned" do
+
+ before do
+ TopicUser.change(user, topic, {:starred_at => yesterday})
+ end
+
+ it "defaults to blank" do
+ topic_user.cleared_pinned_at.should be_blank
+ end
+
end
describe 'notifications' do
it 'should be set to tracking if auto_track_topics is enabled' do
- @user.auto_track_topics_after_msecs = 0
- @user.save
- TopicUser.change(@user, @topic, {:starred_at => DateTime.now})
- TopicUser.get(@topic,@user).notification_level.should == TopicUser::NotificationLevel::TRACKING
+ user.update_column(:auto_track_topics_after_msecs, 0)
+ TopicUser.change(user, topic, {:starred_at => yesterday})
+ TopicUser.get(topic, user).notification_level.should == TopicUser.notification_levels[:tracking]
end
it 'should reset regular topics to tracking topics if auto track is changed' do
- TopicUser.change(@user, @topic, {:starred_at => DateTime.now})
- @user.auto_track_topics_after_msecs = 0
- @user.save
- TopicUser.get(@topic,@user).notification_level.should == TopicUser::NotificationLevel::TRACKING
+ TopicUser.change(user, topic, {:starred_at => yesterday})
+ user.auto_track_topics_after_msecs = 0
+ user.save
+ topic_user.notification_level.should == TopicUser.notification_levels[:tracking]
end
it 'should be set to "regular" notifications, by default on non creators' do
- TopicUser.change(@user, @topic, {:starred_at => DateTime.now})
- TopicUser.get(@topic,@user).notification_level.should == TopicUser::NotificationLevel::REGULAR
+ TopicUser.change(user, topic, {:starred_at => yesterday})
+ TopicUser.get(topic,user).notification_level.should == TopicUser.notification_levels[:regular]
end
it 'reason should reset when changed' do
- @topic.notify_muted!(@topic.user)
- TopicUser.get(@topic,@topic.user).notifications_reason_id.should == TopicUser::NotificationReasons::USER_CHANGED
+ topic.notify_muted!(topic.user)
+ TopicUser.get(topic,topic.user).notifications_reason_id.should == TopicUser.notification_reasons[:user_changed]
end
it 'should have the correct reason for a user change when watched' do
- @topic.notify_watch!(@user)
- tu = TopicUser.get(@topic,@user)
- tu.notification_level.should == TopicUser::NotificationLevel::WATCHING
- tu.notifications_reason_id.should == TopicUser::NotificationReasons::USER_CHANGED
- tu.notifications_changed_at.should_not be_nil
+ topic.notify_watch!(user)
+ topic_user.notification_level.should == TopicUser.notification_levels[:watching]
+ topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed]
+ topic_user.notifications_changed_at.should_not be_nil
end
it 'should have the correct reason for a user change when set to regular' do
- @topic.notify_regular!(@user)
- tu = TopicUser.get(@topic,@user)
- tu.notification_level.should == TopicUser::NotificationLevel::REGULAR
- tu.notifications_reason_id.should == TopicUser::NotificationReasons::USER_CHANGED
- tu.notifications_changed_at.should_not be_nil
+ topic.notify_regular!(user)
+ topic_user.notification_level.should == TopicUser.notification_levels[:regular]
+ topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed]
+ topic_user.notifications_changed_at.should_not be_nil
end
it 'should have the correct reason for a user change when set to regular' do
- @topic.notify_muted!(@user)
- tu = TopicUser.get(@topic,@user)
- tu.notification_level.should == TopicUser::NotificationLevel::MUTED
- tu.notifications_reason_id.should == TopicUser::NotificationReasons::USER_CHANGED
- tu.notifications_changed_at.should_not be_nil
+ topic.notify_muted!(user)
+ topic_user.notification_level.should == TopicUser.notification_levels[:muted]
+ topic_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed]
+ topic_user.notifications_changed_at.should_not be_nil
end
it 'should watch topics a user created' do
- tu = TopicUser.get(@topic,@topic.user)
- tu.notification_level.should == TopicUser::NotificationLevel::WATCHING
- tu.notifications_reason_id.should == TopicUser::NotificationReasons::CREATED_TOPIC
+ topic_creator_user.notification_level.should == TopicUser.notification_levels[:watching]
+ topic_creator_user.notifications_reason_id.should == TopicUser.notification_reasons[:created_topic]
end
end
describe 'visited at' do
- before do
- TopicUser.track_visit!(@topic, @user)
- @topic_user = TopicUser.get(@topic,@user)
+ before do
+ TopicUser.track_visit!(topic, user)
end
it 'set upon initial visit' do
- @topic_user.first_visited_at.to_i.should == @now.to_i
- @topic_user.last_visited_at.to_i.should == @now.to_i
+ topic_user.first_visited_at.to_i.should == yesterday.to_i
+ topic_user.last_visited_at.to_i.should == yesterday.to_i
end
it 'updates upon repeat visit' do
- tomorrow = @now.tomorrow
- DateTime.expects(:now).returns(tomorrow)
+ today = yesterday.tomorrow
+ DateTime.expects(:now).returns(today)
- TopicUser.track_visit!(@topic,@user)
+ TopicUser.track_visit!(topic,user)
# reload is a no go
- @topic_user = TopicUser.get(@topic,@user)
- @topic_user.first_visited_at.to_i.should == @now.to_i
- @topic_user.last_visited_at.to_i.should == tomorrow.to_i
+ topic_user = TopicUser.get(topic,user)
+ topic_user.first_visited_at.to_i.should == yesterday.to_i
+ topic_user.last_visited_at.to_i.should == today.to_i
end
end
describe 'read tracking' do
- before do
- @post = Fabricate(:post, topic: @topic, user: @topic.user)
- TopicUser.update_last_read(@user, @topic.id, 1, 0)
- @topic_user = TopicUser.get(@topic,@user)
- end
- it 'should create a new record for a visit' do
- @topic_user.last_read_post_number.should == 1
- @topic_user.last_visited_at.to_i.should == @now.to_i
- @topic_user.first_visited_at.to_i.should == @now.to_i
- end
+ context "without auto tracking" do
- it 'should update the record for repeat visit' do
- Fabricate(:post, topic: @topic, user: @user)
- TopicUser.update_last_read(@user, @topic.id, 2, 0)
- @topic_user = TopicUser.get(@topic,@user)
- @topic_user.last_read_post_number.should == 2
- @topic_user.last_visited_at.to_i.should == @now.to_i
- @topic_user.first_visited_at.to_i.should == @now.to_i
+ before do
+ TopicUser.update_last_read(user, topic.id, 1, 0)
+ end
+
+ let(:topic_user) { TopicUser.get(topic,user) }
+
+ it 'should create a new record for a visit' do
+ topic_user.last_read_post_number.should == 1
+ topic_user.last_visited_at.to_i.should == yesterday.to_i
+ topic_user.first_visited_at.to_i.should == yesterday.to_i
+ end
+
+ it 'should update the record for repeat visit' do
+ Fabricate(:post, topic: topic, user: user)
+ TopicUser.update_last_read(user, topic.id, 2, 0)
+ topic_user = TopicUser.get(topic,user)
+ topic_user.last_read_post_number.should == 2
+ topic_user.last_visited_at.to_i.should == yesterday.to_i
+ topic_user.first_visited_at.to_i.should == yesterday.to_i
+ end
end
context 'auto tracking' do
+
before do
- Fabricate(:post, topic: @topic, user: @user)
- @new_user = Fabricate(:user, auto_track_topics_after_msecs: 1000)
- TopicUser.update_last_read(@new_user, @topic.id, 2, 0)
- @topic_user = TopicUser.get(@topic,@new_user)
+ TopicUser.update_last_read(new_user, topic.id, 2, 0)
end
it 'should automatically track topics you reply to' do
- post = Fabricate(:post, topic: @topic, user: @new_user)
- @topic_user = TopicUser.get(@topic,@new_user)
- @topic_user.notification_level.should == TopicUser::NotificationLevel::TRACKING
- @topic_user.notifications_reason_id.should == TopicUser::NotificationReasons::CREATED_POST
+ post = Fabricate(:post, topic: topic, user: new_user)
+ topic_new_user.notification_level.should == TopicUser.notification_levels[:tracking]
+ topic_new_user.notifications_reason_id.should == TopicUser.notification_reasons[:created_post]
end
it 'should not automatically track topics you reply to and have set state manually' do
- Fabricate(:post, topic: @topic, user: @new_user)
- TopicUser.change(@new_user, @topic, notification_level: TopicUser::NotificationLevel::REGULAR)
- @topic_user = TopicUser.get(@topic,@new_user)
- @topic_user.notification_level.should == TopicUser::NotificationLevel::REGULAR
- @topic_user.notifications_reason_id.should == TopicUser::NotificationReasons::USER_CHANGED
+ Fabricate(:post, topic: topic, user: new_user)
+ TopicUser.change(new_user, topic, notification_level: TopicUser.notification_levels[:regular])
+ topic_new_user.notification_level.should == TopicUser.notification_levels[:regular]
+ topic_new_user.notifications_reason_id.should == TopicUser.notification_reasons[:user_changed]
end
it 'should automatically track topics after they are read for long enough' do
- @topic_user.notification_level.should == TopicUser::NotificationLevel::REGULAR
- TopicUser.update_last_read(@new_user, @topic.id, 2, 1001)
- @topic_user = TopicUser.get(@topic,@new_user)
- @topic_user.notification_level.should == TopicUser::NotificationLevel::TRACKING
+ topic_new_user.notification_level.should ==TopicUser.notification_levels[:regular]
+ TopicUser.update_last_read(new_user, topic.id, 2, 1001)
+ TopicUser.get(topic, new_user).notification_level.should == TopicUser.notification_levels[:tracking]
end
it 'should not automatically track topics after they are read for long enough if changed manually' do
- TopicUser.change(@new_user, @topic, notification_level: TopicUser::NotificationLevel::REGULAR)
- @topic_user = TopicUser.get(@topic,@new_user)
-
- TopicUser.update_last_read(@new_user, @topic, 2, 1001)
- @topic_user = TopicUser.get(@topic,@new_user)
- @topic_user.notification_level.should == TopicUser::NotificationLevel::REGULAR
+ TopicUser.change(new_user, topic, notification_level: TopicUser.notification_levels[:regular])
+ TopicUser.update_last_read(new_user, topic, 2, 1001)
+ topic_new_user.notification_level.should == TopicUser.notification_levels[:regular]
end
end
end
@@ -162,34 +172,33 @@ describe TopicUser do
it 'creates a forum topic user record' do
lambda {
- TopicUser.change(@user, @topic.id, starred: true)
+ TopicUser.change(user, topic.id, starred: true)
}.should change(TopicUser, :count).by(1)
end
it "only inserts a row once, even on repeated calls" do
lambda {
- TopicUser.change(@user, @topic.id, starred: true)
- TopicUser.change(@user, @topic.id, starred: false)
- TopicUser.change(@user, @topic.id, starred: true)
+ TopicUser.change(user, topic.id, starred: true)
+ TopicUser.change(user, topic.id, starred: false)
+ TopicUser.change(user, topic.id, starred: true)
}.should change(TopicUser, :count).by(1)
end
describe 'after creating a row' do
before do
- TopicUser.change(@user, @topic.id, starred: true)
- @topic_user = TopicUser.where(user_id: @user.id, topic_id: @topic.id).first
+ TopicUser.change(user, topic.id, starred: true)
end
it 'has the correct starred value' do
- @topic_user.should be_starred
+ TopicUser.get(topic, user).should be_starred
end
it 'has a lookup' do
- TopicUser.lookup_for(@user, [@topic]).should be_present
+ TopicUser.lookup_for(user, [topic]).should be_present
end
it 'has a key in the lookup for this forum topic' do
- TopicUser.lookup_for(@user, [@topic]).has_key?(@topic.id).should be_true
+ TopicUser.lookup_for(user, [topic]).has_key?(topic.id).should be_true
end
end