wheelhouse-forms 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+