form_input 0.9.0.pre1
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/.gitignore +5 -0
- data/LICENSE +19 -0
- data/README.md +3160 -0
- data/Rakefile +19 -0
- data/example/controllers/ramaze/press_release.rb +104 -0
- data/example/controllers/ramaze/profile.rb +38 -0
- data/example/controllers/sinatra/press_release.rb +114 -0
- data/example/controllers/sinatra/profile.rb +39 -0
- data/example/forms/change_password_form.rb +17 -0
- data/example/forms/login_form.rb +14 -0
- data/example/forms/lost_password_form.rb +14 -0
- data/example/forms/new_password_form.rb +15 -0
- data/example/forms/password_form.rb +18 -0
- data/example/forms/press_release_form.rb +153 -0
- data/example/forms/profile_form.rb +21 -0
- data/example/forms/signup_form.rb +25 -0
- data/example/views/press_release.slim +65 -0
- data/example/views/profile.slim +28 -0
- data/example/views/snippets/form_block.slim +27 -0
- data/example/views/snippets/form_chunked.slim +25 -0
- data/example/views/snippets/form_hidden.slim +21 -0
- data/example/views/snippets/form_panel.slim +89 -0
- data/form_input.gemspec +32 -0
- data/lib/form_input/core.rb +1165 -0
- data/lib/form_input/localize.rb +49 -0
- data/lib/form_input/r18n/cs.yml +97 -0
- data/lib/form_input/r18n/en.yml +70 -0
- data/lib/form_input/r18n/pl.yml +122 -0
- data/lib/form_input/r18n/sk.yml +120 -0
- data/lib/form_input/r18n.rb +163 -0
- data/lib/form_input/steps.rb +365 -0
- data/lib/form_input/types.rb +176 -0
- data/lib/form_input/version.rb +12 -0
- data/lib/form_input.rb +5 -0
- data/test/helper.rb +21 -0
- data/test/localize/en.yml +63 -0
- data/test/r18n/cs.yml +60 -0
- data/test/r18n/xx.yml +51 -0
- data/test/reference/cs.txt +352 -0
- data/test/reference/cs.yml +14 -0
- data/test/reference/en.txt +76 -0
- data/test/reference/en.yml +8 -0
- data/test/reference/pl.txt +440 -0
- data/test/reference/pl.yml +16 -0
- data/test/reference/sk.txt +352 -0
- data/test/reference/sk.yml +14 -0
- data/test/test_core.rb +1272 -0
- data/test/test_localize.rb +27 -0
- data/test/test_r18n.rb +373 -0
- data/test/test_steps.rb +482 -0
- data/test/test_types.rb +307 -0
- metadata +145 -0
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Rake makefile.
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
desc 'Run tests'
|
8
|
+
task :test do
|
9
|
+
sh "bacon --automatic --quiet"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Run tests with coverage'
|
13
|
+
task :cov do
|
14
|
+
sh "rm -rf coverage"
|
15
|
+
ENV['COVERAGE'] = 'on'
|
16
|
+
Rake::Task[:test].execute
|
17
|
+
end
|
18
|
+
|
19
|
+
# EOF #
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# The press release controller - example of a complex multistep form handling in Ramaze.
|
2
|
+
|
3
|
+
class PRController < Controller
|
4
|
+
|
5
|
+
# Create press release.
|
6
|
+
def create
|
7
|
+
|
8
|
+
# Start with fresh form.
|
9
|
+
|
10
|
+
unless request.post?
|
11
|
+
@form = PressReleaseForm.new(
|
12
|
+
user.active_plan,
|
13
|
+
date: Time.now,
|
14
|
+
city: user.profile.city,
|
15
|
+
state: user.profile.state,
|
16
|
+
country: user.profile.country,
|
17
|
+
contact_name: user.full_name,
|
18
|
+
contact_organization: user.profile.company,
|
19
|
+
contact_phone: user.profile.phone,
|
20
|
+
contact_email: user.email,
|
21
|
+
)
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
# Process the data from the post.
|
26
|
+
|
27
|
+
@form = PressReleaseForm.new( user.active_plan, request )
|
28
|
+
return unless process_form( @form )
|
29
|
+
|
30
|
+
# Save the press release.
|
31
|
+
|
32
|
+
unless user.create_press_release( @form )
|
33
|
+
@form.step = :summary
|
34
|
+
@state = :create_failed
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
redirect r
|
39
|
+
end
|
40
|
+
|
41
|
+
# Edit press release.
|
42
|
+
def edit( hid )
|
43
|
+
|
44
|
+
# Fetch the existing press release.
|
45
|
+
# Note that it can be edited by admins in addition to its owner.
|
46
|
+
|
47
|
+
not_found unless pr = PressRelease.from_hid( hid )
|
48
|
+
|
49
|
+
owner = ( pr.user_id == user.id )
|
50
|
+
forbidden unless owner or user.admin?
|
51
|
+
|
52
|
+
# Start by filling the form from the press release.
|
53
|
+
|
54
|
+
unless request.post?
|
55
|
+
@form = PressReleaseForm.new( pr.user.active_plan, pr.form_hash ).unlock_steps
|
56
|
+
return
|
57
|
+
end
|
58
|
+
|
59
|
+
# Process the data from the post.
|
60
|
+
|
61
|
+
pr = get_press_release( hid )
|
62
|
+
@form = PressReleaseForm.new( pr.user.active_plan, request )
|
63
|
+
return unless process_form( @form )
|
64
|
+
|
65
|
+
# Update the press release.
|
66
|
+
|
67
|
+
unless pr.user.update_press_release( pr, @form, user )
|
68
|
+
@form.step = :summary
|
69
|
+
@state = :update_failed
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
redirect owner ? r : AdminController.r( :press_releases )
|
74
|
+
end
|
75
|
+
|
76
|
+
alias_view :edit, :create
|
77
|
+
|
78
|
+
# Common logic for processing the form steps.
|
79
|
+
def process_form( form )
|
80
|
+
|
81
|
+
# Report errors whenever there are some in the currently finished step.
|
82
|
+
|
83
|
+
@state = :report if form.incomplete_step?
|
84
|
+
|
85
|
+
# That's all until the last steps are reached.
|
86
|
+
|
87
|
+
return if form.step_before?( :summary )
|
88
|
+
|
89
|
+
# In case the form is still invalid, return to the appropriate step.
|
90
|
+
|
91
|
+
unless form.valid?
|
92
|
+
form.step = form.incorrect_step
|
93
|
+
@state = :report
|
94
|
+
return
|
95
|
+
end
|
96
|
+
|
97
|
+
# That's all until the last step is reached.
|
98
|
+
|
99
|
+
return form.last_step?
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
# EOF #
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Example of simple form handling in Ramaze.
|
2
|
+
|
3
|
+
class HomeController < Controller
|
4
|
+
|
5
|
+
# Profile.
|
6
|
+
def profile
|
7
|
+
|
8
|
+
# Prefill the form with current user profile.
|
9
|
+
|
10
|
+
unless request.post?
|
11
|
+
@form = ProfileForm.new( user.profile_hash )
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check new data when the form is posted.
|
16
|
+
|
17
|
+
@form = ProfileForm.new( request )
|
18
|
+
unless @form.valid?
|
19
|
+
@state = :report
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
# Attempt to update the profile.
|
24
|
+
|
25
|
+
unless user.update_profile( @form )
|
26
|
+
@state = :update_failed
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
# Refetch the profile just in case the model changed something, and report success.
|
31
|
+
|
32
|
+
@form = ProfileForm.new( user.profile_hash )
|
33
|
+
@state = :done
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# EOF #
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# The press release controller - example of a complex multistep form handling in Sinatra.
|
2
|
+
|
3
|
+
class App < Sinatra::Base
|
4
|
+
|
5
|
+
# Create press release - start.
|
6
|
+
get '/pr/create' do
|
7
|
+
|
8
|
+
# Start with fresh form, prefilled with some user info.
|
9
|
+
|
10
|
+
@form = PressReleaseForm.new(
|
11
|
+
user.active_plan,
|
12
|
+
date: Time.now,
|
13
|
+
city: user.profile.city,
|
14
|
+
state: user.profile.state,
|
15
|
+
country: user.profile.country,
|
16
|
+
contact_name: user.full_name,
|
17
|
+
contact_organization: user.profile.company,
|
18
|
+
contact_phone: user.profile.phone,
|
19
|
+
contact_email: user.email,
|
20
|
+
)
|
21
|
+
slim :press_release
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create press release - creation.
|
25
|
+
post '/pr/create' do
|
26
|
+
|
27
|
+
# Process the data from the post.
|
28
|
+
|
29
|
+
@form = PressReleaseForm.new( user.active_plan, request )
|
30
|
+
unless process_form( @form )
|
31
|
+
return slim :press_release
|
32
|
+
end
|
33
|
+
|
34
|
+
# Save the press release.
|
35
|
+
|
36
|
+
unless user.create_press_release( @form )
|
37
|
+
@form.step = :summary
|
38
|
+
@state = :create_failed
|
39
|
+
return slim :press_release
|
40
|
+
end
|
41
|
+
|
42
|
+
redirect '/'
|
43
|
+
end
|
44
|
+
|
45
|
+
# Edit press release - start.
|
46
|
+
get '/pr/edit/:hid' do |hid|
|
47
|
+
|
48
|
+
# Fetch the existing press release.
|
49
|
+
# Note that it can be edited by admins in addition to its owner.
|
50
|
+
|
51
|
+
pr = get_press_release( hid )
|
52
|
+
@form = PressReleaseForm.new( pr.user.active_plan, pr.form_hash ).unlock_steps
|
53
|
+
slim :press_release
|
54
|
+
end
|
55
|
+
|
56
|
+
# Edit press release - update.
|
57
|
+
post '/pr/edit/:hid' do |hid|
|
58
|
+
|
59
|
+
# Process the data from the post.
|
60
|
+
|
61
|
+
pr = get_press_release( hid )
|
62
|
+
@form = PressReleaseForm.new( pr.user.active_plan, request )
|
63
|
+
unless process_form( @form )
|
64
|
+
return slim :press_release
|
65
|
+
end
|
66
|
+
|
67
|
+
# Update the press release.
|
68
|
+
|
69
|
+
unless pr.user.update_press_release( pr, @form, user )
|
70
|
+
@form.step = :summary
|
71
|
+
@state = :update_failed
|
72
|
+
return slim :press_release
|
73
|
+
end
|
74
|
+
|
75
|
+
redirect '/'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get the press release for given hashed id.
|
79
|
+
def get_press_release( hid )
|
80
|
+
not_found unless pr = PressRelease.from_hid( hid )
|
81
|
+
|
82
|
+
owner = ( pr.user_id == user.id )
|
83
|
+
forbidden unless owner or user.admin?
|
84
|
+
|
85
|
+
pr
|
86
|
+
end
|
87
|
+
|
88
|
+
# Common logic for processing the form steps.
|
89
|
+
def process_form( form )
|
90
|
+
|
91
|
+
# Report errors whenever there are some in the currently finished step.
|
92
|
+
|
93
|
+
@state = :report if form.incomplete_step?
|
94
|
+
|
95
|
+
# That's all until the last steps are reached.
|
96
|
+
|
97
|
+
return if form.step_before?( :summary )
|
98
|
+
|
99
|
+
# In case the form is still invalid, return to the appropriate step.
|
100
|
+
|
101
|
+
unless form.valid?
|
102
|
+
form.step = form.incorrect_step
|
103
|
+
@state = :report
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
# That's all until the last step is reached.
|
108
|
+
|
109
|
+
return form.last_step?
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# EOF #
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Example of simple form handling in Sinatra.
|
2
|
+
|
3
|
+
class App < Sinatra::Application
|
4
|
+
|
5
|
+
get '/profile' do
|
6
|
+
|
7
|
+
# Prefill the form with current user profile.
|
8
|
+
|
9
|
+
@form = ProfileForm.new( user.profile_hash )
|
10
|
+
slim :profile
|
11
|
+
end
|
12
|
+
|
13
|
+
post '/profile' do
|
14
|
+
|
15
|
+
# Validate the posted data.
|
16
|
+
|
17
|
+
@form = ProfileForm.new( request )
|
18
|
+
unless @form.valid?
|
19
|
+
@state = :report
|
20
|
+
return slim :profile
|
21
|
+
end
|
22
|
+
|
23
|
+
# Attempt to update the profile.
|
24
|
+
|
25
|
+
unless user.update_profile( @form )
|
26
|
+
@state = :update_failed
|
27
|
+
return slim :profile
|
28
|
+
end
|
29
|
+
|
30
|
+
# Refetch the profile just in case the model changed something, and report success.
|
31
|
+
|
32
|
+
@form = ProfileForm.new( user.profile_hash )
|
33
|
+
@state = :done
|
34
|
+
slim :profile
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# EOF #
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Change password form.
|
2
|
+
|
3
|
+
require_relative 'new_password_form'
|
4
|
+
|
5
|
+
class ChangePasswordForm < FormInput
|
6
|
+
|
7
|
+
# Note that we intentionally don't impose any format on this,
|
8
|
+
# as we don't want to assume what they have registered with.
|
9
|
+
# Only strip the password newline in case they cut&paste it from somewhere.
|
10
|
+
|
11
|
+
param! :old_password, "Old password", type: :password, filter: ->{ chomp }
|
12
|
+
|
13
|
+
copy NewPasswordForm
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
# EOF #
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Login form.
|
2
|
+
|
3
|
+
class LoginForm < FormInput
|
4
|
+
|
5
|
+
# Note that we intentionally don't impose any format of these,
|
6
|
+
# as we don't want to assume what they have registered with.
|
7
|
+
# Only strip the password newline in case they cut&paste it from somewhere.
|
8
|
+
|
9
|
+
param! :login, :email, "Email"
|
10
|
+
param! :password, "Password", type: :password, filter: ->{ chomp }
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# EOF #
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Lost password form.
|
2
|
+
|
3
|
+
class LostPasswordForm < FormInput
|
4
|
+
|
5
|
+
# Note that we intentionally don't impose any format of the email,
|
6
|
+
# as we don't want to assume what they have registered with.
|
7
|
+
# We will let them know in case we don't find it anyway.
|
8
|
+
# We merely check if it looks like an email at all.
|
9
|
+
|
10
|
+
param! :email, "Email", match: /@/
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# EOF #
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# New password form.
|
2
|
+
|
3
|
+
require_relative 'password_form'
|
4
|
+
|
5
|
+
class NewPasswordForm < FormInput
|
6
|
+
|
7
|
+
copy PasswordForm, title: "New password"
|
8
|
+
|
9
|
+
param! :password_check, "Repeated password", type: :password,
|
10
|
+
filter: self[ :password ].filter,
|
11
|
+
check: ->{ report( "%p must match password" ) unless value == form.password }
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
# EOF #
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Password form.
|
2
|
+
|
3
|
+
# Separate form with only the password parameter to make it easy to include it wherever necessary.
|
4
|
+
class PasswordForm < FormInput
|
5
|
+
|
6
|
+
# Enforce size and format of the password.
|
7
|
+
# Note that we strip the password newline in case they cut&paste it from somewhere.
|
8
|
+
|
9
|
+
param! :password, "Password", type: :password, min_size: 6,
|
10
|
+
reject: /\P{ASCII}|[\t\r\n]/u,
|
11
|
+
reject_msg: "%p may contain only ASCII characters and spaces",
|
12
|
+
match: [ /[a-z]/i, /\d/ ],
|
13
|
+
msg: "%p must contain at least one digit and one letter",
|
14
|
+
filter: ->{ chomp }
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
# EOF #
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Press Release Form - example of a complex multistep form.
|
2
|
+
|
3
|
+
class PressReleaseForm < FormInput
|
4
|
+
|
5
|
+
### Parameters ###
|
6
|
+
|
7
|
+
# Pricing plan we use for evaluating things.
|
8
|
+
|
9
|
+
attr_reader :plan
|
10
|
+
|
11
|
+
# Creation process steps.
|
12
|
+
|
13
|
+
define_steps(
|
14
|
+
content: "Content",
|
15
|
+
date: "Date",
|
16
|
+
location: "Location",
|
17
|
+
contact: "Contact",
|
18
|
+
keywords: "Keywords",
|
19
|
+
quote: "Quote",
|
20
|
+
images: "Images",
|
21
|
+
videos: "Videos",
|
22
|
+
website: "Web Site",
|
23
|
+
release: "Release Date",
|
24
|
+
channels: "Channels",
|
25
|
+
summary: "Summary",
|
26
|
+
post: nil,
|
27
|
+
)
|
28
|
+
|
29
|
+
# Press release itself.
|
30
|
+
|
31
|
+
param! :title, "Title", 120, tag: :content,
|
32
|
+
subtitle: "(Maximum 120 characters, recommended max 80 characters)",
|
33
|
+
help: "The main title of your press release. It should be brief, clear and to the point."
|
34
|
+
param :subtitle, "Subtitle", 160, tag: :content,
|
35
|
+
subtitle: "(Maximum 160 characters)",
|
36
|
+
help: "Optional but recommended. If used, make sure it's descriptive and builds on the headline."
|
37
|
+
param! :text, "Text", 65000, max_bytesize: 65535, type: :textarea, size: 16, tag: :content,
|
38
|
+
subtitle: ->{ "(Maximum #{plural( form.limit( :words ), 'word' )})" },
|
39
|
+
filter: ->{ strip },
|
40
|
+
check: ->{
|
41
|
+
count = form.feature_count( :words, value )
|
42
|
+
limit = form.limit( :words )
|
43
|
+
report "Your text has #{plural( count, 'word' )}, but the limit is #{delimited( limit )}." if count > limit
|
44
|
+
}
|
45
|
+
|
46
|
+
# Date.
|
47
|
+
|
48
|
+
param! :date, "Date", DATE_ARGS, tag: :date
|
49
|
+
|
50
|
+
# Location.
|
51
|
+
|
52
|
+
param! :city, "City", 100, tag: :location
|
53
|
+
param :state, "State", STATE_ARGS, tag: :location
|
54
|
+
param! :country, "Country", COUNTRY_ARGS, tag: :location
|
55
|
+
|
56
|
+
# Contact info.
|
57
|
+
|
58
|
+
param! :contact_name, "Full Name", 60, tag: :contact
|
59
|
+
param! :contact_organization, "Organization Name", 60, tag: :contact
|
60
|
+
param! :contact_phone, "Phone", 30, PHONE_ARGS, tag: :contact
|
61
|
+
param! :contact_email, "Email Address", 60, EMAIL_ARGS, tag: :contact
|
62
|
+
|
63
|
+
# Keywords.
|
64
|
+
|
65
|
+
array :keywords, "Keywords", 35, PRUNED_ARGS, tag: :keywords,
|
66
|
+
row: :keywords, cols: 5,
|
67
|
+
max_count: ->{ form.limit( :keywords ) }
|
68
|
+
array :keywords_urls, "Keywords URL", PRUNED_ARGS, WEB_URL_ARGS, max_count: 30, tag: :keywords,
|
69
|
+
row: :keywords, cols: 7,
|
70
|
+
max_count: ->{ form.limit( :keywords ) }
|
71
|
+
|
72
|
+
def balance_keywords
|
73
|
+
self.keywords ||= []
|
74
|
+
self.keywords_urls ||= []
|
75
|
+
|
76
|
+
report( :keywords, "Keywords must match the keyword URLs" ) if keywords.count < keywords_urls.count
|
77
|
+
report( :keywords_urls, "Keyword URLs must match the keywords" ) if keywords.count > keywords_urls.count
|
78
|
+
|
79
|
+
self.keywords << "" while keywords.count < keywords_urls.count
|
80
|
+
self.keywords_urls << "" while keywords.count > keywords_urls.count
|
81
|
+
end
|
82
|
+
|
83
|
+
# Quote.
|
84
|
+
|
85
|
+
param :quote, "Quote", 200, tag: :quote,
|
86
|
+
subtitle: "(Maximum 200 characters)",
|
87
|
+
disabled: ->{ form.disabled?( :quote ) }
|
88
|
+
param :quote_author, "Quote Author", 100, tag: :quote,
|
89
|
+
subtitle: "(Maximum 100 characters)",
|
90
|
+
disabled: ->{ form.disabled?( :quote ) }
|
91
|
+
|
92
|
+
# Images.
|
93
|
+
|
94
|
+
array :images, "Images", PRUNED_ARGS, tag: :images,
|
95
|
+
max_count: ->{ form.limit( :images ) }
|
96
|
+
|
97
|
+
# Video.
|
98
|
+
|
99
|
+
array :videos, "Videos", PRUNED_ARGS, tag: :videos,
|
100
|
+
max_count: ->{ form.limit( :videos ) }
|
101
|
+
|
102
|
+
# Website.
|
103
|
+
|
104
|
+
param :website_url, "URL", WEB_URL_ARGS, tag: :website,
|
105
|
+
disabled: ->{ form.disabled?( :website ) }
|
106
|
+
|
107
|
+
# Release date.
|
108
|
+
|
109
|
+
param :release_date, "Release Date", US_DATE_ARGS, tag: :release,
|
110
|
+
disabled: ->{ form.disabled?( :scheduling ) }
|
111
|
+
param :release_time, "Release Time", HOURS_ARGS, tag: :release,
|
112
|
+
disabled: ->{ form.disabled?( :scheduling ) }
|
113
|
+
|
114
|
+
# Distribution.
|
115
|
+
|
116
|
+
array! :channels, "Channels", type: :select, tag: :channels,
|
117
|
+
size: 25,
|
118
|
+
max_count: ->{ form.limit( :channels ) },
|
119
|
+
data: ->{ Channel.all.map{ |x| [ x.code, x.name ] } },
|
120
|
+
filter: ->{ self if Channel[ self ] }
|
121
|
+
|
122
|
+
### Methods ###
|
123
|
+
|
124
|
+
# Initialize new instance.
|
125
|
+
def initialize( plan, *args )
|
126
|
+
@plan = plan
|
127
|
+
super( *args )
|
128
|
+
balance_keywords
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get the items to show in the sidebar.
|
132
|
+
def sidebar_items
|
133
|
+
{ base: 'Base Price' }.merge( step_names )
|
134
|
+
end
|
135
|
+
|
136
|
+
# Get limit for given feature.
|
137
|
+
def limit( feature )
|
138
|
+
plan.extra_limit( feature )
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get count for given feature and value.
|
142
|
+
def feature_count( feature, value )
|
143
|
+
plan.feature_count( feature, value )
|
144
|
+
end
|
145
|
+
|
146
|
+
# Test if given feature is disabled for current plan.
|
147
|
+
def disabled?( feature )
|
148
|
+
not plan.has_feature?( feature )
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
# EOF #
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Profile form.
|
2
|
+
|
3
|
+
require_relative 'signup_form'
|
4
|
+
|
5
|
+
class ProfileForm < FormInput
|
6
|
+
|
7
|
+
copy SignupForm[ :first_name, :last_name ]
|
8
|
+
|
9
|
+
param :company, "Company"
|
10
|
+
param :street, "Street"
|
11
|
+
param :city, "City"
|
12
|
+
param :state, "State", STATE_ARGS
|
13
|
+
param :country, "Country", COUNTRY_ARGS
|
14
|
+
param :zip, "ZIP code", ZIP_ARGS
|
15
|
+
param :phone, "Phone number", PHONE_ARGS
|
16
|
+
param :fax, "Fax number", PHONE_ARGS
|
17
|
+
param :url, "URL", WEB_URL_ARGS
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# EOF #
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Signup form.
|
2
|
+
|
3
|
+
require_relative 'password_form'
|
4
|
+
|
5
|
+
class SignupForm < FormInput
|
6
|
+
|
7
|
+
# Currently we restrict names to latin based alphabets.
|
8
|
+
|
9
|
+
param! :first_name, "First name", match: LATIN_NAMES_RE,
|
10
|
+
msg: "Sorry, only names using latin alphabet are allowed"
|
11
|
+
param! :last_name, "Last name", match: LATIN_NAMES_RE,
|
12
|
+
msg: "Sorry, only surnames using latin alphabet are allowed"
|
13
|
+
|
14
|
+
# Note that we only enforce relaxed format of the emails.
|
15
|
+
# Real problems will be found out by verification anyway.
|
16
|
+
|
17
|
+
param! :login, :email, "Email", EMAIL_ARGS
|
18
|
+
|
19
|
+
# Enforce size and format of the password the same way the standard password form does.
|
20
|
+
|
21
|
+
copy PasswordForm
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# EOF #
|
@@ -0,0 +1,65 @@
|
|
1
|
+
// Press Release editing form - example of a complex multi-step form.
|
2
|
+
|
3
|
+
- @title = "Press Release: #{@form.step_name}"
|
4
|
+
|
5
|
+
- edit_mode = ( page_action == 'edit' )
|
6
|
+
|
7
|
+
form *form_attrs
|
8
|
+
|
9
|
+
// Invisible first submit button to send users to the next page
|
10
|
+
// when they press enter in the text field.
|
11
|
+
button.invisible type='submit' name='next' tabindex='-1' value=@form.next_step
|
12
|
+
|
13
|
+
.row
|
14
|
+
.col-md-3 role='navigation'
|
15
|
+
|
16
|
+
== snippet :sidebar,
|
17
|
+
title: "#{edit_mode ? 'Edit' : 'Create'} Press Release",
|
18
|
+
items: @form.sidebar_items,
|
19
|
+
accessible: @form.accessible_steps,
|
20
|
+
valid: @form.good_steps,
|
21
|
+
invalid: @form.bad_steps,
|
22
|
+
current: @form.step
|
23
|
+
|
24
|
+
.col-md-9 role='main'
|
25
|
+
|
26
|
+
.panel.panel-default
|
27
|
+
.panel-heading
|
28
|
+
= @title
|
29
|
+
.panel-body
|
30
|
+
|
31
|
+
// Additional info derived from @state or @form.step will usually go here.
|
32
|
+
|
33
|
+
fieldset
|
34
|
+
== snippet :form_hidden,
|
35
|
+
params: @form.other_params
|
36
|
+
== snippet :form_chunked,
|
37
|
+
params: @form.chunked_params( @form.current_params ),
|
38
|
+
report: @state == :report,
|
39
|
+
focus: true
|
40
|
+
|
41
|
+
- unless @form.extra_step?
|
42
|
+
.btn-toolbar.pull-left
|
43
|
+
button.btn.btn-default type='submit'
|
44
|
+
Update
|
45
|
+
|
46
|
+
.btn-toolbar.pull-right
|
47
|
+
a.btn.btn-default href=r
|
48
|
+
Cancel
|
49
|
+
- if @form.step == :summary
|
50
|
+
button.btn.btn-primary type='submit' name='next' value='post'
|
51
|
+
Save
|
52
|
+
- else
|
53
|
+
button.btn.btn-default type='submit' name='next' value='summary'
|
54
|
+
Finish
|
55
|
+
- unless edit_mode
|
56
|
+
button.btn.btn-primary.pull-right type='submit' name='next' value=@form.next_step
|
57
|
+
Next
|
58
|
+
|
59
|
+
// These make it easy to debug the form if needed:
|
60
|
+
//
|
61
|
+
// pre = @form.params.map{|x| [ x.name, x.value ] }.inspect
|
62
|
+
// pre = @form.errors.inspect
|
63
|
+
// pre = @state
|
64
|
+
|
65
|
+
// EOF //
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// Update user profile - example of rendering simple form page.
|
2
|
+
|
3
|
+
- @title = "Profile"
|
4
|
+
|
5
|
+
.panel.panel-default
|
6
|
+
.panel-heading
|
7
|
+
= @title
|
8
|
+
.panel-body
|
9
|
+
form *form_attrs
|
10
|
+
|
11
|
+
- case @state
|
12
|
+
- when :done
|
13
|
+
.alert.alert-success.alert-block
|
14
|
+
Your profile was successfully updated.
|
15
|
+
- when :update_failed
|
16
|
+
.alert.alert-danger.alert-block
|
17
|
+
strong Profile update failed!
|
18
|
+
br
|
19
|
+
We apologize for such inconvenience. Please try again later.
|
20
|
+
|
21
|
+
fieldset
|
22
|
+
== snippet :form_panel,
|
23
|
+
params: @form.params,
|
24
|
+
report: @state == :report
|
25
|
+
focus: @state == :report
|
26
|
+
button.btn.btn-default type='submit' Update Profile
|
27
|
+
|
28
|
+
// EOF //
|