incline 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (303) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +186 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +208 -0
  7. data/Rakefile +37 -0
  8. data/app/assets/fonts/incline/.keep +0 -0
  9. data/app/assets/images/incline/.keep +0 -0
  10. data/app/assets/images/incline/barcode-B.svg +181 -0
  11. data/app/assets/javascripts/incline/activate_classed_items.js +11 -0
  12. data/app/assets/javascripts/incline/application.js +30 -0
  13. data/app/assets/javascripts/incline/bootstrap-datepicker.js +1800 -0
  14. data/app/assets/javascripts/incline/datatables.js +22193 -0
  15. data/app/assets/javascripts/incline/escapeHtml.js +10 -0
  16. data/app/assets/javascripts/incline/inline_actions.js +479 -0
  17. data/app/assets/javascripts/incline/jquery.doubleScroll.js +112 -0
  18. data/app/assets/javascripts/incline/jquery.number.js +764 -0
  19. data/app/assets/javascripts/incline/regexMask.js +27 -0
  20. data/app/assets/javascripts/incline/select2/i18n/en.js +3 -0
  21. data/app/assets/javascripts/incline/select2/select2.full.js +6436 -0
  22. data/app/assets/stylesheets/incline/application.css +18 -0
  23. data/app/assets/stylesheets/incline/bootstrap-theme.min.css +5 -0
  24. data/app/assets/stylesheets/incline/custom.scss +279 -0
  25. data/app/assets/stylesheets/incline/datatables.css +494 -0
  26. data/app/assets/stylesheets/incline/datepicker3.css +790 -0
  27. data/app/assets/stylesheets/incline/select2.css +484 -0
  28. data/app/controllers/incline/access_groups_controller.rb +127 -0
  29. data/app/controllers/incline/access_test_controller.rb +30 -0
  30. data/app/controllers/incline/account_activations_controller.rb +28 -0
  31. data/app/controllers/incline/application_controller.rb +11 -0
  32. data/app/controllers/incline/contact_controller.rb +34 -0
  33. data/app/controllers/incline/password_resets_controller.rb +113 -0
  34. data/app/controllers/incline/security_controller.rb +100 -0
  35. data/app/controllers/incline/sessions_controller.rb +50 -0
  36. data/app/controllers/incline/users_controller.rb +304 -0
  37. data/app/controllers/incline/welcome_controller.rb +19 -0
  38. data/app/helpers/incline/.keep +0 -0
  39. data/app/mailers/incline/application_mailer_base.rb +11 -0
  40. data/app/mailers/incline/contact_form.rb +19 -0
  41. data/app/mailers/incline/user_mailer.rb +45 -0
  42. data/app/models/incline/access_group.rb +121 -0
  43. data/app/models/incline/access_group_group_member.rb +12 -0
  44. data/app/models/incline/access_group_user_member.rb +10 -0
  45. data/app/models/incline/action_group.rb +12 -0
  46. data/app/models/incline/action_security.rb +222 -0
  47. data/app/models/incline/contact_message.rb +37 -0
  48. data/app/models/incline/disable_info.rb +20 -0
  49. data/app/models/incline/password_reset.rb +14 -0
  50. data/app/models/incline/password_reset_request.rb +14 -0
  51. data/app/models/incline/user.rb +437 -0
  52. data/app/models/incline/user_login_history.rb +30 -0
  53. data/app/views/incline/access_groups/_details.json.jbuilder +10 -0
  54. data/app/views/incline/access_groups/_form.html.erb +19 -0
  55. data/app/views/incline/access_groups/_list.html.erb +60 -0
  56. data/app/views/incline/access_groups/_messages.json.jbuilder +6 -0
  57. data/app/views/incline/access_groups/edit.html.erb +2 -0
  58. data/app/views/incline/access_groups/index.html.erb +6 -0
  59. data/app/views/incline/access_groups/index.json.jbuilder +16 -0
  60. data/app/views/incline/access_groups/new.html.erb +2 -0
  61. data/app/views/incline/access_groups/show.html.erb +9 -0
  62. data/app/views/incline/access_groups/show.json.jbuilder +11 -0
  63. data/app/views/incline/contact/new.html.erb +22 -0
  64. data/app/views/incline/contact_form/contact.html.erb +16 -0
  65. data/app/views/incline/contact_form/contact.text.erb +13 -0
  66. data/app/views/incline/password_resets/edit.html.erb +16 -0
  67. data/app/views/incline/password_resets/new.html.erb +12 -0
  68. data/app/views/incline/security/_details.json.jbuilder +7 -0
  69. data/app/views/incline/security/_form.html.erb +20 -0
  70. data/app/views/incline/security/_list.html.erb +89 -0
  71. data/app/views/incline/security/_messages.json.jbuilder +6 -0
  72. data/app/views/incline/security/edit.html.erb +2 -0
  73. data/app/views/incline/security/index.html.erb +6 -0
  74. data/app/views/incline/security/index.json.jbuilder +16 -0
  75. data/app/views/incline/security/show.html.erb +31 -0
  76. data/app/views/incline/security/show.json.jbuilder +11 -0
  77. data/app/views/incline/sessions/new.html.erb +26 -0
  78. data/app/views/incline/user_mailer/account_activation.html.erb +7 -0
  79. data/app/views/incline/user_mailer/account_activation.text.erb +6 -0
  80. data/app/views/incline/user_mailer/invalid_password_reset.html.erb +3 -0
  81. data/app/views/incline/user_mailer/invalid_password_reset.text.erb +5 -0
  82. data/app/views/incline/user_mailer/password_reset.html.erb +8 -0
  83. data/app/views/incline/user_mailer/password_reset.text.erb +7 -0
  84. data/app/views/incline/users/_details.json.jbuilder +32 -0
  85. data/app/views/incline/users/_form.html.erb +21 -0
  86. data/app/views/incline/users/_list.html.erb +102 -0
  87. data/app/views/incline/users/_messages.json.jbuilder +6 -0
  88. data/app/views/incline/users/disable_confirm.html.erb +19 -0
  89. data/app/views/incline/users/edit.html.erb +5 -0
  90. data/app/views/incline/users/index.html.erb +6 -0
  91. data/app/views/incline/users/index.json.jbuilder +16 -0
  92. data/app/views/incline/users/new.html.erb +5 -0
  93. data/app/views/incline/users/show.html.erb +12 -0
  94. data/app/views/incline/users/show.json.jbuilder +11 -0
  95. data/app/views/incline/welcome/home.html.erb +5 -0
  96. data/app/views/layouts/application.html.erb +1 -0
  97. data/app/views/layouts/incline/_account_menu.html.erb +18 -0
  98. data/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  99. data/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  100. data/app/views/layouts/incline/_footer.html.erb +13 -0
  101. data/app/views/layouts/incline/_header.html.erb +21 -0
  102. data/app/views/layouts/incline/_html_mailer.html.erb +5 -0
  103. data/app/views/layouts/incline/_incline_app.html.erb +25 -0
  104. data/app/views/layouts/incline/_messages.html.erb +3 -0
  105. data/app/views/layouts/incline/_shim.html.erb +3 -0
  106. data/app/views/layouts/incline/_text_mailer.text.erb +1 -0
  107. data/app/views/layouts/incline/application.html.erb +1 -0
  108. data/app/views/layouts/mailer.html.erb +2 -0
  109. data/app/views/layouts/mailer.text.erb +2 -0
  110. data/bin/rails +12 -0
  111. data/bin/test_scaffold.sh +10 -0
  112. data/config/routes.rb +61 -0
  113. data/db/migrate/20170511230126_create_incline_users.rb +26 -0
  114. data/db/migrate/20170515003052_create_incline_access_groups.rb +10 -0
  115. data/db/migrate/20170515003221_create_incline_user_login_histories.rb +12 -0
  116. data/db/migrate/20170515150908_create_incline_access_group_user_members.rb +11 -0
  117. data/db/migrate/20170515151058_create_incline_access_group_group_members.rb +11 -0
  118. data/db/migrate/20170517193432_add_comments_to_incline_user.rb +5 -0
  119. data/db/migrate/20170622132700_create_incline_action_securities.rb +16 -0
  120. data/db/migrate/20170622172712_create_incline_action_groups.rb +11 -0
  121. data/db/migrate/20170622195742_add_non_standard_to_action_security.rb +5 -0
  122. data/db/migrate/20170622230422_add_visible_to_action_security.rb +5 -0
  123. data/db/seeds.rb +81 -0
  124. data/exe/new_incline_app +42 -0
  125. data/lib/generators/incline/install_generator.rb +259 -0
  126. data/lib/generators/incline/templates/_app_menu_anon.html.erb +1 -0
  127. data/lib/generators/incline/templates/_app_menu_authenticated.html.erb +1 -0
  128. data/lib/generators/incline/templates/incline_application.css +17 -0
  129. data/lib/generators/incline/templates/incline_application.html.erb +1 -0
  130. data/lib/generators/incline/templates/incline_application.js +12 -0
  131. data/lib/generators/incline/templates/incline_database.yml +25 -0
  132. data/lib/generators/incline/templates/incline_email.yml +20 -0
  133. data/lib/generators/incline/templates/incline_mailer.html.erb +2 -0
  134. data/lib/generators/incline/templates/incline_mailer.text.erb +2 -0
  135. data/lib/generators/incline/templates/incline_users.yml +64 -0
  136. data/lib/generators/incline/templates/incline_version.rb +3 -0
  137. data/lib/incline/auth_engine_base.rb +52 -0
  138. data/lib/incline/data_tables_request.rb +336 -0
  139. data/lib/incline/date_time_formats.rb +6 -0
  140. data/lib/incline/engine.rb +212 -0
  141. data/lib/incline/errors.rb +15 -0
  142. data/lib/incline/extensions/action_controller_base.rb +526 -0
  143. data/lib/incline/extensions/action_mailer_base.rb +66 -0
  144. data/lib/incline/extensions/action_view_base.rb +489 -0
  145. data/lib/incline/extensions/active_record_base.rb +308 -0
  146. data/lib/incline/extensions/application.rb +137 -0
  147. data/lib/incline/extensions/application_configuration.rb +50 -0
  148. data/lib/incline/extensions/connection_adapter.rb +55 -0
  149. data/lib/incline/extensions/date_time_value.rb +123 -0
  150. data/lib/incline/extensions/date_value.rb +77 -0
  151. data/lib/incline/extensions/decimal_value.rb +55 -0
  152. data/lib/incline/extensions/erb_scaffold_generator.rb +31 -0
  153. data/lib/incline/extensions/float_value.rb +59 -0
  154. data/lib/incline/extensions/form_builder.rb +617 -0
  155. data/lib/incline/extensions/integer_value.rb +54 -0
  156. data/lib/incline/extensions/jbuilder_generator.rb +38 -0
  157. data/lib/incline/extensions/jbuilder_template.rb +39 -0
  158. data/lib/incline/extensions/main_app.rb +40 -0
  159. data/lib/incline/extensions/numeric.rb +63 -0
  160. data/lib/incline/extensions/object.rb +31 -0
  161. data/lib/incline/extensions/resource_route_generator.rb +53 -0
  162. data/lib/incline/extensions/session.rb +113 -0
  163. data/lib/incline/extensions/string.rb +50 -0
  164. data/lib/incline/extensions/test_case.rb +764 -0
  165. data/lib/incline/extensions/time_zone_converter.rb +40 -0
  166. data/lib/incline/global_status.rb +236 -0
  167. data/lib/incline/helpers/route_hash_formatter.rb +46 -0
  168. data/lib/incline/json_log_formatter.rb +96 -0
  169. data/lib/incline/json_logger.rb +17 -0
  170. data/lib/incline/log.rb +153 -0
  171. data/lib/incline/number_formats.rb +17 -0
  172. data/lib/incline/recaptcha.rb +346 -0
  173. data/lib/incline/user_manager.rb +212 -0
  174. data/lib/incline/validators/email_validator.rb +45 -0
  175. data/lib/incline/validators/ip_address_validator.rb +32 -0
  176. data/lib/incline/validators/recaptcha_validator.rb +37 -0
  177. data/lib/incline/validators/safe_name_validator.rb +31 -0
  178. data/lib/incline/version.rb +3 -0
  179. data/lib/incline/work_path.rb +75 -0
  180. data/lib/incline.rb +197 -0
  181. data/lib/tasks/incline_tasks.rake +4 -0
  182. data/lib/templates/erb/scaffold/_form.html.erb +43 -0
  183. data/lib/templates/erb/scaffold/_list.html.erb +81 -0
  184. data/lib/templates/erb/scaffold/edit.html.erb +1 -0
  185. data/lib/templates/erb/scaffold/index.html.erb +6 -0
  186. data/lib/templates/erb/scaffold/new.html.erb +1 -0
  187. data/lib/templates/erb/scaffold/show.html.erb +34 -0
  188. data/lib/templates/jbuilder/scaffold/_details.json.jbuilder +20 -0
  189. data/lib/templates/jbuilder/scaffold/index.json.jbuilder +16 -0
  190. data/lib/templates/jbuilder/scaffold/show.json.jbuilder +16 -0
  191. data/lib/templates/rails/scaffold_controller/controller.rb +128 -0
  192. data/test/controllers/incline/access_groups_controller_test.rb +65 -0
  193. data/test/controllers/incline/access_test_controller_test.rb +53 -0
  194. data/test/controllers/incline/contact_controller_test.rb +32 -0
  195. data/test/controllers/incline/security_controller_test.rb +39 -0
  196. data/test/controllers/incline/welcome_controller_test.rb +16 -0
  197. data/test/dummy/README.rdoc +28 -0
  198. data/test/dummy/Rakefile +6 -0
  199. data/test/dummy/app/assets/images/.keep +0 -0
  200. data/test/dummy/app/assets/javascripts/application.js +12 -0
  201. data/test/dummy/app/assets/stylesheets/application.css +17 -0
  202. data/test/dummy/app/controllers/application_controller.rb +5 -0
  203. data/test/dummy/app/controllers/concerns/.keep +0 -0
  204. data/test/dummy/app/helpers/application_helper.rb +2 -0
  205. data/test/dummy/app/mailers/.keep +0 -0
  206. data/test/dummy/app/models/.keep +0 -0
  207. data/test/dummy/app/models/concerns/.keep +0 -0
  208. data/test/dummy/app/views/layouts/application.html.erb +1 -0
  209. data/test/dummy/app/views/layouts/incline/_app_menu_anon.html.erb +1 -0
  210. data/test/dummy/app/views/layouts/incline/_app_menu_authenticated.html.erb +1 -0
  211. data/test/dummy/app/views/layouts/mailer.html.erb +2 -0
  212. data/test/dummy/app/views/layouts/mailer.text.erb +2 -0
  213. data/test/dummy/bin/bundle +3 -0
  214. data/test/dummy/bin/rails +4 -0
  215. data/test/dummy/bin/rake +4 -0
  216. data/test/dummy/bin/setup +29 -0
  217. data/test/dummy/config/application.rb +38 -0
  218. data/test/dummy/config/boot.rb +5 -0
  219. data/test/dummy/config/database.yml +34 -0
  220. data/test/dummy/config/email.yml +24 -0
  221. data/test/dummy/config/environment.rb +5 -0
  222. data/test/dummy/config/environments/development.rb +45 -0
  223. data/test/dummy/config/environments/production.rb +85 -0
  224. data/test/dummy/config/environments/test.rb +44 -0
  225. data/test/dummy/config/initializers/assets.rb +11 -0
  226. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  227. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  228. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  229. data/test/dummy/config/initializers/inflections.rb +16 -0
  230. data/test/dummy/config/initializers/mime_types.rb +4 -0
  231. data/test/dummy/config/initializers/session_store.rb +3 -0
  232. data/test/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  233. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  234. data/test/dummy/config/locales/en.yml +23 -0
  235. data/test/dummy/config/routes.rb +6 -0
  236. data/test/dummy/config.ru +4 -0
  237. data/test/dummy/db/schema.rb +108 -0
  238. data/test/dummy/lib/assets/.keep +0 -0
  239. data/test/dummy/log/.keep +0 -0
  240. data/test/dummy/public/404.html +67 -0
  241. data/test/dummy/public/422.html +67 -0
  242. data/test/dummy/public/500.html +66 -0
  243. data/test/dummy/public/favicon.ico +0 -0
  244. data/test/extensions/action_controller_base_extensions_test.rb +21 -0
  245. data/test/extensions/action_mailer_base_extensions_test.rb +20 -0
  246. data/test/extensions/action_view_base_extensions_test.rb +267 -0
  247. data/test/extensions/active_record_extensions_test.rb +173 -0
  248. data/test/extensions/application_configuration_extensions_test.rb +46 -0
  249. data/test/extensions/application_extensions_test.rb +23 -0
  250. data/test/extensions/connection_adapter_extensions_test.rb +54 -0
  251. data/test/extensions/date_time_value_extensions_test.rb +104 -0
  252. data/test/extensions/date_value_extensions_test.rb +102 -0
  253. data/test/extensions/decimal_value_extensions_test.rb +85 -0
  254. data/test/extensions/erb_scaffold_generator_extensions_test.rb +17 -0
  255. data/test/extensions/float_value_extensions_test.rb +78 -0
  256. data/test/extensions/form_builder_extensions_test.rb +28 -0
  257. data/test/extensions/integer_value_extensions_test.rb +78 -0
  258. data/test/extensions/jbuilder_generator_extensions_test.rb +21 -0
  259. data/test/extensions/jbuilder_template_extensions_test.rb +47 -0
  260. data/test/extensions/main_app_extensions_test.rb +55 -0
  261. data/test/extensions/numeric_extensions_test.rb +76 -0
  262. data/test/extensions/object_extensions_test.rb +104 -0
  263. data/test/extensions/session_extensions_test.rb +69 -0
  264. data/test/extensions/string_extensions_test.rb +32 -0
  265. data/test/extensions/test_case_extensions_test.rb +538 -0
  266. data/test/extensions/time_zone_converter_extensions_test.rb +10 -0
  267. data/test/fixtures/incline/access_group_group_members.yml +1 -0
  268. data/test/fixtures/incline/access_group_user_members.yml +1 -0
  269. data/test/fixtures/incline/access_groups.yml +13 -0
  270. data/test/fixtures/incline/action_groups.yml +6 -0
  271. data/test/fixtures/incline/action_securities.yml +18 -0
  272. data/test/fixtures/incline/user_login_histories.yml +1 -0
  273. data/test/fixtures/incline/users.yml +64 -0
  274. data/test/incline_test.rb +63 -0
  275. data/test/integration/incline/users_edit_test.rb +180 -0
  276. data/test/integration/incline/users_login_test.rb +105 -0
  277. data/test/integration/incline/users_signup_test.rb +147 -0
  278. data/test/integration/navigation_test.rb +11 -0
  279. data/test/lib/data_tables_request_test.rb +245 -0
  280. data/test/lib/date_time_formats_test.rb +111 -0
  281. data/test/lib/global_status_test.rb +89 -0
  282. data/test/lib/json_log_formatter_test.rb +43 -0
  283. data/test/lib/log_test.rb +36 -0
  284. data/test/lib/recaptcha_test.rb +75 -0
  285. data/test/lib/user_manager_test.rb +47 -0
  286. data/test/lib/work_path_test.rb +18 -0
  287. data/test/models/incline/access_group_group_member_test.rb +30 -0
  288. data/test/models/incline/access_group_test.rb +60 -0
  289. data/test/models/incline/access_group_user_member_test.rb +29 -0
  290. data/test/models/incline/action_group_test.rb +27 -0
  291. data/test/models/incline/action_security_test.rb +176 -0
  292. data/test/models/incline/contact_message_test.rb +66 -0
  293. data/test/models/incline/disable_info_test.rb +29 -0
  294. data/test/models/incline/password_reset_request_test.rb +35 -0
  295. data/test/models/incline/password_reset_test.rb +51 -0
  296. data/test/models/incline/user_login_history_test.rb +31 -0
  297. data/test/models/incline/user_test.rb +91 -0
  298. data/test/test_helper.rb +42 -0
  299. data/test/validators/email_validator_test.rb +102 -0
  300. data/test/validators/ip_address_validator_test.rb +107 -0
  301. data/test/validators/recaptcha_validator_test.rb +57 -0
  302. data/test/validators/safe_name_validator_test.rb +101 -0
  303. metadata +584 -0
