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,200 @@
1
+ module Symphonia
2
+ module ControllerExtensions
3
+
4
+ extend ActiveSupport::Concern
5
+
6
+
7
+ included do
8
+ class Unauthorized < Exception; end
9
+
10
+ before_action :current_user, :menu_item
11
+ before_action :set_default_locale
12
+
13
+ # force_ssl if: -> {Rails.env.production? && User.current.ssl?}
14
+
15
+ add_flash_types :warning
16
+ add_flash_types :error
17
+
18
+ rescue_from ::ActiveRecord::RecordNotFound do
19
+ render_404
20
+ end
21
+
22
+ rescue_from Unauthorized do
23
+ render_403
24
+ end
25
+
26
+ helper_method :current_user
27
+
28
+ end
29
+
30
+ def back_url
31
+ url = params[:back_url]
32
+ if url.nil? && (referer = request.env['HTTP_REFERER'])
33
+ url = CGI.unescape(referer.to_s)
34
+ end
35
+ url
36
+ end
37
+
38
+ # def redirect_back_or_default(default, options={})
39
+ # back_url = params[:back_url].to_s
40
+ # if back_url.present?
41
+ # begin
42
+ # uri = URI.parse(back_url)
43
+ # # do not redirect user to another host or to the login or register page
44
+ # if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
45
+ # redirect_to(back_url)
46
+ # return
47
+ # end
48
+ # rescue URI::InvalidURIError
49
+ # logger.warn("Could not redirect to invalid URL #{back_url}")
50
+ # # redirect to default
51
+ # end
52
+ # elsif options[:referer]
53
+ # redirect_to_referer_or default
54
+ # return
55
+ # end
56
+ # redirect_to default, options
57
+ # false
58
+ # end
59
+
60
+ # Redirects to the request referer if present, redirects to args or call block otherwise.
61
+ def redirect_to_referer_or(*args, &block)
62
+ redirect_to :back
63
+ rescue ::ActionController::RedirectBackError
64
+ if args.any?
65
+ redirect_to *args
66
+ elsif block_given?
67
+ block.call
68
+ else
69
+ raise "#redirect_to_referer_or takes arguments or a block"
70
+ end
71
+ end
72
+
73
+ # private
74
+
75
+ def set_locale
76
+ client_lang = params.fetch(:locale, nil).presence || session[:locale].presence || request.env['HTTP_ACCEPT_LANGUAGE'].to_s.split(',').collect { |l| l.scan(/^[a-z]{2}/) }.flatten
77
+ @client_lang = Array(client_lang).detect { |l| I18n.available_locales.include?(l.to_sym) }
78
+
79
+ I18n.locale = (@client_lang || I18n.default_locale)
80
+ session[:locale] = I18n.locale
81
+
82
+ logger.debug "* Locale set to '#{I18n.locale}'"
83
+ end
84
+
85
+ def set_default_locale
86
+ if (enforce_default = Symphonia.config.default_locale)
87
+ I18n.locale = enforce_default
88
+ end
89
+ end
90
+
91
+ # protected
92
+
93
+ def login_require(format = nil)
94
+ if current_user.nil? || !current_user.logged_in?
95
+ respond_to do |format|
96
+ format.html do
97
+ store_location
98
+ redirect_to symphonia.login_path, flash: { error: t(:text_login_require) }
99
+ end
100
+ format.json do
101
+ render json: { errors: 'You must be logged in to access this endpoint' }, status: 403
102
+ end
103
+ end
104
+ return false
105
+ end
106
+ true
107
+ end
108
+
109
+ alias_method :require_login, :login_require
110
+ alias_method :require_user, :login_require
111
+
112
+ def admin_require
113
+ return unless login_require
114
+ unless current_user.admin?
115
+ render_403
116
+ return false
117
+ end
118
+ end
119
+
120
+ alias_method :require_admin, :admin_require
121
+
122
+ def render_403
123
+ respond_to do |format|
124
+ format.html { render template: 'common/403', message: :notice_not_authorized, status: 403 }
125
+ format.js { render plain: "alert('#{t :text_access_deny}')", message: :notice_not_authorized, status: 403 }
126
+ format.json { head 403, message: :notice_not_authorized }
127
+ end
128
+ end
129
+
130
+ def render_404
131
+ respond_to do |format|
132
+ format.html { render template: 'common/404', message: :notice_page_not_found, status: 404 }
133
+ format.json { head 404, message: :not_found }
134
+ end
135
+ end
136
+
137
+ # Renders a 200 response for successful updates or deletions via the API
138
+ def render_api_ok
139
+ head :ok
140
+ end
141
+
142
+ # Renders a head API response
143
+ def render_api_head(status)
144
+ head status
145
+ end
146
+
147
+ def menu_item(item = nil)
148
+ @menu_item = (item || controller_name)
149
+ end
150
+
151
+ def current_user_session
152
+ return @current_user_session if defined?(@current_user_session)
153
+ @current_user_session = UserSession.find
154
+ end
155
+
156
+ def current_user
157
+ return (Symphonia::User.current ||= @current_user) if defined?(@current_user)
158
+ @current_user = current_user_session && current_user_session.user
159
+ Symphonia::User.current = @current_user || Symphonia::User::Anonymous.new
160
+ end
161
+
162
+ def authorize
163
+ if Symphonia::User.current.authorize?(controller_name, action_name)
164
+ return true
165
+ else
166
+ if Symphonia::User.current.logged_in?
167
+ raise Unauthorized
168
+ else
169
+ respond_to do |format|
170
+ format.html do
171
+ return redirect_to(symphonia.login_path(back_url: request.path), error: t(:text_error_login_required))
172
+ end
173
+ format.any { return head 401 }
174
+ end
175
+ end
176
+ end
177
+ raise Unauthorized
178
+ end
179
+
180
+ def handle_unverified_request
181
+ # raise an exception
182
+ fail ActionController::InvalidAuthenticityToken
183
+ # or destroy session, redirect
184
+ if current_user_session
185
+ current_user_session.destroy
186
+ end
187
+ redirect_to main_app.root_url
188
+ end
189
+
190
+ def store_location
191
+ session[:return_to] = request.url
192
+ end
193
+
194
+ def redirect_back_or_default(default, options = {})
195
+ options ||= {}
196
+ redirect_to(params[:back_url] || default, options)
197
+ end
198
+
199
+ end
200
+ end
@@ -0,0 +1,137 @@
1
+ require 'symphonia/object'
2
+ require 'symphonia/menu_manager'
3
+ require 'symphonia/permissions'
4
+ require 'symphonia/user_management'
5
+
6
+ require 'rails-i18n'
7
+ require 'turbolinks'
8
+ require 'authlogic'
9
+ require 'bootstrap'
10
+
11
+ require 'will_paginate'
12
+ require 'api-pagination'
13
+ require 'font-awesome-rails'
14
+ require 'jquery-rails'
15
+ require 'jquery-ui-rails'
16
+ require 'rdiscount'
17
+ require 'sortable-table'
18
+ require 'paperclip'
19
+ require 'awesome_nested_set'
20
+ require 'acts_as_list'
21
+ require 'bootstrap_form'
22
+ require 'the_sortable_tree'
23
+ require 'bootstrap-datepicker-rails'
24
+ require 'wicked_pdf'
25
+ require 'swagger/blocks'
26
+
27
+ module Symphonia
28
+
29
+ class Engine < ::Rails::Engine
30
+ isolate_namespace Symphonia
31
+
32
+ config.generators do |g|
33
+ g.test_framework :rspec, fixture: false
34
+ # g.fixture_replacement :factory_girl, dir: 'spec/factories'
35
+ end
36
+
37
+ # Rails 5
38
+ # ActionController::Base.class_eval do
39
+ # include Symphonia::ApplicationController
40
+ # end
41
+
42
+
43
+ initializer :symphonia_extensions do
44
+ end
45
+
46
+ # ActiveSupport::Reloader.to_prepare do
47
+ # ::ApplicationController.send :helper, Symphonia::ApplicationHelper
48
+ # ::ApplicationMailer.send :helper, Symphonia::ApplicationHelper
49
+ # BootstrapForm::FormBuilder.prepend(Symphonia::FormBuilder)
50
+ # end
51
+
52
+ initializer :symphonia_setup do |_app|
53
+ Mime::Type.register 'application/pdf', :pdf
54
+ config.i18n.available_locales ||= %i[cs en]
55
+ config.i18n.default_locale = :cs
56
+
57
+ WillPaginate.per_page = 20
58
+
59
+ Symphonia.configure do |config|
60
+ config.after_login_path = ->(h) { h.symphonia.user_current_path }
61
+ config.allow_registrations = true
62
+ config.default_locale = :cs
63
+ end
64
+
65
+ end
66
+
67
+ initializer :append_migrations do |app|
68
+ if app.root.to_s != root.to_s && app.root != root.join("spec/dummy")
69
+ config.paths['db/migrate'].expanded.each do |expanded_path|
70
+ app.config.paths['db/migrate'] << expanded_path
71
+ end
72
+ end
73
+ end
74
+
75
+ # include helpers
76
+ initializer :load_helper, before: :load_config_initializers do |app|
77
+ # config.active_record.raise_in_transactional_callbacks = false
78
+ if Rails.env.development?
79
+ config.action_mailer.default_url_options ||= { host: 'symphonia.app' }
80
+ config.action_mailer.preview_path = "{#{app.root.join('spec/mailers/previews')},#{root.join('spec/mailers/previews')}}"
81
+ end
82
+ end
83
+
84
+ initializer :assets do |_app|
85
+ if defined?(::Ckeditor)
86
+ config.assets.precompile << 'ckeditor/**/*'
87
+ config.assets.precompile << 'symphonia/symphonia_ckeditor.js'
88
+ end
89
+ end
90
+
91
+ initializer :symphonia_general_permissions do |_app|
92
+ Symphonia::Permissions.map do |m|
93
+ m.register(:view_users).add(:users, %i[index show])
94
+ m.register(:manager_users).add(:users, %i[create update destroy new edit])
95
+ end
96
+
97
+ end
98
+
99
+ initializer :wicked_pdf do |_app|
100
+ WickedPdf.config = {
101
+ layout: 'application.pdf',
102
+ print_media_type: true,
103
+ viewport_size: '1280x800', zoom: 0.7, dpi: 300
104
+ }
105
+ end
106
+
107
+ initializer :api_paginate do
108
+ ApiPagination.configure do |config|
109
+ # If you have both gems included, you can choose a paginator.
110
+ config.paginator = :will_paginate
111
+
112
+ # By default, this is set to 'Total'
113
+ config.total_header = 'X-Total'
114
+
115
+ # By default, this is set to 'Per-Page'
116
+ config.per_page_header = 'X-Per-Page'
117
+
118
+ # Optional: set this to add a header with the current page number.
119
+ config.page_header = 'X-Page'
120
+
121
+ # Optional: what parameter should be used to set the page option
122
+ # config.page_param = :page
123
+ # # or
124
+ # config.page_param do |params|
125
+ # params[:page][:number]
126
+ # end
127
+ #
128
+ # # Optional: what parameter should be used to set the per page option
129
+ # config.per_page_param = :per_page
130
+ # # or
131
+ # config.per_page_param do |params|
132
+ # params[:page][:size]
133
+ # end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,137 @@
1
+ module Symphonia
2
+ class FormBuilder < ::BootstrapForm::FormBuilder
3
+
4
+ def error_messages
5
+ if object.respond_to?(:errors) && object.errors.any?
6
+ list = content_tag(:p, I18n.t('activerecord.errors.template.body')).html_safe
7
+ list += content_tag(:ul) do
8
+ object.errors.full_messages.collect do |error|
9
+ content_tag(:li, error)
10
+ end.join.html_safe
11
+ end
12
+ content_tag(:div, list, class: 'error_explanation')
13
+ end
14
+ end
15
+
16
+ def calendar_field(name, options = {})
17
+ options[:data] ||= {} #{'date-clear-btn': true, 'date-format': 'yyyy-mm-dd'}
18
+ options[:data][:'date-clear-btn'] ||= true
19
+ # options[:data][:'date-format'] ||= 'yyyy-mm-dd'
20
+ text_field(name, options.merge({
21
+ class: 'form-control datepicker',
22
+ append: @template.fa_icon('calendar')
23
+ }))
24
+ end
25
+
26
+ # def form_group_builder(method, options, html_options = nil)
27
+ # options.symbolize_keys!
28
+ # html_options.symbolize_keys! if html_options
29
+ #
30
+ # # Add control_class; allow it to be overridden by :control_class option
31
+ # css_options = html_options || options
32
+ # control_classes = css_options.delete(:control_class) { control_class }
33
+ # css_options[:class] = [control_classes, css_options[:class]].compact.join(" ")
34
+ # css_options[:class] << " is-invalid" if has_error?(method)
35
+ #
36
+ # options = convert_form_tag_options(method, options) if acts_like_form_tag
37
+ #
38
+ # wrapper_class = css_options.delete(:wrapper_class)
39
+ # wrapper_options = css_options.delete(:wrapper)
40
+ # help = options.delete(:help)
41
+ # icon = options.delete(:icon)
42
+ # label_col = options.delete(:label_col)
43
+ # control_col = options.delete(:control_col)
44
+ # layout = get_group_layout(options.delete(:layout))
45
+ # form_group_options = {
46
+ # id: options[:id],
47
+ # help: help,
48
+ # icon: icon,
49
+ # label_col: label_col,
50
+ # control_col: control_col,
51
+ # layout: layout,
52
+ # class: wrapper_class
53
+ # }
54
+ #
55
+ # if wrapper_options.is_a?(Hash)
56
+ # form_group_options.merge!(wrapper_options)
57
+ # end
58
+ #
59
+ # unless options.delete(:skip_label)
60
+ # if options[:label].is_a?(Hash)
61
+ # label_text = options[:label].delete(:text)
62
+ # label_class = options[:label].delete(:class)
63
+ # options.delete(:label)
64
+ # end
65
+ # label_class ||= options.delete(:label_class)
66
+ # label_class = hide_class if options.delete(:hide_label)
67
+ #
68
+ # if options[:label].is_a?(String)
69
+ # label_text ||= options.delete(:label)
70
+ # end
71
+ #
72
+ # form_group_options.merge!(label: {
73
+ # text: label_text,
74
+ # class: label_class,
75
+ # skip_required: options.delete(:skip_required),
76
+ # required: options.delete(:required)
77
+ # })
78
+ # end
79
+ #
80
+ # form_group(method, form_group_options) do
81
+ # yield
82
+ # end
83
+ # end
84
+
85
+ def label_text(name, options)
86
+ name = name.to_s.remove(/_id$/)
87
+
88
+ if label_errors && error?(name)
89
+ (options[:text] || object.class.human_attribute_name(name)).to_s.concat(" #{get_error_messages(name)}")
90
+ else
91
+ options[:text]
92
+ end
93
+ end
94
+ # def generate_label(id, name, options, custom_label_col, group_layout)
95
+ # return if options.blank?
96
+ # # id is the caller's options[:id] at the only place this method is called.
97
+ # # The options argument is a small subset of the options that might have
98
+ # # been passed to generate_label's caller, and definitely doesn't include
99
+ # # :id.
100
+ # options[:for] = id if acts_like_form_tag
101
+ # classes = [options[:class]]
102
+ #
103
+ # if layout_horizontal?(group_layout)
104
+ # classes << "col-form-label"
105
+ # classes << (custom_label_col || label_col)
106
+ # elsif layout_inline?(group_layout)
107
+ # classes << "mr-sm-2"
108
+ # end
109
+ #
110
+ # xname = name.to_s.remove(/_id$/)
111
+ #
112
+ # case options.delete(:required)
113
+ # when true
114
+ # classes << "required"
115
+ # when nil, :default
116
+ # classes << "required" if required_attribute?(object, name)
117
+ # end
118
+ #
119
+ # options[:class] = classes.compact.join(" ").strip
120
+ # options.delete(:class) if options[:class].empty?
121
+ #
122
+ # if label_errors && has_error?(name)
123
+ # error_messages = get_error_messages(name)
124
+ # label_text = (options[:text] || object.class.human_attribute_name(xname)).to_s.concat(" #{error_messages}")
125
+ # options[:class] = [options[:class], "text-danger"].compact.join(" ")
126
+ # label(name, label_text, options.except(:text))
127
+ # else
128
+ # label(name, options[:text], options.except(:text))
129
+ # end
130
+ # end
131
+
132
+ def error_class
133
+ 'is-invalid'
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,23 @@
1
+ module Symphonia
2
+ module MenuManager
3
+ @@mapper = {}
4
+ mattr_accessor :mapper
5
+
6
+ def self.menu(name)
7
+ return mapper[name.to_sym] || {}
8
+ end
9
+
10
+ class << self
11
+ def map(menu_name)
12
+ mapper[menu_name] ||= {}
13
+ if block_given?
14
+ yield mapper[menu_name]
15
+ end
16
+ end
17
+
18
+ def clear(menu_name)
19
+ !mapper.delete(menu_name.to_sym).nil?
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,137 @@
1
+ module Symphonia
2
+ module ModelAttributes
3
+ class Attribute
4
+
5
+ attr_reader :name, :options
6
+ attr_reader :format_options, :sort_column
7
+ attr_accessor :filter
8
+
9
+ def initialize(name, klass, options, formatter)
10
+ @name = name
11
+ @klass = klass
12
+ @formatter = formatter
13
+ @options = options
14
+ @format_options = {}
15
+ end
16
+
17
+ def title
18
+ @klass.human_attribute_name(@name.to_s.remove(/_id$/), @options[:i18n] || {})
19
+ end
20
+
21
+ def value(entity)
22
+ entity.send(name)
23
+ end
24
+
25
+ def format(view, entity, options={})
26
+ @format_options = options
27
+ if @formatter.is_a?(Proc)
28
+ @formatter.call(view, self, entity).to_s
29
+ else
30
+ format_value(view, value(entity), entity)
31
+ end
32
+ end
33
+
34
+ def sort=(sort_options)
35
+ t = @klass.arel_table
36
+ @sort = true
37
+ case sort_options
38
+ when false
39
+ @sort = false
40
+ when Symbol, Array
41
+ sort = Array(sort_options).collect { |i| t[i] }
42
+ asc_desc = { asc: sort.map { |i| i.asc.to_sql }.join(','), desc: sort.map { |i| i.desc.to_sql }.join(',') }
43
+ @sort_column = SortableTable::SortColumnCustomDefinition.new(@name, asc_desc)
44
+ when String
45
+ asc_desc = { asc: sort_options + ' ASC', desc: sort_options + ' DESC' }
46
+ @sort_column = SortableTable::SortColumnCustomDefinition.new(@name, asc_desc)
47
+ else
48
+ raise "Undefined `sort_options` type: #{sort_options.class.name}"
49
+ end
50
+ end
51
+
52
+ def sort?
53
+ @sort
54
+ end
55
+
56
+ def filter?
57
+ !!filter
58
+ end
59
+
60
+ def default?
61
+ !!@options[:default]
62
+ end
63
+
64
+ private
65
+
66
+ def format_value(_view, value, _entity)
67
+ value.to_s
68
+ end
69
+ end
70
+
71
+
72
+ class DecimalAttribute < Attribute
73
+ def format_value(view, value, entity)
74
+ view.format_price(value)
75
+ end
76
+ end
77
+
78
+ class EnumAttribute < Attribute
79
+ # TODO: @klass.name || @klass.base_class.name || entity.class.name
80
+ def format_value(view, value, entity)
81
+ view.t("#{@klass.name.underscore}.#{name.to_s.pluralize}.#{value}", format_options)
82
+ end
83
+ end
84
+
85
+ class LinkAttribute < Attribute
86
+ def format_value(view, value, entity)
87
+ view.link_to value.to_s, entity, format_options
88
+ end
89
+ end
90
+
91
+ class MailAttribute < Attribute
92
+ def format_value(view, value, entity)
93
+ view.mail_to value.to_s, value.to_s, format_options
94
+ end
95
+ end
96
+
97
+ class ReferenceAttribute < Attribute
98
+
99
+ def initialize(name, klass, options, formatter)
100
+ super
101
+ end
102
+
103
+ def title
104
+ @klass.human_attribute_name(@klass.reflect_on_association(name).foreign_key, @options[:i18n] || {})
105
+ end
106
+
107
+ def format_value(view, value, entity)
108
+ view.link_to value.to_s, value, format_options
109
+ end
110
+ end
111
+
112
+ class BooleanAttribute < Attribute
113
+ def format_value(view, value, entity)
114
+ if value.to_boolean
115
+ view.icon('true', view.t(:true))
116
+ else
117
+ view.icon('false', view.t(:false))
118
+ end
119
+ end
120
+ end
121
+
122
+ class DateAttribute < Attribute
123
+ def format_value(view, value, entity)
124
+ value && view.l(value.to_date, format_options)
125
+ end
126
+ end
127
+
128
+ class DateTimeAttribute < Attribute
129
+ def format_value(view, value, entity)
130
+ value && view.l(value, format_options)
131
+ end
132
+ end
133
+ class DatetimeAttribute < DateTimeAttribute
134
+ # alias
135
+ end
136
+ end
137
+ end