wheelhouse-forms 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +39 -0
  3. data/app/assets/images/wheelhouse-forms/checkbox.png +0 -0
  4. data/app/assets/images/wheelhouse-forms/checkboxes.png +0 -0
  5. data/app/assets/images/wheelhouse-forms/content.png +0 -0
  6. data/app/assets/images/wheelhouse-forms/countries.png +0 -0
  7. data/app/assets/images/wheelhouse-forms/csv.png +0 -0
  8. data/app/assets/images/wheelhouse-forms/custom.png +0 -0
  9. data/app/assets/images/wheelhouse-forms/drag.png +0 -0
  10. data/app/assets/images/wheelhouse-forms/field-set.png +0 -0
  11. data/app/assets/images/wheelhouse-forms/form.png +0 -0
  12. data/app/assets/images/wheelhouse-forms/radio-buttons.png +0 -0
  13. data/app/assets/images/wheelhouse-forms/select.png +0 -0
  14. data/app/assets/images/wheelhouse-forms/states.png +0 -0
  15. data/app/assets/images/wheelhouse-forms/submit-button.png +0 -0
  16. data/app/assets/images/wheelhouse-forms/text-area.png +0 -0
  17. data/app/assets/images/wheelhouse-forms/text-field.png +0 -0
  18. data/app/assets/javascripts/wheelhouse-forms/admin.js +125 -0
  19. data/app/assets/javascripts/wheelhouse-forms/jquery.autogrow.js +50 -0
  20. data/app/assets/javascripts/wheelhouse-forms/jquery.options.js +24 -0
  21. data/app/assets/stylesheets/wheelhouse-forms/admin.css.sass +244 -0
  22. data/app/controllers/forms/forms_controller.rb +9 -0
  23. data/app/controllers/forms/submissions_controller.rb +19 -0
  24. data/app/handlers/forms/form_handler.rb +17 -0
  25. data/app/helpers/forms/forms_helper.rb +36 -0
  26. data/app/helpers/forms/mailer_helper.rb +67 -0
  27. data/app/helpers/forms/submissions_helper.rb +71 -0
  28. data/app/mailers/forms/mailer.rb +11 -0
  29. data/app/models/forms/field_collection.rb +21 -0
  30. data/app/models/forms/fields/checkbox.rb +8 -0
  31. data/app/models/forms/fields/checkboxes.rb +10 -0
  32. data/app/models/forms/fields/content_field.rb +7 -0
  33. data/app/models/forms/fields/countries_dropdown.rb +14 -0
  34. data/app/models/forms/fields/custom_field.rb +8 -0
  35. data/app/models/forms/fields/field.rb +28 -0
  36. data/app/models/forms/fields/field_set.rb +14 -0
  37. data/app/models/forms/fields/optioned_field.rb +17 -0
  38. data/app/models/forms/fields/radio_buttons.rb +10 -0
  39. data/app/models/forms/fields/select_field.rb +10 -0
  40. data/app/models/forms/fields/states_dropdown.rb +15 -0
  41. data/app/models/forms/fields/submit_button.rb +7 -0
  42. data/app/models/forms/fields/text_area.rb +8 -0
  43. data/app/models/forms/fields/text_field.rb +21 -0
  44. data/app/models/forms/form.rb +65 -0
  45. data/app/models/forms/submission.rb +53 -0
  46. data/app/renderers/forms/checkbox_renderer.rb +7 -0
  47. data/app/renderers/forms/checkboxes_renderer.rb +18 -0
  48. data/app/renderers/forms/content_field_renderer.rb +5 -0
  49. data/app/renderers/forms/custom_field_renderer.rb +5 -0
  50. data/app/renderers/forms/field_collection_renderer.rb +13 -0
  51. data/app/renderers/forms/field_renderer.rb +57 -0
  52. data/app/renderers/forms/field_set_renderer.rb +10 -0
  53. data/app/renderers/forms/form_renderer.rb +30 -0
  54. data/app/renderers/forms/labelled_field_renderer.rb +10 -0
  55. data/app/renderers/forms/radio_buttons_renderer.rb +18 -0
  56. data/app/renderers/forms/select_field_renderer.rb +9 -0
  57. data/app/renderers/forms/submit_button_renderer.rb +5 -0
  58. data/app/renderers/forms/text_area_renderer.rb +7 -0
  59. data/app/renderers/forms/text_field_renderer.rb +7 -0
  60. data/app/templates/form.html.haml +9 -0
  61. data/app/templates/form.xhr.haml +4 -0
  62. data/app/templates/form/submission.html.haml +15 -0
  63. data/app/views/forms/forms/_checkbox.haml +3 -0
  64. data/app/views/forms/forms/_checkboxes.haml +10 -0
  65. data/app/views/forms/forms/_content_field.haml +3 -0
  66. data/app/views/forms/forms/_countries_dropdown.haml +4 -0
  67. data/app/views/forms/forms/_custom_field.haml +4 -0
  68. data/app/views/forms/forms/_field_set.haml +16 -0
  69. data/app/views/forms/forms/_radio_buttons.haml +10 -0
  70. data/app/views/forms/forms/_select_field.haml +10 -0
  71. data/app/views/forms/forms/_states_dropdown.haml +4 -0
  72. data/app/views/forms/forms/_submissions.haml +20 -0
  73. data/app/views/forms/forms/_submit_button.haml +2 -0
  74. data/app/views/forms/forms/_text_area.haml +3 -0
  75. data/app/views/forms/forms/_text_field.haml +4 -0
  76. data/app/views/forms/forms/designer.haml +56 -0
  77. data/app/views/forms/forms/edit.html.haml +5 -0
  78. data/app/views/forms/forms/form.haml +22 -0
  79. data/app/views/forms/forms/new.html.haml +4 -0
  80. data/app/views/forms/forms/show.html.haml +4 -0
  81. data/app/views/forms/submissions/form.haml +16 -0
  82. data/app/views/forms/submissions/show.html.haml +4 -0
  83. data/config/countries.yml +245 -0
  84. data/config/locales/en.yml +7 -0
  85. data/config/routes.rb +7 -0
  86. data/config/states.yml +62 -0
  87. data/lib/forms/csv_exporter.rb +41 -0
  88. data/lib/wheelhouse-forms.rb +26 -0
  89. metadata +149 -0
