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,9 @@
1
+ class Forms::FormsController < Wheelhouse::ResourceController
2
+ self.resource_class = Forms::Form
3
+ manage_site_breadcrumb
4
+
5
+ def version
6
+ resource.revert_to(params[:version].to_i)
7
+ render :edit
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ require_dependency "forms/csv_exporter"
2
+
3
+ class Forms::SubmissionsController < Wheelhouse::ResourceController
4
+ belongs_to :form, :class_name => Forms::Form
5
+ self.resource_class = Forms::Submission
6
+
7
+ manage_site_breadcrumb
8
+ breadcrumb { [parent.label, parent] }
9
+
10
+ actions :show, :destroy
11
+
12
+ respond_to :csv
13
+
14
+ def index
15
+ respond_with(parent) do |format|
16
+ format.csv { render :text => Forms::CsvExporter.new(parent).to_csv }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class Forms::FormHandler < Wheelhouse::ResourceHandler
2
+ skip_before_filter :verify_authenticity_token
3
+
4
+ before_filter :set_form_context
5
+
6
+ get do
7
+ end
8
+
9
+ post do
10
+ @form.submit(params[:submission])
11
+ end
12
+
13
+ private
14
+ def set_form_context
15
+ @form.view_context = view_context
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module Forms::FormsHelper
2
+ class FormBuilder < ActionView::Helpers::FormBuilder
3
+ def required_field_checkbox
4
+ @template.content_tag(:label, :class => "required") do
5
+ check_box(:required, :id => nil) + " This field is required"
6
+ end
7
+ end
8
+ end
9
+
10
+ def form_field(field, options={}, &block)
11
+ builder = FormBuilder.new(options[:prefix], field, self, {}, block)
12
+ icon = options[:icon] || field.class.model_name.demodulize.underscore.dasherize
13
+
14
+ content_tag(:div, :class => options[:class], :data => { :index => options[:index] }) do
15
+ concat builder.hidden_field(:type, :value => field.type.demodulize, :id => nil)
16
+
17
+ concat content_tag(:div, image_tag("wheelhouse-forms/#{icon}.png", :alt => field.class.model_name.human, :title => field.class.model_name.human), :class => "drag")
18
+
19
+ concat content_tag(:div, capture(builder, &block), :class => "drag-area")
20
+
21
+ concat link_to("Delete", '#', :class => 'delete')
22
+ end
23
+ end
24
+
25
+ def render_fields(fields, prefix)
26
+ fields.map_with_index do |field, index|
27
+ render(:partial => field.class.partial, :object => field, :locals => { :index => index, :prefix => "#{prefix}[#{index}]" })
28
+ end.join("\n").html_safe
29
+ end
30
+
31
+ def field_template(id, klass)
32
+ jquery_template_tag(id) do
33
+ render :partial => klass.partial, :object => klass.new, :locals => { :index => "${index}", :prefix => "${prefix}" }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ module Forms::MailerHelper
2
+ def render_field(field)
3
+ case field
4
+ when Forms::Fields::FieldSet
5
+ render_fieldset(field)
6
+ when Forms::Fields::CustomField
7
+ render_custom_field(field)
8
+ when Forms::Fields::Checkboxes
9
+ render_array(field, @submission.value_for(field) || ["None given"])
10
+ when Forms::Fields::ContentField
11
+ # Nothing
12
+ else
13
+ render_text_field(field, @submission.value_for(field))
14
+ end
15
+ end
16
+
17
+ private
18
+ def render_fieldset(fieldset)
19
+ content_tag(:div, :class => "fieldset") do
20
+ concat content_tag(:h3, fieldset.legend) if fieldset.legend?
21
+ concat safe_join(fieldset.fields.map { |f| render_field(f) }, "\n")
22
+ nil
23
+ end
24
+ end
25
+
26
+ def render_custom_field(field)
27
+ value = @submission.value_for(field)
28
+
29
+ case value
30
+ when Array
31
+ render_array(field, value)
32
+ when Hash
33
+ render_hash(field, value)
34
+ else
35
+ render_text_field(field, value)
36
+ end
37
+ end
38
+
39
+ def render_array(field, array)
40
+ labelled_field(field) do
41
+ concat content_tag(:ul, safe_join(array.map { |i| content_tag(:li, i) }))
42
+ end
43
+ end
44
+
45
+ def render_hash(field, hash)
46
+ labelled_field(field) do
47
+ concat content_tag(:ul, safe_join(hash.map { |k, v|
48
+ content_tag(:li, content_tag(:strong, k) + ": " + v)
49
+ }))
50
+ end
51
+ end
52
+
53
+ def render_text_field(field, value)
54
+ labelled_field(field) do
55
+ concat field.is_a?(Forms::Fields::TextArea) ? tag(:br) : " "
56
+ concat value
57
+ end
58
+ end
59
+
60
+ def labelled_field(field)
61
+ content_tag(:p, :class => "field") do
62
+ concat content_tag(:strong, field.label + ":")
63
+ yield
64
+ nil
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,71 @@
1
+ module Forms::SubmissionsHelper
2
+ def render_field(field)
3
+ case field
4
+ when Forms::Fields::FieldSet
5
+ render_fieldset(field)
6
+ when Forms::Fields::CustomField
7
+ render_custom_field(field)
8
+ when Forms::Fields::Checkboxes
9
+ labelled_field(field) do
10
+ value = @submission.value_for(field)
11
+ render_array(field, value || [""])
12
+ end
13
+ when Forms::Fields::TextArea
14
+ labelled_field(field) do
15
+ value = @submission.value_for(field)
16
+ form.text_area field.label, :value => value || "", :readonly => true, :rows => 5
17
+ end
18
+ when Forms::Fields::ContentField
19
+ # Nothing
20
+ else
21
+ labelled_field(field) do
22
+ value = @submission.value_for(field)
23
+ form.text_field field.label, :value => value || "", :readonly => true
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ def labelled_field(field)
30
+ field(field.label, :label => field.label.html_safe) do
31
+ yield
32
+ end
33
+ end
34
+
35
+ def render_fieldset(fieldset)
36
+ content_tag(:div, :class => "shaded") do
37
+ concat content_tag(:h2, fieldset.legend) if fieldset.legend?
38
+ concat safe_join(fieldset.fields.map { |f| render_field(f) })
39
+ nil
40
+ end
41
+ end
42
+
43
+ def render_custom_field(field)
44
+ labelled_field(field) do
45
+ value = @submission.value_for(field)
46
+
47
+ case value
48
+ when Array
49
+ render_array(field, value)
50
+ when Hash
51
+ render_hash(field, value)
52
+ else
53
+ form.text_field field.label, :value => value || "", :readonly => true
54
+ end
55
+ end
56
+ end
57
+
58
+ def render_array(field, array)
59
+ content_tag(:ul, safe_join(array.map { |value|
60
+ content_tag(:li, form.text_field("#{field.label}[]", :value => value || "", :readonly => true))
61
+ }))
62
+ end
63
+
64
+ def render_hash(field, hash)
65
+ content_tag(:div, :class => "inline") do
66
+ safe_join(hash.map { |key, value|
67
+ form.label("#{field.label} #{key}", key) + form.text_field("#{field.label} #{key}", :value => value || "", :readonly => true, :size => nil)
68
+ })
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ class Forms::Mailer < ActionMailer::Base
2
+ default :template_path => "form"
3
+
4
+ def submission(form, submission)
5
+ @form, @submission = form, submission
6
+
7
+ mail(:from => "no-reply@#{form.site.domain}",
8
+ :to => form.recipients,
9
+ :subject => form.subject)
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ class Forms::FieldCollection < MongoModel::Collection[Forms::Fields::Field]
2
+ def map_with_index
3
+ result = []
4
+ each_with_index { |item, index| result[index] = yield(item, index) }
5
+ result
6
+ end
7
+
8
+ def self.cast(collection)
9
+ case collection
10
+ when Hash
11
+ sorted_attrs = collection.sort { |(a, _), (b, _)| a.to_i <=> b.to_i }.map { |_, attrs| attrs }
12
+ super(sorted_attrs)
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def render(submission, template)
19
+ Forms::FieldCollectionRenderer.new(self, submission, template).render
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module Forms::Fields
2
+ class Checkbox < Field
3
+ property :label, String, :required => true
4
+ property :required, Boolean, :default => false
5
+
6
+ self.renderer = Forms::CheckboxRenderer
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module Forms::Fields
2
+ class Checkboxes < Field
3
+ include OptionedField
4
+
5
+ property :label, String, :required => true
6
+ property :required, Boolean, :default => false
7
+
8
+ self.renderer = Forms::CheckboxesRenderer
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Forms::Fields
2
+ class ContentField < Field
3
+ property :content, Translated[ActionView::OutputBuffer]
4
+
5
+ self.renderer = Forms::ContentFieldRenderer
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ module Forms::Fields
2
+ class CountriesDropdown < Field
3
+ property :label, String, :default => "Country", :required => true
4
+ property :required, Boolean, :default => false
5
+
6
+ self.renderer = Forms::SelectFieldRenderer
7
+
8
+ def options
9
+ COUNTRIES
10
+ end
11
+
12
+ COUNTRIES = Forms::Plugin.load_yaml("countries.yml")
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module Forms::Fields
2
+ class CustomField < Field
3
+ property :label, String, :required => true
4
+ property :partial, String
5
+
6
+ self.renderer = Forms::CustomFieldRenderer
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ module Forms::Fields
2
+ class Field < Wheelhouse::EmbeddedResource
3
+ class_attribute :renderer
4
+ self.renderer = Forms::FieldRenderer
5
+
6
+ def render(submission, template)
7
+ self.class.renderer.new(self, submission, template).render
8
+ end
9
+
10
+ def required?
11
+ read_attribute(:required)
12
+ end
13
+
14
+ def self.partial
15
+ name.demodulize.underscore
16
+ end
17
+
18
+ def self.cast(attrs)
19
+ case attrs
20
+ when Hash
21
+ type = attrs.delete('type')
22
+ Forms::Fields.const_get(type).new(attrs)
23
+ else
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module Forms::Fields
2
+ class FieldSet < Field
3
+ property :legend, String
4
+ property :fields, Forms::FieldCollection
5
+
6
+ validates_associated :fields
7
+
8
+ self.renderer = Forms::FieldSetRenderer
9
+
10
+ def to_ary
11
+ fields
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Forms::Fields
2
+ module OptionedField
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ property :options, MongoModel::Collection[String]
7
+ end
8
+
9
+ def options=(options)
10
+ write_attribute(:options, options.reject(&:blank?))
11
+ end
12
+
13
+ def options(include_blank = false)
14
+ read_attribute(:options) + (include_blank ? [""] : [])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Forms::Fields
2
+ class RadioButtons < Field
3
+ include OptionedField
4
+
5
+ property :label, String, :required => true
6
+ property :required, Boolean, :default => false
7
+
8
+ self.renderer = Forms::RadioButtonsRenderer
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Forms::Fields
2
+ class SelectField < Field
3
+ include OptionedField
4
+
5
+ property :label, String, :required => true
6
+ property :required, Boolean, :default => false
7
+
8
+ self.renderer = Forms::SelectFieldRenderer
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module Forms::Fields
2
+ class StatesDropdown < Field
3
+ property :label, String, :default => "State", :required => true
4
+ property :country, String, :required => true
5
+ property :required, Boolean, :default => false
6
+
7
+ self.renderer = Forms::SelectFieldRenderer
8
+
9
+ def options
10
+ STATES[country]
11
+ end
12
+
13
+ STATES = Forms::Plugin.load_yaml("states.yml").tap { |hash| hash.each { |k, v| hash[k] = v.invert } }
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Forms::Fields
2
+ class SubmitButton < Field
3
+ property :label, String, :default => "Submit", :required => true
4
+
5
+ self.renderer = Forms::SubmitButtonRenderer
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Forms::Fields
2
+ class TextArea < Field
3
+ property :label, String, :required => true
4
+ property :required, Boolean, :default => false
5
+
6
+ self.renderer = Forms::TextAreaRenderer
7
+ end
8
+ end