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("
") 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