coalla-cms 0.5.2.4 → 0.6.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile +1 -1
  3. data/app/assets/javascripts/admin/admin.js +233 -34
  4. data/app/assets/stylesheets/admin/admin.scss +1 -2
  5. data/app/assets/stylesheets/admin/vendor/bootstrap.min.css +6242 -4
  6. data/app/assets/stylesheets/admin/vendor/token-input-bootstrap.scss +144 -0
  7. data/app/controllers/admin/autocomplete_controller.rb +3 -1
  8. data/app/controllers/admin/base_controller.rb +35 -0
  9. data/app/controllers/concerns/admin/editable_columns.rb +55 -0
  10. data/app/controllers/concerns/admin/sortable_columns.rb +16 -0
  11. data/app/controllers/{admin/sortable_controller.rb → concerns/admin/sortable_models.rb} +1 -5
  12. data/app/helpers/admin/alerts_helper.rb +31 -0
  13. data/app/helpers/admin/content_helper.rb +29 -0
  14. data/app/helpers/admin/form_helper.rb +50 -0
  15. data/app/helpers/admin/resource_helpers.rb +102 -0
  16. data/app/helpers/admin/search_helper.rb +44 -40
  17. data/app/helpers/admin/table_helper.rb +40 -0
  18. data/app/helpers/common_helper.rb +2 -2
  19. data/app/models/file_upload.rb +1 -1
  20. data/app/models/lookup.rb +1 -1
  21. data/app/models/site_meta_tags.rb +2 -2
  22. data/app/views/admin/common/_menu_section.haml +1 -1
  23. data/app/views/admin/common/_nested_fields_for.html.haml +10 -5
  24. data/app/views/admin/common/_nested_fields_for_element.html.haml +21 -5
  25. data/app/views/admin/common/_table_template.html.haml +26 -7
  26. data/app/views/admin/common/_upload_form.html.haml +2 -1
  27. data/app/views/admin/editable_columns/edit/_boolean.haml +2 -0
  28. data/app/views/admin/editable_columns/edit/_date.haml +2 -0
  29. data/app/views/admin/editable_columns/edit/_datetime.haml +3 -0
  30. data/app/views/admin/editable_columns/edit/_enumerize.haml +2 -0
  31. data/app/views/admin/editable_columns/edit/_string.haml +2 -0
  32. data/app/views/admin/editable_columns/edit/_text.haml +2 -0
  33. data/app/views/admin/editable_columns/edit/_time.haml +3 -0
  34. data/app/views/admin/editable_columns/edit_column.haml +8 -0
  35. data/app/views/admin/editable_columns/result/_boolean.haml +2 -0
  36. data/app/views/admin/editable_columns/result/_date.haml +1 -0
  37. data/app/views/admin/editable_columns/result/_datetime.haml +1 -0
  38. data/app/views/admin/editable_columns/result/_enumerize.haml +1 -0
  39. data/app/views/admin/editable_columns/result/_string.haml +1 -0
  40. data/app/views/admin/editable_columns/result/_text.haml +1 -0
  41. data/app/views/admin/editable_columns/result/_time.haml +1 -0
  42. data/app/views/admin/site_meta_tags/index.html.haml +2 -2
  43. data/app/views/layouts/admin.html.haml +12 -0
  44. data/app/views/structure/_section.haml +2 -9
  45. data/coalla-cms.gemspec +10 -11
  46. data/lib/coalla/builders/actions_column_definition.rb +44 -0
  47. data/lib/coalla/builders/admin_structure.rb +86 -0
  48. data/lib/coalla/builders/column_definition.rb +9 -0
  49. data/lib/coalla/builders/form_builder.rb +291 -0
  50. data/lib/coalla/builders/link_renderer.rb +43 -0
  51. data/lib/coalla/builders/policy.rb +30 -0
  52. data/lib/coalla/builders/table_builder.rb +99 -0
  53. data/lib/coalla/builders/table_formatter.rb +133 -0
  54. data/lib/coalla/builders/text_formatter.rb +22 -0
  55. data/lib/coalla/cms/engine.rb +3 -2
  56. data/lib/coalla/cms/version.rb +2 -2
  57. data/lib/coalla/ext/mapper.rb +7 -0
  58. data/lib/coalla/orm/{relation.rb → multi_field.rb} +7 -8
  59. data/lib/coalla/orm/page_slider.rb +2 -28
  60. data/lib/coalla/orm/sanitized.rb +1 -5
  61. data/lib/coalla/orm/sortable_association.rb +30 -0
  62. data/lib/coalla/uploaders/file_uploader.rb +9 -0
  63. data/lib/coalla/uploaders/image_uploader.rb +62 -0
  64. data/lib/coalla/uploaders/meta_image_uploader.rb +7 -0
  65. data/lib/coalla-cms.rb +10 -0
  66. data/lib/generators/coalla/cms/create_admin_administrators_generator.rb +5 -7
  67. data/lib/generators/coalla/cms/create_admin_generator.rb +9 -7
  68. data/lib/generators/coalla/cms/create_admin_login_view_generator.rb +4 -6
  69. data/lib/generators/coalla/cms/create_markup_generator.rb +2 -2
  70. data/lib/generators/coalla/cms/file_uploads/install_generator.rb +0 -17
  71. data/lib/generators/coalla/cms/file_uploads/mount_generator.rb +4 -7
  72. data/lib/generators/coalla/cms/image/install_generator.rb +0 -3
  73. data/lib/generators/coalla/cms/image/mount_generator.rb +0 -3
  74. data/lib/generators/coalla/cms/image/templates/mount/uploader.rb.erb +1 -1
  75. data/lib/generators/coalla/cms/image/templates/uploader.rb.erb +1 -1
  76. data/lib/generators/coalla/cms/init_generator.rb +42 -49
  77. data/lib/generators/coalla/cms/lookups/install_generator.rb +0 -3
  78. data/lib/generators/coalla/cms/meta_tags/install_generator.rb +0 -2
  79. data/lib/generators/coalla/cms/scaffold_generator.rb +7 -7
  80. data/lib/generators/coalla/cms/setup_admin_generator.rb +9 -9
  81. data/lib/generators/coalla/cms/setup_routes_generator.rb +3 -3
  82. data/lib/generators/coalla/cms/slider/init_generator.rb +1 -3
  83. data/lib/generators/coalla/cms/slider/templates/slider_image.rb.erb +0 -1
  84. data/lib/generators/coalla/cms/slider/templates/slider_image_uploader.rb.erb +1 -1
  85. data/lib/generators/coalla/cms/templates/.gitignore +3 -3
  86. data/lib/generators/coalla/cms/templates/controllers/admin/administrators_controller.rb +2 -40
  87. data/lib/generators/coalla/cms/templates/controllers/admin/scaffold_controller_template.rb.erb +0 -39
  88. data/lib/generators/coalla/cms/templates/initializers/carrierwave.rb +0 -90
  89. data/lib/generators/coalla/cms/templates/locales/activerecord.en.yml +14 -1
  90. data/lib/generators/coalla/cms/templates/views/admin/administrators/_form.html.haml +1 -1
  91. data/lib/generators/coalla/cms/templates/views/admin/administrators/edit.html.haml +1 -1
  92. data/lib/generators/coalla/cms/templates/views/admin/administrators/index.html.haml +5 -5
  93. data/lib/generators/coalla/cms/templates/views/admin/administrators/new.html.haml +1 -1
  94. data/lib/generators/coalla/cms/templates/views/admin/scaffold_template/_form.html.haml.erb +1 -1
  95. data/lib/generators/coalla/cms/templates/views/admin/scaffold_template/edit.html.haml.erb +1 -1
  96. data/lib/generators/coalla/cms/templates/views/admin/scaffold_template/index.html.haml.erb +5 -5
  97. data/lib/generators/coalla/cms/templates/views/admin/scaffold_template/new.html.haml.erb +1 -1
  98. data/lib/generators/coalla/cms/templates/views/administrators/sessions/new.html.haml +1 -1
  99. data/lib/generators/coalla/cms/utils/orm.rb +29 -0
  100. data/lib/generators/coalla/cms/utils/scaffold.rb +11 -0
  101. metadata +57 -100
  102. data/Gemfile.lock +0 -198
  103. data/app/assets/stylesheets/admin/vendor/token-input-facebook.patched.css +0 -126
  104. data/app/assets/stylesheets/admin/vendor/token-input.patched.css +0 -116
  105. data/app/controllers/admin/notifier_controller.rb +0 -7
  106. data/app/helpers/admin_helper.rb +0 -29
  107. data/app/helpers/nested_fields_helper.rb +0 -12
  108. data/app/helpers/static_text_formatter.rb +0 -25
  109. data/app/helpers/twitter_builder_helper.rb +0 -253
  110. data/app/helpers/twitter_form_builder.rb +0 -202
  111. data/app/uploaders/file_uploader.rb +0 -10
  112. data/app/uploaders/generic_image_uploader.rb +0 -75
  113. data/app/uploaders/meta_tags_image_uploader.rb +0 -5
  114. data/app/utils/admin_structure.rb +0 -82
  115. data/app/utils/bootstrap_link_renderer.rb +0 -40
  116. data/app/utils/table_helpers.rb +0 -196
  117. data/lib/generators/coalla/cms/market/install_generator.rb +0 -71
  118. data/lib/generators/coalla/cms/market/templates/controllers/categories_controller.rb +0 -47
  119. data/lib/generators/coalla/cms/market/templates/controllers/products_controller.rb +0 -50
  120. data/lib/generators/coalla/cms/market/templates/controllers/properties_controller.rb +0 -59
  121. data/lib/generators/coalla/cms/market/templates/market.rb +0 -5
  122. data/lib/generators/coalla/cms/market/templates/market.ru.yml +0 -53
  123. data/lib/generators/coalla/cms/market/templates/migrations/create_categories.rb +0 -15
  124. data/lib/generators/coalla/cms/market/templates/migrations/create_product_properties.rb +0 -18
  125. data/lib/generators/coalla/cms/market/templates/migrations/create_products.rb +0 -18
  126. data/lib/generators/coalla/cms/market/templates/migrations/create_properties.rb +0 -11
  127. data/lib/generators/coalla/cms/market/templates/models/category.rb +0 -36
  128. data/lib/generators/coalla/cms/market/templates/models/product.rb +0 -16
  129. data/lib/generators/coalla/cms/market/templates/models/product_property.rb +0 -14
  130. data/lib/generators/coalla/cms/market/templates/models/property.rb +0 -7
  131. data/lib/generators/coalla/cms/market/templates/views/categories/_form.html.haml +0 -10
  132. data/lib/generators/coalla/cms/market/templates/views/categories/edit.html.haml +0 -5
  133. data/lib/generators/coalla/cms/market/templates/views/categories/index.html.haml +0 -17
  134. data/lib/generators/coalla/cms/market/templates/views/categories/new.html.haml +0 -4
  135. data/lib/generators/coalla/cms/market/templates/views/products/_form.html.haml +0 -17
  136. data/lib/generators/coalla/cms/market/templates/views/products/_product_property_fields.html.haml +0 -13
  137. data/lib/generators/coalla/cms/market/templates/views/products/edit.html.haml +0 -5
  138. data/lib/generators/coalla/cms/market/templates/views/products/index.html.haml +0 -17
  139. data/lib/generators/coalla/cms/market/templates/views/products/new.html.haml +0 -4
  140. data/lib/generators/coalla/cms/market/templates/views/properties/_form.html.haml +0 -9
  141. data/lib/generators/coalla/cms/market/templates/views/properties/edit.html.haml +0 -5
  142. data/lib/generators/coalla/cms/market/templates/views/properties/index.html.haml +0 -15
  143. data/lib/generators/coalla/cms/market/templates/views/properties/new.html.haml +0 -4
  144. data/lib/generators/coalla/cms/news/scaffold_generator.rb +0 -50
  145. data/lib/generators/coalla/cms/news/templates/entity.rb.erb +0 -10
  146. data/lib/generators/coalla/cms/news/templates/entity_controller_template.rb.erb +0 -47
  147. data/lib/generators/coalla/cms/news/templates/entity_image_uploader.rb.erb +0 -7
  148. data/lib/generators/coalla/cms/news/templates/migration.rb.erb +0 -15
  149. data/lib/generators/coalla/cms/news/templates/views/_form.haml.erb +0 -16
  150. data/lib/generators/coalla/cms/news/templates/views/edit.haml.erb +0 -5
  151. data/lib/generators/coalla/cms/news/templates/views/index.haml.erb +0 -18
  152. data/lib/generators/coalla/cms/news/templates/views/new.haml.erb +0 -5
  153. data/lib/generators/coalla/cms/orm_helpers.rb +0 -31
  154. data/lib/generators/coalla/cms/scaffold_helper.rb +0 -11
  155. data/lib/generators/coalla/cms/templates/controllers/admin/base_controller.rb.erb +0 -35
  156. /data/{lib/generators/coalla/cms/templates/controllers/admin/home_controller.rb.erb → app/controllers/admin/home_controller.rb} +0 -0
