symphonia 2.1.7

Sign up to get free protection for your applications and to get access to all the features.
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