symphonia 2.1.7

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.
Files changed (188) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +16 -0
  4. data/Rakefile +44 -0
  5. data/app/assets/images/bg-checker.png +0 -0
  6. data/app/assets/images/bullet.gif +0 -0
  7. data/app/assets/images/close.png +0 -0
  8. data/app/assets/images/loading.gif +0 -0
  9. data/app/assets/images/next.png +0 -0
  10. data/app/assets/images/prev.png +0 -0
  11. data/app/assets/javascripts/ckeditor/plugins/autogrow/plugin.js +232 -0
  12. data/app/assets/javascripts/ckeditor/plugins/autogrow/samples/autogrow.html +102 -0
  13. data/app/assets/javascripts/ckeditor/plugins/image_chooser/icons/addimage.png +0 -0
  14. data/app/assets/javascripts/ckeditor/plugins/image_chooser/plugin.js +15 -0
  15. data/app/assets/javascripts/symphonia/Sortable.js +1249 -0
  16. data/app/assets/javascripts/symphonia/_core.js +45 -0
  17. data/app/assets/javascripts/symphonia/application.js.erb +147 -0
  18. data/app/assets/javascripts/symphonia/filters.js +44 -0
  19. data/app/assets/javascripts/symphonia/symphonia_bootstrap_dialog.js +136 -0
  20. data/app/assets/javascripts/symphonia/symphonia_ckeditor.js +55 -0
  21. data/app/assets/stylesheets/symphonia/application.css +4 -0
  22. data/app/assets/stylesheets/symphonia/basic.scss +218 -0
  23. data/app/assets/stylesheets/symphonia/filters.scss +19 -0
  24. data/app/assets/stylesheets/symphonia/symphonia_bootstrap.scss +56 -0
  25. data/app/channels/application_cable/channel.rb +5 -0
  26. data/app/controllers/symphonia/accounts_controller.rb +169 -0
  27. data/app/controllers/symphonia/admin_controller.rb +22 -0
  28. data/app/controllers/symphonia/api_controller.rb +64 -0
  29. data/app/controllers/symphonia/application_controller.rb +8 -0
  30. data/app/controllers/symphonia/attachments_controller.rb +37 -0
  31. data/app/controllers/symphonia/filters_controller.rb +23 -0
  32. data/app/controllers/symphonia/images_controller.rb +16 -0
  33. data/app/controllers/symphonia/login_controller.rb +80 -0
  34. data/app/controllers/symphonia/roles_controller.rb +100 -0
  35. data/app/controllers/symphonia/user_sessions_controller.rb +16 -0
  36. data/app/controllers/symphonia/users_controller.rb +168 -0
  37. data/app/helpers/symphonia/application_helper.rb +422 -0
  38. data/app/helpers/symphonia/bootstrap_modal_helper.rb +59 -0
  39. data/app/helpers/symphonia/form_helper.rb +50 -0
  40. data/app/helpers/symphonia/renderer_helper.rb +85 -0
  41. data/app/mailers/symphonia/application_mailer.rb +6 -0
  42. data/app/mailers/symphonia/notifier.rb +42 -0
  43. data/app/models/symphonia/admin_module.rb +18 -0
  44. data/app/models/symphonia/application_record.rb +14 -0
  45. data/app/models/symphonia/attachment.rb +16 -0
  46. data/app/models/symphonia/common_file.rb +9 -0
  47. data/app/models/symphonia/email_preference.rb +5 -0
  48. data/app/models/symphonia/image.rb +46 -0
  49. data/app/models/symphonia/preference.rb +12 -0
  50. data/app/models/symphonia/role.rb +55 -0
  51. data/app/models/symphonia/swagger/error_model.rb +19 -0
  52. data/app/models/symphonia/swagger/responses.rb +27 -0
  53. data/app/models/symphonia/user.rb +141 -0
  54. data/app/models/symphonia/user_session.rb +19 -0
  55. data/app/views/common/403.html.erb +5 -0
  56. data/app/views/common/404.html.erb +2 -0
  57. data/app/views/layouts/symphonia/_modal.html.erb +19 -0
  58. data/app/views/layouts/symphonia/_query.html.erb +51 -0
  59. data/app/views/layouts/symphonia/application.html.erb +45 -0
  60. data/app/views/layouts/symphonia/application.pdf.erb +15 -0
  61. data/app/views/layouts/symphonia/mailer.html.erb +13 -0
  62. data/app/views/symphonia/accounts/_detail.html.erb +65 -0
  63. data/app/views/symphonia/accounts/_form.html.erb +14 -0
  64. data/app/views/symphonia/accounts/edit.html.erb +9 -0
  65. data/app/views/symphonia/accounts/edit.js.erb +5 -0
  66. data/app/views/symphonia/accounts/lost_password.html.erb +6 -0
  67. data/app/views/symphonia/accounts/lost_password.js.erb +3 -0
  68. data/app/views/symphonia/accounts/new_activation.html.erb +11 -0
  69. data/app/views/symphonia/accounts/new_activation.js.erb +6 -0
  70. data/app/views/symphonia/accounts/register.html.erb +20 -0
  71. data/app/views/symphonia/accounts/reset_password.html.erb +18 -0
  72. data/app/views/symphonia/accounts/update.js.erb +1 -0
  73. data/app/views/symphonia/admin/index.html.erb +15 -0
  74. data/app/views/symphonia/attachments/destroy.js.erb +1 -0
  75. data/app/views/symphonia/common/_editable_images_grid.html.erb +12 -0
  76. data/app/views/symphonia/common/_filters.html.erb +23 -0
  77. data/app/views/symphonia/common/_locale_chooser.html.erb +16 -0
  78. data/app/views/symphonia/common/_share_links.html.erb +5 -0
  79. data/app/views/symphonia/filters/options.html.erb +36 -0
  80. data/app/views/symphonia/filters/options.js.erb +9 -0
  81. data/app/views/symphonia/filters/table.html.erb +21 -0
  82. data/app/views/symphonia/login/_form.html.erb +19 -0
  83. data/app/views/symphonia/login/new.html.erb +11 -0
  84. data/app/views/symphonia/login_sessions/new.html.erb +1 -0
  85. data/app/views/symphonia/notifier/activation_user.html.erb +7 -0
  86. data/app/views/symphonia/notifier/activation_user.text.erb +3 -0
  87. data/app/views/symphonia/notifier/reset_password_user.html.erb +7 -0
  88. data/app/views/symphonia/notifier/reset_password_user.text.erb +3 -0
  89. data/app/views/symphonia/notifier/test_mail.html.erb +2 -0
  90. data/app/views/symphonia/notifier/test_mail.text.erb +3 -0
  91. data/app/views/symphonia/notifier/user_change_to_active.html.erb +3 -0
  92. data/app/views/symphonia/notifier/user_change_to_active.text.erb +1 -0
  93. data/app/views/symphonia/notifier/user_registered.html.erb +13 -0
  94. data/app/views/symphonia/notifier/user_registered.text.erb +8 -0
  95. data/app/views/symphonia/roles/_form.html.erb +30 -0
  96. data/app/views/symphonia/roles/edit.html.erb +5 -0
  97. data/app/views/symphonia/roles/index.html.erb +6 -0
  98. data/app/views/symphonia/roles/new.html.erb +4 -0
  99. data/app/views/symphonia/roles/show.html.erb +5 -0
  100. data/app/views/symphonia/users/_form.html.erb +13 -0
  101. data/app/views/symphonia/users/edit.html.erb +26 -0
  102. data/app/views/symphonia/users/edit.js.erb +3 -0
  103. data/app/views/symphonia/users/edit_current.html.erb +7 -0
  104. data/app/views/symphonia/users/index.html.erb +5 -0
  105. data/app/views/symphonia/users/new.html.erb +8 -0
  106. data/app/views/symphonia/users/show.html.erb +63 -0
  107. data/config/locales/cs.yml +233 -0
  108. data/config/locales/en.yml +47 -0
  109. data/config/routes.rb +52 -0
  110. data/db/migrate/20130714140500_create_users.rb +49 -0
  111. data/db/migrate/20130714140501_create_roles.rb +16 -0
  112. data/db/migrate/20130714140502_create_preferences.rb +26 -0
  113. data/db/migrate/20130828175114_create_attachments.rb +20 -0
  114. data/db/migrate/20141213204351_create_admin_modules.rb +20 -0
  115. data/db/seeds.rb +12 -0
  116. data/lib/generators/symphonia/entity_controller/entity_controller_generator.rb +48 -0
  117. data/lib/generators/symphonia/entity_controller/templates/controller.rb +100 -0
  118. data/lib/generators/symphonia/query/query_generator.rb +37 -0
  119. data/lib/generators/symphonia/setup/setup_generator.rb +52 -0
  120. data/lib/generators/symphonia/setup/templates/404.html +26 -0
  121. data/lib/generators/symphonia/setup/templates/500.html +37 -0
  122. data/lib/generators/symphonia/setup/templates/Gemfile +18 -0
  123. data/lib/generators/symphonia/setup/templates/base_layout.html.erb +46 -0
  124. data/lib/generators/symphonia/setup/templates/design.scss +4 -0
  125. data/lib/generators/symphonia/setup/templates/settings.rb +65 -0
  126. data/lib/generators/symphonia/setup/templates/spec_helper.rb +18 -0
  127. data/lib/symphonia/action_cable/connection.rb +31 -0
  128. data/lib/symphonia/admin_constraint.rb +9 -0
  129. data/lib/symphonia/attachable.rb +35 -0
  130. data/lib/symphonia/base_controller.rb +96 -0
  131. data/lib/symphonia/bootstrap_link_render.rb +69 -0
  132. data/lib/symphonia/controller_extensions.rb +200 -0
  133. data/lib/symphonia/engine.rb +137 -0
  134. data/lib/symphonia/form_builder.rb +137 -0
  135. data/lib/symphonia/menu_manager.rb +23 -0
  136. data/lib/symphonia/model_attributes/attribute.rb +137 -0
  137. data/lib/symphonia/model_attributes.rb +102 -0
  138. data/lib/symphonia/model_filters/base.rb +82 -0
  139. data/lib/symphonia/model_filters/boolean_filter.rb +26 -0
  140. data/lib/symphonia/model_filters/date_filter.rb +81 -0
  141. data/lib/symphonia/model_filters/integer_filter.rb +18 -0
  142. data/lib/symphonia/model_filters/select_filter.rb +48 -0
  143. data/lib/symphonia/model_filters/string_filter.rb +18 -0
  144. data/lib/symphonia/model_filters.rb +10 -0
  145. data/lib/symphonia/object.rb +31 -0
  146. data/lib/symphonia/permissions.rb +93 -0
  147. data/lib/symphonia/query.rb +275 -0
  148. data/lib/symphonia/query_columns/attribute_column.rb +43 -0
  149. data/lib/symphonia/query_columns/generic_column.rb +165 -0
  150. data/lib/symphonia/query_columns.rb +8 -0
  151. data/lib/symphonia/spec_helper.rb +4 -0
  152. data/lib/symphonia/user_management.rb +58 -0
  153. data/lib/symphonia/version.rb +4 -0
  154. data/lib/symphonia.rb +20 -0
  155. data/spec/controllers/account_controller_spec.rb +90 -0
  156. data/spec/controllers/admin_controller_spec.rb +35 -0
  157. data/spec/controllers/api_controller_spec.rb +9 -0
  158. data/spec/controllers/filters_controller_spec.rb +35 -0
  159. data/spec/controllers/images_controller_spec.rb +5 -0
  160. data/spec/controllers/login_controller_spec.rb +20 -0
  161. data/spec/controllers/roles_controller_spec.rb +12 -0
  162. data/spec/controllers/users_controller_spec.rb +47 -0
  163. data/spec/factories/factories.rb +52 -0
  164. data/spec/helpers/symphonia/application_helper_spec.rb +62 -0
  165. data/spec/mailers/previews/symphonia/notifier_preview.rb +27 -0
  166. data/spec/mailers/symphonia/notifier_spec.rb +76 -0
  167. data/spec/models/attachment_spec.rb +22 -0
  168. data/spec/models/query/attribute_spec.rb +8 -0
  169. data/spec/models/query/symphonia_query_spec.rb +70 -0
  170. data/spec/models/role_spec.rb +13 -0
  171. data/spec/models/user_spec.rb +10 -0
  172. data/spec/rails_helper.rb +13 -0
  173. data/spec/requests/accounts_spec.rb +118 -0
  174. data/spec/requests/attachments_controller_spec.rb +23 -0
  175. data/spec/requests/login_spec.rb +26 -0
  176. data/spec/requests/roles_spec.rb +10 -0
  177. data/spec/requests/users_spec.rb +50 -0
  178. data/spec/spec_helper.rb +101 -0
  179. data/spec/support/common_file.txt +2 -0
  180. data/spec/support/query.rb +36 -0
  181. data/spec/support/shared.rb +62 -0
  182. data/spec/support/shared_controllers.rb +31 -0
  183. data/spec/support/stub_users.rb +32 -0
  184. data/spec/support/symphonia.jpg +0 -0
  185. data/spec/support/wait_for_ajax.rb +15 -0
  186. data/spec/version_spec.rb +5 -0
  187. data/spec/views/filters/options.html.erb_spec.rb +14 -0
  188. metadata +697 -0
