formbuilder-rb 0.0.1
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +24 -0
- data/app/controllers/formbuilder/forms_controller.rb +53 -0
- data/app/models/formbuilder/entry_attachment.rb +15 -0
- data/app/models/formbuilder/form.rb +12 -0
- data/app/models/formbuilder/response_field.rb +85 -0
- data/app/models/formbuilder/response_field_address.rb +349 -0
- data/app/models/formbuilder/response_field_checkboxes.rb +68 -0
- data/app/models/formbuilder/response_field_date.rb +46 -0
- data/app/models/formbuilder/response_field_dropdown.rb +30 -0
- data/app/models/formbuilder/response_field_email.rb +26 -0
- data/app/models/formbuilder/response_field_file.rb +44 -0
- data/app/models/formbuilder/response_field_number.rb +33 -0
- data/app/models/formbuilder/response_field_paragraph.rb +21 -0
- data/app/models/formbuilder/response_field_price.rb +47 -0
- data/app/models/formbuilder/response_field_radio.rb +52 -0
- data/app/models/formbuilder/response_field_section_break.rb +17 -0
- data/app/models/formbuilder/response_field_text.rb +16 -0
- data/app/models/formbuilder/response_field_time.rb +53 -0
- data/app/models/formbuilder/response_field_website.rb +31 -0
- data/app/uploaders/formbuilder/entry_attachment_uploader.rb +26 -0
- data/db/migrate/20130924185726_create_formbuilder_forms.rb +10 -0
- data/db/migrate/20130924185814_create_formbuilder_response_fields.rb +16 -0
- data/db/migrate/20130924185815_create_formbuilder_entry_attachments.rb +10 -0
- data/lib/formbuilder/engine.rb +5 -0
- data/lib/formbuilder/entry.rb +242 -0
- data/lib/formbuilder/entry_renderer.rb +47 -0
- data/lib/formbuilder/entry_table_renderer.rb +58 -0
- data/lib/formbuilder/entry_validator.rb +107 -0
- data/lib/formbuilder/form_renderer.rb +102 -0
- data/lib/formbuilder/version.rb +3 -0
- data/lib/formbuilder.rb +12 -0
- data/lib/tasks/formbuilder_tasks.rake +4 -0
- metadata +258 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldDropdown < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@options_field = true
|
6
|
+
@field_type = 'dropdown'
|
7
|
+
}
|
8
|
+
|
9
|
+
def render_input(value, opts = {})
|
10
|
+
options = ""
|
11
|
+
options += "<option></option>" if self[:field_options]['include_blank_option']
|
12
|
+
|
13
|
+
Array(self[:field_options]['options']).each_with_index do |option, i|
|
14
|
+
selected = (value == option["label"]) || (value.blank? && option["checked"])
|
15
|
+
options += "<option value='#{option["label"]}' #{selected ? 'selected' : ''}>#{option['label']}</option>"
|
16
|
+
end
|
17
|
+
|
18
|
+
"""
|
19
|
+
<select name='response_fields[#{self[:id]}]'>
|
20
|
+
#{options}
|
21
|
+
</select>
|
22
|
+
"""
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_entry(value, opts = {})
|
26
|
+
value
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldEmail < ResponseField
|
3
|
+
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
|
6
|
+
after_initialize -> {
|
7
|
+
@field_type = 'email'
|
8
|
+
}
|
9
|
+
|
10
|
+
def render_input(value, opts = {})
|
11
|
+
tag(:input, type: 'text', name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
|
12
|
+
data: self.length_validations, value: value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_entry(value, opts = {})
|
16
|
+
"<a href='mailto:#{value}'>#{value}</a>"
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_response(value)
|
20
|
+
unless value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
|
21
|
+
"isn't a valid email address."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldFile < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@field_type = 'file'
|
6
|
+
}
|
7
|
+
|
8
|
+
# @todo dropzone?
|
9
|
+
def render_input(value, opts = {})
|
10
|
+
attachment = value && EntryAttachment.find(value)
|
11
|
+
|
12
|
+
"""
|
13
|
+
<span class='existing-filename'>#{attachment.try(:upload).try(:file).try(:filename).try(:gsub, /\?.*$/, '')}</span>
|
14
|
+
<input type='file' name='response_fields[#{self[:id]}]' />
|
15
|
+
"""
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_entry(value, opts = {})
|
19
|
+
attachment = EntryAttachment.find(value)
|
20
|
+
|
21
|
+
return unless attachment
|
22
|
+
|
23
|
+
str = ""
|
24
|
+
|
25
|
+
str += """
|
26
|
+
<a href='#{attachment.upload.url}' target='_blank'>
|
27
|
+
"""
|
28
|
+
|
29
|
+
if attachment.upload.send(:active_versions).include?(:thumb)
|
30
|
+
str += """
|
31
|
+
<img src='#{attachment.upload.thumb.url}' /><br />
|
32
|
+
"""
|
33
|
+
end
|
34
|
+
|
35
|
+
str +="""
|
36
|
+
#{attachment.upload.try(:file).try(:filename).try(:gsub, /\?.*$/, '')}
|
37
|
+
</a>
|
38
|
+
"""
|
39
|
+
|
40
|
+
str
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldNumber < ResponseField
|
3
|
+
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
|
6
|
+
after_initialize -> {
|
7
|
+
@sort_as_numeric = true
|
8
|
+
@field_type = 'number'
|
9
|
+
}
|
10
|
+
|
11
|
+
def render_input(value, opts = {})
|
12
|
+
str = tag(:input, type: 'text', name: "response_fields[#{self.id}]",
|
13
|
+
class: "rf-size-#{self[:field_options]['size']}", value: value)
|
14
|
+
|
15
|
+
if (units = self[:field_options]['units'].presence)
|
16
|
+
str += "<span class='units'>#{units}</span>".html_safe
|
17
|
+
end
|
18
|
+
|
19
|
+
str
|
20
|
+
end
|
21
|
+
|
22
|
+
def render_entry(value, opts = {})
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate_response(value)
|
27
|
+
unless (Float(value) rescue nil)
|
28
|
+
"isn't a valid number."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldParagraph < ResponseField
|
3
|
+
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
include ActionView::Context
|
6
|
+
|
7
|
+
after_initialize -> {
|
8
|
+
@field_type = 'paragraph'
|
9
|
+
}
|
10
|
+
|
11
|
+
def render_input(value, opts = {})
|
12
|
+
content_tag(:textarea, name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
|
13
|
+
data: self.length_validations) { value }
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_entry(value, opts = {})
|
17
|
+
ActionController::Base.helpers.simple_format(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldPrice < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@serialized = true
|
6
|
+
@sort_as_numeric = true
|
7
|
+
@field_type = 'price'
|
8
|
+
}
|
9
|
+
|
10
|
+
def render_input(value, opts = {})
|
11
|
+
"""
|
12
|
+
<div class='input-line'>
|
13
|
+
<span class='above-line'>$</span>
|
14
|
+
|
15
|
+
<span class='dollars'>
|
16
|
+
<input type='text' name='response_fields[#{self[:id]}][dollars]' value='#{value['dollars']}' />
|
17
|
+
<label>Dollars</label>
|
18
|
+
</span>
|
19
|
+
|
20
|
+
<span class='above-line'>.</span>
|
21
|
+
|
22
|
+
<span class='cents'>
|
23
|
+
<input type='text' name='response_fields[#{self[:id]}][cents]' value='#{value['cents']}' maxlength='2' />
|
24
|
+
<label>Cents</label>
|
25
|
+
</span>
|
26
|
+
</div>
|
27
|
+
"""
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_entry(value, opts = {})
|
31
|
+
"$#{sprintf('%.2f', "#{value['dollars']}.#{value['cents']}".to_f)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# format: [dollars] [cents]
|
35
|
+
# only one is required, and it must consist only of numbers
|
36
|
+
def validate_response(value)
|
37
|
+
value.select { |k, v| k.in?(['dollars', 'cents']) && v.present? }.each do |k, v|
|
38
|
+
unless (Float(v) rescue 0) > 0
|
39
|
+
return "isn't a valid price."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldRadio < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@options_field = true
|
6
|
+
@field_type = 'radio'
|
7
|
+
}
|
8
|
+
|
9
|
+
def render_input(value, opts = {})
|
10
|
+
value ||= {}
|
11
|
+
|
12
|
+
str = (self[:field_options]["options"] || []).each_with_index.map do |option, i|
|
13
|
+
|
14
|
+
checked = (value == option["label"]) || (value.blank? && option["checked"])
|
15
|
+
|
16
|
+
"""
|
17
|
+
<label>
|
18
|
+
<input type='radio' name='response_fields[#{self[:id]}]' #{checked ? 'checked' : ''} value='#{option['label']}' />
|
19
|
+
#{option['label']}
|
20
|
+
</label>
|
21
|
+
"""
|
22
|
+
end.join('')
|
23
|
+
|
24
|
+
if self[:field_options]['include_other_option']
|
25
|
+
str += """
|
26
|
+
<div class='other-option'>
|
27
|
+
<label>
|
28
|
+
<input type='radio' name='response_fields[#{self[:id]}]' #{value == 'Other' ? 'checked' : ''} value='Other' />
|
29
|
+
Other
|
30
|
+
</label>
|
31
|
+
|
32
|
+
<input type='text' name='response_fields[#{self[:id]}_other]'
|
33
|
+
value='#{opts.try(:[], :entry).try(:responses).try(:[], "#{self[:id]}_other")}' />
|
34
|
+
</div>
|
35
|
+
"""
|
36
|
+
end
|
37
|
+
|
38
|
+
str
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_entry(value, opts = {})
|
42
|
+
str = value
|
43
|
+
|
44
|
+
if (other = opts.try(:[], :entry).try(:responses).try(:[], "#{self.id}_other"))
|
45
|
+
str += " (#{other})"
|
46
|
+
end
|
47
|
+
|
48
|
+
str
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldSectionBreak < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@input_field = false
|
6
|
+
@field_type = 'section_break'
|
7
|
+
}
|
8
|
+
|
9
|
+
def render_input(value, opts = {})
|
10
|
+
"""
|
11
|
+
<label class='section-name'>#{self[:label]}</label>
|
12
|
+
<p>#{self[:field_options]['description']}</p>
|
13
|
+
"""
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldText < ResponseField
|
3
|
+
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
|
6
|
+
after_initialize -> {
|
7
|
+
@field_type = 'text'
|
8
|
+
}
|
9
|
+
|
10
|
+
def render_input(value, opts = {})
|
11
|
+
tag(:input, type: 'text', name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
|
12
|
+
data: self.length_validations, value: value)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldTime < ResponseField
|
3
|
+
|
4
|
+
after_initialize -> {
|
5
|
+
@serialized = true
|
6
|
+
@sort_as_numeric = true
|
7
|
+
@field_type = 'time'
|
8
|
+
}
|
9
|
+
|
10
|
+
def render_input(value, opts = {})
|
11
|
+
"""
|
12
|
+
<div class='input-line'>
|
13
|
+
<span class='hours'>
|
14
|
+
<input type='text' name='response_fields[#{self[:id]}][hours]' value='#{value['hours']}' maxlength='2' />
|
15
|
+
<label>HH</label>
|
16
|
+
</span>
|
17
|
+
|
18
|
+
<span class='above-line'>:</span>
|
19
|
+
|
20
|
+
<span class='minutes'>
|
21
|
+
<input type='text' name='response_fields[#{self[:id]}][minutes]' value='#{value['minutes']}' maxlength='2' />
|
22
|
+
<label>MM</label>
|
23
|
+
</span>
|
24
|
+
|
25
|
+
<span class='above-line'>:</span>
|
26
|
+
|
27
|
+
<span class='seconds'>
|
28
|
+
<input type='text' name='response_fields[#{self[:id]}][seconds]' value='#{value['seconds']}' maxlength='2' />
|
29
|
+
<label>SS</label>
|
30
|
+
</span>
|
31
|
+
|
32
|
+
<span class='am_pm'>
|
33
|
+
<select name='response_fields[#{self[:id]}][am_pm]'>
|
34
|
+
<option value='AM' #{value['am_pm'] == "AM" ? 'selected' : ''}>AM</option>
|
35
|
+
<option value='PM' #{value['am_pm'] == "PM" ? 'selected' : ''}>PM</option>
|
36
|
+
</select>
|
37
|
+
</span>
|
38
|
+
</div>
|
39
|
+
"""
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_entry(value, opts = {})
|
43
|
+
"#{value['hours']}:#{value['minutes']}#{if !value['seconds'].blank? then ':'+value['seconds'] end} #{value['am_pm']}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_response(value)
|
47
|
+
if !value['hours'].to_i.between?(1, 12) || !value['minutes'].to_i.between?(0, 60) || !value['seconds'].to_i.between?(0, 60)
|
48
|
+
"isn't a valid time."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Formbuilder
|
2
|
+
class ResponseFieldWebsite < ResponseField
|
3
|
+
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
|
6
|
+
after_initialize -> {
|
7
|
+
@field_type = 'website'
|
8
|
+
}
|
9
|
+
|
10
|
+
def render_input(value, opts = {})
|
11
|
+
tag(:input, type: 'text', name: "response_fields[#{self.id}]", class: "rf-size-#{self[:field_options]['size']}",
|
12
|
+
value: value, placeholder: 'http://')
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_entry(value, opts = {})
|
16
|
+
"<a href='#{value}' target='_blank' rel='nofollow'>#{value}</a>"
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_response(value)
|
20
|
+
require 'uri'
|
21
|
+
|
22
|
+
# add http if not present
|
23
|
+
value = "http://#{value}" unless value[/^http:\/\//] || value[/^https:\/\//]
|
24
|
+
|
25
|
+
if !(value =~ URI::regexp) # this doesn't really validate, since almost *anything* matches
|
26
|
+
"isn't a valid URL."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'carrierwave/processing/mime_types'
|
2
|
+
|
3
|
+
module Formbuilder
|
4
|
+
class EntryAttachmentUploader < CarrierWave::Uploader::Base
|
5
|
+
include CarrierWave::MimeTypes
|
6
|
+
include CarrierWave::RMagick
|
7
|
+
|
8
|
+
@fog_public = false
|
9
|
+
|
10
|
+
process :set_content_type
|
11
|
+
|
12
|
+
version :thumb, :if => :image? do
|
13
|
+
process resize_to_limit: [250, 250]
|
14
|
+
end
|
15
|
+
|
16
|
+
def store_dir
|
17
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def image?(file)
|
22
|
+
content_type = file.content_type || model.try(:content_type)
|
23
|
+
content_type && content_type.include?('image')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateFormbuilderResponseFields < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :formbuilder_response_fields do |t|
|
4
|
+
t.integer :form_id
|
5
|
+
t.text :label
|
6
|
+
t.string :type
|
7
|
+
t.text :field_options
|
8
|
+
t.integer :sort_order
|
9
|
+
t.boolean :required, default: false
|
10
|
+
t.boolean :blind, default: false
|
11
|
+
t.boolean :admin_only, default: false
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|