Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature flexible resource filters #2091

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
tvdeyen marked this conversation as resolved.
Show resolved Hide resolved
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
tvdeyen marked this conversation as resolved.
Show resolved Hide resolved
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