@@ -0,0 +1,275 @@
1
+ module Symphonia
2
+ class SymphoniaQueryNotRegistered < StandardError
3
+ end
4
+ class FilterNotFound < StandardError
5
+ end
6
+
7
+ class Query
8
+ attr_reader :set_filter, :controller
9
+ attr_writer :entity_scope, :filters
10
+ attr_accessor :disable_search, :active_filters, :options, :default_filters
11
+ attr_accessor :operators
12
+
13
+ class_attribute :model
14
+
15
+ # @param [nil, String] sort
16
+ # @param [Hash] filters
17
+ # @param [nil, Array] columns
18
+ def initialize(c = nil, sort: "", filters: {}, columns: nil, **options)
19
+ if c
20
+ ActiveSupport::Deprecation.warn("initialize allow only hash. #{c} - update your controller:")
21
+ #
22
+ # model.query.new sort: "default sort", filters: {default: filter}, columns: [columns, names]
23
+ # @query.from_params params
24
+ #
25
+ end
26
+ if options[:default_order]
27
+ ActiveSupport::Deprecation.warn("default_order is deprecated, use just `sort`")
28
+ end
29
+ @active_filters = {}
30
+ @default_sort = sort
31
+ @default_filters = filters
32
+ @default_column_names = columns
33
+ @options = options
34
+ end
35
+
36
+ def from_params(params)
37
+ @sort_col = params[:sort]
38
+ @sort_dir = params[:direction]
39
+ self.entity_scope = entity.like(@q) if (@q = params[:q].presence)
40
+ if (@set_filter = params[:set_filter].to_boolean)
41
+ setup_filters(params)
42
+ end
43
+ setup_columns(params)
44
+ end
45
+
46
+ protected
47
+
48
+ def setup_filters(params)
49
+ available_filters.each do |filter|
50
+ if (q = params[filter.name])
51
+ if q.blank?
52
+ active_filters.delete(filter.name)
53
+ else
54
+ operators.delete(filter.name)
55
+ active_filters[filter.name] = q.try(:downcase) || q
56
+ if (o = params[:o])
57
+ operators[filter.name] = o[filter.name]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def setup_columns(params)
65
+ if (c = params[:column_names]).present?
66
+ self.column_names = c
67
+ end
68
+ end
69
+
70
+ public
71
+
72
+ def sort_column
73
+ c, d = @default_sort.split(':')
74
+ c ||= available_columns[:name]&.name
75
+ @sort_table ||= SortableTable::SortTable.new(available_columns.collect { |_n, c| c.sort_definition }.compact,
76
+ default_column: c&.to_sym,
77
+ default_direction: d&.to_sym)
78
+ @sort_column ||= @sort_table.sort_column(@sort_col, @sort_dir)
79
+ rescue StandardError
80
+ raise "Incorrect default column: '#{options[:default_sort]}'"
81
+ end
82
+
83
+ def search?
84
+ disable_search.nil? && model.respond_to?(:like)
85
+ end
86
+
87
+ def activate_filter(name, value)
88
+ @active_filters[name] ||= value.downcase
89
+ end
90
+
91
+ alias add_filter activate_filter
92
+
93
+ def entity
94
+ model
95
+ end
96
+
97
+ def column_names=(names)
98
+ @column_names = (names.is_a?(Array) ? names : names.to_s.split('|')).map(&:to_sym)
99
+ end
100
+
101
+ def column_names
102
+ @column_names ||= default_column_names
103
+ end
104
+
105
+ def columns(all = false)
106
+ return available_columns.values if all || Array(column_names).blank?
107
+
108
+ @columns ||= column_names.map { |c| available_columns[c] }.compact
109
+ end
110
+
111
+ def render_filters
112
+ (available_filters | filters).each do |filter|
113
+ yield filter
114
+ end
115
+ end
116
+
117
+ def entity_scope
118
+ @entity_scope || model
119
+ end
120
+
121
+ def filters
122
+ setup_filters(default_filters) unless @set_filter
123
+ @filters ||= available_filters.select do |filter|
124
+ if (q = active_filters[filter.name])
125
+ operators && (filter.operator = operators[filter.name])
126
+ filter.value = q
127
+ end
128
+ end
129
+ @filters
130
+ end
131
+
132
+ def scope
133
+ opts = columns.each_with_object(includes: [], preload: [], joins: []) do |var, mem|
134
+ mem[:includes].concat var.includes
135
+ mem[:preload].concat var.preload
136
+ mem[:joins].concat var.joins
137
+ end
138
+ x = entity_scope.includes(opts[:includes].uniq).preload(opts[:preload].uniq).joins(opts[:joins].uniq)
139
+ filters.each do |filter|
140
+ x = filter.apply(x)
141
+ end
142
+ x
143
+ end
144
+
145
+ def entities
146
+ return @entities unless @entities.nil?
147
+
148
+ order = sort_column.order
149
+ @entities = scope.order(order.is_a?(String) && Arel.sql(order) || order)
150
+ end
151
+
152
+ def entity_count
153
+ entities.reorder(nil).count
154
+ end
155
+
156
+ def summarize_columns
157
+ columns.select(&:summarize?)
158
+ end
159
+
160
+ def to_partial_path
161
+ 'symphonia/common/query'
162
+ end
163
+
164
+ def to_params(force = false)
165
+ if @q
166
+ { q: @q }
167
+ elsif @set_filter || force
168
+ filters.each_with_object(set_filter: 1) { |var, mem| f, v = var; mem[f.send(:form_field_name).to_sym] = v; }
169
+ else
170
+ {}
171
+ end
172
+ end
173
+
174
+ def inspect
175
+ "#<#{self.class.name} set_filter=#{@set_filter} >"
176
+ end
177
+
178
+
179
+ def available_filters
180
+ self.active_filters ||= {}
181
+ self.operators ||= {}
182
+
183
+ if @available_filters.nil?
184
+ @available_filters = []
185
+ register_filters
186
+ end
187
+ @available_filters
188
+ end
189
+
190
+ private
191
+
192
+ # @param [Symbol] filter_type
193
+ # @param [Symphonia::ModelAttributes::Attribute] attribute
194
+ # @param [Hash] options
195
+ def register_filter(filter_type, attribute, options = {})
196
+ @available_filters ||= []
197
+ klass = "Symphonia::ModelFilters::#{filter_type.to_s.classify}Filter".safe_constantize
198
+ return if klass.nil?
199
+
200
+ filter = klass.new(attribute, self, options)
201
+ yield filter if block_given?
202
+ @available_filters << filter
203
+ end
204
+
205
+ # def default_filter
206
+ # {}
207
+ # end
208
+
209
+ def register_filters
210
+ model.registered_attributes.each do |_name, attribute|
211
+ register_filter(attribute.filter, attribute) if attribute.filter
212
+ end
213
+ end
214
+
215
+ def available_columns
216
+ if @available_columns.nil?
217
+ @available_columns = {}
218
+ register_columns
219
+ else
220
+ @available_columns
221
+ end
222
+ @available_columns
223
+ end
224
+
225
+ def default_column_names
226
+ if @default_column_names.nil?
227
+ @default_column_names = []
228
+ available_columns.each { |k, v| v.default? && @default_column_names << k }
229
+ end
230
+ @default_column_names
231
+ end
232
+
233
+ def add_column(_type, name, options = {})
234
+ @available_columns[name] = "Symphonia::QueryColumns::#{_type.to_s.classify}Column".constantize.new(name, self, options)
235
+ end
236
+
237
+ # aliases
238
+
239
+ def add_generic_column(name, options = {})
240
+ add_column(:generic, name, options)
241
+ end
242
+
243
+ def add_text_column(name, options = {})
244
+ add_column(:text, name, options)
245
+ end
246
+
247
+ def add_price_column(name, options = {})
248
+ add_column(:price, name, options)
249
+ end
250
+
251
+ def add_date_column(name, options = {})
252
+ add_column(:date, name, options)
253
+ end
254
+
255
+ def add_boolean_column(name, options = {})
256
+ add_column(:boolean, name, options)
257
+ end
258
+ #
259
+ def add_attribute_column(attribute, options = {})
260
+ @available_columns[attribute.name] = ::Symphonia::QueryColumns::AttributeColumn.new(attribute, self, options)
261
+ end
262
+
263
+ protected
264
+
265
+ def session_key(name)
266
+ [self.class.name.hash, name].join('-')
267
+ end
268
+
269
+ def register_columns
270
+ model.registered_attributes.values.each do |attribute|
271
+ add_attribute_column(attribute)
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,43 @@
1
+ module Symphonia
2
+ module QueryColumns
3
+ class AttributeColumn < GenericColumn
4
+
5
+ attr_reader :attribute
6
+ attr_accessor :no_header, :sort_options
7
+ attr_writer :header, :sort
8
+
9
+ delegate :default?, :title, to: :attribute
10
+
11
+ def initialize(attribute, query, options={})
12
+ @attribute = attribute
13
+ @name = attribute.name
14
+ @query = query
15
+ @model = query.model
16
+ @options = options
17
+
18
+ @includes = options.delete(:includes) || reference? && [@name] || []
19
+ @preload = options.delete(:preload) || reference? && [@name] || []
20
+ @joins = Array(options.delete(:joins)) || []
21
+ @sort_definition = attribute.sort_column
22
+ @sort = attribute.sort?
23
+ end
24
+
25
+ def reference?
26
+ @attribute.is_a?(Symphonia::ModelAttributes::ReferenceAttribute)
27
+ end
28
+
29
+ # def header(table_header_tag)
30
+ # if @header === false
31
+ # table_header_tag.th(@name, '', sort: false)
32
+ # else
33
+ # table_header_tag.th(@name, title, {sort: @sort.nil?, sort_options: (sort_options || {}).merge({column: sort_column_name})})
34
+ # end
35
+ # end
36
+
37
+ def format_value(view, entity)
38
+ @attribute.format(view, entity)
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,165 @@
1
+ module Symphonia
2
+ module QueryColumns
3
+ class GenericColumn
4
+
5
+ attr_reader :name, :query, :model, :includes, :preload, :joins
6
+ attr_writer :sort_column_name, :sort_definition
7
+ attr_accessor :sort, :header, :default
8
+
9
+ def initialize(name, klass = nil, options={})
10
+ ::ActiveSupport::Deprecation.warn 'Maybe this class is over...'
11
+ @name = name
12
+ if klass.is_a? Symphonia::Query
13
+ @query = klass
14
+ @model = @query.model
15
+ elsif klass < ActiveRecord::Base
16
+ @model = klass
17
+ else # default fallback
18
+ @model = ActiveRecord::Base
19
+ end
20
+ @options = options
21
+
22
+ # @sort_options = @options.delete(:sort_options) || {}
23
+ self.header = @options.delete(:header).nil?
24
+ self.sort = @options.delete(:sort).nil?
25
+
26
+ @includes = Array(options.delete(:includes)) || []
27
+ @preload = Array(options.delete(:preload)) || []
28
+ @joins = Array(options.delete(:joins)) || []
29
+ end
30
+
31
+ def summarize?
32
+ false
33
+ end
34
+
35
+ def header?
36
+ !(@header === false)
37
+ end
38
+
39
+ def sort?
40
+ !(@sort === false)
41
+ end
42
+
43
+ def default?
44
+ !!@default
45
+ end
46
+
47
+ def header(view)
48
+ ::ActiveSupport::Deprecation.warn 'Header method is deprecated -> use app/helpers/symphonia/renderer_helper.rb:59'
49
+ if @no_header || @header === false
50
+ title
51
+ else
52
+ view.symphonia_query_sort_tag(self)
53
+ end
54
+ end
55
+
56
+ def sort_definition
57
+ return nil unless sort?
58
+ @sort_definition ||= SortableTable::SortColumnCustomDefinition.new(@name, {asc: "#{sort_column_name} asc", desc: "#{sort_column_name} desc"})
59
+ end
60
+
61
+ def title
62
+ ::ActiveSupport::Deprecation.warn 'method is deprecated'
63
+ @model.human_attribute_name(@name, @options[:i18n] || {})
64
+ end
65
+
66
+ def sort_options
67
+ @sort_options
68
+ end
69
+
70
+ def value(entity)
71
+ entity.send(name)
72
+ end
73
+
74
+ def format_value(view, entity)
75
+ if @options[:formatter].is_a?(Proc)
76
+ @options[:formatter].call(view, entity).to_s
77
+ else
78
+ case @options[:with_link]
79
+ when :entity, true
80
+ view.link_to(value(entity).to_s, entity)
81
+ when :value
82
+ view.link_to(value(entity).to_s, value(entity))
83
+ else
84
+ value(entity).to_s
85
+ end
86
+ end
87
+ end
88
+
89
+ def sum_value(view)
90
+ format_value(view, OpenStruct.new(name => sum))
91
+ end
92
+
93
+ def sum
94
+ return nil unless summarize?
95
+ @sum ||= query.entities.sum(name)
96
+ end
97
+
98
+ def css_classes
99
+ @name.to_s
100
+ end
101
+
102
+ def inspect
103
+ "#<#{self.class.name} name='#{name}' options=#{@options.inspect}>"
104
+ end
105
+
106
+ private
107
+
108
+ def t(*args)
109
+ I18n.translate(*args)
110
+ end
111
+
112
+ def l(*args)
113
+ I18n.localize(*args)
114
+ end
115
+
116
+ def sort_column_name
117
+ @sort_column_name.presence || [@model.table_name, @name].join('.')
118
+ end
119
+ end
120
+
121
+ class TextColumn < GenericColumn
122
+
123
+ def format_value(view, entity)
124
+ view.format_text(super(view, entity))
125
+ end
126
+
127
+ end
128
+
129
+ class NumberColumn < GenericColumn
130
+
131
+ def summarize?
132
+ true
133
+ end
134
+
135
+ end
136
+
137
+ class PriceColumn < NumberColumn
138
+
139
+ def format_value(view, entity)
140
+ view.format_price(super(view, entity))
141
+ end
142
+
143
+ end
144
+
145
+ class DateColumn < GenericColumn
146
+
147
+ def format_value(view, entity)
148
+ value(entity) && l(value(entity), format: @options[:format])
149
+ end
150
+
151
+ end
152
+
153
+ class BooleanColumn < GenericColumn
154
+
155
+ def format_value(view, entity)
156
+ if value(entity)
157
+ view.fa_icon('true', title: t(:true))
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ end
@@ -0,0 +1,8 @@
1
+ require_dependency('symphonia/query_columns/generic_column')
2
+ require_dependency('symphonia/query_columns/attribute_column')
3
+
4
+ module Symphonia
5
+ module QueryColumns
6
+
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../../../spec/spec_helper', __FILE__)
2
+ Dir.glob(File.expand_path('../../../spec/{factories,support}/*.rb', __FILE__)).each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,58 @@
1
+ module Symphonia
2
+ module UserManagement
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+
8
+ enum status: { active: 1, pending: 5, archived: 9 }
9
+
10
+ scope :sorted, -> { order(:first_name, :last_name) }
11
+
12
+ # validates :mail, uniqueness: true, format: { with: Authlogic::Regex::EMAIL_NONASCII }
13
+ # validates :password, presence: true, if: ->(u){ u.new_record? }
14
+
15
+ validates :login, :mail, :first_name, :last_name, presence: true
16
+
17
+ def self.current=(entity)
18
+ Rails.logger.info("* #{entity.class.name}: #{entity.login} id: #{entity.id}")
19
+ Thread.current[:"current_#{name.underscore}"] = entity
20
+ end
21
+
22
+ def self.current
23
+ Thread.current[:"current_#{name.underscore}"] ||= "#{self}::Anonymous".constantize.new
24
+ end
25
+
26
+ def name
27
+ "#{first_name} #{last_name}"
28
+ end
29
+
30
+ def status_new?
31
+ pending?
32
+ end
33
+
34
+ def lock?
35
+ !active?
36
+ end
37
+
38
+ alias_method :archive?, :lock?
39
+
40
+ def archive!
41
+ archived!
42
+ end
43
+
44
+ def unarchive!
45
+ active!
46
+ end
47
+
48
+ alias_method :activate!, :unarchive!
49
+
50
+ def css_classes
51
+ css = ''
52
+ css << ' -active' if self.active?
53
+ css << ' lock' if self.lock?
54
+ css
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,4 @@
1
+ module Symphonia
2
+ VERSION = '2.1.7'
3
+ VERSION_NAME = '2019.03.31'
4
+ end
data/lib/symphonia.rb ADDED
@@ -0,0 +1,20 @@
1
+ # Dir[File.dirname(__FILE__) + '/symphonia/*.rb'].each {|file| require file }
2
+ module Symphonia
3
+ include ::ActiveSupport::Configurable
4
+
5
+ autoload :BaseController, 'symphonia/base_controller'
6
+ autoload :ControllerExtensions, 'symphonia/controller_extensions'
7
+ autoload :FormBuilder, 'symphonia/form_builder'
8
+ autoload :BootstrapLinkRender, 'symphonia/bootstrap_link_render'
9
+ autoload :Query, 'symphonia/query'
10
+ autoload :ModelAttributes, 'symphonia/model_attributes'
11
+ autoload :ModelFilters, 'symphonia/model_filters'
12
+ autoload :Attachable, 'symphonia/attachable'
13
+ autoload :QueryColumns, 'symphonia/query_columns'
14
+
15
+ module ActionCable
16
+ autoload :Connection, 'symphonia/action_cable/connection'
17
+ end
18
+ end
19
+
20
+ require 'symphonia/engine'
@@ -0,0 +1,90 @@
1
+ module Symphonia
2
+ RSpec.describe AccountsController do
3
+
4
+ include ActiveJob::TestHelper
5
+
6
+ routes { Symphonia::Engine.routes }
7
+
8
+ let(:regular_user) { FactoryBot.create(:user) }
9
+
10
+ context 'logged', logged: true do
11
+ context '#update' do
12
+
13
+ it 'valid' do
14
+ put :update, params: { user: { login: Faker::Internet.user_name } }
15
+ expect(response).to redirect_to account_path
16
+ end
17
+
18
+ it 'invalid' do
19
+ put :update, params: { user: { login: '' } }
20
+ expect(response).to have_http_status :success
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ context '#resend_activation' do
28
+
29
+ let(:email) { Faker::Internet.email }
30
+ subject { get :resend_activation, params: { mail: email } }
31
+
32
+ it 'non exist user' do
33
+ is_expected.to redirect_to :root
34
+ expect(flash[:error]).not_to be :blank
35
+ end
36
+
37
+ it 'active user' do
38
+ _user = FactoryBot.create(:user, email: email, status: 'active')
39
+ is_expected.to redirect_to :root
40
+ expect(flash[:error]).not_to be :blank
41
+ end
42
+
43
+ it 'pending user' do
44
+ user = FactoryBot.create(:user, email: email, status: 'pending')
45
+ token = user.perishable_token.dup
46
+
47
+ expect { get :resend_activation, params: { mail: email } }.to have_enqueued_job.on_queue('mailers')
48
+ expect(response).to have_http_status :redirect
49
+
50
+ expect(user.reload.perishable_token).not_to eq token
51
+ expect(flash[:error]).to be_nil
52
+ expect(flash[:notice]).not_to be :blank
53
+ end
54
+ end
55
+
56
+ context '#activation' do
57
+ let(:token) { SecureRandom.hex(3) }
58
+ subject { get :activation, params: { activation_code: token } }
59
+
60
+ it 'valid token' do
61
+ user = FactoryBot.create(:user, status: 'pending')
62
+ user.update_columns(perishable_token: token)
63
+ is_expected.to redirect_to :login
64
+ expect(user.reload.status).to eq 'active'
65
+ end
66
+
67
+ it 'invalid token' do
68
+ is_expected.to redirect_to :root
69
+ expect(flash[:error]).not_to be :blank
70
+ end
71
+ end
72
+
73
+ context '#lost_password' do
74
+
75
+ it 'with mail' do
76
+ user = FactoryBot.create(:user, status: 'active')
77
+
78
+ expect { post :lost_password, params: { mail: user.email } }.to have_enqueued_job.on_queue('mailers')
79
+ expect(flash[:error]).to be_nil
80
+ end
81
+
82
+ it 'with inactive user' do
83
+ user = FactoryBot.create(:user, status: 'pending')
84
+ expect { post :lost_password, params: { mail: user.email } }.not_to have_enqueued_job.on_queue('mailers')
85
+ expect(flash[:error]).not_to be :blank
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,35 @@
1
+ module Symphonia
2
+ RSpec.describe AdminController do
3
+ routes { Symphonia::Engine.routes }
4
+
5
+ context 'no user' do
6
+
7
+ it '#index' do
8
+ get :index
9
+ expect(response).to redirect_to login_path
10
+ end
11
+
12
+ it '#test_mail' do
13
+ get :test_mail
14
+ expect(response).to redirect_to login_path
15
+ end
16
+
17
+ end
18
+
19
+ context 'logged in', logged: true do
20
+ include ActiveJob::TestHelper
21
+
22
+ it '#index' do
23
+ get :index
24
+ expect(response).to have_http_status :success
25
+ end
26
+
27
+ it '#test_mail' do
28
+ Rails.application.config.action_mailer.delivery_method = :test
29
+ expect { get :test_mail }.to have_enqueued_job.on_queue('mailers')
30
+ expect(response).to have_http_status :redirect
31
+ end
32
+
33
+ end
34
+ end
35
+ end