@@ -0,0 +1,291 @@
1
+ #encoding: utf-8
2
+ require 'coalla/builders/text_formatter'
3
+
4
+ module Coalla
5
+ class FormBuilder < ActionView::Helpers::FormBuilder
6
+ include ActionView::Helpers::FormTagHelper
7
+
8
+ COLS_CLASS = 'col-md-8'
9
+
10
+ def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
11
+ return unless policy.field_enabled?(method)
12
+ super
13
+ end
14
+
15
+ def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
16
+ return unless policy.field_enabled?(method)
17
+ super
18
+ end
19
+
20
+ def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
21
+ return unless policy.field_enabled?(method)
22
+ super
23
+ end
24
+
25
+ def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
26
+ return unless policy.field_enabled?(method)
27
+ super
28
+ end
29
+
30
+ def date_select(method, options = {}, html_options = {})
31
+ return unless policy.field_enabled?(method)
32
+ super
33
+ end
34
+
35
+ def datetime_select(method, options = {}, html_options = {})
36
+ return unless policy.field_enabled?(method)
37
+ super
38
+ end
39
+
40
+ def fields_for(record_name, record_object = nil, fields_options = {}, &block)
41
+ return unless policy.field_enabled?(record_name)
42
+ super
43
+ end
44
+
45
+ def file_field(method, options = {})
46
+ return unless policy.field_enabled?(method)
47
+ super
48
+ end
49
+
50
+ def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
51
+ return unless policy.field_enabled?(method)
52
+ super
53
+ end
54
+
55
+ def radio_button(method, tag_value, options = {})
56
+ return unless policy.field_enabled?(method)
57
+ super
58
+ end
59
+
60
+ def select(method, choices = nil, options = {}, html_options = {}, &block)
61
+ return unless policy.field_enabled?(method)
62
+ super
63
+ end
64
+
65
+ def time_select(method, options = {}, html_options = {})
66
+ return unless policy.field_enabled?(method)
67
+ super
68
+ end
69
+
70
+ def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
71
+ return unless policy.field_enabled?(method)
72
+ super
73
+ end
74
+
75
+ def errors
76
+ return nil unless @object.errors.any?
77
+
78
+ messages = @object.errors.full_messages.collect do |msg|
79
+ "<p>#{ERB::Util.h(msg)}</p>".html_safe
80
+ end
81
+
82
+ content_tag(:div,
83
+ content_tag(:div,
84
+ content_tag(:div,
85
+ content_tag(:button, '&times;'.html_safe, class: 'close', type: 'button', :'data-dismiss' => 'alert') + messages.join('').html_safe,
86
+ class: 'alert alert-danger'),
87
+ class: 'col-md-8 col-md-offset-2'),
88
+ class: 'row')
89
+ end
90
+
91
+ def static_text(method, options = {})
92
+ row method, content_tag(:p, static_text_formatter.format(method), class: 'form-control-static'), options
93
+ end
94
+
95
+ # Displays multi-line static text in form
96
+ def static_memo_text(method, options = {})
97
+ row method, simple_format(@object.send(method), class: 'form-control-static'), options
98
+ end
99
+
100
+ def string(method, options = {})
101
+ row method, text_field(method, with_default_options(options)), options
102
+ end
103
+
104
+ def combobox(method, choices, options = {}, html_options = {})
105
+ row method, select(method, choices, options, with_default_options(html_options)), div_class: options.delete(:div_class) || 'col-md-6'
106
+ end
107
+
108
+ def enum(method, options = {}, html_options = {})
109
+ return unless policy.field_enabled?(method)
110
+ combobox method, object.class.send(method.to_s.pluralize).collect { |s| [s[0].humanize, s[0]] }, options, html_options
111
+ end
112
+
113
+ def enumerize(method, options = {}, html_options = {})
114
+ return unless policy.field_enabled?(method)
115
+ combobox method, object.class.send(method).values.collect { |s| [s.text, s] }, options, html_options
116
+ end
117
+
118
+ def list_all_combobox(association_name, options = {}, html_options = {})
119
+ return unless policy.field_enabled?(association_name)
120
+ collection_class = object.class.reflect_on_association(association_name).klass
121
+ combobox("#{association_name}_id", collection_class.all.sort_by(&:name).map { |element| [element.name, element.id] }, {include_blank: true}.merge(options), html_options)
122
+ end
123
+
124
+ def textarea(method, options = {})
125
+ row method, text_area(method, with_default_options(options)), options
126
+ end
127
+
128
+ def checkbox(method, options = {}, checked_value = '1', unchecked_value = '0')
129
+ row method, content_tag(:div, check_box(method, options, checked_value, unchecked_value), class: 'checkbox'), options
130
+ end
131
+
132
+ def wysiwyg(method, options = {})
133
+ textarea method, with_default_options(merge_classes(options, 'wymeditor'))
134
+ end
135
+
136
+ def ckeditor(method, options = {})
137
+ row method, cktext_area(method, with_default_options(options))
138
+ end
139
+
140
+ def password(method, options = {})
141
+ row method, password_field(method, with_default_options(options)), options
142
+ end
143
+
144
+ def date(method, options = {})
145
+ string(method, merge_options({data: {'calendar-date' => true}, div_class: 'col-md-2'}, options))
146
+ end
147
+
148
+ def datetime(method, options = {})
149
+ value = object.try(method).try(:strftime, '%Y-%m-%d %H:%M')
150
+ string(method, merge_options({data: {'calendar-datetime' => true}, div_class: 'col-md-3', value: value}, options))
151
+ end
152
+
153
+ def time(method, options = {})
154
+ value = object.try(method).try(:strftime, '%H:%M')
155
+ string(method, merge_options({data: {'calendar-time' => true}, div_class: 'col-md-2', value: value}, options))
156
+ end
157
+
158
+ # Editing has_many collection
159
+ # parameters: association - name of has_many association
160
+ # association should be defined as accepts_nested_attributes with allow_destroy = true in model
161
+ # You should create partial with name #{association)_fields
162
+ def nested_fields_for(association, record_object = nil, options = {}, &block)
163
+ return unless policy.field_enabled?(association)
164
+
165
+ @template.render('admin/common/nested_fields_for',
166
+ f: self,
167
+ section_name: options[:title] || @object.class.human_attribute_name(association),
168
+ collection: association,
169
+ record_object: record_object,
170
+ block: block,
171
+ options: {include_id: false, destroy: true, add: true, sort: false}.merge(options))
172
+ end
173
+
174
+ def image_upload(method, options = {})
175
+ return unless policy.field_enabled?(method)
176
+
177
+ version = options[:version]
178
+
179
+ uri = if version.present?
180
+ @object.send("#{method}_url", version)
181
+ else
182
+ @object.send("#{method}_url")
183
+ end
184
+
185
+ if options[:size]
186
+ url = uri || 'placeholder'
187
+ else
188
+ url = uri
189
+ end
190
+
191
+ upload_uri = options[:upload_path]
192
+ unless upload_uri
193
+ upload_uri = @template.admin_upload_image_path(image_class: @object.class.name.underscore, field: method)
194
+ end
195
+ @template.render partial: '/admin/common/image_upload_template', locals: {
196
+ f: self, url: url, size: options[:size],
197
+ version: version || '',
198
+ title: options[:title] || '',
199
+ text: options[:text],
200
+ field: method, upload_uri: upload_uri,
201
+ container: options[:parent_id] || upload_container_id(method),
202
+ options: options.delete(:options) || {},
203
+ image_style: options.delete(:image_style) || ''
204
+ }
205
+ end
206
+
207
+ def file_upload(method)
208
+ return unless policy.field_enabled?(method)
209
+
210
+ @template.render partial: '/admin/common/file_upload_template', locals: {
211
+ f: self,
212
+ field: method
213
+ }
214
+ end
215
+
216
+ def multi_field(relation_name, options = {})
217
+ reflection = self.object.class.reflections[relation_name] || self.object.class.reflections[relation_name.to_s]
218
+ options = {search_field_name: :name,
219
+ show_all_on_focus: false,
220
+ use_cache: true,
221
+ relation_model_name: reflection.klass.model_name.singular}.merge!(options)
222
+ options[:source] ||= @template.admin_autocomplete_path(options[:relation_model_name], options[:search_field_name])
223
+ string "#{relation_name}_tokens", title: self.object.class.human_attribute_name(relation_name),
224
+ data: {
225
+ multi_field: true,
226
+ source: options[:source],
227
+ pre: self.object.send("#{relation_name}_json", options[:search_field_name]),
228
+ show_all_on_focus: options[:show_all_on_focus],
229
+ use_cache: options[:use_cache],
230
+ object_url_name: options[:object_url_name]}.merge!(options[:data] || {})
231
+ end
232
+
233
+ def row(method, controls, options = {})
234
+ return unless policy.field_enabled?(method)
235
+
236
+ label_class = options[:label_class] || 'control-label col-md-4 col-lg-2'
237
+ label_tag = label(method, options[:title], class: label_class) unless options[:hide_label]
238
+ options[:div_class] ||= COLS_CLASS
239
+ div_tag = content_tag :div, controls.html_safe, class: options[:div_class]
240
+ content_tag :div, "#{label_tag}#{div_tag}".html_safe, class: 'form-group'
241
+ end
242
+
243
+ def save(text = I18n.t('admin.common.save'))
244
+ submit text, name: :save, class: 'btn btn-success wymupdate'
245
+ end
246
+
247
+ def apply(text = I18n.t('admin.common.apply'))
248
+ submit text, name: :apply, class: 'btn btn-default wymupdate'
249
+ end
250
+
251
+ def cancel
252
+ @template.back_action
253
+ end
254
+
255
+ private
256
+
257
+ def upload_container_id(method)
258
+ fake_method = "#{method}_upload"
259
+ ActionView::Helpers::Tags::Base.new(@object_name, fake_method, @template).send(:tag_id)
260
+ end
261
+
262
+ def with_default_options(options)
263
+ merge_classes(options, 'form-control')
264
+ options
265
+ end
266
+
267
+ def merge_classes(options, *clazz)
268
+ classes = [options[:class]].compact
269
+ classes.push(*clazz)
270
+ options[:class] = classes.uniq.join(' ')
271
+ options
272
+ end
273
+
274
+ def merge_options(default_options, options)
275
+ return default_options if options.blank?
276
+ new_options = default_options.merge(options)
277
+ if default_options.has_key?(:data) && options.has_key?(:data)
278
+ new_options[:data] = default_options[:data].merge(options[:data])
279
+ end
280
+ new_options
281
+ end
282
+
283
+ def static_text_formatter
284
+ @static_text_formatter ||= TextFormatter.new(@object)
285
+ end
286
+
287
+ def policy
288
+ @policy ||= @template.try(:policy) || Coalla.policy.new(@template)
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,43 @@
1
+ require 'will_paginate/view_helpers/action_view'
2
+
3
+ module Coalla
4
+ class LinkRenderer < WillPaginate::ActionView::LinkRenderer
5
+
6
+ protected
7
+
8
+ def html_container(html)
9
+ tag(:div, tag(:ul, html, class: 'pagination'), class: 't-center')
10
+ end
11
+
12
+ def page_number(page)
13
+ if page == current_page
14
+ tag(:li, tag(:a, page), class: 'active')
15
+ else
16
+ tag(:li, link(page, page, rel: rel_value(page)))
17
+ end
18
+ end
19
+
20
+ def gap
21
+ text = @template.will_paginate_translate(:page_gap) { '&hellip;' }
22
+ tag(:li, tag(:a, text), class: 'disabled')
23
+ end
24
+
25
+ def previous_page
26
+ num = @collection.current_page > 1 && @collection.current_page - 1
27
+ previous_or_next_page(num, @options[:previous_label])
28
+ end
29
+
30
+ def next_page
31
+ num = @collection.current_page < @collection.total_pages && @collection.current_page + 1
32
+ previous_or_next_page(num, @options[:next_label])
33
+ end
34
+
35
+ def previous_or_next_page(page, text)
36
+ if page
37
+ tag(:li, link(text, page))
38
+ else
39
+ tag(:li, tag(:a, text), class: 'disabled')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module Coalla
2
+ class DefaultPolicy
3
+ def initialize(context)
4
+ @context = context
5
+ end
6
+
7
+ def action_enabled?(action, options = {})
8
+ return true
9
+ end
10
+
11
+ def field_enabled?(field, options = {})
12
+ return true
13
+ end
14
+
15
+ def section_enabled?(section, options = {})
16
+ return true
17
+ end
18
+
19
+ def sanitize_params(params, options = {})
20
+ return params
21
+ end
22
+
23
+ protected
24
+
25
+ attr_reader :context
26
+ end
27
+
28
+ mattr_accessor :policy
29
+ @@policy = DefaultPolicy
30
+ end
@@ -0,0 +1,99 @@
1
+ require 'coalla/builders/column_definition'
2
+ require 'coalla/builders/actions_column_definition'
3
+ require 'coalla/builders/table_formatter'
4
+
5
+ module Coalla
6
+ class TableBuilder
7
+ def initialize(context, model_class, options = {})
8
+ @context, @model_class = context, model_class
9
+ @options = options
10
+ @columns = []
11
+ @table_formatter = TableFormatter.for(@model_class)
12
+ end
13
+
14
+ def content(collection, &block)
15
+ @collection = collection
16
+ block.call(self) if block_given?
17
+ create_content
18
+ end
19
+
20
+ # Метод добавляет столбец к таблице
21
+ def column(method, options = {})
22
+ helper = @table_formatter[method]
23
+ cd = ColumnDefinition.new
24
+ cd.title = options[:title] || I18n.t("activerecord.attributes.#{@model_class.model_name.singular}.#{method}")
25
+ cd.cols = options[:cols]
26
+ cd.col_class = options[:class]
27
+ cd.sort = sort_value(method, options.fetch(:sort, false))
28
+ cd.edit = edit_options(method, options.fetch(:edit, false))
29
+ cd.value_extractor = ->(item) { helper.format_value(item, options[:format]) }
30
+ cd.align = options[:align] || helper.respond_to?(:align) && helper.align
31
+ @columns << cd
32
+ end
33
+
34
+ # Метод позволяет передать блок, который должен вычислять класс для строки. В этот блок передается объект,
35
+ # по которому данная строка рендерится. Таким образом, можно управлять, например, цветом строки в зависимости
36
+ # от состояния объекта (статусы заказов и прочее).
37
+ def row_class(lmb)
38
+ @row_class = lmb
39
+ end
40
+
41
+ # Метод добавляет кнопку в столбец действий
42
+ def action(value, options = {})
43
+ add_action(value, options)
44
+ end
45
+
46
+ # Метод добавляет кнопку редактирования сущности в столбец действий
47
+ def edit(options = {})
48
+ add_action(:edit, options)
49
+ end
50
+
51
+ # Метод добавляет кнопку удаления сущности в столбец действий
52
+ def delete(options = {})
53
+ add_action(:delete, options)
54
+ end
55
+
56
+ # Метод добавляет кнопки добавления и удаления сущностей в столбец действий
57
+ def edit_and_delete(options = {})
58
+ self.edit(options)
59
+ self.delete(options)
60
+ end
61
+
62
+ private
63
+
64
+ def add_action(value, options)
65
+ action_name = value.is_a?(Proc) ? options[:action_name] : value
66
+ return unless policy.action_enabled?(action_name, options)
67
+ @action_definition ||= ActionsColumnDefinition.new
68
+ @action_definition.action(value, options)
69
+ if @columns.exclude?(@action_definition)
70
+ @columns << @action_definition
71
+ end
72
+ end
73
+
74
+ def sort_value(method, sort_option)
75
+ return sort_option.to_s if sort_option.is_a?(String) || sort_option.is_a?(Symbol)
76
+ method.to_s if sort_option
77
+ end
78
+
79
+ def edit_options(method, options)
80
+ return unless options
81
+ result_options = {column: method}
82
+ if options.is_a?(Hash)
83
+ result_options.merge!(options)
84
+ else
85
+ result_options
86
+ end
87
+ return false unless policy.field_enabled?(result_options[:column])
88
+ result_options
89
+ end
90
+
91
+ def create_content
92
+ @context.render(partial: '/admin/common/table_template', locals: {definitions: @columns, items: @collection, row_class: @row_class})
93
+ end
94
+
95
+ def policy
96
+ @policy ||= @context.try(:policy) || Coalla.policy.new(@context)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,133 @@
1
+ module Coalla
2
+ class TableFormatter
3
+ # TODO (vl): move formatters to separate files and lambdas
4
+
5
+ class SimpleFormatter
6
+ def format_value(item, format)
7
+ return '' unless item
8
+ return format.call(item) if format.is_a?(Proc)
9
+ customize(item)
10
+ end
11
+
12
+ def customize(item)
13
+ item
14
+ end
15
+ end
16
+
17
+ class BooleanFormatter < SimpleFormatter
18
+ def customize(item)
19
+ item ? '<i class="glyphicon glyphicon-check"></i>'.html_safe : ''
20
+ end
21
+
22
+ def align
23
+ :center
24
+ end
25
+ end
26
+
27
+ class DateFormatter < SimpleFormatter
28
+ def initialize(default_format = '%d.%m.%Y')
29
+ @default_format = default_format
30
+ end
31
+
32
+ def customize(item)
33
+ item = item.localtime if item.respond_to?(:localtime)
34
+ Russian::strftime(item, @default_format)
35
+ end
36
+ end
37
+
38
+ class EnumerizeFormatter < SimpleFormatter
39
+ def customize(item)
40
+ item.try(:text)
41
+ end
42
+ end
43
+
44
+ class SelfFormatter
45
+ def format_value(item, format)
46
+ raise 'Format should be lambda' unless format.is_a?(Proc)
47
+ format.call(item)
48
+ end
49
+ end
50
+
51
+ class ReflectionFormatter
52
+ def initialize(method)
53
+ @method = method
54
+ end
55
+
56
+ def format_value(item, format)
57
+ target = item.send(@method)
58
+ raise 'Format should be lambda' unless format.is_a?(Proc)
59
+ format.call(target)
60
+ end
61
+ end
62
+
63
+ class MethodFormatter
64
+ FORMATS = {
65
+ boolean: BooleanFormatter.new,
66
+ date: DateFormatter.new,
67
+ datetime: DateFormatter.new('%d.%m.%Y %H:%M'),
68
+ enumerize: EnumerizeFormatter.new
69
+ }
70
+
71
+ FORMATS.default = SimpleFormatter.new
72
+
73
+ def initialize(method, type)
74
+ @method = method
75
+ @formatter = FORMATS[type]
76
+ end
77
+
78
+ def format_value(item, format)
79
+ @formatter.format_value(item.send(@method), format)
80
+ end
81
+
82
+ def align
83
+ @align || @formatter.respond_to?(:align) && @formatter.align || :left
84
+ end
85
+
86
+ def align=(value)
87
+ @align = value
88
+ end
89
+
90
+ def respond_to?(method)
91
+ return (@align.present? || @formatter.respond_to?(method)) if method == :align
92
+ super
93
+ end
94
+ end
95
+
96
+ def initialize(model_class)
97
+ @model_class = model_class
98
+ @columns = model_class.columns.index_by(&:name).with_indifferent_access
99
+ @reflections = model_class.reflections.dup.with_indifferent_access
100
+ @helpers = {}
101
+ end
102
+
103
+ def self.for(model_class)
104
+ new(model_class)
105
+ end
106
+
107
+ def [](method)
108
+ return @helpers[method] if @helpers[method]
109
+ @helpers[method] = load_helper(method)
110
+ @helpers[method]
111
+ end
112
+
113
+ private
114
+
115
+ def load_helper(method)
116
+ return SelfFormatter.new if method == :self
117
+
118
+ reflection = @reflections[method]
119
+ return ReflectionFormatter.new(method) if reflection && reflection.macro == :belongs_to
120
+
121
+ # TODO (vl): refactor this
122
+ if @model_class.respond_to?(:enumerized_attributes) && @model_class.enumerized_attributes[method]
123
+ type = :enumerize
124
+ else
125
+ type = @columns[method] && @columns[method].type || :default
126
+ end
127
+
128
+ helper = MethodFormatter.new(method, type)
129
+ helper.align = :right if [:integer, :float, :decimal].include?(type.to_sym)
130
+ helper
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,22 @@
1
+ module Coalla
2
+ class TextFormatter
3
+ def initialize(object)
4
+ @object = object
5
+ # TODO (vl): refactor this
6
+ @columns = @object.class.columns.index_by(&:name).with_indifferent_access
7
+ end
8
+
9
+ def format(method)
10
+ method_value = @object.send(method)
11
+ return unless method_value
12
+
13
+ method_type = @columns[method].try(:type)
14
+ if method_type == :datetime
15
+ method_value = method_value.localtime if method_value.respond_to?(:localtime)
16
+ Russian.strftime(method_value, '%d.%m.%Y %H:%M')
17
+ else
18
+ method_value
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,10 +4,11 @@ module Coalla
4
4
  require 'active_support/i18n'
