mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 10:20:58 -06:00
0c42a1e5f3
Allows creating a bookmark with the `for_topic` flag introduced ind1d2298a4c
set to true. This happens when clicking on the Bookmark button in the topic footer when no other posts are bookmarked. In a later PR, when clicking on these topic-level bookmarks the user will be taken to the last unread post in the topic, not the OP. Only the OP can have a topic level bookmark, and users can also make a post-level bookmark on the OP of the topic. I had to do some pretty heavy refactors because most of the bookmark code in the JS topics controller was centred around instances of Post JS models, but the topic level bookmark is not centred around a post. Some refactors were just for readability as well. Also removes some missed reminderType code from the purge in41e19adb0d
203 lines
6.0 KiB
Ruby
203 lines
6.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Bookmark < ActiveRecord::Base
|
|
self.ignored_columns = [
|
|
"topic_id", # TODO (martin) (2021-12-01): remove
|
|
"reminder_type" # TODO (martin) (2021-12-01): remove
|
|
]
|
|
|
|
belongs_to :user
|
|
belongs_to :post
|
|
has_one :topic, through: :post
|
|
|
|
delegate :topic_id, to: :post
|
|
|
|
def self.auto_delete_preferences
|
|
@auto_delete_preferences ||= Enum.new(
|
|
never: 0,
|
|
when_reminder_sent: 1,
|
|
on_owner_reply: 2
|
|
)
|
|
end
|
|
|
|
# TODO (martin) (2021-12-01) Remove this once plugins are not using it.
|
|
def self.reminder_types
|
|
@reminder_types ||= Enum.new(
|
|
later_today: 1,
|
|
next_business_day: 2,
|
|
tomorrow: 3,
|
|
next_week: 4,
|
|
next_month: 5,
|
|
custom: 6,
|
|
start_of_next_business_week: 7,
|
|
later_this_week: 8
|
|
)
|
|
end
|
|
|
|
validate :unique_per_post_for_user,
|
|
on: [:create, :update],
|
|
if: Proc.new { |b| b.will_save_change_to_post_id? || b.will_save_change_to_user_id? }
|
|
|
|
validate :for_topic_must_use_first_post,
|
|
on: [:create, :update],
|
|
if: Proc.new { |b| b.will_save_change_to_post_id? || b.will_save_change_to_for_topic? }
|
|
|
|
validate :ensure_sane_reminder_at_time
|
|
validate :bookmark_limit_not_reached
|
|
validates :name, length: { maximum: 100 }
|
|
|
|
def unique_per_post_for_user
|
|
exists = if is_for_first_post?
|
|
Bookmark.exists?(user_id: user_id, post_id: post_id, for_topic: for_topic)
|
|
else
|
|
Bookmark.exists?(user_id: user_id, post_id: post_id)
|
|
end
|
|
|
|
if exists
|
|
self.errors.add(:base, I18n.t("bookmarks.errors.already_bookmarked_post"))
|
|
end
|
|
end
|
|
|
|
def for_topic_must_use_first_post
|
|
if !is_for_first_post? && self.for_topic
|
|
self.errors.add(:base, I18n.t("bookmarks.errors.for_topic_must_use_first_post"))
|
|
end
|
|
end
|
|
|
|
def ensure_sane_reminder_at_time
|
|
return if reminder_at.blank?
|
|
if reminder_at < Time.zone.now
|
|
self.errors.add(:base, I18n.t("bookmarks.errors.cannot_set_past_reminder"))
|
|
end
|
|
if reminder_at > 10.years.from_now.utc
|
|
self.errors.add(:base, I18n.t("bookmarks.errors.cannot_set_reminder_in_distant_future"))
|
|
end
|
|
end
|
|
|
|
def bookmark_limit_not_reached
|
|
return if user.bookmarks.count < SiteSetting.max_bookmarks_per_user
|
|
return if !new_record?
|
|
|
|
self.errors.add(
|
|
:base,
|
|
I18n.t(
|
|
"bookmarks.errors.too_many",
|
|
user_bookmarks_url: "#{Discourse.base_url}/my/activity/bookmarks",
|
|
limit: SiteSetting.max_bookmarks_per_user
|
|
)
|
|
)
|
|
end
|
|
|
|
def is_for_first_post?
|
|
@is_for_first_post ||= new_record? ? Post.exists?(id: post_id, post_number: 1) : post.post_number == 1
|
|
end
|
|
|
|
def no_reminder?
|
|
self.reminder_at.blank?
|
|
end
|
|
|
|
def auto_delete_when_reminder_sent?
|
|
self.auto_delete_preference == Bookmark.auto_delete_preferences[:when_reminder_sent]
|
|
end
|
|
|
|
def auto_delete_on_owner_reply?
|
|
self.auto_delete_preference == Bookmark.auto_delete_preferences[:on_owner_reply]
|
|
end
|
|
|
|
def reminder_at_ics(offset: 0)
|
|
(reminder_at + offset).strftime(I18n.t("datetime_formats.formats.calendar_ics"))
|
|
end
|
|
|
|
def clear_reminder!
|
|
update!(
|
|
reminder_at: nil,
|
|
reminder_last_sent_at: Time.zone.now,
|
|
reminder_set_at: nil
|
|
)
|
|
end
|
|
|
|
scope :with_reminders, -> do
|
|
where("reminder_at IS NOT NULL")
|
|
end
|
|
|
|
scope :pending_reminders, ->(before_time = Time.now.utc) do
|
|
with_reminders.where("reminder_at <= :before_time", before_time: before_time)
|
|
end
|
|
|
|
scope :pending_reminders_for_user, ->(user) do
|
|
pending_reminders.where(user: user)
|
|
end
|
|
|
|
scope :for_user_in_topic, ->(user_id, topic_id) {
|
|
joins(:post).where(user_id: user_id, posts: { topic_id: topic_id })
|
|
}
|
|
|
|
def self.find_for_topic_by_user(topic_id, user_id)
|
|
for_user_in_topic(user_id, topic_id).where(for_topic: true).first
|
|
end
|
|
|
|
def self.count_per_day(opts = nil)
|
|
opts ||= {}
|
|
result = where('bookmarks.created_at >= ?', opts[:start_date] || (opts[:since_days_ago] || 30).days.ago)
|
|
|
|
if opts[:end_date]
|
|
result = result.where('bookmarks.created_at <= ?', opts[:end_date])
|
|
end
|
|
|
|
if opts[:category_id]
|
|
result = result.joins(:topic).merge(Topic.in_category_and_subcategories(opts[:category_id]))
|
|
end
|
|
|
|
result.group('date(bookmarks.created_at)')
|
|
.order('date(bookmarks.created_at)')
|
|
.count
|
|
end
|
|
|
|
##
|
|
# Deletes bookmarks that are attached to posts/topics that were deleted
|
|
# more than X days ago. We don't delete bookmarks instantly when a post/topic
|
|
# is deleted so that there is a grace period to un-delete.
|
|
def self.cleanup!
|
|
grace_time = 3.days.ago
|
|
topics_deleted = DB.query(<<~SQL, grace_time: grace_time)
|
|
DELETE FROM bookmarks b
|
|
USING topics t, posts p
|
|
WHERE (t.id = p.topic_id AND b.post_id = p.id)
|
|
AND (t.deleted_at < :grace_time OR p.deleted_at < :grace_time)
|
|
RETURNING t.id AS topic_id
|
|
SQL
|
|
|
|
topics_deleted_ids = topics_deleted.map(&:topic_id).uniq
|
|
topics_deleted_ids.each do |topic_id|
|
|
Jobs.enqueue(:sync_topic_user_bookmarked, topic_id: topic_id)
|
|
end
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: bookmarks
|
|
#
|
|
# id :bigint not null, primary key
|
|
# user_id :bigint not null
|
|
# post_id :bigint not null
|
|
# name :string(100)
|
|
# reminder_at :datetime
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# reminder_last_sent_at :datetime
|
|
# reminder_set_at :datetime
|
|
# auto_delete_preference :integer default(0), not null
|
|
# pinned :boolean default(FALSE)
|
|
# for_topic :boolean default(FALSE), not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_bookmarks_on_post_id (post_id)
|
|
# index_bookmarks_on_reminder_at (reminder_at)
|
|
# index_bookmarks_on_reminder_set_at (reminder_set_at)
|
|
# index_bookmarks_on_topic_id (topic_id)
|
|
# index_bookmarks_on_user_id (user_id)
|
|
# index_bookmarks_on_user_id_and_post_id_and_for_topic (user_id,post_id,for_topic) UNIQUE
|
|
#
|