@@ -0,0 +1,21 @@
1
+ module Forms::Fields
2
+ class TextField < Field
3
+ property :label, String, :required => true
4
+ property :input_type, String, :default => "text"
5
+ property :required, Boolean, :default => false
6
+
7
+ self.renderer = Forms::TextFieldRenderer
8
+
9
+ INPUT_TYPES = [
10
+ ["Text", "text"],
11
+ ["Email", "email"],
12
+ ["Telephone", "tel"],
13
+ ["URL", "url"],
14
+ ["Date", "date"],
15
+ ["Date & Time", "datetime"],
16
+ ["Number", "number"],
17
+ ["Search", "search"],
18
+ ["Color", "color"]
19
+ ]
20
+ end
21
+ end
@@ -0,0 +1,65 @@
1
+ class Forms::Form < Wheelhouse::Resource
2
+ include Wheelhouse::Resource::Node
3
+ include Wheelhouse::Resource::Renderable
4
+ include Wheelhouse::Resource::Addressable
5
+ include Wheelhouse::Resource::Versioned
6
+ include Wheelhouse::Resource::Content
7
+
8
+ property :title, String, :required => true, :translate => true
9
+ property :fields, FieldCollection, :default => [Forms::Fields::FieldSet.new]
10
+ property :recipients, MongoModel::Collection[String]
11
+ property :subject, String, :default => "Form Submission"
12
+
13
+ validates_associated :fields
14
+
15
+ has_many :submissions, :class => "Forms::Submission"
16
+
17
+ activities :all
18
+
19
+ icon "wheelhouse-forms/form.png"
20
+
21
+ attr_accessor :view_context, :current_submission
22
+
23
+ def to_s
24
+ render(view_context)
25
+ end
26
+
27
+ def render(template)
28
+ Forms::FormRenderer.new(self, current_submission, template).render
29
+ end
30
+
31
+ def submit(params)
32
+ submission = submissions.build(:params => params)
33
+
34
+ if submission.save
35
+ deliver(submission)
36
+ @success = true
37
+ else
38
+ self.current_submission = submission
39
+ @success = false
40
+ end
41
+ end
42
+
43
+ def deliver(submission)
44
+ Forms::Mailer.submission(self, submission).deliver unless recipients.empty?
45
+ rescue
46
+ # Mail delivery failed
47
+ end
48
+
49
+ def success?
50
+ @success
51
+ end
52
+
53
+ def first_content_field
54
+ @first_content_field ||= fields.flatten.find { |f| f.respond_to?(:label) }
55
+ end
56
+
57
+ def recipients=(recipients)
58
+ recipients = recipients.split(/,/) if recipients.is_a?(String)
59
+ write_attribute(:recipients, recipients.map(&:strip))
60
+ end
61
+
62
+ def handler
63
+ Forms::FormHandler
64
+ end
65
+ end
@@ -0,0 +1,53 @@
1
+ class Forms::Submission < Wheelhouse::BasicResource
2
+ class Parameters < Hash
3
+ def to_mongo
4
+ result = []
5
+ each do |key, value|
6
+ result << [key, value]
7
+ end
8
+ result
9
+ end
10
+
11
+ def self.cast(params)
12
+ case params
13
+ when Hash
14
+ self[params]
15
+ when Array
16
+ params.each_with_object({}) { |v, hash| hash[v[0]] = v[1] }
17
+ end
18
+ end
19
+
20
+ def self.from_mongo(params)
21
+ cast(params)
22
+ end
23
+ end
24
+
25
+ include Wheelhouse::Resource::AdminPath
26
+
27
+ property :params, Parameters
28
+ timestamps!
29
+
30
+ belongs_to :form, :class => "Forms::Form"
31
+
32
+ default_scope order(:created_at.desc)
33
+
34
+ validate do
35
+ form.fields.flatten.each do |field|
36
+ if field.required? && value_for(field).blank?
37
+ errors.add(field.label, "This field is required")
38
+ end
39
+ end
40
+ end
41
+
42
+ def value_for(field)
43
+ params[field.label]
44
+ end
45
+
46
+ def admin_path
47
+ form_submission_path(form_id, id)
48
+ end
49
+
50
+ def admin_url
51
+ form_submission_url(form, id, :host => form.site.domain)
52
+ end
53
+ end
@@ -0,0 +1,7 @@
1
+ class Forms::CheckboxRenderer < Forms::FieldRenderer
2
+ def render
3
+ super do
4
+ label_tag { check_box_tag(name, "Yes", value, html_options) + " " + field.label }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ class Forms::CheckboxesRenderer < Forms::FieldRenderer
2
+ include Forms::LabelledFieldRenderer
3
+
4
+ def render
5
+ super { content_tag(:ul, safe_join(list_items, "\n")) }
6
+ end
7
+
8
+ protected
9
+ def list_items
10
+ field.options.map { |o| content_tag(:li, checkbox(o)) }
11
+ end
12
+
13
+ def checkbox(option)
14
+ content_tag(:label, :class => "option #{option.parameterize}") do
15
+ check_box_tag("#{name}[]", option, (value || []).include?(option), :id => nil) + " " + option
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ class Forms::ContentFieldRenderer < Forms::FieldRenderer
2
+ def render
3
+ field.content.to_s
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Forms::CustomFieldRenderer < Forms::FieldRenderer
2
+ def render
3
+ super { template.render :partial => field.partial, :locals => { :field => field } }
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ class Forms::FieldCollectionRenderer
2
+ include ActionView::Helpers::OutputSafetyHelper
3
+
4
+ attr_reader :collection, :submission, :template
5
+
6
+ def initialize(collection, submission, template)
7
+ @collection, @submission, @template = collection, submission, template
8
+ end
9
+
10
+ def render
11
+ safe_join(collection.map { |field| field.render(submission, template) }, "\n")
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ class Forms::FieldRenderer
2
+ include ActionView::Helpers::FormTagHelper
3
+ include ActionView::Helpers::OutputSafetyHelper
4
+
5
+ attr_accessor :output_buffer
6
+ attr_reader :field, :submission, :template
7
+
8
+ def initialize(field, submission, template)
9
+ @field, @submission, @template = field, submission, template
10
+ end
11
+
12
+ def render
13
+ content_tag(:div, :class => classes.join(" ")) do
14
+ concat yield if block_given?
15
+ concat content_tag(:p, error, :class => "error") if has_errors?
16
+ end
17
+ end
18
+
19
+ protected
20
+ def name
21
+ "submission[#{reference}]"
22
+ end
23
+
24
+ def reference
25
+ field.label
26
+ end
27
+
28
+ def field_class
29
+ field.class.partial.dasherize
30
+ end
31
+
32
+ def classes
33
+ classes = ["field", field_class]
34
+ classes << reference.parameterize
35
+ classes << "required" if field.required?
36
+ classes << "error" if has_errors?
37
+ classes.uniq
38
+ end
39
+
40
+ def value
41
+ submission.value_for(field) if submission
42
+ end
43
+
44
+ def has_errors?
45
+ submission && submission.errors[field.label].any?
46
+ end
47
+
48
+ def error
49
+ submission.errors[field.label].first
50
+ end
51
+
52
+ def html_options
53
+ options = {}
54
+ options[:required] = "required" if field.required?
55
+ options
56
+ end
57
+ end
@@ -0,0 +1,10 @@
1
+ class Forms::FieldSetRenderer < Forms::FieldRenderer
2
+ def render
3
+ content_tag(:fieldset, :class => field.legend? ? field.legend.parameterize : '') do
4
+ ActiveSupport::SafeBuffer.new.tap do |result|
5
+ result << content_tag(:legend, field.legend) if field.legend?
6
+ result << field.fields.render(submission, template)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ class Forms::FormRenderer
2
+ include ActionView::Helpers::FormTagHelper
3
+
4
+ attr_accessor :output_buffer
5
+ attr_reader :form, :submission, :template
6
+
7
+ def initialize(form, submission, template)
8
+ @form, @submission, @template = form, submission, template
9
+ end
10
+
11
+ def render
12
+ form_tag(form.path) do
13
+ concat form.fields.render(submission, template)
14
+ concat default_submit_button unless has_submit_button?
15
+ end
16
+ end
17
+
18
+ def protect_against_forgery?
19
+ false
20
+ end
21
+
22
+ private
23
+ def has_submit_button?
24
+ form.fields.flatten.any? { |f| f.is_a?(Forms::Fields::SubmitButton) }
25
+ end
26
+
27
+ def default_submit_button
28
+ content_tag(:div, submit_tag("Submit"), :class => "submit")
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module Forms::LabelledFieldRenderer
2
+ def render
3
+ super do
4
+ result = ActiveSupport::SafeBuffer.new
5
+ result << label_tag(name, field.label.html_safe) if field.label?
6
+ result << yield if block_given?
7
+ result
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ class Forms::RadioButtonsRenderer < Forms::FieldRenderer
2
+ include Forms::LabelledFieldRenderer
3
+
4
+ def render
5
+ super { content_tag(:ul, safe_join(list_items, "\n")) }
6
+ end
7
+
8
+ protected
9
+ def list_items
10
+ field.options.map { |o| content_tag(:li, radio_button(o)) }
11
+ end
12
+
13
+ def radio_button(option)
14
+ content_tag(:label, :class => "option #{option.parameterize}") do
15
+ radio_button_tag(name, option, value == option, :id => nil) + " " + option
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ class Forms::SelectFieldRenderer < Forms::FieldRenderer
2
+ include Forms::LabelledFieldRenderer
3
+
4
+ include ActionView::Helpers::FormOptionsHelper
5
+
6
+ def render
7
+ super { select_tag(name, options_for_select(field.options, value), html_options) }
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class Forms::SubmitButtonRenderer < Forms::FieldRenderer
2
+ def render
3
+ content_tag(:div, submit_tag(field.label), :class => "field submit")
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Forms::TextAreaRenderer < Forms::FieldRenderer
2
+ include Forms::LabelledFieldRenderer
3
+
4
+ def render
5
+ super { text_area_tag(name, value, html_options) }
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Forms::TextFieldRenderer < Forms::FieldRenderer
2
+ include Forms::LabelledFieldRenderer
3
+
4
+ def render
5
+ super { text_field_tag(name, value, html_options.merge(:type => field.input_type)) }
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ - template.name = "Default Form"
2
+ - template.resource = Forms::Form
3
+
4
+ = @form.content
5
+
6
+ - if @form.success?
7
+ = @form.content("Success Message", :rows => 15)
8
+ - else
9
+ = @form
@@ -0,0 +1,4 @@
1
+ - if @form.success?
2
+ = @form.content("Success Message")
3
+ - else
4
+ = @form
@@ -0,0 +1,15 @@
1
+ !!!
2
+
3
+ %html
4
+ %head
5
+
6
+ %body
7
+ %p= link_to "View this submission online", @submission.admin_url
8
+
9
+ %p
10
+ %strong Submitted at:
11
+ = @submission.created_at.to_s(:long)
12
+
13
+ - @form.fields.each do |field|
14
+ = render_field(field)
15
+
@@ -0,0 +1,3 @@
1
+ = form_field(checkbox, :index => index, :prefix => prefix, :class => "checkbox") do |f|
2
+ = f.text_field :label, :title => "Checkbox Label", :placeholder => "Checkbox Label", :id => nil
3
+ = f.required_field_checkbox
@@ -0,0 +1,10 @@
1
+ = form_field(checkboxes, :index => index, :prefix => prefix, :class => "checkboxes") do |f|
2
+ = f.text_field :label, :title => "Checkboxes Label", :placeholder => "Checkboxes Label", :id => nil
3
+ = f.required_field_checkbox
4
+
5
+ %ul.options
6
+ %li
7
+ %label Options:
8
+
9
+ - checkboxes.options(true).each do |option|
10
+ %li= text_field_tag "#{prefix}[options][]", option, :id => nil
@@ -0,0 +1,3 @@
1
+ = form_field(content_field, :index => index, :prefix => prefix, :class => "content-field", :icon => "content") do |f|
2
+ = f.text_area :content, :class => "editor", :rows => 5
3
+
@@ -0,0 +1,4 @@
1
+ = form_field(countries_dropdown, :index => index, :prefix => prefix, :class => "countries-dropdown", :icon => "countries") do |f|
2
+ = f.text_field :label, :title => "Countries Dropdown Label", :placeholder => "Countries Dropdown Label", :id => nil
3
+ = f.required_field_checkbox
4
+
@@ -0,0 +1,4 @@
1
+ = form_field(custom_field, :index => index, :prefix => prefix, :class => "custom-field", :icon => "custom") do |f|
2
+ = f.text_field :label, :title => "Custom Field Label", :placeholder => "Custom Field Label", :id => nil
3
+ = f.text_field :partial, :title => "Partial", :placeholder => "Partial", :id => nil
4
+
@@ -0,0 +1,16 @@
1
+ .field-set{ "data-index" => index }
2
+ = hidden_field_tag "#{prefix}[type]", field_set.type.demodulize, :id => nil
3
+
4
+ .drag
5
+
6
+ .drag-area
7
+ = label_tag "fieldset_#{index}_legend", "Fieldset Legend:"
8
+ = text_field_tag "#{prefix}[legend]", field_set.legend, :id => "fieldset_#{index}_legend"
9
+
10
+ %span.note (leave blank for none)
11
+
12
+ .fields{ "data-prefix" => "form[fields][#{index}][fields]" }
13
+ = render_fields(field_set.fields, "form[fields][#{index}][fields]")
14
+
15
+ = link_to "Delete", '#', :class => 'delete'
16
+