otwarchive-symphonyarchive/app/controllers/bookmarks_controller.rb

398 lines
14 KiB
Ruby
Raw Permalink Normal View History

2026-03-11 22:22:11 +00:00
class BookmarksController < ApplicationController
before_action :load_collection
before_action :load_owner, only: [:index]
before_action :load_bookmarkable, only: [:index, :new, :create]
before_action :users_only, only: [:new, :create, :edit, :update]
before_action :check_user_status, only: [:new, :create, :edit, :update]
before_action :check_parent_visible, only: [:index, :new]
before_action :load_bookmark, only: [:show, :edit, :update, :destroy, :confirm_delete, :share]
before_action :check_visibility, only: [:show, :share]
before_action :check_ownership, only: [:edit, :update, :destroy, :confirm_delete, :share]
before_action :check_pseud_ownership, only: [:create, :update]
skip_before_action :store_location, only: [:share]
def check_pseud_ownership
if params[:bookmark][:pseud_id]
pseud = Pseud.find(bookmark_params[:pseud_id])
unless pseud && current_user && current_user.pseuds.include?(pseud)
flash[:error] = ts("You can't bookmark with that pseud.")
redirect_to root_path and return
end
end
end
def check_parent_visible
check_visibility_for(@bookmarkable)
end
# get the parent
def load_bookmarkable
if params[:work_id]
@bookmarkable = Work.find(params[:work_id])
elsif params[:external_work_id]
@bookmarkable = ExternalWork.find(params[:external_work_id])
elsif params[:series_id]
@bookmarkable = Series.find(params[:series_id])
end
end
def load_bookmark
@bookmark = Bookmark.find(params[:id])
@check_ownership_of = @bookmark
@check_visibility_of = @bookmark
end
def search
@languages = Language.default_order
options = params[:bookmark_search].present? ? bookmark_search_params : {}
options.merge!(page: params[:page]) if params[:page].present?
options[:show_private] = false
options[:show_restricted] = logged_in? || logged_in_as_admin?
@search = BookmarkSearchForm.new(options)
@page_subtitle = ts("Search Bookmarks")
if params[:bookmark_search].present? && params[:edit_search].blank?
if @search.query.present?
@page_subtitle = ts("Bookmarks Matching '%{query}'", query: @search.query)
end
@bookmarks = @search.search_results.scope(:for_blurb)
flash_search_warnings(@bookmarks)
set_own_bookmarks
render 'search_results'
end
end
def index
if @bookmarkable
@bookmarks = @bookmarkable.bookmarks.not_private.order_by_created_at.paginate(page: params[:page], per_page: ArchiveConfig.ITEMS_PER_PAGE)
@bookmarks = @bookmarks.where(hidden_by_admin: false) unless logged_in_as_admin?
else
base_options = {
show_private: (@user.present? && @user == current_user),
show_restricted: logged_in? || logged_in_as_admin?,
page: params[:page]
}
options = params[:bookmark_search].present? ? bookmark_search_params : {}
if params[:include_bookmark_search].present?
params[:include_bookmark_search].keys.each do |key|
options[key] ||= []
options[key] << params[:include_bookmark_search][key]
options[key].flatten!
end
end
if params[:exclude_bookmark_search].present?
params[:exclude_bookmark_search].keys.each do |key|
# Keep bookmarker tags separate, so we can search for them on bookmarks
# and search for the rest on bookmarkables
options_key = key == "tag_ids" ? :excluded_bookmark_tag_ids : :excluded_tag_ids
options[options_key] ||= []
options[options_key] << params[:exclude_bookmark_search][key]
options[options_key].flatten!
end
end
options.merge!(base_options)
@page_subtitle = index_page_title
if @owner.present?
@search = BookmarkSearchForm.new(options.merge(faceted: true, parent: @owner))
if @user.blank?
# When it's not a particular user's bookmarks, we want
# to list *bookmarkable* items to avoid duplication
@bookmarkable_items = @search.bookmarkable_search_results.scope(:for_blurb)
flash_search_warnings(@bookmarkable_items)
@facets = @bookmarkable_items.facets
else
# We're looking at a particular user's bookmarks, so
# just retrieve the standard search results and their facets.
@bookmarks = @search.search_results.scope(:for_blurb)
flash_search_warnings(@bookmarks)
@facets = @bookmarks.facets
end
if @search.options[:excluded_tag_ids].present? || @search.options[:excluded_bookmark_tag_ids].present?
# Excluded tags do not appear in search results, so we need to generate empty facets
# to keep them as checkboxes on the filters.
excluded_tag_ids = @search.options[:excluded_tag_ids] || []
excluded_bookmark_tag_ids = @search.options[:excluded_bookmark_tag_ids] || []
# It's possible to determine the tag types by looking at
# the original parameters params[:exclude_bookmark_search],
# but we need the tag names too, so a database query is unavoidable.
tags = Tag.where(id: excluded_tag_ids + excluded_bookmark_tag_ids)
tags.each do |tag|
if excluded_tag_ids.include?(tag.id.to_s)
key = tag.class.to_s.underscore
@facets[key] ||= []
@facets[key] << QueryFacet.new(tag.id, tag.name, 0)
end
if excluded_bookmark_tag_ids.include?(tag.id.to_s)
key = 'tag'
@facets[key] ||= []
@facets[key] << QueryFacet.new(tag.id, tag.name, 0)
end
end
end
elsif use_caching?
@bookmarks = Rails.cache.fetch("bookmarks/index/latest/v2_true", expires_in: ArchiveConfig.SECONDS_UNTIL_BOOKMARK_INDEX_EXPIRE.seconds) do
search = BookmarkSearchForm.new(show_private: false, show_restricted: false, sort_column: 'created_at')
results = search.search_results.scope(:for_blurb)
flash_search_warnings(results)
results.to_a
end
else
@bookmarks = Bookmark.latest.for_blurb.to_a
end
end
set_own_bookmarks
@pagy =
if @bookmarks.respond_to?(:total_pages)
pagy_query_result(@bookmarks)
elsif @bookmarkable_items.respond_to?(:total_pages)
pagy_query_result(@bookmarkable_items)
end
end
# GET /:locale/bookmark/:id
# GET /:locale/users/:user_id/bookmarks/:id
# GET /:locale/works/:work_id/bookmark/:id
# GET /:locale/external_works/:external_work_id/bookmark/:id
def show
end
# GET /bookmarks/new
# GET /bookmarks/new.xml
def new
@bookmark = Bookmark.new
respond_to do |format|
format.html
format.js {
@button_name = ts("Create")
@action = :create
render action: "bookmark_form_dynamic"
}
end
end
# GET /bookmarks/1/edit
def edit
@bookmarkable = @bookmark.bookmarkable
respond_to do |format|
format.html
format.js {
@button_name = ts("Update")
@action = :update
render action: "bookmark_form_dynamic"
}
end
end
# POST /bookmarks
# POST /bookmarks.xml
def create
@bookmarkable ||= ExternalWork.new(external_work_params)
@bookmark = Bookmark.new(bookmark_params.merge(bookmarkable: @bookmarkable))
if @bookmark.errors.empty? && @bookmark.save
flash[:notice] = ts("Bookmark was successfully created. It should appear in bookmark listings within the next few minutes.")
redirect_to(bookmark_path(@bookmark))
else
render :new
end
end
# PUT /bookmarks/1
# PUT /bookmarks/1.xml
def update
new_collections = []
unapproved_collections = []
errors = []
bookmark_params[:collection_names]&.split(",")&.map(&:strip)&.uniq&.each do |collection_name|
collection = Collection.find_by(name: collection_name)
if collection.nil?
errors << ts("%{name} does not exist.", name: collection_name)
else
if @bookmark.collections.include?(collection)
next
elsif collection.closed? && !collection.user_is_maintainer?(User.current_user)
errors << ts("%{title} is closed to new submissions.", title: collection.title)
elsif @bookmark.add_to_collection(collection) && @bookmark.save
if @bookmark.approved_collections.include?(collection)
new_collections << collection
else
unapproved_collections << collection
end
else
errors << ts("Something went wrong trying to add collection %{title}, sorry!", title: collection.title)
end
end
end
# messages to the user
unless errors.empty?
flash[:error] = ts("We couldn't add your submission to the following collections: ") + errors.join("<br />")
end
unless new_collections.empty?
flash[:notice] = ts("Added to collection(s): %{collections}.",
collections: new_collections.collect(&:title).join(", "))
end
unless unapproved_collections.empty?
flash[:notice] = flash[:notice] ? flash[:notice] + " " : ""
flash[:notice] += if unapproved_collections.size > 1
ts("You have submitted your bookmark to moderated collections (%{all_collections}). It will not become a part of those collections until it has been approved by a moderator.", all_collections: unapproved_collections.map(&:title).join(", "))
else
ts("You have submitted your bookmark to the moderated collection '%{collection}'. It will not become a part of the collection until it has been approved by a moderator.", collection: unapproved_collections.first.title)
end
end
flash[:notice] = (flash[:notice]).html_safe unless flash[:notice].blank?
flash[:error] = (flash[:error]).html_safe unless flash[:error].blank?
if @bookmark.update(bookmark_params) && errors.empty?
flash[:notice] = flash[:notice] ? " " + flash[:notice] : ""
flash[:notice] = ts("Bookmark was successfully updated.").html_safe + flash[:notice]
flash[:notice] = flash[:notice].html_safe
redirect_to(@bookmark)
else
@bookmarkable = @bookmark.bookmarkable
render :edit and return
end
end
# GET /bookmarks/1/share
def share
if request.xhr?
if @bookmark.bookmarkable.is_a?(Work) && @bookmark.bookmarkable.unrevealed?
render template: "errors/404", status: :not_found
else
render layout: false
end
else
# Avoid getting an unstyled page if JavaScript is disabled
flash[:error] = ts("Sorry, you need to have JavaScript enabled for this.")
redirect_back(fallback_location: root_path)
end
end
def confirm_delete
end
# DELETE /bookmarks/1
# DELETE /bookmarks/1.xml
def destroy
@bookmark.destroy
flash[:notice] = ts("Bookmark was successfully deleted.")
redirect_to user_bookmarks_path(current_user)
end
protected
def load_owner
if params[:user_id].present?
@user = User.find_by(login: params[:user_id])
unless @user
raise ActiveRecord::RecordNotFound, "Couldn't find user named '#{params[:user_id]}'"
end
if params[:pseud_id].present?
@pseud = @user.pseuds.find_by(name: params[:pseud_id])
unless @pseud
raise ActiveRecord::RecordNotFound, "Couldn't find pseud named '#{params[:pseud_id]}'"
end
end
end
if params[:tag_id]
@tag = Tag.find_by_name(params[:tag_id])
unless @tag
raise ActiveRecord::RecordNotFound, "Couldn't find tag named '#{params[:tag_id]}'"
end
unless @tag.canonical?
if @tag.merger.present?
redirect_to tag_bookmarks_path(@tag.merger) and return
else
redirect_to tag_path(@tag) and return
end
end
end
@owner = @bookmarkable || @pseud || @user || @collection || @tag
end
def index_page_title
if @owner.present?
owner_name = case @owner.class.to_s
when 'Pseud'
@owner.name
when 'User'
@owner.login
when 'Collection'
@owner.title
else
@owner.try(:name)
end
"#{owner_name} - Bookmarks".html_safe
else
"Latest Bookmarks"
end
end
def set_own_bookmarks
return unless @bookmarks
@own_bookmarks = []
if current_user.is_a?(User)
pseud_ids = current_user.pseuds.pluck(:id)
@own_bookmarks = @bookmarks.select do |b|
pseud_ids.include?(b.pseud_id)
end
end
end
private
def bookmark_params
params.require(:bookmark).permit(
:pseud_id, :bookmarker_notes, :tag_string, :collection_names, :private, :rec
)
end
def external_work_params
params.require(:external_work).permit(
:url, :author, :title, :fandom_string, :rating_string, :relationship_string,
:character_string, :summary, category_strings: []
)
end
def bookmark_search_params
params.require(:bookmark_search).permit(
:bookmark_query,
:bookmarkable_query,
:bookmarker,
:bookmark_notes,
:rec,
:with_notes,
:bookmarkable_type,
:language_id,
:date,
:bookmarkable_date,
:sort_column,
:other_tag_names,
:excluded_tag_names,
:other_bookmark_tag_names,
:excluded_bookmark_tag_names,
rating_ids: [],
warning_ids: [], # backwards compatibility
archive_warning_ids: [],
category_ids: [],
fandom_ids: [],
character_ids: [],
relationship_ids: [],
freeform_ids: [],
tag_ids: [],
)
end
end