5
5
  I18n.load_path += Dir[File.expand_path('../../../config/locales/*.yml', __FILE__)]
6
6
 
7
- initializer :init_slider do
7
+ initializer :init_orm do
8
8
  ActiveSupport.on_load :active_record do
9
+ require 'coalla/orm/sortable_association'
9
10
  require 'coalla/orm/page_slider'
10
- require 'coalla/orm/relation'
11
+ require 'coalla/orm/multi_field'
11
12
  require 'coalla/orm/sanitized'
12
13
  end
13
14
  end
@@ -1,5 +1,5 @@
1
1
  module Coalla
2
2
  module Cms
3
- VERSION = '0.5.2.4'
3
+ VERSION = '0.6.0.9'
4
4
  end
5
- end
5
+ end
@@ -9,6 +9,13 @@ module ActionDispatch::Routing
9
9
  end
10
10
  end
11
11
 
12
+ def editable_columns
13
+ member do
14
+ get :edit_column
15
+ put :update_column
16
+ end
17
+ end
18
+
12
19
  end
13
20
 
14
21
  end
@@ -1,12 +1,11 @@
1
1
  module Coalla
2
-
3
- module Relation
4
-
2
+ module MultiField
5
3
  def multi_field(relation_name, options = {})
6
4
  through_collection_name = options[:through_collection_name] || "#{self.model_name.singular}_#{relation_name}".to_sym
