Skip to content

Commit

Permalink
Merge pull request #2091 from robinboening/feature_flexible_resource_…
Browse files Browse the repository at this point in the history
…filters

Feature flexible resource filters
  • Loading branch information
tvdeyen authored Jul 9, 2021
2 parents fa2fe39 + 46a501c commit f3a9a2d
Show file tree
Hide file tree
Showing 34 changed files with 540 additions and 380 deletions.
14 changes: 14 additions & 0 deletions app/assets/stylesheets/alchemy/archive.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
padding: 2 * $default-padding;
}

.applied-filter {
display: inline-block;
padding: 2px 6px;
border-radius: $default-border-radius;
border: 1px solid $default-border-color;
white-space: nowrap;
font-weight: normal;

.dismiss-filter {
position: relative;
top: -1px;
}
}

.resources-table-wrapper {
padding-bottom: 60px;

Expand Down
6 changes: 3 additions & 3 deletions app/controllers/alchemy/admin/attachments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def index
@attachments = @attachments.tagged_with(search_filter_params[:tagged_with])
end

if search_filter_params[:file_type].present?
@attachments = @attachments.with_file_type(search_filter_params[:file_type])
if search_filter_params[:filter].present?
@attachments = apply_filters(@attachments)
end

@attachments = @attachments
Expand Down Expand Up @@ -77,8 +77,8 @@ def download
def search_filter_params
@_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES + [:attachment]).permit(
*common_search_filter_includes + [
:file_type,
:form_field_id,
:content_id,
]
)
end
Expand Down
10 changes: 1 addition & 9 deletions app/controllers/alchemy/admin/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ def index
end

if search_filter_params[:filter].present?
items = items.public_send(sanitized_filter_params)
end

if search_filter_params[:page_layout].present?
items = items.where(page_layout: search_filter_params[:page_layout])
items = apply_filters(items)
end

items = items.page(params[:page] || 1).per(items_per_page)
Expand Down Expand Up @@ -244,10 +240,6 @@ def resource_handler
@_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module, Alchemy::Page)
end

def common_search_filter_includes
super.push(:page_layout, :view)
end

def set_view
@view = params[:view] || session[:alchemy_pages_view] || "tree"
session[:alchemy_pages_view] = @view
Expand Down
29 changes: 21 additions & 8 deletions app/controllers/alchemy/admin/pictures_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,19 @@ class PicturesController < Alchemy::Admin::ResourcesController

def index
@query = Picture.ransack(search_filter_params[:q])
@pictures = Picture.search_by(
search_filter_params,
@query,
items_per_page,
)
@pictures = @pictures.includes(:thumbs)
@pictures = filtered_pictures.includes(:thumbs)

if in_overlay?
archive_overlay
end
end

def show
@previous = @picture.previous(params)
@next = @picture.next(params)
@query = Picture.ransack(params[:q])
@previous = filtered_pictures.where("name < ?", @picture.name).last
@next = filtered_pictures.where("name > ?", @picture.name).first
@assignments = @picture.essence_pictures.joins(content: { element: :page })

render action: "show"
end

Expand Down Expand Up @@ -130,6 +127,22 @@ def destroy
redirect_to_index
end

def filtered_pictures
pictures = @query.result

if params[:tagged_with].present?
pictures = pictures.tagged_with(params[:tagged_with])
end

if search_filter_params[:filter].present?
pictures = apply_filters(pictures)
end

pictures = pictures.page(params[:page] || 1).per(items_per_page)

pictures.order(:name)
end

def items_per_page
if in_overlay?
case @size
Expand Down
94 changes: 84 additions & 10 deletions app/controllers/alchemy/admin/resources_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "csv"
require "alchemy/resource"
require "alchemy/resources_helper"
require "alchemy/resource_filter"

module Alchemy
module Admin
Expand All @@ -13,7 +14,8 @@ class ResourcesController < Alchemy::Admin::BaseController

helper Alchemy::ResourcesHelper, TagsHelper
helper_method :resource_handler, :search_filter_params,
:items_per_page, :items_per_page_options
:items_per_page, :items_per_page_options, :resource_has_filters,
:resource_filters_for_select

before_action :load_resource,
only: [:show, :edit, :update, :destroy]
Expand All @@ -34,7 +36,7 @@ def index
end

if search_filter_params[:filter].present?
items = items.public_send(sanitized_filter_params)
items = apply_filters(items)
end

respond_to do |format|
Expand Down Expand Up @@ -90,8 +92,79 @@ def resource_handler
@_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module)
end

def resource_has_filters
resource_model.respond_to?(:alchemy_resource_filters)
end

def resource_has_deprecated_filters
resource_model.alchemy_resource_filters.any? { |f| !f.is_a?(Hash) }
end

def resource_filters
return unless resource_has_filters

