cck_forms 3.0.0
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/LICENSE +1 -0
- data/README.md +1 -0
- data/Rakefile +40 -0
- data/lib/cck_forms/date_time.rb +58 -0
- data/lib/cck_forms/engine.rb +23 -0
- data/lib/cck_forms/form_builder_extensions.rb +18 -0
- data/lib/cck_forms/parameter_type_class/album.rb +100 -0
- data/lib/cck_forms/parameter_type_class/base.rb +275 -0
- data/lib/cck_forms/parameter_type_class/boolean.rb +24 -0
- data/lib/cck_forms/parameter_type_class/checkboxes.rb +202 -0
- data/lib/cck_forms/parameter_type_class/date.rb +35 -0
- data/lib/cck_forms/parameter_type_class/date_range.rb +53 -0
- data/lib/cck_forms/parameter_type_class/date_time.rb +76 -0
- data/lib/cck_forms/parameter_type_class/enum.rb +53 -0
- data/lib/cck_forms/parameter_type_class/file.rb +80 -0
- data/lib/cck_forms/parameter_type_class/float.rb +20 -0
- data/lib/cck_forms/parameter_type_class/image.rb +20 -0
- data/lib/cck_forms/parameter_type_class/integer.rb +77 -0
- data/lib/cck_forms/parameter_type_class/integer_range.rb +150 -0
- data/lib/cck_forms/parameter_type_class/map.rb +259 -0
- data/lib/cck_forms/parameter_type_class/phones.rb +204 -0
- data/lib/cck_forms/parameter_type_class/string.rb +13 -0
- data/lib/cck_forms/parameter_type_class/string_collection.rb +23 -0
- data/lib/cck_forms/parameter_type_class/text.rb +18 -0
- data/lib/cck_forms/parameter_type_class/time.rb +30 -0
- data/lib/cck_forms/parameter_type_class/work_hours.rb +369 -0
- data/lib/cck_forms/version.rb +3 -0
- data/lib/cck_forms.rb +4 -0
- data/vendor/assets/javascripts/cck_forms/jquery.workhours.js +399 -0
- data/vendor/assets/javascripts/cck_forms/map.js.coffee +322 -0
- metadata +116 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
class CckForms::ParameterTypeClass::Time
|
2
|
+
include CckForms::ParameterTypeClass::Base
|
3
|
+
include CckForms::DateTime
|
4
|
+
|
5
|
+
def self.name
|
6
|
+
'Время'
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_form(form_builder, options)
|
10
|
+
set_value_in_hash options
|
11
|
+
value = CckForms::ParameterTypeClass::Time::date_object_from_what_stored_in_database(options[:value])
|
12
|
+
form_element_options, form_element_html = CckForms::ParameterTypeClass::Time::default_options_for_date_time_selectors(value)
|
13
|
+
form_element_options.merge!({ignore_date: true, minute_step: 5})
|
14
|
+
form_element_html.merge!({required: options[:required]})
|
15
|
+
('<div class="form-inline">%s</div>' % form_builder.fields_for(:value) { |datetime_builder| datetime_builder.time_select '', form_element_options, form_element_html})
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s(options = nil)
|
19
|
+
if value.is_a? Time
|
20
|
+
the_value = {
|
21
|
+
'(4i)' => value.hour,
|
22
|
+
'(5i)' => value.min,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
the_value ||= value
|
27
|
+
|
28
|
+
"#{the_value.try(:[], '(4i)')}:#{the_value.try(:[], '(5i)')}"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,369 @@
|
|
1
|
+
class CckForms::ParameterTypeClass::WorkHours
|
2
|
+
include CckForms::ParameterTypeClass::Base
|
3
|
+
|
4
|
+
def self.name
|
5
|
+
'Часы работы'
|
6
|
+
end
|
7
|
+
|
8
|
+
DAYS = %w{ mon tue wed thu fri sat sun }
|
9
|
+
DAYS_RU_SHORT = %w{ Пн Вт Ср Чт Пт Сб Вс }
|
10
|
+
|
11
|
+
# mon -> Пн
|
12
|
+
def self.day_en_to_ru_short(day)
|
13
|
+
DAYS_RU_SHORT[DAYS.index(day.to_s)]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Входящий хэш или массив объектов WorkHoursDat
|
17
|
+
#
|
18
|
+
# mon: {open_time: ..., open_24_hours: ...}, tue: {...}, ...
|
19
|
+
#
|
20
|
+
# преобразует в хэш для Монго.
|
21
|
+
def mongoize
|
22
|
+
return {} unless value.is_a? Hash
|
23
|
+
|
24
|
+
value.reduce({}) do |r, (day_name, day_data)|
|
25
|
+
r[day_name] = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day_data).mongoize if day_name.in? CckForms::ParameterTypeClass::WorkHours::DAYS
|
26
|
+
r
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Конструирует хэш объектов WorkHoursDay (ключ - название дня вида :mon, см. DAYS).
|
31
|
+
def self.demongoize_value(value, parameter_type_class=nil)
|
32
|
+
return {} unless value.is_a? Hash
|
33
|
+
value.reduce({}) do |r, (day_name, day_row)|
|
34
|
+
day_row = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.demongoize(day_row.merge(day: day_name)) if day_row.is_a? Hash
|
35
|
+
r[day_name] = day_row
|
36
|
+
r
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Строит форму для редактирования режима работы. 1 строка формы - 1 день со всеми своими параметрами.
|
41
|
+
def build_form(form_builder, options)
|
42
|
+
set_value_in_hash options
|
43
|
+
|
44
|
+
options = {
|
45
|
+
value: {}
|
46
|
+
}.merge options
|
47
|
+
|
48
|
+
value = options[:value]
|
49
|
+
value = {} unless value.is_a? Hash
|
50
|
+
|
51
|
+
result = []
|
52
|
+
met_days = Hash[ CckForms::ParameterTypeClass::WorkHours::DAYS.zip(CckForms::ParameterTypeClass::WorkHours::DAYS.dup.fill(false)) ]
|
53
|
+
|
54
|
+
value.each_value do |day|
|
55
|
+
day = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new day unless day.is_a? CckForms::ParameterTypeClass::WorkHours::WorkHoursDay
|
56
|
+
form_builder.fields_for(:value, index: day.day) { |day_builder| result << day.build_form(day_builder, false, options) }
|
57
|
+
met_days[day.day] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
met_days.reject! { |_, value| value }
|
61
|
+
|
62
|
+
met_days.keys.each do |day_name|
|
63
|
+
form_builder.fields_for(:value, index: day_name) { |day_builder| result << CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day: day_name, open_24_hours: true).build_form(day_builder) }
|
64
|
+
end
|
65
|
+
|
66
|
+
form_builder.fields_for(:template) do |day_builder|
|
67
|
+
result << CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new({}).build_form(day_builder, true, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
sprintf '<div class="work-hours" id="%1$s">%2$s</div><script type="text/javascript">$(function() {$("#%1$s").workhours()})</script>', form_builder_name_to_id(form_builder), result.join
|
71
|
+
end
|
72
|
+
|
73
|
+
# Строит строку вида: "Пн—Ср 10:00—23:00; Чт—Сб круглосуточно"
|
74
|
+
def to_html(options = nil)
|
75
|
+
value = self.value
|
76
|
+
return value.to_s unless value.respond_to? :each
|
77
|
+
|
78
|
+
with_tags = options && !options.try(:[], :with_tags).nil? ? options[:with_tags] : true
|
79
|
+
|
80
|
+
value = value.deep_stringify_keys if value.respond_to? :deep_stringify_keys
|
81
|
+
|
82
|
+
# разобьем на группы дней с одинаковым значением (режимом работы), типа {'круглосуточно' => %w{mon tue wed}, ...}
|
83
|
+
groups = {}
|
84
|
+
value.send(value.respond_to?(:each_value) ? :each_value : :each) do |day|
|
85
|
+
day = CckForms::ParameterTypeClass::WorkHours::WorkHoursDay.new(day) unless day.is_a? CckForms::ParameterTypeClass::WorkHours::WorkHoursDay
|
86
|
+
hash = day.to_s_without_day
|
87
|
+
groups[hash] = [] unless groups[hash]
|
88
|
+
groups[hash] << day.day
|
89
|
+
end
|
90
|
+
|
91
|
+
# построим строки для групп
|
92
|
+
result = []
|
93
|
+
groups.each_pair do |hours_description, days|
|
94
|
+
if hours_description.present?
|
95
|
+
if days.length == 7
|
96
|
+
template = with_tags ? '<span class="workhours-group">%s, <span class="workhours-group-novac">без выходных</span></span>' : '%s, без выходных'
|
97
|
+
result << sprintf(template, hours_description)
|
98
|
+
else
|
99
|
+
if days == %w{ mon tue wed thu fri }
|
100
|
+
days_description = 'будние'
|
101
|
+
elsif days == %w{ sat sun }
|
102
|
+
days_description = 'сб, вс'
|
103
|
+
else
|
104
|
+
days_description = CckForms::ParameterTypeClass::WorkHours.grouped_days_string(days).mb_chars.downcase
|
105
|
+
end
|
106
|
+
template = with_tags ? '<span class="workhours-group">%s <span class="workhours-group-days">(%s)</span></span>' : '%s (%s)'
|
107
|
+
result << sprintf(template, hours_description, days_description)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
result.join('; ').html_safe
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_s(options = nil)
|
116
|
+
to_html with_tags: false
|
117
|
+
end
|
118
|
+
|
119
|
+
# Входной массив вида %w{mon, tue, wed, sat} преобразует в сгруппированную строку вида: "Пн—Ср, Сб".
|
120
|
+
def self.grouped_days_string(days)
|
121
|
+
|
122
|
+
# разобьем на непрерывные группы типа [%w{mon tue wed}, %w{sat}]
|
123
|
+
days.sort! { |a, b| DAYS.index(a) <=> DAYS.index(b) }
|
124
|
+
prev_index = -2
|
125
|
+
groups = []
|
126
|
+
days.each do |day|
|
127
|
+
index = DAYS.index(day)
|
128
|
+
if prev_index + 1 != index
|
129
|
+
groups << []
|
130
|
+
end
|
131
|
+
|
132
|
+
groups.last << day_en_to_ru_short(day)
|
133
|
+
prev_index = index
|
134
|
+
end
|
135
|
+
|
136
|
+
# получившиеся группы преобразуем в строки и сольем воедино
|
137
|
+
groups.map do |group|
|
138
|
+
if group.length == 1
|
139
|
+
group[0]
|
140
|
+
elsif group.length == 2
|
141
|
+
group.join ', '
|
142
|
+
else
|
143
|
+
sprintf '%s–%s', group.first, group.last
|
144
|
+
end
|
145
|
+
end.join ', '
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
# Модель-представление рабочего графика одного дня недели. При получении данных из Монги преобразовываем в эту модель,
|
151
|
+
# для удобства работы (чтобы не с хэшами возиться).
|
152
|
+
#
|
153
|
+
# day - строка из массива CckForms::ParameterTypeClass::WorkHours::DAYS.
|
154
|
+
# open_time и close_time хранятся в виде хэшей {hours: 10, minutes: 5}.
|
155
|
+
class WorkHoursDay
|
156
|
+
|
157
|
+
attr_accessor :day, :open_time, :close_time, :open_24_hours, :open_until_last_client
|
158
|
+
|
159
|
+
# Инициализирует свои поля из хэша.
|
160
|
+
def initialize(other)
|
161
|
+
if other.is_a? Hash
|
162
|
+
other = other.symbolize_keys
|
163
|
+
@day = other[:day]
|
164
|
+
@open_time = other[:open_time]
|
165
|
+
@close_time = other[:close_time]
|
166
|
+
@open_24_hours = form_to_boolean(other[:open_24_hours])
|
167
|
+
@open_until_last_client = form_to_boolean(other[:open_until_last_client])
|
168
|
+
elsif other.is_a? WorkHoursDay
|
169
|
+
@day = other.day
|
170
|
+
@open_time = other.open_time
|
171
|
+
@close_time = other.close_time
|
172
|
+
@open_24_hours = other.open_24_hours
|
173
|
+
@open_until_last_client = other.open_until_last_client
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Равны ли два объекта. Да, если все их поля равны.
|
178
|
+
def ==(other)
|
179
|
+
other = self.class.new(other) unless other.is_a? self.class
|
180
|
+
|
181
|
+
self.day == other.day and
|
182
|
+
self.open_time == other.open_time and
|
183
|
+
self.close_time == other.close_time and
|
184
|
+
self.open_24_hours == other.open_24_hours and
|
185
|
+
self.open_until_last_client == other.open_until_last_client
|
186
|
+
end
|
187
|
+
|
188
|
+
# Строит целочисленный хэш на основе всех полей, кроме day, чтобы группировать одинаковые режимы работы.
|
189
|
+
def hash_without_day
|
190
|
+
sprintf('%s:%s:%s:%s', open_time, close_time, open_24_hours, open_until_last_client).hash
|
191
|
+
end
|
192
|
+
|
193
|
+
# Строит строковое описание режима работы в формате: "с 12:00 до последнего клиента"
|
194
|
+
def to_s_without_day
|
195
|
+
result = ''
|
196
|
+
if open_24_hours
|
197
|
+
return 'круглосуточно'
|
198
|
+
elsif time_present?(open_time) or time_present?(close_time)
|
199
|
+
ots, cts = time_to_s(open_time), time_to_s(close_time)
|
200
|
+
if ots and cts
|
201
|
+
result = sprintf('%s–%s', ots, cts)
|
202
|
+
elsif ots
|
203
|
+
result = sprintf('с %s', ots)
|
204
|
+
else
|
205
|
+
result = sprintf('до %s', cts)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
if open_until_last_client
|
210
|
+
result += ' ' if result.present?
|
211
|
+
result += 'до последнего клиента'
|
212
|
+
end
|
213
|
+
|
214
|
+
result
|
215
|
+
end
|
216
|
+
|
217
|
+
# Строит форму редактирования одного дня.
|
218
|
+
def build_form(form_builder, template = false, options = {})
|
219
|
+
form_builder.object = self
|
220
|
+
|
221
|
+
open_time_form = form_builder.fields_for(:open_time) { |time_form| build_time_form(time_form, open_time) }
|
222
|
+
close_time_form = form_builder.fields_for(:close_time) { |time_form| build_time_form(time_form, close_time) }
|
223
|
+
|
224
|
+
input_multi_mark = if options[:multi_days]
|
225
|
+
"data-multi-days='true'"
|
226
|
+
end
|
227
|
+
|
228
|
+
if template
|
229
|
+
header = ['<ul class="nav nav-pills">']
|
230
|
+
CckForms::ParameterTypeClass::WorkHours::DAYS.each { |day| header << '<li><a href="#"><input name="' + form_builder.object_name + '[days]" type="checkbox" value="' + day + '" /> ' + CckForms::ParameterTypeClass::WorkHours.day_en_to_ru_short(day) + '</a></li>' }
|
231
|
+
header = header.push('</ul>').join
|
232
|
+
else
|
233
|
+
header = sprintf '<strong>%s</strong>:%s', CckForms::ParameterTypeClass::WorkHours::day_en_to_ru_short(day), form_builder.hidden_field(:day)
|
234
|
+
end
|
235
|
+
|
236
|
+
open_until_last_client_html = unless options[:hide_open_until_last_client]
|
237
|
+
<<HTML
|
238
|
+
<div class="checkbox">
|
239
|
+
<label class="form_work_hours_option">#{ form_builder.check_box :open_until_last_client } до последнего клиента</label>
|
240
|
+
</div>
|
241
|
+
HTML
|
242
|
+
end
|
243
|
+
|
244
|
+
<<HTML
|
245
|
+
<div #{input_multi_mark} class="form_work_hours_day#{template ? ' form_work_hours_day_template" style="display: none' : ''}">
|
246
|
+
<div class="form_work_hours_time">
|
247
|
+
#{header}
|
248
|
+
</div>
|
249
|
+
<div class="form_work_hours_time">
|
250
|
+
<table width="100%">
|
251
|
+
<tr>
|
252
|
+
<td width="60%" class="form-inline">
|
253
|
+
с #{ open_time_form }
|
254
|
+
по #{ close_time_form }
|
255
|
+
</td>
|
256
|
+
<td width="40%">
|
257
|
+
<div class="checkbox">
|
258
|
+
<label class="form_work_hours_option">#{ form_builder.check_box :open_24_hours } круглосуточно</label>
|
259
|
+
</div>
|
260
|
+
#{open_until_last_client_html}
|
261
|
+
</td>
|
262
|
+
</tr>
|
263
|
+
</table>
|
264
|
+
</div>
|
265
|
+
</div>
|
266
|
+
HTML
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
private
|
272
|
+
|
273
|
+
# Преобразует значение из запроса (чексбокс) в булево, типа 1 -> true.
|
274
|
+
def form_to_boolean(value)
|
275
|
+
return value == '1' if value.is_a? String
|
276
|
+
!!value
|
277
|
+
end
|
278
|
+
|
279
|
+
# Преобразует хэше времени {hours: ..., minutes: ...} в строку "10:42"
|
280
|
+
def time_to_s(time)
|
281
|
+
return nil unless time.is_a?(Hash) and time['hours'].present? and time['minutes'].present?
|
282
|
+
sprintf '%s:%s', time['hours'].to_s.rjust(2, '0'), time['minutes'].to_s.rjust(2, '0')
|
283
|
+
end
|
284
|
+
|
285
|
+
# Не пустое ли значение времени?
|
286
|
+
def time_present?(time)
|
287
|
+
return time.is_a?(Hash) && time['hours'].present? && time['minutes'].present?
|
288
|
+
end
|
289
|
+
|
290
|
+
# Строим форму с селектами времени вида: [18]:[45]
|
291
|
+
def build_time_form(form_builder, value)
|
292
|
+
hours = []
|
293
|
+
24.times { |hour| hours << [hour.to_s.rjust(2, '0'), hour] }
|
294
|
+
|
295
|
+
minutes = []
|
296
|
+
(60/5).times { |minute| minutes << [(minute *= 5).to_s.rjust(2, '0'), minute] }
|
297
|
+
|
298
|
+
sprintf(
|
299
|
+
'%s : %s',
|
300
|
+
form_builder.select(:hours, hours, {include_blank: true, selected: value.try(:[], 'hours')}, class: 'form-control input-sm', style: 'width: 60px'),
|
301
|
+
form_builder.select(:minutes, minutes, {include_blank: true, selected: value.try(:[], 'minutes')}, class: 'form-control input-sm', style: 'width: 60px')
|
302
|
+
).html_safe
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
public
|
308
|
+
|
309
|
+
# Преборазование самого себя для сохранения в Монго (хэш).
|
310
|
+
def mongoize
|
311
|
+
{
|
312
|
+
'day' => day.to_s,
|
313
|
+
'open_time' => self.class.mongoize_time(open_time),
|
314
|
+
'close_time' => self.class.mongoize_time(close_time),
|
315
|
+
'open_24_hours' => open_24_hours,
|
316
|
+
'open_until_last_client' => open_until_last_client,
|
317
|
+
}
|
318
|
+
end
|
319
|
+
|
320
|
+
class << self
|
321
|
+
|
322
|
+
# Преборазование самого себя из представления Монго (из хэша).
|
323
|
+
def demongoize(object)
|
324
|
+
object = object.symbolize_keys
|
325
|
+
WorkHoursDay.new(
|
326
|
+
day: object[:day].to_s,
|
327
|
+
open_time: self.demongoize_time(object[:open_time]),
|
328
|
+
close_time: self.demongoize_time(object[:close_time]),
|
329
|
+
open_24_hours: object[:open_24_hours],
|
330
|
+
open_until_last_client: object[:open_until_last_client],
|
331
|
+
)
|
332
|
+
end
|
333
|
+
|
334
|
+
# "Статическое" преборазование самого себя для сохранения в Монго (хэш).
|
335
|
+
def mongoize(object)
|
336
|
+
case object
|
337
|
+
when WorkHoursDay then object.mongoize
|
338
|
+
when Hash then WorkHoursDay.new(object).mongoize
|
339
|
+
else object
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# TODO: сделать нормальный evolve
|
344
|
+
def evolve(object)
|
345
|
+
object
|
346
|
+
end
|
347
|
+
|
348
|
+
# Преобразовываем значение времени для Монго. Берет Time или DateTime или хэш и выдает хэш вида:
|
349
|
+
#
|
350
|
+
# {hours: 10, minutes: 5}
|
351
|
+
def mongoize_time(time)
|
352
|
+
if time.is_a? Time or time.is_a? DateTime
|
353
|
+
{'hours' => time.hour, 'minutes' => time.min}
|
354
|
+
elsif time.is_a? Hash
|
355
|
+
time = time.stringify_keys
|
356
|
+
{'hours' => time['hours'].present? ? time['hours'].to_i : nil, 'minutes' => time['minutes'].present? ? time['minutes'].to_i : nil}
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Преобразовываем значение времени для Монго. Берет Time или DateTime или хэш и выдает хэш вида:
|
361
|
+
#
|
362
|
+
# {hours: 10, minutes: 5}
|
363
|
+
def demongoize_time(time)
|
364
|
+
mongoize_time(time)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
end
|
data/lib/cck_forms.rb
ADDED