fat_free_crm 0.19.2 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fat_free_crm might be problematic. Click here for more details.

Files changed (226) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +46 -0
  3. data/.github/workflows/rubocop-analysis.yml +40 -0
  4. data/.github/workflows/ruby.yml +52 -0
  5. data/.rubocop_todo.yml +49 -43
  6. data/.travis.yml +1 -20
  7. data/CHANGELOG.md +13 -0
  8. data/Gemfile +9 -5
  9. data/Gemfile.lock +163 -119
  10. data/README.md +4 -3
  11. data/SECURITY.md +11 -0
  12. data/app/assets/javascripts/application.js.erb +3 -0
  13. data/app/assets/stylesheets/application.css.erb +1 -0
  14. data/app/assets/stylesheets/base.scss +9 -2
  15. data/app/assets/stylesheets/bootstrap-custom.scss +206 -0
  16. data/app/assets/stylesheets/common.scss +87 -67
  17. data/app/assets/stylesheets/header.scss +0 -8
  18. data/app/controllers/admin/field_groups_controller.rb +1 -1
  19. data/app/controllers/admin/fields_controller.rb +1 -1
  20. data/app/controllers/admin/groups_controller.rb +1 -1
  21. data/app/controllers/admin/tags_controller.rb +2 -2
  22. data/app/controllers/admin/users_controller.rb +2 -1
  23. data/app/controllers/application_controller.rb +10 -0
  24. data/app/controllers/comments_controller.rb +1 -1
  25. data/app/controllers/entities/accounts_controller.rb +1 -1
  26. data/app/controllers/entities/campaigns_controller.rb +1 -1
  27. data/app/controllers/home_controller.rb +3 -1
  28. data/app/controllers/lists_controller.rb +1 -1
  29. data/app/controllers/tasks_controller.rb +3 -3
  30. data/app/controllers/users_controller.rb +3 -1
  31. data/app/helpers/accounts_helper.rb +12 -0
  32. data/app/helpers/application_helper.rb +3 -5
  33. data/app/helpers/tasks_helper.rb +2 -2
  34. data/app/models/entities/account.rb +1 -1
  35. data/app/models/entities/account_contact.rb +1 -1
  36. data/app/models/entities/account_opportunity.rb +1 -1
  37. data/app/models/entities/campaign.rb +1 -1
  38. data/app/models/entities/contact.rb +1 -1
  39. data/app/models/entities/lead.rb +1 -1
  40. data/app/models/entities/opportunity.rb +1 -1
  41. data/app/models/fields/custom_field_pair.rb +2 -2
  42. data/app/models/observers/opportunity_observer.rb +1 -1
  43. data/app/models/polymorphic/address.rb +1 -1
  44. data/app/models/polymorphic/comment.rb +1 -1
  45. data/app/models/polymorphic/email.rb +1 -1
  46. data/app/models/polymorphic/task.rb +1 -1
  47. data/app/models/setting.rb +4 -2
  48. data/app/models/users/preference.rb +2 -2
  49. data/app/models/users/user.rb +2 -2
  50. data/app/views/accounts/_edit.html.haml +2 -2
  51. data/app/views/accounts/_new.html.haml +2 -2
  52. data/app/views/accounts/_sidebar_index.html.haml +2 -2
  53. data/app/views/accounts/_sidebar_show.html.haml +19 -19
  54. data/app/views/accounts/create.js.haml +1 -2
  55. data/app/views/accounts/destroy.js.haml +1 -1
  56. data/app/views/accounts/show.html.haml +1 -0
  57. data/app/views/accounts/update.js.haml +2 -3
  58. data/app/views/admin/field_groups/create.js.haml +0 -1
  59. data/app/views/admin/field_groups/destroy.js.haml +0 -1
  60. data/app/views/admin/field_groups/update.js.haml +0 -1
  61. data/app/views/admin/fields/create.js.haml +0 -1
  62. data/app/views/admin/fields/destroy.js.haml +0 -1
  63. data/app/views/admin/fields/update.js.haml +0 -1
  64. data/app/views/admin/groups/create.js.haml +0 -1
  65. data/app/views/admin/groups/destroy.js.haml +0 -1
  66. data/app/views/admin/groups/update.js.haml +0 -1
  67. data/app/views/admin/tags/create.js.haml +0 -1
  68. data/app/views/admin/tags/destroy.js.haml +0 -1
  69. data/app/views/admin/tags/update.js.haml +0 -1
  70. data/app/views/admin/users/create.js.haml +0 -1
  71. data/app/views/admin/users/destroy.js.haml +0 -1
  72. data/app/views/admin/users/update.js.haml +0 -1
  73. data/app/views/campaigns/_edit.html.haml +1 -1
  74. data/app/views/campaigns/_new.html.haml +1 -1
  75. data/app/views/campaigns/_sidebar_index.html.haml +2 -2
  76. data/app/views/campaigns/_sidebar_show.html.haml +68 -67
  77. data/app/views/campaigns/create.js.haml +1 -2
  78. data/app/views/campaigns/destroy.js.haml +1 -1
  79. data/app/views/campaigns/show.html.haml +1 -0
  80. data/app/views/campaigns/update.js.haml +3 -4
  81. data/app/views/comments/_edit.html.haml +1 -1
  82. data/app/views/comments/_new.html.haml +1 -1
  83. data/app/views/comments/update.js.haml +0 -1
  84. data/app/views/contacts/_edit.html.haml +1 -1
  85. data/app/views/contacts/_new.html.haml +1 -1
  86. data/app/views/contacts/_sidebar_show.html.haml +18 -18
  87. data/app/views/contacts/create.js.haml +0 -1
  88. data/app/views/contacts/destroy.js.haml +1 -1
  89. data/app/views/contacts/show.html.haml +1 -0
  90. data/app/views/contacts/update.js.haml +2 -3
  91. data/app/views/entities/_title_bar.html.haml +1 -1
  92. data/app/views/entities/attach.js.haml +2 -2
  93. data/app/views/entities/discard.js.haml +2 -2
  94. data/app/views/home/_opportunity.html.haml +1 -1
  95. data/app/views/home/_task.html.haml +1 -1
  96. data/app/views/home/index.xls.builder +51 -0
  97. data/app/views/layouts/_sidebar.html.haml +2 -2
  98. data/app/views/layouts/_tabbed.html.haml +14 -11
  99. data/app/views/layouts/application.html.haml +2 -2
  100. data/app/views/leads/_edit.html.haml +2 -2
  101. data/app/views/leads/_new.html.haml +2 -2
  102. data/app/views/leads/_sidebar_index.html.haml +2 -2
  103. data/app/views/leads/_sidebar_show.html.haml +30 -30
  104. data/app/views/leads/create.js.haml +2 -3
  105. data/app/views/leads/destroy.js.haml +2 -2
  106. data/app/views/leads/promote.js.haml +3 -4
  107. data/app/views/leads/reject.js.haml +3 -3
  108. data/app/views/leads/show.html.haml +1 -1
  109. data/app/views/leads/update.js.haml +4 -5
  110. data/app/views/lists/create.js.haml +0 -1
  111. data/app/views/opportunities/_edit.html.haml +1 -1
  112. data/app/views/opportunities/_index_brief.html.haml +1 -1
  113. data/app/views/opportunities/_index_long.html.haml +1 -1
  114. data/app/views/opportunities/_new.html.haml +1 -1
  115. data/app/views/opportunities/_sidebar_index.html.haml +2 -2
  116. data/app/views/opportunities/_sidebar_show.html.haml +47 -46
  117. data/app/views/opportunities/create.js.haml +3 -4
  118. data/app/views/opportunities/destroy.js.haml +3 -3
  119. data/app/views/opportunities/show.html.haml +1 -0
  120. data/app/views/opportunities/update.js.haml +5 -6
  121. data/app/views/shared/_inline_styles.html.haml +0 -26
  122. data/app/views/tasks/_assigned.html.haml +1 -1
  123. data/app/views/tasks/_completed.html.haml +1 -1
  124. data/app/views/tasks/_edit.html.haml +1 -1
  125. data/app/views/tasks/_new.html.haml +1 -1
  126. data/app/views/tasks/_pending.html.haml +1 -1
  127. data/app/views/tasks/_related.html.haml +1 -1
  128. data/app/views/tasks/_selector.html.haml +7 -8
  129. data/app/views/tasks/_sidebar_index.html.haml +2 -2
  130. data/app/views/tasks/_title.html.haml +1 -1
  131. data/app/views/tasks/complete.js.haml +1 -1
  132. data/app/views/tasks/create.js.haml +1 -2
  133. data/app/views/tasks/destroy.js.haml +1 -1
  134. data/app/views/tasks/uncomplete.js.haml +1 -2
  135. data/app/views/tasks/update.js.haml +0 -1
  136. data/app/views/users/change_password.js.haml +0 -1
  137. data/app/views/users/update.js.haml +0 -1
  138. data/app/views/users/upload_avatar.js.haml +0 -1
  139. data/app/views/versions/_version_item.html.haml +1 -1
  140. data/bin/setup +6 -6
  141. data/config/application.rb +1 -1
  142. data/config/environment.rb +1 -1
  143. data/config/initializers/content_security_policy.rb +5 -0
  144. data/config/initializers/new_framework_defaults_6_0.rb +46 -0
  145. data/config/initializers/simple_form.rb +135 -55
  146. data/config/initializers/simple_form_bootstrap.rb +433 -0
  147. data/config/locales/fat_free_crm.cs.yml +86 -47
  148. data/config/locales/fat_free_crm.ru.yml +1 -0
  149. data/config/settings.default.yml +0 -41
  150. data/db/migrate/20200806004152_add_pattern_to_fields.rb +7 -0
  151. data/db/migrate/20200806004459_add_html5_to_fields.rb +10 -0
  152. data/db/schema.rb +11 -6
  153. data/fat_free_crm.gemspec +4 -4
  154. data/lib/fat_free_crm/core_ext.rb +1 -1
  155. data/lib/fat_free_crm/gem_ext.rb +0 -1
  156. data/lib/fat_free_crm/tabs.rb +2 -2
  157. data/lib/fat_free_crm/version.rb +2 -2
  158. data/lib/gravatar_image_tag.rb +1 -1
  159. data/lib/tasks/ffcrm/preference_update.rake +19 -0
  160. data/lib/tasks/ffcrm/setup.rake +1 -1
  161. data/lib/templates/erb/scaffold/_form.html.erb +4 -2
  162. data/spec/controllers/comments_controller_spec.rb +6 -6
  163. data/spec/controllers/home_controller_spec.rb +3 -3
  164. data/spec/controllers/users_controller_spec.rb +13 -1
  165. data/spec/factories/user_factories.rb +5 -2
  166. data/spec/features/acceptance_helper.rb +1 -1
  167. data/spec/features/accounts_spec.rb +2 -2
  168. data/spec/features/admin/groups_spec.rb +1 -1
  169. data/spec/features/admin/users_spec.rb +1 -1
  170. data/spec/features/campaigns_spec.rb +2 -2
  171. data/spec/features/contacts_spec.rb +2 -5
  172. data/spec/features/leads_spec.rb +11 -7
  173. data/spec/features/opportunities_spec.rb +4 -4
  174. data/spec/features/support/browser.rb +6 -2
  175. data/spec/features/support/selector_helpers.rb +10 -1
  176. data/spec/features/tasks_spec.rb +6 -6
  177. data/spec/helpers/accounts_helper_spec.rb +57 -0
  178. data/spec/models/entities/opportunity_spec.rb +2 -2
  179. data/spec/models/fields/custom_field_pair_spec.rb +2 -2
  180. data/spec/models/observers/entity_observer_spec.rb +3 -3
  181. data/spec/models/polymorphic/task_spec.rb +11 -11
  182. data/spec/models/polymorphic/version_spec.rb +2 -2
  183. data/spec/models/users/preference_spec.rb +1 -1
  184. data/spec/views/accounts/_edit.haml_spec.rb +1 -1
  185. data/spec/views/accounts/_new.haml_spec.rb +1 -1
  186. data/spec/views/accounts/create.js.haml_spec.rb +0 -1
  187. data/spec/views/accounts/update.js.haml_spec.rb +2 -5
  188. data/spec/views/admin/field_groups/create.js.haml_spec.rb +0 -1
  189. data/spec/views/admin/field_groups/update.js.haml_spec.rb +0 -1
  190. data/spec/views/admin/users/create.js.haml_spec.rb +0 -1
  191. data/spec/views/admin/users/destroy.js.haml_spec.rb +0 -6
  192. data/spec/views/admin/users/update.js.haml_spec.rb +1 -2
  193. data/spec/views/campaigns/_edit.haml_spec.rb +1 -1
  194. data/spec/views/campaigns/_new.haml_spec.rb +1 -1
  195. data/spec/views/campaigns/create.js.haml_spec.rb +0 -1
  196. data/spec/views/campaigns/destroy.js.haml_spec.rb +0 -1
  197. data/spec/views/campaigns/update.js.haml_spec.rb +2 -6
  198. data/spec/views/contacts/_edit.haml_spec.rb +1 -1
  199. data/spec/views/contacts/_new.haml_spec.rb +1 -1
  200. data/spec/views/contacts/create.js.haml_spec.rb +0 -1
  201. data/spec/views/contacts/update.js.haml_spec.rb +3 -7
  202. data/spec/views/home/index.haml_spec.rb +2 -2
  203. data/spec/views/leads/_edit.haml_spec.rb +1 -1
  204. data/spec/views/leads/_new.haml_spec.rb +1 -1
  205. data/spec/views/leads/create.js.haml_spec.rb +0 -3
  206. data/spec/views/leads/destroy.js.haml_spec.rb +0 -2
  207. data/spec/views/leads/promote.js.haml_spec.rb +3 -11
  208. data/spec/views/leads/reject.js.haml_spec.rb +0 -3
  209. data/spec/views/leads/update.js.haml_spec.rb +3 -11
  210. data/spec/views/opportunities/_edit.haml_spec.rb +1 -1
  211. data/spec/views/opportunities/_new.haml_spec.rb +1 -1
  212. data/spec/views/opportunities/create.js.haml_spec.rb +0 -2
  213. data/spec/views/opportunities/destroy.js.haml_spec.rb +0 -3
  214. data/spec/views/opportunities/update.js.haml_spec.rb +3 -11
  215. data/spec/views/tasks/_edit.haml_spec.rb +1 -1
  216. data/spec/views/tasks/complete.js.haml_spec.rb +0 -1
  217. data/spec/views/tasks/create.js.haml_spec.rb +0 -2
  218. data/spec/views/tasks/destroy.js.haml_spec.rb +0 -1
  219. data/spec/views/tasks/uncomplete.js.haml_spec.rb +0 -1
  220. data/spec/views/tasks/update.js.haml_spec.rb +1 -4
  221. data/spec/views/users/change_password.js.haml_spec.rb +1 -2
  222. data/spec/views/users/update.js.haml_spec.rb +1 -2
  223. data/spec/views/users/upload_avatar.js.haml_spec.rb +1 -2
  224. metadata +23 -14
  225. data/config/initializers/new_framework_defaults_5_2.rb +0 -40
  226. data/lib/fat_free_crm/gem_ext/simple_form/action_view_extensions/form_helper.rb +0 -24
