bureaucrat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +124 -0
- data/lib/bureaucrat/fields.rb +461 -0
- data/lib/bureaucrat/forms.rb +346 -0
- data/lib/bureaucrat/formsets.rb +256 -0
- data/lib/bureaucrat/quickfields.rb +59 -0
- data/lib/bureaucrat/utils.rb +84 -0
- data/lib/bureaucrat/validation.rb +130 -0
- data/lib/bureaucrat/validation_old.rb +148 -0
- data/lib/bureaucrat/widgets.rb +397 -0
- data/lib/bureaucrat/wizard.rb +220 -0
- data/lib/bureaucrat.rb +10 -0
- data/test/fields_test.rb +577 -0
- data/test/forms_test.rb +131 -0
- data/test/formsets_test.rb +51 -0
- data/test/test_helper.rb +22 -0
- data/test/widgets_test.rb +328 -0
- metadata +71 -0
@@ -0,0 +1,346 @@
|
|
1
|
+
require 'bureaucrat/utils'
|
2
|
+
require 'bureaucrat/validation'
|
3
|
+
require 'bureaucrat/widgets'
|
4
|
+
require 'bureaucrat/fields'
|
5
|
+
|
6
|
+
module Bureaucrat; module Forms
|
7
|
+
class BoundField
|
8
|
+
include Utils
|
9
|
+
|
10
|
+
attr_accessor :label
|
11
|
+
|
12
|
+
def initialize(form, field, name)
|
13
|
+
@form = form
|
14
|
+
@field = field
|
15
|
+
@name = name
|
16
|
+
@html_name = form.add_prefix(name).to_sym
|
17
|
+
@html_initial_name = form.add_initial_prefix(name).to_sym
|
18
|
+
@label = @field.label || pretty_name(name)
|
19
|
+
@help_text = @field.help_text || ''
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@field.show_hidden_initial ? as_widget + as_hidden(nil, true) : as_widget
|
24
|
+
end
|
25
|
+
|
26
|
+
def errors
|
27
|
+
@form.errors.fetch(@name, @form.error_class.new)
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_widget(widget=nil, attrs=nil, only_initial=false)
|
31
|
+
widget ||= @field.widget
|
32
|
+
attrs ||= {}
|
33
|
+
auto_id = self.auto_id
|
34
|
+
attrs[:id] ||= auto_id if auto_id && !widget.attrs.key?(:id)
|
35
|
+
|
36
|
+
if !@form.bound?
|
37
|
+
data = @form.initial.fetch(@name, @field.initial)
|
38
|
+
data = data.call if data.respond_to?(:call)
|
39
|
+
else
|
40
|
+
if @field.is_a?(Fields::FileField) && @data.nil?
|
41
|
+
data = @form.initial.fetch(@name, @field.initial)
|
42
|
+
else
|
43
|
+
data = self.data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
name = only_initial ? @html_initial_name : @html_name
|
48
|
+
|
49
|
+
widget.render(name.to_s, data, attrs)
|
50
|
+
end
|
51
|
+
|
52
|
+
def as_text(attrs=nil, only_initial=false)
|
53
|
+
as_widget(Widgets::TextInput.new, attrs, only_initial)
|
54
|
+
end
|
55
|
+
|
56
|
+
def as_textarea(attrs=nil, only_initial=false)
|
57
|
+
as_widget(Widgets::Textarea.new, attrs, only_initial)
|
58
|
+
end
|
59
|
+
|
60
|
+
def as_hidden(attrs=nil, only_initial=false)
|
61
|
+
as_widget(@field.hidden_widget, attrs, only_initial)
|
62
|
+
end
|
63
|
+
|
64
|
+
def data
|
65
|
+
@field.widget.value_from_datahash(@form.data, @form.files, @html_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def label_tag(contents=nil, attrs=nil)
|
69
|
+
contents ||= conditional_escape(@label)
|
70
|
+
widget = @field.widget
|
71
|
+
id_ = widget.attrs[:id] || self.auto_id
|
72
|
+
|
73
|
+
if id_
|
74
|
+
attrs = attrs ? flattatt(attrs) : ''
|
75
|
+
contents = "<label for=\"#{Widgets::Widget.id_for_label(id_)}\"#{attrs}>#{contents}</label>"
|
76
|
+
end
|
77
|
+
|
78
|
+
mark_safe(contents)
|
79
|
+
end
|
80
|
+
|
81
|
+
def hidden?
|
82
|
+
@field.widget.hidden?
|
83
|
+
end
|
84
|
+
|
85
|
+
def auto_id
|
86
|
+
fauto_id = @form.auto_id
|
87
|
+
fauto_id ? fauto_id % @html_name : ''
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Form
|
92
|
+
include Utils
|
93
|
+
include Validation
|
94
|
+
|
95
|
+
class << self
|
96
|
+
attr_writer :base_fields
|
97
|
+
|
98
|
+
def base_fields
|
99
|
+
@base_fields ||= Utils::OrderedHash.new
|
100
|
+
end
|
101
|
+
|
102
|
+
def field(name, field_obj)
|
103
|
+
base_fields[name] = field_obj
|
104
|
+
end
|
105
|
+
|
106
|
+
# Copy data to the child class
|
107
|
+
def inherited(c)
|
108
|
+
super(c)
|
109
|
+
c.base_fields = base_fields.dup
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_accessor :error_class, :auto_id, :initial, :data, :files, :cleaned_data
|
114
|
+
|
115
|
+
def bound? ; @is_bound; end
|
116
|
+
|
117
|
+
def initialize(data=nil, options={})
|
118
|
+
@is_bound = !data.nil?
|
119
|
+
@data = data ? data.dup : {}
|
120
|
+
@data.each {|k, v| @data[k.to_sym] ||= v}
|
121
|
+
@files = options.fetch(:files, {})
|
122
|
+
@auto_id = options.fetch(:auto_id, 'id_%s')
|
123
|
+
@prefix = options[:prefix]
|
124
|
+
@initial = options.fetch(:initial, {})
|
125
|
+
@error_class = options.fetch(:error_class, Fields::ErrorList)
|
126
|
+
@label_suffix = options.fetch(:label_suffix, ':')
|
127
|
+
@empty_permitted = options.fetch(:empty_permitted, false)
|
128
|
+
@errors = nil
|
129
|
+
@changed_data = nil
|
130
|
+
|
131
|
+
@fields = self.class.base_fields.dup
|
132
|
+
@fields.each { |key, value| @fields[key] = value.dup }
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_s
|
136
|
+
as_table
|
137
|
+
end
|
138
|
+
|
139
|
+
def [](name)
|
140
|
+
field = @fields[name] or return nil
|
141
|
+
BoundField.new(self, field, name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def errors
|
145
|
+
full_clean if @errors.nil?
|
146
|
+
@errors
|
147
|
+
end
|
148
|
+
|
149
|
+
def valid?
|
150
|
+
@is_bound && (errors.nil? || errors.empty?)
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_prefix(field_name)
|
154
|
+
@prefix ? :"#{@prefix}-#{field_name}" : field_name
|
155
|
+
end
|
156
|
+
|
157
|
+
def add_initial_prefix(field_name)
|
158
|
+
"initial-#{add_prefix(field_name)}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def empty_permitted?
|
162
|
+
@empty_permitted
|
163
|
+
end
|
164
|
+
|
165
|
+
def as_table
|
166
|
+
html_output('<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',
|
167
|
+
'<tr><td colspan="2">%s</td></tr>', '</td></tr>',
|
168
|
+
'<br />%s', false)
|
169
|
+
end
|
170
|
+
|
171
|
+
def as_ul
|
172
|
+
html_output('<li>%(errors)s%(label)s %(field)s%(help_text)s</li>',
|
173
|
+
'<li>%s</li>', '</li>', ' %s', false)
|
174
|
+
end
|
175
|
+
|
176
|
+
def as_p
|
177
|
+
html_output('<p>%(label)s %(field)s%(help_text)s</p>',
|
178
|
+
'%s', '</p>', ' %s', true)
|
179
|
+
end
|
180
|
+
|
181
|
+
def non_field_errors
|
182
|
+
errors.fetch(:__NON_FIELD_ERRORS, @error_class.new)
|
183
|
+
end
|
184
|
+
|
185
|
+
def full_clean
|
186
|
+
@errors = Fields::ErrorHash.new
|
187
|
+
|
188
|
+
return unless bound?
|
189
|
+
|
190
|
+
@cleaned_data = {}
|
191
|
+
|
192
|
+
return if empty_permitted? && !changed?
|
193
|
+
|
194
|
+
@fields.each do |name, field|
|
195
|
+
value = field.widget.value_from_datahash(@data, @files,
|
196
|
+
add_prefix(name))
|
197
|
+
|
198
|
+
begin
|
199
|
+
if field.is_a?(Fields::FileField)
|
200
|
+
initial = @initial.fetch(name, field.initial)
|
201
|
+
@cleaned_data[name] = field.clean(value, initial)
|
202
|
+
else
|
203
|
+
@cleaned_data[name] = field.clean(value)
|
204
|
+
end
|
205
|
+
|
206
|
+
clean_method = 'clean_%s' % name
|
207
|
+
@cleaned_data[name] = send(clean_method) if respond_to?(clean_method)
|
208
|
+
rescue Fields::FieldValidationError => e
|
209
|
+
@errors[name] = e.messages
|
210
|
+
@cleaned_data.clear
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
begin
|
215
|
+
@cleaned_data = clean
|
216
|
+
rescue Fields::FieldValidationError => e
|
217
|
+
@errors[:__NON_FIELD_ERRORS] = e.messages
|
218
|
+
end
|
219
|
+
@cleaned_data = nil if @errors && !@errors.empty?
|
220
|
+
end
|
221
|
+
|
222
|
+
def clean
|
223
|
+
@cleaned_data
|
224
|
+
end
|
225
|
+
|
226
|
+
def changed?
|
227
|
+
changed_data && !changed_data.empty?
|
228
|
+
end
|
229
|
+
|
230
|
+
def changed_data
|
231
|
+
if @changed_data.nil?
|
232
|
+
@changed_data = []
|
233
|
+
|
234
|
+
@fields.each do |name, field|
|
235
|
+
prefixed_name = add_prefix(name)
|
236
|
+
data_value = field.widget.value_from_datahash(@data, @files,
|
237
|
+
prefixed_name)
|
238
|
+
if !field.show_hidden_initial
|
239
|
+
initial_value = @initial.fetch(name, field.initial)
|
240
|
+
else
|
241
|
+
initial_prefixed_name = add_initial_prefix(name)
|
242
|
+
hidden_widget = field.hidden_widget.new
|
243
|
+
initial_value = hidden_widget.value_from_datahash(@data, @files,
|
244
|
+
initial_prefixed_name)
|
245
|
+
end
|
246
|
+
|
247
|
+
@changed_data << name if
|
248
|
+
field.widget.has_changed?(initial_value, data_value)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
@changed_data
|
253
|
+
end
|
254
|
+
|
255
|
+
def media
|
256
|
+
@fields.values.inject(Widgets::Media.new) do |media, field|
|
257
|
+
media + field.widget.media
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def multipart?
|
262
|
+
@fields.any? {|f| f.widgetneeds_multipart_form?}
|
263
|
+
end
|
264
|
+
|
265
|
+
def hidden_fields
|
266
|
+
@fields.select {|f| f.hidden?}
|
267
|
+
end
|
268
|
+
|
269
|
+
def visible_fields
|
270
|
+
@fields.select {|f| !f.hidden?}
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
def html_output(normal_row, error_row, row_ender, help_text_html,
|
275
|
+
errors_on_separate_row)
|
276
|
+
top_errors = non_field_errors
|
277
|
+
output, hidden_fields = [], []
|
278
|
+
|
279
|
+
add_fields_output(output, hidden_fields, normal_row, error_row,
|
280
|
+
help_text_html, errors_on_separate_row, top_errors)
|
281
|
+
output = [error_row % top_errors] + output unless top_errors.empty?
|
282
|
+
add_hidden_fields_output(output, hidden_fields, row_ender)
|
283
|
+
|
284
|
+
mark_safe(output.join("\n"))
|
285
|
+
end
|
286
|
+
|
287
|
+
def add_fields_output(output, hidden_fields, normal_row, error_row,
|
288
|
+
help_text_html, errors_on_separate_row,
|
289
|
+
top_errors)
|
290
|
+
@fields.each do |name, field|
|
291
|
+
bf = BoundField.new(self, field, name)
|
292
|
+
bf_errors = @error_class.new(bf.errors.map {|e| conditional_escape(e)})
|
293
|
+
if bf.hidden?
|
294
|
+
top_errors += bf_errors.map do |e|
|
295
|
+
"(Hidden field #{name}) #{e.to_s}"
|
296
|
+
end unless bf_errors.empty?
|
297
|
+
hidden_fields << bf.to_s
|
298
|
+
else
|
299
|
+
output << error_row % bf_errors if
|
300
|
+
errors_on_separate_row && !bf_errors.empty?
|
301
|
+
|
302
|
+
label = ''
|
303
|
+
unless bf.label.nil? || bf.label.empty?
|
304
|
+
label = conditional_escape(bf.label)
|
305
|
+
label += @label_suffix if @label_suffix && label[-1,1] !~ /[:?.!]/
|
306
|
+
label = bf.label_tag(label)
|
307
|
+
end
|
308
|
+
|
309
|
+
help_text = field.help_text.empty? ? '' : help_text_html % field.help_text
|
310
|
+
vars = {
|
311
|
+
:errors => bf_errors, :label => label,
|
312
|
+
:field => bf, :help_text => help_text
|
313
|
+
}
|
314
|
+
output << format_string(normal_row, vars)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def add_hidden_fields_output(output, hidden_fields, row_ender)
|
320
|
+
unless hidden_fields.empty?
|
321
|
+
str_hidden = hidden_fields.join('')
|
322
|
+
|
323
|
+
unless output.empty?
|
324
|
+
last_row = output[-1]
|
325
|
+
if last_row !~ /#{row_ender}$/
|
326
|
+
vars = {
|
327
|
+
:errors => '', :label => '', :field => '', :help_text => ''
|
328
|
+
}
|
329
|
+
last_row = format_string(normal_row, vars)
|
330
|
+
output << last_row
|
331
|
+
end
|
332
|
+
output[-1] = last_row[0...-row_ender.length] + str_hidden + row_ender
|
333
|
+
else
|
334
|
+
output << str_hidden
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def raw_value(fieldname)
|
340
|
+
field = @fields.fetch(fieldname)
|
341
|
+
prefix = add_prefix(fieldname)
|
342
|
+
field.widget.value_from_datahash(@data, @files, prefix)
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
end; end
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require 'bureaucrat/utils'
|
2
|
+
require 'bureaucrat/validation'
|
3
|
+
require 'bureaucrat/widgets'
|
4
|
+
require 'bureaucrat/forms'
|
5
|
+
|
6
|
+
# TODO: needs more testing
|
7
|
+
module Bureaucrat
|
8
|
+
module Formsets
|
9
|
+
TOTAL_FORM_COUNT = :'TOTAL_FORMS'
|
10
|
+
INITIAL_FORM_COUNT = :'INITIAL_FORMS'
|
11
|
+
ORDERING_FIELD_NAME = :'ORDER'
|
12
|
+
DELETION_FIELD_NAME = :'DELETE'
|
13
|
+
|
14
|
+
class ManagementForm < Forms::Form
|
15
|
+
include Fields
|
16
|
+
include Widgets
|
17
|
+
|
18
|
+
field TOTAL_FORM_COUNT, IntegerField.new(:widget => HiddenInput)
|
19
|
+
field INITIAL_FORM_COUNT, IntegerField.new(:widget => HiddenInput)
|
20
|
+
end
|
21
|
+
|
22
|
+
class BaseFormSet
|
23
|
+
include Utils
|
24
|
+
include Validation
|
25
|
+
include Fields
|
26
|
+
include Forms
|
27
|
+
|
28
|
+
def self.default_prefix
|
29
|
+
'form'
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_accessor :form, :extra, :can_order, :can_delete, :max_num
|
33
|
+
|
34
|
+
def initialize(data=nil, options={})
|
35
|
+
@is_bound = !data.nil?
|
36
|
+
@prefix = options.fetch(:prefix, self.class.default_prefix)
|
37
|
+
@auto_id = options.fetch(:auto_id, 'id_%s')
|
38
|
+
@data = data
|
39
|
+
@files = options[:files]
|
40
|
+
@initial = options[:initial]
|
41
|
+
@error_class = options.fetch(:error_class, ErrorList)
|
42
|
+
@errors = nil
|
43
|
+
@non_form_errors = nil
|
44
|
+
|
45
|
+
construct_forms
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
as_table
|
50
|
+
end
|
51
|
+
|
52
|
+
def management_form
|
53
|
+
if @data || @files
|
54
|
+
form = ManagementForm.new(@data, :auto_id => @auto_id,
|
55
|
+
:prefix => @prefix)
|
56
|
+
raise ValidationError.new('ManagementForm data is missing or has been tampered with') unless
|
57
|
+
form.valid?
|
58
|
+
else
|
59
|
+
form = ManagementForm.new(nil, :auto_id => @auto_id,
|
60
|
+
:prefix => @prefix,
|
61
|
+
:initial => {
|
62
|
+
TOTAL_FORM_COUNT => total_form_count,
|
63
|
+
INITIAL_FORM_COUNT => initial_form_count
|
64
|
+
})
|
65
|
+
end
|
66
|
+
form
|
67
|
+
end
|
68
|
+
|
69
|
+
def total_form_count
|
70
|
+
if @data || @files
|
71
|
+
management_form.cleaned_data[TOTAL_FORM_COUNT]
|
72
|
+
else
|
73
|
+
n = initial_form_count + self.extra
|
74
|
+
(n > self.max_num && self.max_num > 0) ? self.max_num : n
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def initial_form_count
|
79
|
+
if @data || @files
|
80
|
+
management_form.cleaned_data[INITIAL_FORM_COUNT]
|
81
|
+
else
|
82
|
+
n = @initial ? @initial.length : 0
|
83
|
+
(n > self.max_num && self.max_num > 0) ? self.max_num : n
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def construct_forms
|
88
|
+
@forms = (0...total_form_count).map { |i| construct_form(i) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def construct_form(i, options={})
|
92
|
+
defaults = {:auto_id => @auto_id, :prefix => add_prefix(i)}
|
93
|
+
defaults[:files] = @files if @files
|
94
|
+
defaults[:initial] = @initial[i] if @initial && @initial[i]
|
95
|
+
|
96
|
+
# Allow extra forms to be empty.
|
97
|
+
defaults[:empty_permitted] = true if i >= initial_form_count
|
98
|
+
defaults.merge!(options)
|
99
|
+
form = self.form.new(@data, defaults)
|
100
|
+
add_fields(form, i)
|
101
|
+
form
|
102
|
+
end
|
103
|
+
|
104
|
+
def initial_forms
|
105
|
+
@forms[0, initial_form_count]
|
106
|
+
end
|
107
|
+
|
108
|
+
def extra_forms
|
109
|
+
n = initial_form_count
|
110
|
+
@forms[n, @forms.length - n]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Maybe this should just go away?
|
114
|
+
def cleaned_data
|
115
|
+
unless valid?
|
116
|
+
raise NoMethodError.new("'#{self.class.name}' object has no method 'cleaned_data'")
|
117
|
+
end
|
118
|
+
@forms.collect(&:cleaned_data)
|
119
|
+
end
|
120
|
+
|
121
|
+
def deleted_forms
|
122
|
+
unless valid? && self.can_delete
|
123
|
+
raise NoMethodError.new("'#{self.class.name}' object has no method 'deleted_forms'")
|
124
|
+
end
|
125
|
+
|
126
|
+
if @deleted_form_indexes.nil?
|
127
|
+
@deleted_form_indexes = (0...total_form_count).select do |i|
|
128
|
+
form = @forms[i]
|
129
|
+
(i < initial_form_count || form.changed?) && form.cleaned_data[DELETION_FIELD_NAME]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@deleted_form_indexes.map {|i| @forms[i]}
|
133
|
+
end
|
134
|
+
|
135
|
+
def ordered_forms
|
136
|
+
unless valid? && self.can_order
|
137
|
+
raise NoMethodError.new("'#{self.class.name}' object has no method 'ordered_forms'")
|
138
|
+
end
|
139
|
+
|
140
|
+
if @ordering.nil?
|
141
|
+
@ordering = (0...total_form_count).map do |i|
|
142
|
+
form = @forms[i]
|
143
|
+
next if i >= initial_form_count && !form.changed?
|
144
|
+
next if self.can_delete && form.cleaned_data[DELETION_FIELD_NAME]
|
145
|
+
[i, form.cleaned_data[ORDERING_FIELD_NAME]]
|
146
|
+
end.compact
|
147
|
+
@ordering.sort! do |a, b|
|
148
|
+
if x[1].nil? then 1
|
149
|
+
elsif y[1].nil? then -1
|
150
|
+
else x[1] - y[1]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
@ordering.map {|i| @forms[i.first]}
|
156
|
+
end
|
157
|
+
|
158
|
+
def non_form_errors
|
159
|
+
@non_form_errors || @error_class.new
|
160
|
+
end
|
161
|
+
|
162
|
+
def errors
|
163
|
+
full_clean if @errors.nil?
|
164
|
+
@errors
|
165
|
+
end
|
166
|
+
|
167
|
+
def valid?
|
168
|
+
return false unless @is_bound
|
169
|
+
forms_valid = true
|
170
|
+
(0...total_form_count).each do |i|
|
171
|
+
form = @forms[i]
|
172
|
+
if self.can_delete
|
173
|
+
field = form.fields[DELETION_FIELD_NAME]
|
174
|
+
raw_value = form.send(:raw_value, DELETION_FIELD_NAME)
|
175
|
+
should_delete = field.clean(raw_value)
|
176
|
+
next if should_delete
|
177
|
+
end
|
178
|
+
forms_valid = false unless errors[i].empty?
|
179
|
+
end
|
180
|
+
forms_valid && non_form_errors.empty?
|
181
|
+
end
|
182
|
+
|
183
|
+
def full_clean
|
184
|
+
if @is_bound
|
185
|
+
@errors = @forms.collect(&:errors)
|
186
|
+
|
187
|
+
begin
|
188
|
+
self.clean
|
189
|
+
rescue ValidationError => e
|
190
|
+
@non_form_errors = e.messages
|
191
|
+
end
|
192
|
+
else
|
193
|
+
@errors = []
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def clean
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_fields(form, index)
|
201
|
+
if self.can_order
|
202
|
+
attrs = {:label => 'Order', :required => false}
|
203
|
+
attrs[:initial] = index + 1 if index < initial_form_count
|
204
|
+
form.fields[ORDERING_FIELD_NAME] = IntegerField.new(attrs)
|
205
|
+
end
|
206
|
+
if self.can_delete
|
207
|
+
field = BooleanField.new(:label => 'Delete', :required => false)
|
208
|
+
form.fields[DELETION_FIELD_NAME] = field
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_prefix(index)
|
213
|
+
'%s-%s' % [@prefix, index]
|
214
|
+
end
|
215
|
+
|
216
|
+
def multipart?
|
217
|
+
@forms && @forms.first.multipart?
|
218
|
+
end
|
219
|
+
|
220
|
+
def media
|
221
|
+
@forms ? @forms.first.media : Media.new
|
222
|
+
end
|
223
|
+
|
224
|
+
def as_table
|
225
|
+
forms = @forms.map(&:as_table).join(' ')
|
226
|
+
mark_safe([management_form.to_s, forms].join("\n"))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
module_function
|
231
|
+
|
232
|
+
def make_formset_class(form, options={})
|
233
|
+
formset = options.fetch(:formset, BaseFormSet)
|
234
|
+
attrs = {
|
235
|
+
:form => form,
|
236
|
+
:extra => options.fetch(:extra, 1),
|
237
|
+
:can_order => options.fetch(:can_order, false),
|
238
|
+
:can_delete => options.fetch(:can_delete, false),
|
239
|
+
:max_num => options.fetch(:max_num, 0)
|
240
|
+
}
|
241
|
+
Class.new(formset) do
|
242
|
+
attrs.each do |name, value|
|
243
|
+
define_method(name) { value }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def all_valid?(formsets)
|
249
|
+
valid = true
|
250
|
+
formsets.each do |formset|
|
251
|
+
valid = false unless formset.valid?
|
252
|
+
end
|
253
|
+
valid
|
254
|
+
end
|
255
|
+
|
256
|
+
end; end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'bureaucrat/fields'
|
2
|
+
|
3
|
+
module Bureaucrat
|
4
|
+
module Quickfields
|
5
|
+
include Fields
|
6
|
+
|
7
|
+
def hide(name)
|
8
|
+
base_fields[name].widget = Widgets::HiddenInput.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def string(name, options={})
|
12
|
+
field name, CharField.new(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def password(name, options={})
|
16
|
+
field name, CharField.new(options.merge(:widget => Widgets::PasswordInput.new))
|
17
|
+
end
|
18
|
+
|
19
|
+
def integer(name, options={})
|
20
|
+
field name, IntegerField.new(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def decimal(name, options={})
|
24
|
+
field name, BigDecimalField.new(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def regex(name, regexp, options={})
|
28
|
+
field name, RegexField.new(regexp, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def email(name, options={})
|
32
|
+
field name, EmailField.new(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def file(name, options={})
|
36
|
+
field name, FileField.new(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def boolean(name, options={})
|
40
|
+
field name, BooleanField.new(options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def null_boolean(name, options={})
|
44
|
+
field name, NullBooleanField.new(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def choice(name, choices=[], options={})
|
48
|
+
field name, ChoiceField.new(choices, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def typed_choice(name, choices=[], options={})
|
52
|
+
field name, TypedChoiceField.new(choices, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def multiple_choice(name, choices=[], options={})
|
56
|
+
field name, MultipleChoiceField.new(choices, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
end; end
|