@@ -0,0 +1,489 @@
1
+ require 'cgi/util'
2
+ silence_warnings do
3
+ require 'redcarpet'
4
+ end
5
+
6
+ require 'action_view'
7
+
8
+ module Incline::Extensions
9
+
10
+ ##
11
+ # Add some extra functionality to the base view definition.
12
+ module ActionViewBase
13
+
14
+ ##
15
+ # Gets the full title of the page.
16
+ #
17
+ # If +page_title+ is left blank, then the +app_name+ attribute of your application is returned.
18
+ # Otherwise the +app_name+ attribute is appended to the +page_title+ after a pipe symbol.
19
+ #
20
+ # # app_name = 'My App'
21
+ # full_title # 'My App'
22
+ # full_title 'Welcome' # 'Welcome | My App'
23
+ #
24
+ def full_title(page_title = '')
25
+ aname = Rails.application.app_name.strip
26
+ return aname if page_title.blank?
27
+ "#{page_title.strip} | #{aname}"
28
+ end
29
+
30
+ ##
31
+ # Shows a small check glyph if the +bool_val+ is true.
32
+ #
33
+ # This is most useful when displaying information in tables..
34
+ # It makes it much easier to quickly include a visual indication of a true value,
35
+ # while leaving the view blank for a false value.
36
+ #
37
+ def show_check_if(bool_val)
38
+ if bool_val.to_bool
39
+ '<i class="glyphicon glyphicon-ok glyphicon-small"></i>'.html_safe
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Shows a glyph with an optional size.
45
+ #
46
+ # The glyph +name+ should be a valid {bootstrap glyph}[http://getbootstrap.com/components/#glyphicons] name.
47
+ # Strip the prefixed 'glyphicon-' from the name.
48
+ #
49
+ # The size can be left blank, or set to 'small' or 'large'.
50
+ #
51
+ # glyph('cloud') # '<i class="glyphicon glyphicon-cloud"></i>'
52
+ #
53
+ def glyph(name, size = '')
54
+ size =
55
+ case size.to_s.downcase
56
+ when 'small', 'sm'
57
+ 'glyphicon-small'
58
+ when 'large', 'lg'
59
+ 'glyphicon-large'
60
+ else
61
+ nil
62
+ end
63
+
64
+ name = name.to_s.strip
65
+ return nil if name.blank?
66
+
67
+ result = '<i class="glyphicon glyphicon-' + CGI::escape_html(name)
68
+ result += ' ' + size unless size.blank?
69
+ result += '"></i>'
70
+ result.html_safe
71
+ end
72
+
73
+ ##
74
+ # Renders a dropdown list that can be used for filtering a data table.
75
+ #
76
+ # This works in conjunction with the 'filter_column()' JS function.
77
+ # The Incline scaffold generates this function for you, so this helper
78
+ # can be used with generated lists.
79
+ #
80
+ # The +label+ is the text to display for the header.
81
+ # The +column+ is the column number of the data table to filter.
82
+ # The +list+ is an enumerable containing the data to filter with.
83
+ # An option will be added to the top of the list titled '- All -'.
84
+ def dt_header_filter(label, column, list)
85
+ column = CGI::escape_html(column.to_s)
86
+ label = CGI::escape_html(label.to_s)
87
+
88
+ list =
89
+ if list.is_a?(::Array) && list.any?
90
+ list
91
+ .map{|v| CGI::escape_html(v.to_s) }
92
+ .map{|v| "<li><a href=\"javascript:filter_column(#{column}, '#{v.gsub('\'','\\\'')}')\" title=\"#{v}\">#{v}</a></li>" }
93
+ elsif list.is_a?(::Hash) && list.any?
94
+ list
95
+ .inject({}){|memo,(display,value)| memo[CGI::escape_html(display.to_s)] = CGI::escape_html(value.to_s); memo }
96
+ .to_a
97
+ .map{|(d,v)| "<li><a href=\"javascript:filter_column(#{column}, '#{v.gsub('\'','\\\'')}')\" title=\"#{d}\">#{d}</a></li>" }
98
+ else
99
+ [ ]
100
+ end
101
+
102
+ if list&.any?
103
+ <<-HTML.html_safe
104
+ <div class="header-filter"><div class="dropdown">
105
+ <a href="#" class="dropdown-toggle" id="header_filter_#{column}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">#{label} <span class="caret"></span></a>
106
+ <ul class="dropdown-menu scrollable-menu" aria-labelledby="header_filter_#{CGI::escape_html(column)}">
107
+ <li><a href="javascript:filter_column(#{column}, '')" title="- All -">- All -</a></li>
108
+ #{list.join("\n")}
109
+ </ul>
110
+ </div></div>
111
+ HTML
112
+ else
113
+ label.html_safe
114
+ end
115
+ end
116
+
117
+ ##
118
+ # Renders a dismissible alert message.
119
+ #
120
+ # The +type+ can be :info, :notice, :success, :danger, :alert, or :warning.
121
+ # Optionally, you can prefix the +type+ with 'safe_'.
122
+ # This tells the system that the message you are passing is HTML safe and does not need to be escaped.
123
+ # If you want to include HTML (ie - <br>) in your message, you need to ensure it is actually safe and
124
+ # set the type as :safe_info, :safe_notice, :safe_success, :safe_danger, :safe_alert, or :safe_warning.
125
+ #
126
+ # The +message+ is the data you want to display.
127
+ # * Safe messages must be String values. No processing is done on safe messages.
128
+ # * Unsafe messages can be a Symbol, a String, an Array, or a Hash.
129
+ # * An array can contain Symbols, Strings, Arrays, or Hashes.
130
+ # * Each subitem is processed individually.
131
+ # * Arrays within arrays are essentially flattened into one array.
132
+ # * A Hash is converted into an unordered list.
133
+ # * The keys should be either Symbols or Strings.
134
+ # * The values can be Symbols, Strings, Arrays, or Hashes.
135
+ # * A Symbol will be converted into a string, humanized, and capitalized.
136
+ # * A String will be escaped for HTML, rendered for Markdown, and then returned.
137
+ # * The Markdown will allow you to customize simple strings by adding some basic formatting.
138
+ #
139
+ # Finally, there is one more parameter, +array_auto_hide+, that can be used to tidy up otherwise
140
+ # long alert dialogs. If set to a positive integer, this is the maximum number of items to show initially from any
141
+ # array. When items get hidden, a link is provided to show all items.
142
+ # This is particularly useful when you have a long list of errors to show to a user, they will then be able
143
+ # to show all of the errors if they desire.
144
+ #
145
+ # # render_alert :info, 'Hello World'
146
+ # <div class="alert alert-info alert-dismissible">
147
+ # <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
148
+ # <span>Hello World</span>
149
+ # </div>
150
+ #
151
+ # # render_alert :success, [ 'Item 1 was successful.', 'Item 2 was successful' ]
152
+ # <div class="alert alert-info alert-dismissible">
153
+ # <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
154
+ # <span>Item 1 was successful.</span><br>
155
+ # <span>Item 2 was successful.</span>
156
+ # </div>
157
+ #
158
+ # # render_alert :error, { :name => [ 'cannot be blank', 'must be unique' ], :age => 'must be greater than 18' }
159
+ # <div class="alert alert-info alert-dismissible">
160
+ # <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
161
+ # <div>
162
+ # Name
163
+ # <ul>
164
+ # <li>cannot be blank</li>
165
+ # <li>must be unique</li>
166
+ # </ul>
167
+ # </div>
168
+ # <div>
169
+ # Age
170
+ # <ul>
171
+ # <li>must be greater than 18</li>
172
+ # </ul>
173
+ # </div>
174
+ # </div>
175
+ #
176
+ # # render_alert :error, [ '__The model could not be saved.__', { :name => [ 'cannot be blank', 'must be unique' ], :age => 'must be greater than 18' } ]
177
+ # <div class="alert alert-info alert-dismissible">
178
+ # <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
179
+ # <span><strong>The model could not be saved.</strong></span><br>
180
+ # <div>
181
+ # Name
182
+ # <ul>
183
+ # <li>cannot be blank</li>
184
+ # <li>must be unique</li>
185
+ # </ul>
186
+ # </div>
187
+ # <div>
188
+ # Age
189
+ # <ul>
190
+ # <li>must be greater than 18</li>
191
+ # </ul>
192
+ # </div>
193
+ # </div>
194
+ #
195
+ def render_alert(type, message, array_auto_hide = nil)
196
+ return nil if message.blank?
197
+
198
+ if type.to_s =~ /\Asafe_/
199
+ type = type.to_s[5..-1]
200
+ message = message.to_s.html_safe
201
+ end
202
+
203
+ type = type.to_sym
204
+
205
+ type = :info if type == :notice
206
+ type = :danger if type == :alert
207
+ type = :danger if type == :error
208
+ type = :warning if type == :warn
209
+
210
+ type = :info unless [:info, :success, :danger, :warning].include?(type)
211
+
212
+ array_auto_hide = nil unless array_auto_hide.is_a?(::Integer) && array_auto_hide > 0
213
+
214
+ contents = render_alert_message(message, array_auto_hide)
215
+
216
+ html =
217
+ "<div class=\"alert alert-#{type} alert-dismissible\">" +
218
+ '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
219
+ contents[:text]
220
+
221
+ unless contents[:script].blank?
222
+ html += <<-EOS
223
+ <script type="text/javascript">
224
+ <![CDATA[
225
+ #{contents[:script]}
226
+ ]]>
227
+ </script>
228
+ EOS
229
+ end
230
+
231
+ html += '</div>'
232
+
233
+ html.html_safe
234
+ end
235
+
236
+ ##
237
+ # Renders the error summary for the specified model.
238
+ def error_summary(model)
239
+ return nil unless model&.respond_to?(:errors)
240
+ return nil unless model.errors&.any?
241
+ contents = render_alert_message(
242
+ {
243
+ "__The form contains #{model.errors.count} error#{model.errors.count == 1 ? '' : 's'}.__" => model.errors.full_messages
244
+ },
245
+ 5
246
+ )
247
+
248
+ html = '<div id="error_explanation"><div class="alert alert-danger">' + contents[:text]
249
+
250
+ unless contents[:script].blank?
251
+ html += <<-EOS
252
+ <script type="text/javascript">
253
+ <![CDATA[
254
+ #{contents[:script]}
255
+ ]]>
256
+ </script>
257
+ EOS
258
+ end
259
+
260
+ html += '</div></div>'
261
+
262
+ html.html_safe
263
+ end
264
+
265
+
266
+ ##
267
+ # Formats a date in US format (M/D/YYYY).
268
+ #
269
+ # The +date+ can be a string in the correct format, or a Date/Time object.
270
+ # If the +date+ is blank, then nil will be returned.
271
+ def fmt_date(date)
272
+ return nil if date.blank?
273
+ # We want our date in a string format, so only convert to time if we aren't in a string, time, or date.
274
+ if date.respond_to?(:to_time) && !date.is_a?(::String) && !date.is_a?(::Time) && !date.is_a?(::Date)
275
+ date = date.to_time
276
+ end
277
+ # Now if we have a Date or Time value, convert to string.
278
+ if date.respond_to?(:strftime)
279
+ date = date.strftime('%m/%d/%Y')
280
+ end
281
+ return nil unless date.is_a?(::String)
282
+
283
+ # Now the string has to match one of our expected formats.
284
+ if date =~ Incline::DateTimeFormats::ALMOST_ISO_DATE_FORMAT
285
+ m,d,y = [ $2, $3, $1 ].map{|v| v.to_i}
286
+ "#{m}/#{d}/#{y.to_s.rjust(4,'0')}"
287
+ elsif date =~ Incline::DateTimeFormats::US_DATE_FORMAT
288
+ m,d,y = [ $1, $2, $3 ].map{|v| v.to_i}
289
+ "#{m}/#{d}/#{y.to_s.rjust(4,'0')}"
290
+ else
291
+ nil
292
+ end
293
+ end
294
+
295
+ ##
296
+ # Formats a number with the specified number of decimal places.
297
+ #
298
+ # The +value+ can be any valid numeric expression that can be converted into a float.
299
+ def fmt_num(value, places = 2)
300
+ return nil if value.blank?
301
+
302
+ value =
303
+ if value.respond_to?(:to_f)
304
+ value.to_f
305
+ else
306
+ nil
307
+ end
308
+
309
+ return nil unless value.is_a?(::Float)
310
+
311
+ "%0.#{places}f" % value.round(places)
312
+ end
313
+
314
+ ##
315
+ # Returns the Gravatar for the given user.
316
+ #
317
+ # Based on the tutorial from [www.railstutorial.org](www.railstutorial.org).
318
+ #
319
+ # The +user+ is the user you want to get the gravatar for.
320
+ #
321
+ # Valid options:
322
+ # size::
323
+ # The size (in pixels) for the returned gravatar. The gravatar will be a square image using this
324
+ # value as both the width and height. The default is 80 pixels.
325
+ # default::
326
+ # The default image to return when no image is set. This can be nil, :mm, :identicon, :monsterid,
327
+ # :wavatar, or :retro. The default is :identicon.
328
+ def gravatar_for(user, options = {})
329
+ return nil unless user
330
+
331
+ options = { size: 80, default: :identicon }.merge(options || {})
332
+ options[:default] = options[:default].to_s.to_sym unless options[:default].nil? || options[:default].is_a?(::Symbol)
333
+ gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
334
+ size = options[:size]
335
+ default = [:mm, :identicon, :monsterid, :wavatar, :retro].include?(options[:default]) ? "&d=#{options[:default]}" : ''
336
+ gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}#{default}"
337
+ image_tag(gravatar_url, alt: user.name, class: 'gravatar', style: "width: #{size}px, height: #{size}px")
338
+ end
339
+
340
+ ##
341
+ # Creates a panel with the specified title.
342
+ #
343
+ # Valid options:
344
+ # type::
345
+ # Type can be :primary, :success, :info, :warning, or :danger. Default value is :primary.
346
+ # size::
347
+ # Size can be any value from 1 through 12. Default value is 6.
348
+ # offset::
349
+ # Offset can be any value from 1 through 12. Default value is 3.
350
+ # Common sense is required, for instance you would likely never use an offset of 12, but it is available.
351
+ # Likewise an offset of 8 with a size of 8 would usually have the same effect as an offset of 12 because
352
+ # there are only 12 columns to fit your 8 column wide panel in.
353
+ # open_body::
354
+ # This can be true or false. Default value is true.
355
+ # If true, the body division is opened (and closed) by this helper.
356
+ # If false, then the panel is opened and closed, but the body division is not created.
357
+ # This allows you to add tables and divisions as you see fit.
358
+ #
359
+ # Provide a block to render content within the panel.
360
+ def panel(title, options = { }, &block)
361
+ options = {
362
+ type: 'primary',
363
+ size: 6,
364
+ offset: 3,
365
+ open_body: true
366
+ }.merge(options || {})
367
+
368
+ options[:type] = options[:type].to_s.downcase
369
+ options[:type] = 'primary' unless %w(primary success info warning danger).include?(options[:type])
370
+ options[:size] = 6 unless (1..12).include?(options[:size])
371
+ options[:offset] = 3 unless (0..12).include?(options[:offset])
372
+
373
+ ret = "<div class=\"col-md-#{options[:size]} col-md-offset-#{options[:offset]}\"><div class=\"panel panel-#{options[:type]}\"><div class=\"panel-heading\"><h4 class=\"panel-title\">#{h title}</h4></div>"
374
+ ret += '<div class="panel-body">' if options[:open_body]
375
+
376
+ if block_given?
377
+ content = capture { block.call }
378
+ content = CGI::escape_html(content) unless content.html_safe?
379
+ ret += content
380
+ end
381
+
382
+ ret += '</div>' if options[:open_body]
383
+ ret += '</div></div>'
384
+
385
+ ret.html_safe
386
+ end
387
+
388
+
389
+ private
390
+
391
+ def redcarpet
392
+ @redcarpet ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(no_intra_emphasis: true, fenced_code_blocks: true, strikethrough: true, autolink: true))
393
+ end
394
+
395
+ def render_md(text)
396
+ text = redcarpet.render(text)
397
+ # Use /Z to match before a trailing newline.
398
+ if /\A<p>(.*)<\/p>\Z/i =~ text
399
+ $1
400
+ else
401
+ text
402
+ end
403
+ end
404
+
405
+ def render_alert_message(message, array_auto_hide, bottom = true, state = nil)
406
+ state ||= { text: '', script: '' }
407
+
408
+ if message.is_a?(::Array)
409
+
410
+ # flatten the array, then map to the text values.
411
+ message = message.flatten
412
+ .map { |v| render_alert_message(v, array_auto_hide, bottom, nil) }
413
+ .map do |v|
414
+ state[:script] += v[:script]
415
+ v[:text]
416
+ end
417
+
418
+ if array_auto_hide && message.count > array_auto_hide && array_auto_hide > 0
419
+ # We want to hide some records.
420
+
421
+ # Generate a random ID for these items.
422
+ id = 'alert_' + SecureRandom.random_number(1<<20).to_s(16).rjust(5,'0')
423
+
424
+ init_count = array_auto_hide
425
+ remaining = message.count - init_count
426
+
427
+ # Rebuild the array by inserting the link after the initial records.
428
+ # The link gets a class of 'alert_#####_show' and the items following it get a class of 'alert_#####'.
429
+ # The items following also get a 'display: none;' style to hide them.
430
+ message = message[0...init_count] +
431
+ [
432
+ (bottom ? '<span' : '<li') + " class=\"#{id}_show\">" +
433
+ "<a href=\"javascript:show_#{id}()\" title=\"Show #{remaining} more\">... plus #{remaining} more</a>" +
434
+ (bottom ? '</span>' : '</li>')
435
+ ] +
436
+ message[init_count..-1].map{|v| v.gsub(/\A<(li|span|div)>/, "<\\1 class=\"#{id}\" style=\"display: none;\">") }
437
+
438
+ state[:text] += message.join(bottom ? '<br>' : '')
439
+
440
+ # When the link gets clicked, hide the link and show the hidden items.
441
+ state[:script] += "function show_#{id}() { $('.#{id}_show').hide(); $('.#{id}').show(); }\n"
442
+ else
443
+ state[:text] += message.join(bottom ? '<br>' : '')
444
+ end
445
+
446
+ elsif message.is_a?(::Hash)
447
+ # Process each item as <li>::KEY::<ul>::VALUES::</ul></li>
448
+ message.each do |k,v|
449
+ state[:text] += bottom ? '<div>' : '<li>'
450
+ if k.is_a?(::Symbol)
451
+ state[:text] += CGI::escape_html(k.to_s.humanize.capitalize)
452
+ elsif k.is_a?(::String)
453
+ state[:text] += render_md(CGI::escape_html(k))
454
+ else
455
+ state[:text] += CGI::escape_html(k.inspect)
456
+ end
457
+ unless v.blank?
458
+ state[:text] += '<ul>'
459
+ render_alert_message v, array_auto_hide, false, state
460
+ state[:text] += '</ul>'
461
+ end
462
+ state[:text] += bottom ? '</div>' : '</li>'
463
+ end
464
+ else
465
+ # Make the text safe.
466
+ # If the message is an HTML safe string, don't process it.
467
+ text =
468
+ if message.html_safe?
469
+ message
470
+ else
471
+ render_md(CGI::escape_html(message.to_s))
472
+ end
473
+
474
+ if bottom
475
+ state[:text] += "<span>#{text}</span>"
476
+ else
477
+ state[:text] += "<li>#{text}</li>"
478
+ end
479
+ end
480
+ state
481
+ end
482
+
483
+
484
+ end
485
+
486
+ end
487
+
488
+ ActionView::Base.include Incline::Extensions::ActionViewBase
489
+ ActionDispatch::IntegrationTest.include Incline::Extensions::ActionViewBase