@@ -0,0 +1,433 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Please do not make direct changes to this file!
4
+ # This generator is maintained by the community around simple_form-bootstrap:
5
+ # https://github.com/rafaelfranca/simple_form-bootstrap
6
+ # All future development, tests, and organization should happen there.
7
+ # Background history: https://github.com/heartcombo/simple_form/issues/1561
8
+
9
+ # Uncomment this and change the path if necessary to include your own
10
+ # components.
11
+ # See https://github.com/heartcombo/simple_form#custom-components
12
+ # to know more about custom components.
13
+ # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
14
+
15
+ # Use this setup block to configure all options available in SimpleForm.
16
+ SimpleForm.setup do |config|
17
+ # Default class for buttons
18
+ config.button_class = 'btn'
19
+
20
+ # Define the default class of the input wrapper of the boolean input.
21
+ config.boolean_label_class = 'form-check-label'
22
+
23
+ # How the label text should be generated altogether with the required text.
24
+ config.label_text = ->(label, required, _explicit_label) { "#{label} #{required}" }
25
+
26
+ # Define the way to render check boxes / radio buttons with labels.
27
+ config.boolean_style = :inline
28
+
29
+ # You can wrap each item in a collection of radio/check boxes with a tag
30
+ config.item_wrapper_tag = :div
31
+
32
+ # Defines if the default input wrapper class should be included in radio
33
+ # collection wrappers.
34
+ config.include_default_input_wrapper_class = false
35
+
36
+ # CSS class to add for error notification helper.
37
+ config.error_notification_class = 'alert alert-danger'
38
+
39
+ # Method used to tidy up errors. Specify any Rails Array method.
40
+ # :first lists the first message for each field.
41
+ # :to_sentence to list all errors for each field.
42
+ config.error_method = :to_sentence
43
+
44
+ # add validation classes to `input_field`
45
+ config.input_field_error_class = 'is-invalid'
46
+ config.input_field_valid_class = 'is-valid'
47
+
48
+ # vertical forms
49
+ #
50
+ # vertical default_wrapper
51
+ config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
52
+ b.use :html5
53
+ b.use :placeholder
54
+ b.optional :maxlength
55
+ b.optional :minlength
56
+ b.optional :pattern
57
+ b.optional :min_max
58
+ b.optional :readonly
59
+ b.use :label
60
+ b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
61
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
62
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
63
+ end
64
+
65
+ # vertical input for boolean
66
+ config.wrappers :vertical_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
67
+ b.use :html5
68
+ b.optional :readonly
69
+ b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
70
+ bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
71
+ bb.use :label, class: 'form-check-label'
72
+ bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
73
+ bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
74
+ end
75
+ end
76
+
77
+ # vertical input for radio buttons and check boxes
78
+ config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
79
+ b.use :html5
80
+ b.optional :readonly
81
+ b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
82
+ ba.use :label_text
83
+ end
84
+ b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
85
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
86
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
87
+ end
88
+
89
+ # vertical input for inline radio buttons and check boxes
90
+ config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
91
+ b.use :html5
92
+ b.optional :readonly
93
+ b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
94
+ ba.use :label_text
95
+ end
96
+ b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
97
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
98
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
99
+ end
100
+
101
+ # vertical file input
102
+ config.wrappers :vertical_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
103
+ b.use :html5
104
+ b.use :placeholder
105
+ b.optional :maxlength
106
+ b.optional :minlength
107
+ b.optional :readonly
108
+ b.use :label
109
+ b.use :input, class: 'form-control-file', error_class: 'is-invalid', valid_class: 'is-valid'
110
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
111
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
112
+ end
113
+
114
+ # vertical multi select
115
+ config.wrappers :vertical_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
116
+ b.use :html5
117
+ b.optional :readonly
118
+ b.use :label
119
+ b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
120
+ ba.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
121
+ end
122
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
123
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
124
+ end
125
+
126
+ # vertical range input
127
+ config.wrappers :vertical_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
128
+ b.use :html5
129
+ b.use :placeholder
130
+ b.optional :readonly
131
+ b.optional :step
132
+ b.use :label
133
+ b.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
134
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
135
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
136
+ end
137
+
138
+ # horizontal forms
139
+ #
140
+ # horizontal default_wrapper
141
+ config.wrappers :horizontal_form, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
142
+ b.use :html5
143
+ b.use :placeholder
144
+ b.optional :maxlength
145
+ b.optional :minlength
146
+ b.optional :pattern
147
+ b.optional :min_max
148
+ b.optional :readonly
149
+ b.use :label, class: 'col-sm-3 col-form-label'
150
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
151
+ ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
152
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
153
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
154
+ end
155
+ end
156
+
157
+ # horizontal input for boolean
158
+ config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
159
+ b.use :html5
160
+ b.optional :readonly
161
+ b.wrapper tag: 'label', class: 'col-sm-3' do |ba|
162
+ ba.use :label_text
163
+ end
164
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |wr|
165
+ wr.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
166
+ bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
167
+ bb.use :label, class: 'form-check-label'
168
+ bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
169
+ bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
170
+ end
171
+ end
172
+ end
173
+
174
+ # horizontal input for radio buttons and check boxes
175
+ config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
176
+ b.use :html5
177
+ b.optional :readonly
178
+ b.use :label, class: 'col-sm-3 col-form-label pt-0'
179
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
180
+ ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
181
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
182
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
183
+ end
184
+ end
185
+
186
+ # horizontal input for inline radio buttons and check boxes
187
+ config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
188
+ b.use :html5
189
+ b.optional :readonly
190
+ b.use :label, class: 'col-sm-3 col-form-label pt-0'
191
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
192
+ ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
193
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
194
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
195
+ end
196
+ end
197
+
198
+ # horizontal file input
199
+ config.wrappers :horizontal_file, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
200
+ b.use :html5
201
+ b.use :placeholder
202
+ b.optional :maxlength
203
+ b.optional :minlength
204
+ b.optional :readonly
205
+ b.use :label, class: 'col-sm-3 col-form-label'
206
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
207
+ ba.use :input, error_class: 'is-invalid', valid_class: 'is-valid'
208
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
209
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
210
+ end
211
+ end
212
+
213
+ # horizontal multi select
214
+ config.wrappers :horizontal_multi_select, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
215
+ b.use :html5
216
+ b.optional :readonly
217
+ b.use :label, class: 'col-sm-3 col-form-label'
218
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
219
+ ba.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
220
+ bb.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
221
+ end
222
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
223
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
224
+ end
225
+ end
226
+
227
+ # horizontal range input
228
+ config.wrappers :horizontal_range, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
229
+ b.use :html5
230
+ b.use :placeholder
231
+ b.optional :readonly
232
+ b.optional :step
233
+ b.use :label, class: 'col-sm-3 col-form-label'
234
+ b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
235
+ ba.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
236
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
237
+ ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
238
+ end
239
+ end
240
+
241
+ # inline forms
242
+ #
243
+ # inline default_wrapper
244
+ config.wrappers :inline_form, tag: 'span', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
245
+ b.use :html5
246
+ b.use :placeholder
247
+ b.optional :maxlength
248
+ b.optional :minlength
249
+ b.optional :pattern
250
+ b.optional :min_max
251
+ b.optional :readonly
252
+ b.use :label, class: 'sr-only'
253
+
254
+ b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
255
+ b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
256
+ b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
257
+ end
258
+
259
+ # inline input for boolean
260
+ config.wrappers :inline_boolean, tag: 'span', class: 'form-check mb-2 mr-sm-2', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
261
+ b.use :html5
262
+ b.optional :readonly
263
+ b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
264
+ b.use :label, class: 'form-check-label'
265
+ b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
266
+ b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
267
+ end
268
+
269
+ # bootstrap custom forms
270
+ #
271
+ # custom input for boolean
272
+ config.wrappers :custom_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
273
+ b.use :html5
274
+ b.optional :readonly
275
+ b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-checkbox' do |bb|
276
+ bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
277
+ bb.use :label, class: 'custom-control-label'
278
+ bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
279
+ bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
280
+ end
281
+ end
282
+
283
+ # custom input switch for boolean
284
+ config.wrappers :custom_boolean_switch, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
285
+ b.use :html5
286
+ b.optional :readonly
287
+ b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-switch' do |bb|
288
+ bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
289
+ bb.use :label, class: 'custom-control-label'
290
+ bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
291
+ bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
292
+ end
293
+ end
294
+
295
+ # custom input for radio buttons and check boxes
296
+ config.wrappers :custom_collection, item_wrapper_class: 'custom-control', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
297
+ b.use :html5
298
+ b.optional :readonly
299
+ b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
300
+ ba.use :label_text
301
+ end
302
+ b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
303
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
304
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
305
+ end
306
+
307
+ # custom input for inline radio buttons and check boxes
308
+ config.wrappers :custom_collection_inline, item_wrapper_class: 'custom-control custom-control-inline', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
309
+ b.use :html5
310
+ b.optional :readonly
311
+ b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
312
+ ba.use :label_text
313
+ end
314
+ b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
315
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
316
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
317
+ end
318
+
319
+ # custom file input
320
+ config.wrappers :custom_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
321
+ b.use :html5
322
+ b.use :placeholder
323
+ b.optional :maxlength
324
+ b.optional :minlength
325
+ b.optional :readonly
326
+ b.use :label
327
+ b.wrapper :custom_file_wrapper, tag: 'div', class: 'custom-file' do |ba|
328
+ ba.use :input, class: 'custom-file-input', error_class: 'is-invalid', valid_class: 'is-valid'
329
+ ba.use :label, class: 'custom-file-label'
330
+ ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
331
+ end
332
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
333
+ end
334
+
335
+ # custom multi select
336
+ config.wrappers :custom_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
337
+ b.use :html5
338
+ b.optional :readonly
339
+ b.use :label
340
+ b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
341
+ ba.use :input, class: 'custom-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
342
+ end
343
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
344
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
345
+ end
346
+
347
+ # custom range input
348
+ config.wrappers :custom_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
349
+ b.use :html5
350
+ b.use :placeholder
351
+ b.optional :readonly
352
+ b.optional :step
353
+ b.use :label
354
+ b.use :input, class: 'custom-range', error_class: 'is-invalid', valid_class: 'is-valid'
355
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
356
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
357
+ end
358
+
359
+ # Input Group - custom component
360
+ # see example app and config at https://github.com/rafaelfranca/simple_form-bootstrap
361
+ # config.wrappers :input_group, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
362
+ # b.use :html5
363
+ # b.use :placeholder
364
+ # b.optional :maxlength
365
+ # b.optional :minlength
366
+ # b.optional :pattern
367
+ # b.optional :min_max
368
+ # b.optional :readonly
369
+ # b.use :label
370
+ # b.wrapper :input_group_tag, tag: 'div', class: 'input-group' do |ba|
371
+ # ba.optional :prepend
372
+ # ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
373
+ # ba.optional :append
374
+ # end
375
+ # b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
376
+ # b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
377
+ # end
378
+
379
+ # Floating Labels form
380
+ #
381
+ # floating labels default_wrapper
382
+ config.wrappers :floating_labels_form, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
383
+ b.use :html5
384
+ b.use :placeholder
385
+ b.optional :maxlength
386
+ b.optional :minlength
387
+ b.optional :pattern
388
+ b.optional :min_max
389
+ b.optional :readonly
390
+ b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
391
+ b.use :label
392
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
393
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
394
+ end
395
+
396
+ # custom multi select
397
+ config.wrappers :floating_labels_select, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
398
+ b.use :html5
399
+ b.optional :readonly
400
+ b.use :input, class: 'custom-select', error_class: 'is-invalid', valid_class: 'is-valid'
401
+ b.use :label
402
+ b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
403
+ b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
404
+ end
405
+
406
+ # The default wrapper to be used by the FormBuilder.
407
+ config.default_wrapper = :vertical_form
408
+
409
+ # Custom wrappers for input types. This should be a hash containing an input
410
+ # type as key and the wrapper that will be used for all inputs with specified type.
411
+ config.wrapper_mappings = {
412
+ boolean: :vertical_boolean,
413
+ check_boxes: :vertical_collection,
414
+ date: :vertical_multi_select,
415
+ datetime: :vertical_multi_select,
416
+ file: :vertical_file,
417
+ radio_buttons: :vertical_collection,
418
+ range: :vertical_range,
419
+ time: :vertical_multi_select
420
+ }
421
+
422
+ # enable custom form wrappers
423
+ # config.wrapper_mappings = {
424
+ # boolean: :custom_boolean,
425
+ # check_boxes: :custom_collection,
426
+ # date: :custom_multi_select,
427
+ # datetime: :custom_multi_select,
428
+ # file: :custom_file,
429
+ # radio_buttons: :custom_collection,
430
+ # range: :custom_range,
431
+ # time: :custom_multi_select
432
+ # }
433
+ end