formbuilder-rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|