incline 0.1.5

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 (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