@_resource_filters ||= deprecated_resource_filters || resource_model.alchemy_resource_filters
end

def resource_filters_for_select
resource_filters.map do |filter|
ResourceFilter.new(filter, resource_handler.resource_name)
end
end

protected

def deprecated_resource_filters
if resource_has_deprecated_filters
Alchemy::Deprecation.warn(
"#{resource_model}.alchemy_resource_filters is using a legacy data structure. " \
"Please use an Array of Hashes instead. i.e. [{ name: 'foo', values: ['bar', 'baz'] }, ...] " \
"where values are scopes. With Alchemy 6.1 only the new structure will be supported."
)

@_resource_filters ||= [
{
name: :misc,
values: resource_model.alchemy_resource_filters,
},
]
end
end

def apply_filters(items)
sanitize_filter_params!

search_filter_params[:filter].each do |filter|
if argument_scope_filter?(filter)
items = items.public_send(filter[0], filter[1])
elsif simple_scope_filter?(filter)
items = items.public_send(filter[1])
else
raise "Can't apply filter #{filter[0]}. Either the name or the values must be defined as class methods / scopes on the model."
end
end

items
end

def simple_scope_filter?(filter)
resource_model.respond_to?(filter[1])
end

def argument_scope_filter?(filter)
resource_model.respond_to?(filter[0])
end

def sanitize_filter_params!
search_filter_params[:filter].reject! do |_, v|
eligible_resource_filter_values.exclude?(v)
end
end

def eligible_resource_filter_values
resource_filters.map(&:values).flatten
end

# Returns a translated +flash[:notice]+.
# The key should look like "Modelname successfully created|updated|destroyed."
def flash_notice_for_resource_action(action = params[:action])
Expand Down Expand Up @@ -136,27 +209,28 @@ def resource_params
params.require(resource_handler.namespaced_resource_name).permit!
end

def sanitized_filter_params
resource_model.alchemy_resource_filters.detect do |filter|
filter == search_filter_params[:filter]
end || :all
end

def search_filter_params
@_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES).permit(*common_search_filter_includes).to_h
end

def common_search_filter_includes
[
search_filters = [
{ q: [
resource_handler.search_field_name,
:s,
] },
:tagged_with,
:filter,
:page,
:per_page,
]

if resource_has_filters
search_filters << {
filter: resource_filters.map { |f| f[:name] },
}
end

search_filters
end

def items_per_page
Expand Down
31 changes: 24 additions & 7 deletions app/models/alchemy/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class Attachment < BaseRecord
has_many :elements, through: :contents
has_many :pages, through: :elements

scope :by_file_type, ->(file_type) { where(file_mime_type: file_type) }
scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) }
scope :without_tag, -> { left_outer_joins(:taggings).where(gutentag_taggings: { id: nil }) }

# We need to define this method here to have it available in the validations below.
class << self
# The class used to generate URLs for attachments
Expand All @@ -51,20 +55,33 @@ def url_class=(klass)
@_url_class = klass
end

def alchemy_resource_filters
[
{
name: :by_file_type,
values: distinct.pluck(:file_mime_type).map { |type| [Alchemy.t(type, scope: "mime_types"), type] }.sort_by(&:first),
},
{
name: :misc,
values: %w(recent last_upload without_tag),
},
]
end

def last_upload
last_id = Attachment.maximum(:id)
return Attachment.all unless last_id

where(id: last_id)
end

def searchable_alchemy_resource_attributes
%w(name file_name)
end

def allowed_filetypes
Config.get(:uploader).fetch("allowed_filetypes", {}).fetch("alchemy/attachments", [])
end

def file_types_for_select
file_types = Alchemy::Attachment.pluck(:file_mime_type).uniq.map do |type|
[Alchemy.t(type, scope: "mime_types"), type]
end
file_types.sort_by(&:first)
end
end

validates_presence_of :file
Expand Down
11 changes: 10 additions & 1 deletion app/models/alchemy/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,16 @@ def url_path_class=(klass)
end

def alchemy_resource_filters
%w[published not_public restricted]
[
{
name: :by_page_layout,
values: PageLayout.all.map { |p| [Alchemy.t(p["name"], scope: "page_layout_names"), p["name"]] },
},
{
name: :status,
values: %w[published not_public restricted],
},
]
end

def searchable_alchemy_resource_attributes
Expand Down
4 changes: 4 additions & 0 deletions app/models/alchemy/page/page_scopes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ module PageScopes
extend ActiveSupport::Concern

included do
# All pages of given page layout
#
scope :by_page_layout, ->(page_layout) { where(page_layout: page_layout) }

# All language root pages
#
scope :language_roots, -> { where(language_root: true) }
Expand Down
Loading

0 comments on commit f3a9a2d

Please sign in to comment.