7
5
  reflection = self.reflections[through_collection_name] || self.reflections[through_collection_name.to_s]
8
6
  through_class = reflection.klass
9
- self_foreign_key = reflection.foreign_key
7
+
8
+ self_foreign_key_method = options.fetch(:self_foreign_key_method, reflection.foreign_key.gsub('_id', ''))
10
9
 
11
10
  reflection = self.reflections[relation_name] || self.reflections[relation_name.to_s]
12
11
  association_model_name = reflection.source_reflection_name.to_sym
@@ -17,7 +16,9 @@ module Coalla
17
16
 
18
17
  define_method "#{tokens_attribute_name}=" do |ids|
19
18
  new_through_collection = ids.split(',').each_with_index.map do |id, position|
20
- through_class.new(self_foreign_key => self.id, association_foreign_key => id, position: position)
19
+ item = through_class.find_or_initialize_by(self_foreign_key_method => self, association_foreign_key => id)
20
+ item.position = position
21
+ item
21
22
  end
22
23
  self.send("#{through_collection_name}=", new_through_collection)
23
24
  end
@@ -27,9 +28,7 @@ module Coalla
27
28
  end
28
29
 
29
30
  end
30
-
31
31
  end
32
-
33
32
  end
34
33
 
35
- ActiveRecord::Base.extend Coalla::Relation
34
+ ActiveRecord::Base.extend(Coalla::MultiField)