require 'csv'
require 'alchemy/resource'
require 'alchemy/resources_helper'
require 'handles_sortable_columns'
module Alchemy
module Admin
class ResourcesController < Alchemy::Admin::BaseController
include Alchemy::ResourcesHelper
helper Alchemy::ResourcesHelper
helper_method :resource_handler
before_filter :load_resource,
only: [:show, :edit, :update, :destroy]
before_filter do
authorize!(action_name.to_sym, resource_instance_variable || resource_handler.model)
end
handles_sortable_columns do |c|
c.default_sort_value = :name
c.link_class = 'sortable'
c.indicator_class = {:asc => "sorted asc", :desc => "sorted desc"}
c.indicator_text = {:asc => " ↓ ", :desc => " ↑ "}
end
def index
items = resource_handler.model
if contains_relations?
items = items.includes(*resource_relations_names)
end
if params[:query].present?
items = query_items(items)
end
items = items.order(sort_order)
respond_to do |format|
format.html {
items = items.page(params[:page] || 1).per(per_page_value_for_screen_size)
instance_variable_set("@#{resource_handler.resources_name}", items)
}
format.csv {
instance_variable_set("@#{resource_handler.resources_name}", items)
}
end
end
def new
instance_variable_set("@#{resource_handler.resource_name}", resource_handler.model.new)
end
def show
render action: 'edit'
end
def edit; end
def create
instance_variable_set("@#{resource_handler.resource_name}", resource_handler.model.new(resource_params))
resource_instance_variable.save
render_errors_or_redirect(
resource_instance_variable,
resources_path,
flash_notice_for_resource_action
)
end
def update
resource_instance_variable.update_attributes(resource_params)
render_errors_or_redirect(
resource_instance_variable,
resources_path,
flash_notice_for_resource_action
)
end
def destroy
resource_instance_variable.destroy
flash_notice_for_resource_action
do_redirect_to resource_url_proxy.url_for(action: 'index')
end
def resource_handler
@_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module)
end
protected
# Returns a translated +flash[:notice]+.
# The key should look like "Modelname successfully created|updated|destroyed."
def flash_notice_for_resource_action(action = params[:action])
return if resource_instance_variable.errors.any?
case action.to_sym
when :create
verb = "created"
when :update
verb = "updated"
when :destroy
verb = "removed"
end
flash[:notice] = _t("#{resource_handler.resource_name.classify} successfully #{verb}", :default => _t("Succesfully #{verb}"))
end
def is_alchemy_module?
not alchemy_module.nil? and not alchemy_module['engine_name'].nil?
end
def alchemy_module
@alchemy_module ||= module_definition_for(:controller => params[:controller], :action => 'index')
end
def load_resource
instance_variable_set("@#{resource_handler.resource_name}", resource_handler.model.find(params[:id]))
end
# Returns a sort order for AR#sort method
#
# Falls back to fallback_sort_order, if the requested column is not a column of model.
#
# If the column is a tablename and column combination that matches any resource relations, than this order will be taken.
#
def sort_order
sortable_column_order do |column, direction|
if resource_handler.model_associations.present? && column.match(/\./)
table, column = column.split('.')
if resource_handler.model_associations.detect { |a| a.table_name == table }
"#{table}.#{column} #{direction}"
else
fallback_sort_order(direction)
end
elsif resource_handler.model.column_names.include?(column.to_s)
"#{resource_handler.model.table_name}.#{column} #{direction}"
else
fallback_sort_order(direction)
end
end
end
# Default sort order fallback
#
# Overwrite this in your controller to define custom fallback
#
def fallback_sort_order(direction)
"#{resource_handler.model.table_name}.id #{direction}"
end
# Returns an activerecord object that contains items matching params[:query]
#
def query_items(items)
query = params[:query].downcase.split(' ').join('%')
query = ActiveRecord::Base.sanitize("%#{query}%")
items.eager_load(resource_handler.model_association_names).where(search_query(query))
end
# Returns a search query string
#
# It queries all searchable attributes from resource model via LIKE and joins them via OR.
#
# If the attribute is a relation it builds the query for the associated table.
#
def search_query(search_terms)
resource_handler.searchable_attributes.map do |attribute|
if relation = attribute[:relation]
"LOWER(#{relation[:model_association].klass.table_name}.#{relation[:attr_method]}) LIKE #{search_terms}"
else
"LOWER(#{resource_handler.model.table_name}.#{attribute[:name]}) LIKE #{search_terms}"
end
end.join(" OR ")
end
# Permits all parameters as default!
#
# THIS IS INSECURE! Although only signed in admin users can send requests anyway, but we should change this.
#
# Please define this method in your inheriting controller and set the parameters you want to permit.
#
# TODO: Hook this into authorization provider.
#
def resource_params
params.require(resource_handler.namespaced_resource_name).permit!
end
end
end
end