module AdminFormHelper class FormBuilderDecorator def initialize(decorated) @target = decorated end def method_missing(method, *args, &block) @target.send(method, *args, &block) end def actions(*args, &proc) @target.template.form_actions(&proc) end def default_action action(@target.object.persisted? ? :update : :create, :primary => true) end def form_errors(options = {:wrap => true}) wrap = options.delete(:wrap) options[:exclude] ||= [:slug] f = @target unless f.object.errors.empty? if wrap @target.template.content_tag :div, :class => "alert alert-block" do @target.template.link_to "×", :class => "close", :data => {:dismiss => "alert"} @target.template.content_tag(:h4, I18n.t('fullstack.admin.form.correct_these_errors_and_retry', :default => "Correct these errors and retry"), :class => "alert-heading") f.semantic_errors *(f.object.errors.keys - (options[:exclude] || [])) end else f.semantic_errors *(f.object.errors.keys - (options[:exclude] || [])) end end end alias :errors :form_errors def action(method, options = {}) default_label = I18n.t("fullstack.admin_form.labels.#{method}", :default => "#{method}".humanize) options[:type] ||= !!options[:primary] ? :primary : nil @target.template.button((options.delete(:label) || default_label), options) end def resource_inputs(*args) model = @target.object.class options = args.extract_options! only_attributes = options[:only] || [] except_arg = options[:except] || [] except_attributes = except_arg + model.protected_attributes.to_a + %W(created_at updated_at slug slugs lat lng position) only_attributes.map! {|a| a.to_s} except_attributes.map! {|a| a.to_s} columns = model.schema.hierarchy_field_names # =============== # = Attachments = # =============== attachment_definitions = (model.attachment_definitions || {}).keys attachment_columns = attachment_definitions.map {|a| ["#{a}_file_name", "#{a}_file_size", "#{a}_content_type", "#{a}_updated_at"] }.flatten columns -= attachment_columns columns += attachment_definitions # ================ # = Associations = # ================ columns = columns.map {|field_name| field_name.to_s.gsub(/_id$/, "").gsub(/_type$/, "") }.uniq belongs_to_associations = model.reflect_on_all_associations(:belongs_to).select {|a| !a.options[:polymorphic] && !a.options[:autosave] }.map {|assoc| assoc.name.to_s} polymorphic_associations = model.reflect_on_all_associations(:belongs_to).select {|a| a.options[:polymorphic] && !a.options[:autosave] }.map {|assoc| assoc.name.to_s} autosave_belongs_to = model.reflect_on_all_associations(:belongs_to).select {|a| a.options[:autosave] }.map {|assoc| assoc.name.to_s}.compact has_many_associations = model.reflect_on_all_associations(:has_many).select {|a| a.options[:autosave] }.map {|assoc| assoc.name.to_s} columns -= polymorphic_associations columns += has_many_associations # ==================================== # = Intersect with :only and :except = # ==================================== if only_attributes.any? columns = columns.select {|k| only_attributes.include?(k)} elsif except_attributes.any? columns = columns.delete_if {|k| except_attributes.include?(k)} end # ============= # = Rendering = # ============= buff = "" columns.each do |column| sym = column.to_sym field = model.schema.hierarchy_fields[sym] is_belongs_to_associaiton = belongs_to_associations.include?(column) is_has_many_association = has_many_associations.include?(column) is_autosave_belongs_to = autosave_belongs_to.include?(column) buff << if is_belongs_to_associaiton @target.input(sym, :as => :select) elsif is_has_many_association || is_autosave_belongs_to association_inputs(sym) else opts = {} args = [sym] if field && field.options[:markup] opts[:as] = :markup elsif field && field.options[:simple_markup] opts[:as] = :simple_markup elsif field && field.options[:in] opts[:as] = :select opts[:collection] = field.options[:in] elsif column == "locale" opts[:as] = :select opts[:collection] = I18n.available_locales.map {|locale| [I18n.t("locale_names.#{locale}", :default => "#{locale}".humanize), locale.to_s] } else nil end args << opts unless opts.empty? @target.input(*args) end end inputs do buff.html_safe end end def resource_submit @target.template.button (@target.object.persisted? ? I18n.t('fullstack.admin.update', :default => "Update") : I18n.t('fullstack.admin.create', :default => "Create")), :type => :primary, :size => :large end def actions(&block) @target.template.form_actions(&block) end def admin_fields_for(*args) @target.semantic_fields_for(*args) do |f| yield(FormBuilderDecorator.new(f)) end end def sort_association(association, options = {}) assoc_str = association.to_s @target.template.render :partial => "sort", :locals => { :association => association, :f => self, :options => options } end def association_inputs(association, options = {}) assoc_str = association.to_s is_singular = assoc_str.pluralize != assoc_str and assoc_str.singularize == assoc_str if is_singular if @target.template.partial?("nested_belongs_to_#{assoc_str}_fields") @target.template.render :partial => "nested_belongs_to_#{assoc_str}_fields", :locals => { :association => association, :f => self, :options => options } else @target.template.render :partial => "nested_belongs_to_fields", :locals => { :association => association, :f => self, :options => options } end else if @target.template.partial?("associated_#{assoc_str}_table") @target.template.render :partial => "associated_#{assoc_str}_table", :locals => { :association => association, :f => self, :options => options } else @target.template.render :partial => "associated_resources_table", :locals => { :association => association, :f => self, :options => options } end end end end # ~ def admin_form_for(record_or_name_or_array, *args) options = args.extract_options! options[:builder] ||= FormtasticBootstrap::FormBuilder semantic_form_for(record_or_name_or_array, *(args << options)) do |f| yield(FormBuilderDecorator.new(f)) end end end