208 lines
6.2 KiB
Ruby
208 lines
6.2 KiB
Ruby
module Locomotive
|
|
class ContentType
|
|
|
|
include Locomotive::Mongoid::Document
|
|
|
|
## extensions ##
|
|
include CustomFields::Source
|
|
include Extensions::ContentType::ItemTemplate
|
|
|
|
## fields ##
|
|
field :name
|
|
field :description
|
|
field :slug
|
|
field :label_field_id, :type => BSON::ObjectId
|
|
field :label_field_name
|
|
field :group_by_field_id, :type => BSON::ObjectId
|
|
field :order_by
|
|
field :order_direction, :default => 'asc'
|
|
field :public_submission_enabled, :type => Boolean, :default => false
|
|
field :public_submission_accounts, :type => Array
|
|
|
|
## associations ##
|
|
belongs_to :site, :class_name => 'Locomotive::Site'
|
|
has_many :entries, :class_name => 'Locomotive::ContentEntry', :dependent => :destroy
|
|
|
|
## named scopes ##
|
|
scope :ordered, :order_by => :updated_at.desc
|
|
|
|
## indexes ##
|
|
index [[:site_id, Mongo::ASCENDING], [:slug, Mongo::ASCENDING]]
|
|
|
|
## callbacks ##
|
|
before_validation :normalize_slug
|
|
after_validation :bubble_fields_errors_up
|
|
before_save :set_default_values
|
|
before_update :update_label_field_name_in_entries
|
|
|
|
## validations ##
|
|
validates_presence_of :site, :name, :slug
|
|
validates_uniqueness_of :slug, :scope => :site_id
|
|
validates_size_of :entries_custom_fields, :minimum => 1, :message => :too_few_custom_fields
|
|
|
|
## behaviours ##
|
|
custom_fields_for :entries
|
|
|
|
## methods ##
|
|
|
|
def order_manually?
|
|
self.order_by == '_position'
|
|
end
|
|
|
|
def order_by_definition
|
|
direction = self.order_manually? ? 'asc' : self.order_direction || 'asc'
|
|
[order_by_attribute, direction]
|
|
end
|
|
|
|
def ordered_entries(conditions = {})
|
|
self.entries.order_by([order_by_definition]).where(conditions)
|
|
end
|
|
|
|
def groupable?
|
|
!!self.group_by_field && %w(select belongs_to).include?(group_by_field.type)
|
|
end
|
|
|
|
def group_by_field
|
|
self.entries_custom_fields.find(self.group_by_field_id) rescue nil
|
|
end
|
|
|
|
def list_or_group_entries
|
|
if self.groupable?
|
|
if group_by_field.type == 'select'
|
|
self.entries.group_by_select_option(self.group_by_field.name, self.order_by_definition)
|
|
else
|
|
group_by_belongs_to_field(self.group_by_field)
|
|
end
|
|
else
|
|
self.ordered_entries
|
|
end
|
|
end
|
|
|
|
def class_name_to_content_type(class_name)
|
|
self.class.class_name_to_content_type(class_name, self.site)
|
|
end
|
|
|
|
def label_field_name=(value)
|
|
# mandatory if we allow the API to set the label field name without an id of the field
|
|
@new_label_field_name = value unless value.blank?
|
|
super(value)
|
|
end
|
|
|
|
# Retrieve from a class name the associated content type within the scope of a site.
|
|
# If no content type is found, the method returns nil
|
|
#
|
|
# @param [ String ] class_name The class name
|
|
# @param [ Locomotive::Site ] site The Locomotive site
|
|
#
|
|
# @return [ Locomotive::ContentType ] The content type matching the class_name
|
|
#
|
|
def self.class_name_to_content_type(class_name, site)
|
|
if class_name =~ /^Locomotive::Entry(.*)/
|
|
site.content_types.find($1)
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def to_presenter
|
|
Locomotive::ContentTypePresenter.new(self)
|
|
end
|
|
|
|
def as_json(options = {})
|
|
self.to_presenter.as_json
|
|
end
|
|
|
|
protected
|
|
|
|
def group_by_belongs_to_field(field)
|
|
grouped_entries = self.ordered_entries.group_by(&:"#{field.name}_id")
|
|
columns = grouped_entries.keys
|
|
target_content_type = self.class_name_to_content_type(field.class_name)
|
|
all_columns = target_content_type.ordered_entries
|
|
|
|
all_columns.map do |column|
|
|
if columns.include?(column._id)
|
|
{
|
|
:name => column._label(target_content_type),
|
|
:entries => grouped_entries.delete(column._id)
|
|
}
|
|
else
|
|
nil
|
|
end
|
|
end.compact.tap do |groups|
|
|
unless grouped_entries.empty? # "orphans" ?
|
|
groups << { :name => nil, :entries => grouped_entries.values.flatten }
|
|
end
|
|
end
|
|
end
|
|
|
|
def order_by_attribute
|
|
return self.order_by if %w(created_at updated_at _position).include?(self.order_by)
|
|
self.entries_custom_fields.find(self.order_by).name rescue 'created_at'
|
|
end
|
|
|
|
def set_default_values
|
|
self.order_by ||= 'created_at'
|
|
|
|
if @new_label_field_name.present?
|
|
self.label_field_id = self.entries_custom_fields.detect { |f| f.name == @new_label_field_name.underscore }._id
|
|
end
|
|
|
|
if self.label_field_id.blank?
|
|
self.label_field_id = self.entries_custom_fields.first._id
|
|
end
|
|
|
|
field = self.entries_custom_fields.find(self.label_field_id)
|
|
|
|
# the label field should always be required
|
|
field.required = true
|
|
|
|
self.label_field_name = field.name
|
|
end
|
|
|
|
def normalize_slug
|
|
self.slug = self.name.clone if self.slug.blank? && self.name.present?
|
|
self.slug.permalink! if self.slug.present?
|
|
end
|
|
|
|
def bubble_fields_errors_up
|
|
return if self.errors[:entries_custom_fields].empty?
|
|
|
|
hash = { :base => self.errors[:entries_custom_fields] }
|
|
|
|
self.entries_custom_fields.each do |field|
|
|
next if field.valid?
|
|
key = field.persisted? ? field._id.to_s : field.position.to_i
|
|
hash[key] = field.errors.to_a
|
|
end
|
|
|
|
self.errors.set(:entries_custom_fields, hash)
|
|
end
|
|
|
|
def update_label_field_name_in_entries
|
|
self.klass_with_custom_fields(:entries).update_all :_label_field_name => self.label_field_name
|
|
end
|
|
|
|
# Makes sure the class_name filled in a belongs_to or has_many field
|
|
# does not belong to another site. Adds an error if it presents a
|
|
# security problem.
|
|
#
|
|
# @param [ CustomFields::Field ] field The field to check
|
|
#
|
|
def ensure_class_name_security(field)
|
|
if field.class_name =~ /^Locomotive::Entry([a-z0-9]+)$/
|
|
content_type = Locomotive::ContentType.find($1)
|
|
|
|
if content_type.site_id != self.site_id
|
|
field.errors.add :class_name, :security
|
|
end
|
|
else
|
|
# for now, does not allow external classes
|
|
field.errors.add :class_name, :security
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|