discourse/app/models/bookmark.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

291 lines
10 KiB
Ruby
Raw Normal View History

Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
# frozen_string_literal: true
class Bookmark < ActiveRecord::Base
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
cattr_accessor :registered_bookmarkables
self.registered_bookmarkables = []
def self.registered_bookmarkable_from_type(type)
Bookmark.registered_bookmarkables.find { |bm| bm.model.name == type }
end
def self.register_bookmarkable(bookmarkable_klass)
return if Bookmark.registered_bookmarkable_from_type(bookmarkable_klass.model.name).present?
Bookmark.registered_bookmarkables << RegisteredBookmarkable.new(bookmarkable_klass)
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
end
##
# This is called when the app loads, similar to AdminDashboardData.reset_problem_checks,
# so the default Post and Topic bookmarkables are registered on
# boot.
#
# This method also can be used in testing to reset bookmarkables between
# tests. It will also fire multiple times in development mode because
# classes are not cached.
def self.reset_bookmarkables
self.registered_bookmarkables = []
Bookmark.register_bookmarkable(PostBookmarkable)
Bookmark.register_bookmarkable(TopicBookmarkable)
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
end
reset_bookmarkables
def self.valid_bookmarkable_types
Bookmark.registered_bookmarkables.map(&:model).map(&:to_s)
end
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
belongs_to :user
belongs_to :post
has_one :topic, through: :post
belongs_to :bookmarkable, polymorphic: true
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
def topic_id
return if SiteSetting.use_polymorphic_bookmarks
post.topic_id
end
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
def self.auto_delete_preferences
@auto_delete_preferences ||= Enum.new(
never: 0,
when_reminder_sent: 1,
on_owner_reply: 2,
clear_reminder: 3,
)
end
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
def self.select_type(bookmarks_relation, type)
bookmarks_relation.select { |bm| bm.bookmarkable_type == type }
end
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
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? }
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
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 :polymorphic_columns_present, on: [:create, :update]
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
validate :valid_bookmarkable_type, on: [:create, :update]
validate :unique_per_bookmarkable,
on: [:create, :update],
if: Proc.new { |b|
b.will_save_change_to_bookmarkable_id? || b.will_save_change_to_bookmarkable_type? || b.will_save_change_to_user_id?
}
validate :ensure_sane_reminder_at_time, if: :will_save_change_to_reminder_at?
validate :bookmark_limit_not_reached
validates :name, length: { maximum: 100 }
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
def registered_bookmarkable
Bookmark.registered_bookmarkable_from_type(self.bookmarkable_type)
end
def polymorphic_columns_present
return if !SiteSetting.use_polymorphic_bookmarks
return if self.bookmarkable_id.present? && self.bookmarkable_type.present?
self.errors.add(:base, I18n.t("bookmarks.errors.bookmarkable_id_type_required"))
end
def unique_per_bookmarkable
return if !SiteSetting.use_polymorphic_bookmarks
return if !Bookmark.exists?(user_id: user_id, bookmarkable_id: bookmarkable_id, bookmarkable_type: bookmarkable_type)
self.errors.add(:base, I18n.t("bookmarks.errors.already_bookmarked", type: bookmarkable_type))
end
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
def unique_per_post_for_user
return if SiteSetting.use_polymorphic_bookmarks
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
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
end
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
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
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
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
FEATURE: Polymorphic bookmarks pt. 2 (lists, search) (#16335) This pull request follows on from https://github.com/discourse/discourse/pull/16308. This one does the following: * Changes `BookmarkQuery` to allow for querying more than just Post and Topic bookmarkables * Introduces a `Bookmark.register_bookmarkable` method which requires a model, serializer, fields and preload includes for searching. These registered `Bookmarkable` types are then used when validating new bookmarks, and also when determining which serializer to use for the bookmark list. The `Post` and `Topic` bookmarkables are registered by default. * Adds new specific types for Post and Topic bookmark serializers along with preloading of associations in `UserBookmarkList` * Changes to the user bookmark list template to allow for more generic bookmarkable types alongside the Post and Topic ones which need to display in a particular way All of these changes are gated behind the `use_polymorphic_bookmarks` site setting, apart from the .hbs changes where I have updated the original `UserBookmarkSerializer` with some stub methods. Following this PR will be several plugin PRs (for assign, chat, encrypt) that will register their own bookmarkable types or otherwise alter the bookmark serializers in their own way, also gated behind `use_polymorphic_bookmarks`. This commit also removes `BookmarkQuery.preloaded_custom_fields` and the functionality surrounding it. It was added in https://github.com/discourse/discourse/commit/0cd502a55838d5d27f96f13c0794f3669ac41fcc but only used by one plugin (discourse-assign) where it has since been removed, and is now used by no plugins. We don't need it anymore.
2022-04-21 18:23:42 -04:00
def valid_bookmarkable_type
return if !SiteSetting.use_polymorphic_bookmarks
return if Bookmark.valid_bookmarkable_types.include?(self.bookmarkable_type)
self.errors.add(:base, I18n.t("bookmarks.errors.invalid_bookmarkable", type: self.bookmarkable_type))
end
# TODO (martin) [POLYBOOK] Not relevant once polymorphic bookmarks are implemented.
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 auto_delete_when_reminder_sent?
self.auto_delete_preference == Bookmark.auto_delete_preferences[:when_reminder_sent]
end
# TODO (martin) [POLYBOOK] This is only relevant for post/topic bookmarkables, need to
# think of a way to do this gracefully.
def auto_delete_on_owner_reply?
self.auto_delete_preference == Bookmark.auto_delete_preferences[:on_owner_reply]
end
def auto_clear_reminder_when_reminder_sent?
self.auto_delete_preference == Bookmark.auto_delete_preferences[:clear_reminder]
end
def reminder_at_ics(offset: 0)
(reminder_at + offset).strftime(I18n.t("datetime_formats.formats.calendar_ics"))
end
def clear_reminder!
update!(
reminder_last_sent_at: Time.zone.now,
reminder_set_at: nil,
)
end
scope :with_reminders, -> do
where("reminder_at IS NOT NULL")
end
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
scope :pending_reminders, ->(before_time = Time.now.utc) do
with_reminders.where("reminder_at <= ?", before_time).where(reminder_last_sent_at: nil)
FEATURE: Send notifications for time-based and At Desktop bookmark reminders (#9071) * This PR implements the scheduling and notification system for bookmark reminders. Every 5 minutes a schedule runs to check any reminders that need to be sent before now, limited to **300** reminders at a time. Any leftover reminders will be sent in the next run. This is to avoid having to deal with fickle sidekiq and reminders in the far-flung future, which would necessitate having a background job anyway to clean up any missing `enqueue_at` reminders. * If a reminder is sent its `reminder_at` time is cleared and the `reminder_last_sent_at` time is filled in. Notifications are only user-level notifications for now. * All JavaScript and frontend code related to displaying the bookmark reminder notification is contained here. The reminder functionality is now re-enabled in the bookmark modal as well. * This PR also implements the "Remind me next time I am at my desktop" bookmark reminder functionality. When the user is on a mobile device they are able to select this option. When they choose this option we set a key in Redis saying they have a pending at desktop reminder. The next time they change devices we check if the new device is desktop, and if it is we send reminders using a DistributedMutex. There is also a job to ensure consistency of these reminders in Redis (in case Redis drops the ball) and the at desktop reminders expire after 20 days. * Also in this PR is a fix to delete all Bookmarks for a user via `UserDestroyer`
2020-03-11 20:16:00 -04:00
end
scope :pending_reminders_for_user, ->(user) do
pending_reminders.where(user: user)
end
scope :for_user_in_topic, ->(user_id, topic_id) {
if SiteSetting.use_polymorphic_bookmarks
joins("LEFT JOIN posts ON posts.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Post'")
.joins("LEFT JOIN topics ON topics.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Topic'")
.where(
"bookmarks.user_id = :user_id AND (topics.id = :topic_id OR posts.topic_id = :topic_id)",
user_id: user_id, topic_id: topic_id
)
else
joins(:post).where(user_id: user_id, posts: { topic_id: topic_id })
end
}
def self.find_for_topic_by_user(topic_id, user_id)
if SiteSetting.use_polymorphic_bookmarks
find_by(user_id: user_id, bookmarkable_id: topic_id, bookmarkable_type: "Topic")
else
for_user_in_topic(user_id, topic_id).where(for_topic: true).first
end
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]
if SiteSetting.use_polymorphic_bookmarks
result = result
.joins("LEFT JOIN posts ON posts.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Post'")
.joins("LEFT JOIN topics ON (topics.id = bookmarks.bookmarkable_id AND bookmarks.bookmarkable_type = 'Topic') OR (topics.id = posts.topic_id)")
.where("topics.deleted_at IS NULL AND posts.deleted_at IS NULL")
.merge(Topic.in_category_and_subcategories(opts[:category_id]))
else
result = result.joins(:topic).merge(Topic.in_category_and_subcategories(opts[:category_id]))
end
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
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
end
# == Schema Information
#
# Table name: bookmarks
#
2020-07-22 06:32:31 -04:00
# id :bigint not null, primary key
# user_id :bigint not null
# post_id :bigint
2020-07-22 06:32:31 -04:00
# 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
DEV: Re-add polymorphic bookmark columns (#16261) This commit is a redo of2f1ddadff7dd47f824070c8a3f633f00a27aacde which we reverted because it blew up an internal CI check. I looked into it, and it happened because the old migration to add the bookmark columns still existed, and those columns were dropped in a post migrate, so the two migrations to add the columns were conflicting before the post migrate was run. ------ This commit only includes the creation of the new columns and index, and does not add any triggers, backfilling, or new data. A backfill will be done in the final PR when we switch this over. Intermediate PRs will look something like this: Add an experimental site setting for using polymorphic bookmarks, and make sure in the places where bookmarks are created or updated we fill in the columns. This setting will be used in subsequent PRs as well. Listing and searching bookmarks based on polymorphic associations Creating post and topic bookmarks using polymorphic associations, and changing special for_topic logic to just rely on the Topic bookmarkable_type Querying bookmark reminders based on polymorphic associations Make sure various other areas like importers, bookmark guardian, and others all rely on the associations Prepare plugins that rely on the Bookmark model to use polymorphic associations The final core PR will remove all the setting gates and switch over to using the polymorphic associations, backfill the bookmarks table columns, and ignore the old post_id and for_topic colummns. Then it will just be a matter of dropping the old columns down the line.
2022-03-22 22:43:08 -04:00
# bookmarkable_id :integer
# bookmarkable_type :string
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
#
# Indexes
#
DEV: Re-add polymorphic bookmark columns (#16261) This commit is a redo of2f1ddadff7dd47f824070c8a3f633f00a27aacde which we reverted because it blew up an internal CI check. I looked into it, and it happened because the old migration to add the bookmark columns still existed, and those columns were dropped in a post migrate, so the two migrations to add the columns were conflicting before the post migrate was run. ------ This commit only includes the creation of the new columns and index, and does not add any triggers, backfilling, or new data. A backfill will be done in the final PR when we switch this over. Intermediate PRs will look something like this: Add an experimental site setting for using polymorphic bookmarks, and make sure in the places where bookmarks are created or updated we fill in the columns. This setting will be used in subsequent PRs as well. Listing and searching bookmarks based on polymorphic associations Creating post and topic bookmarks using polymorphic associations, and changing special for_topic logic to just rely on the Topic bookmarkable_type Querying bookmark reminders based on polymorphic associations Make sure various other areas like importers, bookmark guardian, and others all rely on the associations Prepare plugins that rely on the Bookmark model to use polymorphic associations The final core PR will remove all the setting gates and switch over to using the polymorphic associations, backfill the bookmarks table columns, and ignore the old post_id and for_topic colummns. Then it will just be a matter of dropping the old columns down the line.
2022-03-22 22:43:08 -04:00
# idx_bookmarks_user_polymorphic_unique (user_id,bookmarkable_type,bookmarkable_id) UNIQUE
# 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_user_id (user_id)
# index_bookmarks_on_user_id_and_post_id_and_for_topic (user_id,post_id,for_topic) UNIQUE
Improving bookmarks part 1 (#8466) Note: All of this functionality is hidden behind a hidden, default false, site setting called `enable_bookmarks_with_reminders`. Also, any feedback on Ember code would be greatly appreciated! This is part 1 of the bookmark improvements. The next PR will address the backend logic to send reminder notifications for bookmarked posts to users. This PR adds the following functionality: * We are adding a new `bookmarks` table and `Bookmark` model to make the bookmarks a first-class citizen and to allow attaching reminders to them. * Posts now have a new button in their actions menu that has the icon of an actual book * Clicking the button opens the new bookmark modal. * Both name and the reminder type are optional. * If you close the modal without doing anything, the bookmark is saved with no reminder. * If you click the Cancel button, no bookmark is saved at all. * All of the reminder type tiles are dynamic and the times they show will be based on your user timezone set in your profile (this should already be set for you). * If for some reason a user does not have their timezone set they will not be able to set a reminder, but they will still be able to create a bookmark. * A bookmark can be deleted by clicking on the book icon again which will be red if the post is bookmarked. This PR does NOT do anything to migrate or change existing bookmarks in the form of `PostActions`, the two features live side-by-side here. Also this does nothing to the topic bookmarking.
2019-12-10 23:04:02 -05:00
#