fe 0.0.4 → 2.0.0

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 (456) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -4
  3. data/Rakefile +16 -36
  4. data/app/assets/config/fe/manifest.js +3 -0
  5. data/app/assets/config/manifest.js +3 -0
  6. data/app/assets/javascripts/application.js.erb +1 -1
  7. data/app/assets/javascripts/fe/admin.js +0 -2
  8. data/app/assets/javascripts/fe/fe.admin.js +40 -20
  9. data/app/assets/javascripts/fe/fe.common.js.erb +98 -0
  10. data/app/assets/javascripts/fe/fe.public.js +2 -416
  11. data/app/assets/javascripts/fe/fe.public.nojquery.js.erb +526 -0
  12. data/app/assets/javascripts/fe/jquery.html5_upload.js +258 -0
  13. data/app/assets/javascripts/fe/jquery.validate.pack.js +3 -15
  14. data/app/assets/stylesheets/fe/fe.screen.css.scss.erb +90 -10
  15. data/app/assets/stylesheets/fe/validation.css +5 -1
  16. data/app/controllers/concerns/fe/admin/question_sheets_controller_concern.rb +111 -0
  17. data/app/controllers/concerns/fe/answer_pages_controller_concern.rb +131 -0
  18. data/app/controllers/{fe/concerns → concerns/fe}/answer_sheets_controller_concern.rb +15 -7
  19. data/app/controllers/{fe/concerns → concerns/fe}/application_controller_concern.rb +15 -2
  20. data/app/controllers/fe/admin/elements_controller.rb +102 -64
  21. data/app/controllers/fe/admin/email_templates_controller.rb +7 -7
  22. data/app/controllers/fe/admin/question_pages_controller.rb +9 -9
  23. data/app/controllers/fe/admin/question_sheets_controller.rb +2 -103
  24. data/app/controllers/fe/reference_pages_controller.rb +11 -11
  25. data/app/controllers/fe/reference_sheets_controller.rb +6 -4
  26. data/app/controllers/fe/references_controller.rb +20 -19
  27. data/app/controllers/fe/submit_pages_controller.rb +5 -5
  28. data/app/helpers/fe/answer_pages_helper.rb +1 -1
  29. data/app/helpers/fe/application_helper.rb +8 -3
  30. data/app/jobs/fe/update_reference_sheet_visibility_job.rb +11 -0
  31. data/app/mailers/fe/notifier.rb +12 -5
  32. data/app/models/answer_sheet.rb +2 -0
  33. data/app/models/application_record.rb +3 -0
  34. data/app/models/{fe/concerns → concerns/fe}/answer_concern.rb +2 -2
  35. data/app/models/{fe/concerns → concerns/fe}/answer_pages_presenter_concern.rb +15 -5
  36. data/app/models/concerns/fe/answer_sheet_concern.rb +125 -0
  37. data/app/models/{fe/concerns → concerns/fe}/choice_field_concern.rb +56 -31
  38. data/app/models/fe/address.rb +2 -2
  39. data/app/models/fe/answer.rb +1 -1
  40. data/app/models/fe/answer_sheet.rb +1 -1
  41. data/app/models/fe/answer_sheet_question_sheet.rb +2 -2
  42. data/app/models/fe/application.rb +15 -10
  43. data/app/models/fe/condition.rb +4 -4
  44. data/app/models/fe/date_field.rb +2 -2
  45. data/app/models/fe/element.rb +201 -45
  46. data/app/models/fe/email_address.rb +2 -2
  47. data/app/models/fe/email_template.rb +1 -1
  48. data/app/models/fe/page.rb +138 -31
  49. data/app/models/fe/page_element.rb +8 -3
  50. data/app/models/fe/page_link.rb +6 -3
  51. data/app/models/fe/paragraph.rb +1 -1
  52. data/app/models/fe/person.rb +16 -13
  53. data/app/models/fe/phone_number.rb +1 -1
  54. data/app/models/fe/question.rb +64 -44
  55. data/app/models/fe/question_grid.rb +16 -4
  56. data/app/models/fe/question_grid_with_total.rb +15 -0
  57. data/app/models/fe/question_set.rb +54 -14
  58. data/app/models/fe/question_sheet.rb +54 -19
  59. data/app/models/fe/reference_question.rb +9 -13
  60. data/app/models/fe/reference_sheet.rb +113 -54
  61. data/app/models/fe/state_chooser.rb +2 -2
  62. data/app/models/fe/text_field.rb +2 -2
  63. data/app/models/fe/user.rb +1 -1
  64. data/app/models/staff.rb +8 -6
  65. data/app/validators/email_validator.rb +11 -0
  66. data/app/views/fe/admin/elements/create.js.erb +4 -3
  67. data/app/views/fe/admin/elements/destroy.js.erb +1 -1
  68. data/app/views/fe/admin/elements/drop.js.erb +2 -1
  69. data/app/views/fe/admin/elements/duplicate.js.erb +2 -1
  70. data/app/views/fe/admin/elements/edit.js.erb +1 -1
  71. data/app/views/fe/admin/elements/error.js.erb +1 -1
  72. data/app/views/fe/admin/elements/new.js.erb +13 -6
  73. data/app/views/fe/admin/elements/update.js.erb +1 -1
  74. data/app/views/fe/admin/email_templates/_form.html.erb +3 -3
  75. data/app/views/fe/admin/email_templates/edit.html.erb +3 -3
  76. data/app/views/fe/admin/email_templates/index.html.erb +3 -3
  77. data/app/views/fe/admin/email_templates/new.html.erb +3 -3
  78. data/app/views/fe/admin/panels/_advanced_options.html.erb +16 -10
  79. data/app/views/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  80. data/app/views/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  81. data/app/views/fe/admin/panels/_common_fields.html.erb +18 -19
  82. data/app/views/fe/admin/panels/_condition.html.erb +1 -1
  83. data/app/views/fe/admin/panels/_insert.html.erb +25 -25
  84. data/app/views/fe/admin/panels/_nav_controls.html.erb +4 -4
  85. data/app/views/fe/admin/panels/_page.html.erb +2 -2
  86. data/app/views/fe/admin/panels/_pages_list.html.erb +3 -3
  87. data/app/views/fe/admin/panels/_prop_attachment_field.html.erb +2 -2
  88. data/app/views/fe/admin/panels/_prop_choice_field.html.erb +52 -24
  89. data/app/views/fe/admin/panels/_prop_date_field.html.erb +1 -1
  90. data/app/views/fe/admin/panels/_prop_element.html.erb +7 -14
  91. data/app/views/fe/admin/panels/_prop_page.html.erb +11 -6
  92. data/app/views/fe/admin/panels/_prop_paragraph.html.erb +38 -24
  93. data/app/views/fe/admin/panels/_prop_question_grid.html.erb +7 -2
  94. data/app/views/fe/admin/panels/_prop_question_grid_with_total.html.erb +2 -2
  95. data/app/views/fe/admin/panels/_prop_reference_question.html.erb +3 -3
  96. data/app/views/fe/admin/panels/_prop_section.html.erb +8 -2
  97. data/app/views/fe/admin/panels/_prop_sheet.html.erb +7 -4
  98. data/app/views/fe/admin/panels/_prop_text_field.html.erb +12 -12
  99. data/app/views/fe/admin/question_pages/_element.html.erb +16 -6
  100. data/app/views/fe/admin/question_pages/_element_show.html.erb +2 -2
  101. data/app/views/fe/admin/question_pages/_question_page.html.erb +4 -4
  102. data/app/views/fe/admin/question_pages/create.js.erb +3 -3
  103. data/app/views/fe/admin/question_pages/destroy.js.erb +3 -3
  104. data/app/views/fe/admin/question_pages/edit.js.erb +1 -1
  105. data/app/views/fe/admin/question_pages/error.js.erb +1 -1
  106. data/app/views/fe/admin/question_pages/show.js.erb +2 -2
  107. data/app/views/fe/admin/question_pages/show_panel.js.erb +1 -1
  108. data/app/views/fe/admin/question_pages/update.js.erb +1 -1
  109. data/app/views/fe/admin/question_sheets/edit.js.erb +1 -1
  110. data/app/views/fe/admin/question_sheets/error.js.erb +1 -1
  111. data/app/views/fe/admin/question_sheets/index.html.erb +8 -8
  112. data/app/views/fe/admin/question_sheets/new.html.erb +3 -3
  113. data/app/views/fe/admin/question_sheets/show.html.erb +5 -5
  114. data/app/views/fe/admin/question_sheets/update.js.erb +1 -1
  115. data/app/views/fe/answer_pages/_answer_page.html.erb +14 -14
  116. data/app/views/fe/answer_pages/_element.html.erb +25 -6
  117. data/app/views/fe/answer_pages/_page_name.html.erb +1 -0
  118. data/app/views/fe/answer_pages/show.html.erb +39 -0
  119. data/app/views/fe/answer_pages/update.js.erb +11 -3
  120. data/app/views/fe/answer_sheets/_answer_sheet.html.erb +6 -6
  121. data/app/views/fe/answer_sheets/_element.html.erb +55 -35
  122. data/app/views/fe/answer_sheets/_incomplete.html.erb +1 -1
  123. data/app/views/fe/answer_sheets/_page_link.html.erb +9 -7
  124. data/app/views/fe/answer_sheets/_pages_list.html.erb +4 -4
  125. data/app/views/fe/answer_sheets/_submit_to.html.erb +1 -0
  126. data/app/views/fe/answer_sheets/_title.html.erb +1 -1
  127. data/app/views/fe/answer_sheets/edit.html.erb +24 -24
  128. data/app/views/fe/answer_sheets/incomplete.js.erb +9 -3
  129. data/app/views/fe/answer_sheets/index.html.erb +3 -3
  130. data/app/views/fe/answer_sheets/show.html.erb +1 -1
  131. data/app/views/fe/applications/_logout.html.erb +1 -0
  132. data/app/views/fe/applications/show.html.erb +1 -0
  133. data/app/views/fe/questions/fe/_acceptance.html.erb +11 -10
  134. data/app/views/fe/questions/fe/_attachment_field.html.erb +133 -10
  135. data/app/views/fe/questions/fe/_checkbox_field.html.erb +34 -30
  136. data/app/views/fe/questions/fe/_country.html.erb +6 -6
  137. data/app/views/fe/questions/fe/_date_field.html.erb +5 -5
  138. data/app/views/fe/questions/fe/_date_field_mmyy.html.erb +8 -8
  139. data/app/views/fe/questions/fe/_drop_down_field.html.erb +7 -6
  140. data/app/views/fe/questions/fe/_paragraph.html.erb +1 -1
  141. data/app/views/fe/questions/fe/_question_grid.html.erb +21 -16
  142. data/app/views/fe/questions/fe/_question_grid_with_total.html.erb +25 -18
  143. data/app/views/fe/questions/fe/_questions.html.erb +11 -5
  144. data/app/views/fe/questions/fe/_radio_button_field.html.erb +42 -25
  145. data/app/views/fe/questions/fe/_rating.html.erb +57 -18
  146. data/app/views/fe/questions/fe/_reference_discipler.html.erb +1 -1
  147. data/app/views/fe/questions/fe/_reference_friend.html.erb +1 -1
  148. data/app/views/fe/questions/fe/_reference_parent.html.erb +1 -1
  149. data/app/views/fe/questions/fe/_reference_peer.html.erb +1 -1
  150. data/app/views/fe/questions/fe/_reference_question.html.erb +44 -21
  151. data/app/views/fe/questions/fe/_reference_roommate.html.erb +1 -1
  152. data/app/views/fe/questions/fe/_reference_spiritual.html.erb +1 -1
  153. data/app/views/fe/questions/fe/_reference_staff.html.erb +1 -1
  154. data/app/views/fe/questions/fe/_section.html.erb +1 -1
  155. data/app/views/fe/questions/fe/_state_chooser.html.erb +6 -6
  156. data/app/views/fe/questions/fe/_text_area_field.html.erb +17 -6
  157. data/app/views/fe/questions/fe/_text_field.html.erb +7 -6
  158. data/app/views/fe/questions/fe/_yes_no.html.erb +8 -8
  159. data/app/views/fe/questions/fe/_yes_no_field.erb +12 -9
  160. data/app/views/fe/reference_pages/_reference.html.erb +15 -15
  161. data/app/views/fe/reference_pages/edit.html.erb +9 -9
  162. data/app/views/fe/reference_sheets/done.html.erb +2 -2
  163. data/app/views/fe/reference_sheets/not_found.html.erb +4 -4
  164. data/app/views/fe/references/edit.html.erb +6 -6
  165. data/app/views/fe/references/show.html.erb +8 -8
  166. data/app/views/fe/references/submit.js.erb +3 -3
  167. data/app/views/fe/submit_pages/_thankyou.html.erb +1 -1
  168. data/app/views/fe/submit_pages/edit.html.erb +12 -12
  169. data/app/views/fe/submit_pages/error.js.erb +1 -1
  170. data/app/views/fe/submit_pages/submit.js.erb +2 -2
  171. data/app/views/layouts/fe/_error_messages_for.html.erb +7 -0
  172. data/app/views/layouts/fe/application.html.erb +4 -5
  173. data/app/views/layouts/fe/fe_admin.html.erb +30 -0
  174. data/app/views.current/fe/admin/elements/_errors.html.erb +11 -0
  175. data/app/views.current/fe/admin/elements/create.js.erb +12 -0
  176. data/app/views.current/fe/admin/elements/destroy.js.erb +4 -0
  177. data/app/views.current/fe/admin/elements/drop.js.erb +3 -0
  178. data/app/views.current/fe/admin/elements/duplicate.js.erb +3 -0
  179. data/app/views.current/fe/admin/elements/edit.js.erb +4 -0
  180. data/app/views.current/fe/admin/elements/error.js.erb +4 -0
  181. data/app/views.current/fe/admin/elements/new.js.erb +17 -0
  182. data/app/views.current/fe/admin/elements/reorder.js.erb +0 -0
  183. data/app/views.current/fe/admin/elements/update.js.erb +9 -0
  184. data/app/views.current/fe/admin/email_templates/_form.html.erb +8 -0
  185. data/app/views.current/fe/admin/email_templates/edit.html.erb +13 -0
  186. data/app/views.current/fe/admin/email_templates/index.html.erb +20 -0
  187. data/app/views.current/fe/admin/email_templates/new.html.erb +11 -0
  188. data/app/views.current/fe/admin/panels/_advanced_options.html.erb +49 -0
  189. data/app/views.current/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  190. data/app/views.current/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  191. data/app/views.current/fe/admin/panels/_common_fields.html.erb +23 -0
  192. data/app/views.current/fe/admin/panels/_condition.html.erb +6 -0
  193. data/app/views.current/fe/admin/panels/_insert.html.erb +39 -0
  194. data/app/views.current/fe/admin/panels/_nav_controls.html.erb +6 -0
  195. data/app/views.current/fe/admin/panels/_page.html.erb +3 -0
  196. data/app/views.current/fe/admin/panels/_pages_list.html.erb +16 -0
  197. data/app/views.current/fe/admin/panels/_prop_attachment_field.html.erb +2 -0
  198. data/app/views.current/fe/admin/panels/_prop_choice_field.html.erb +74 -0
  199. data/app/views.current/fe/admin/panels/_prop_date_field.html.erb +7 -0
  200. data/app/views.current/fe/admin/panels/_prop_element.html.erb +23 -0
  201. data/app/views.current/fe/admin/panels/_prop_page.html.erb +26 -0
  202. data/app/views.current/fe/admin/panels/_prop_paragraph.html.erb +46 -0
  203. data/app/views.current/fe/admin/panels/_prop_question_grid.html.erb +28 -0
  204. data/app/views.current/fe/admin/panels/_prop_question_grid_with_total.html.erb +14 -0
  205. data/app/views.current/fe/admin/panels/_prop_reference_question.html.erb +12 -0
  206. data/app/views.current/fe/admin/panels/_prop_section.html.erb +8 -0
  207. data/app/views.current/fe/admin/panels/_prop_sheet.html.erb +20 -0
  208. data/app/views.current/fe/admin/panels/_prop_text_field.html.erb +20 -0
  209. data/app/views.current/fe/admin/question_pages/_element.html.erb +28 -0
  210. data/app/views.current/fe/admin/question_pages/_element_show.html.erb +10 -0
  211. data/app/views.current/fe/admin/question_pages/_errors.html.erb +10 -0
  212. data/app/views.current/fe/admin/question_pages/_question_page.html.erb +13 -0
  213. data/app/views.current/fe/admin/question_pages/create.js.erb +11 -0
  214. data/app/views.current/fe/admin/question_pages/destroy.js.erb +5 -0
  215. data/app/views.current/fe/admin/question_pages/edit.js.erb +3 -0
  216. data/app/views.current/fe/admin/question_pages/error.js.erb +4 -0
  217. data/app/views.current/fe/admin/question_pages/show.js.erb +9 -0
  218. data/app/views.current/fe/admin/question_pages/show_panel.js.erb +3 -0
  219. data/app/views.current/fe/admin/question_pages/update.js.erb +2 -0
  220. data/app/views.current/fe/admin/question_sheets/_errors.html.erb +11 -0
  221. data/app/views.current/fe/admin/question_sheets/edit.js.erb +3 -0
  222. data/app/views.current/fe/admin/question_sheets/error.js.erb +5 -0
  223. data/app/views.current/fe/admin/question_sheets/index.html.erb +41 -0
  224. data/app/views.current/fe/admin/question_sheets/new.html.erb +15 -0
  225. data/app/views.current/fe/admin/question_sheets/show.html.erb +27 -0
  226. data/app/views.current/fe/admin/question_sheets/update.js.erb +2 -0
  227. data/app/views.current/fe/answer_pages/_answer_page.html.erb +53 -0
  228. data/app/views.current/fe/answer_pages/_element.html.erb +32 -0
  229. data/app/views.current/fe/answer_pages/_page_name.html.erb +1 -0
  230. data/app/views.current/fe/answer_pages/show.html.erb +39 -0
  231. data/app/views.current/fe/answer_pages/update.js.erb +13 -0
  232. data/app/views.current/fe/answer_sheets/_answer_sheet.html.erb +26 -0
  233. data/app/views.current/fe/answer_sheets/_element.html.erb +74 -0
  234. data/app/views.current/fe/answer_sheets/_incomplete.html.erb +10 -0
  235. data/app/views.current/fe/answer_sheets/_page_link.html.erb +9 -0
  236. data/app/views.current/fe/answer_sheets/_pages_list.html.erb +11 -0
  237. data/app/views.current/fe/answer_sheets/_submit_to.html.erb +1 -0
  238. data/app/views.current/fe/answer_sheets/_title.html.erb +1 -0
  239. data/app/views.current/fe/answer_sheets/edit.html.erb +66 -0
  240. data/app/views.current/fe/answer_sheets/incomplete.js.erb +11 -0
  241. data/app/views.current/fe/answer_sheets/index.html.erb +18 -0
  242. data/app/views.current/fe/answer_sheets/send_reference_invite.js.erb +8 -0
  243. data/app/views.current/fe/answer_sheets/show.html.erb +13 -0
  244. data/app/views.current/fe/applications/_logout.html.erb +1 -0
  245. data/app/views.current/fe/applications/show.html.erb +1 -0
  246. data/app/views.current/fe/help/builder.html +33 -0
  247. data/app/views.current/fe/help/question_grid.html +18 -0
  248. data/app/views.current/fe/questions/fe/_acceptance.html.erb +14 -0
  249. data/app/views.current/fe/questions/fe/_attachment_field.html.erb +165 -0
  250. data/app/views.current/fe/questions/fe/_checkbox_field.html.erb +53 -0
  251. data/app/views.current/fe/questions/fe/_country.html.erb +7 -0
  252. data/app/views.current/fe/questions/fe/_date_field.html.erb +7 -0
  253. data/app/views.current/fe/questions/fe/_date_field_mmyy.html.erb +9 -0
  254. data/app/views.current/fe/questions/fe/_drop_down_field.html.erb +8 -0
  255. data/app/views.current/fe/questions/fe/_paragraph.html.erb +1 -0
  256. data/app/views.current/fe/questions/fe/_question_grid.html.erb +70 -0
  257. data/app/views.current/fe/questions/fe/_question_grid_with_total.html.erb +64 -0
  258. data/app/views.current/fe/questions/fe/_questions.html.erb +15 -0
  259. data/app/views.current/fe/questions/fe/_radio_button_field.html.erb +60 -0
  260. data/app/views.current/fe/questions/fe/_rating.html.erb +64 -0
  261. data/app/views.current/fe/questions/fe/_reference_discipler.html.erb +1 -0
  262. data/app/views.current/fe/questions/fe/_reference_friend.html.erb +1 -0
  263. data/app/views.current/fe/questions/fe/_reference_parent.html.erb +1 -0
  264. data/app/views.current/fe/questions/fe/_reference_peer.html.erb +1 -0
  265. data/app/views.current/fe/questions/fe/_reference_question.html.erb +61 -0
  266. data/app/views.current/fe/questions/fe/_reference_roommate.html.erb +1 -0
  267. data/app/views.current/fe/questions/fe/_reference_spiritual.html.erb +1 -0
  268. data/app/views.current/fe/questions/fe/_reference_staff.html.erb +1 -0
  269. data/app/views.current/fe/questions/fe/_section.html.erb +1 -0
  270. data/app/views.current/fe/questions/fe/_state_chooser.html.erb +7 -0
  271. data/app/views.current/fe/questions/fe/_text_area_field.html.erb +17 -0
  272. data/app/views.current/fe/questions/fe/_text_field.html.erb +9 -0
  273. data/app/views.current/fe/questions/fe/_yes_no.html.erb +17 -0
  274. data/app/views.current/fe/questions/fe/_yes_no_field.erb +20 -0
  275. data/app/views.current/fe/reference_pages/_reference.html.erb +31 -0
  276. data/app/views.current/fe/reference_pages/edit.html.erb +24 -0
  277. data/app/views.current/fe/reference_sheets/done.html.erb +2 -0
  278. data/app/views.current/fe/reference_sheets/not_found.html.erb +5 -0
  279. data/app/views.current/fe/reference_sheets/submitted.js.erb +1 -0
  280. data/app/views.current/fe/references/edit.html.erb +8 -0
  281. data/app/views.current/fe/references/send_invite.js.erb +7 -0
  282. data/app/views.current/fe/references/show.html.erb +18 -0
  283. data/app/views.current/fe/references/submit.js.erb +3 -0
  284. data/app/views.current/fe/submit_pages/_errors.html.erb +1 -0
  285. data/app/views.current/fe/submit_pages/_thankyou.html.erb +2 -0
  286. data/app/views.current/fe/submit_pages/edit.html.erb +36 -0
  287. data/app/views.current/fe/submit_pages/error.js.erb +1 -0
  288. data/app/views.current/fe/submit_pages/submit.js.erb +3 -0
  289. data/app/views.current/layouts/fe/_error_messages_for.html.erb +7 -0
  290. data/app/views.current/layouts/fe/application.html.erb +47 -0
  291. data/app/{views/layouts/fe/fe.admin.html.erb → views.current/layouts/fe/fe_admin.html.erb} +4 -4
  292. data/config/initializers/paper_trail.rb +3 -0
  293. data/config/routes.rb +3 -37
  294. data/db/migrate/20131003041856_core.rb +23 -23
  295. data/db/migrate/20131003044250_create_reference_sheets.rb +2 -1
  296. data/db/migrate/20131003044436_add_element_and_answer_fields.rb +3 -3
  297. data/db/migrate/20131003044518_create_email_templates.rb +2 -2
  298. data/db/migrate/20131003044621_add_max_lengths.rb +1 -1
  299. data/db/migrate/20131003044714_create_join_table.rb +1 -1
  300. data/db/migrate/20131016162128_remove_question_id_from_element.rb +1 -1
  301. data/db/migrate/20140623153424_create_fe_people.rb +2 -2
  302. data/db/migrate/20140624180246_create_fe_addresses.rb +2 -2
  303. data/db/migrate/{20140624182216_create_create_fe_phone_numbers.rb → 20140624182216_create_fe_phone_numbers.rb} +2 -2
  304. data/db/migrate/20140625160545_create_fe_users.rb +2 -2
  305. data/db/migrate/20140808202507_add_conditional_type_to_elements.rb +1 -1
  306. data/db/migrate/20140808203609_add_conditional_answer_to_elements.rb +1 -1
  307. data/db/migrate/20141103204704_remove_short_value_column.rb +1 -1
  308. data/db/migrate/20141109154522_move_conditional_ids_used_for_choice_field_to_their_own_column.rb +1 -1
  309. data/db/migrate/20150504221439_add_all_element_ids_to_pages.rb +5 -0
  310. data/db/migrate/20150713022326_add_locale_columns.rb +9 -0
  311. data/db/migrate/20150714220730_add_locale_to_answer_sheet.rb +5 -0
  312. data/db/migrate/20150925181652_add_share_to_elements.rb +5 -0
  313. data/db/migrate/20150928085325_change_pages_all_element_ids_to_text.rb +5 -0
  314. data/db/migrate/20150930191538_add_locale_to_reference_sheets.rb +5 -0
  315. data/db/migrate/20151021181928_switch_conditional_answer_separator_to_semicolon.rb +7 -0
  316. data/db/migrate/20151021184250_add_question_sheet_id_in_refs.rb +12 -0
  317. data/db/migrate/20160201185838_add_visible_and_visibility_cache_key_to_reference_sheets.rb +6 -0
  318. data/db/migrate/20160805221415_add_rating_extra_labels.rb +10 -0
  319. data/db/migrate/20181108201746_create_versions.rb +80 -0
  320. data/db/migrate/20181218201130_increase_slug_length.rb +5 -0
  321. data/lib/access_key_generator.rb +12 -0
  322. data/lib/distinct_distinct_patch.rb +20 -0
  323. data/lib/fe/engine.rb +26 -14
  324. data/lib/fe/version.rb +1 -1
  325. data/lib/fe.rb +11 -1
  326. data/spec/controllers/fe/admin/elements_controller_spec.rb +211 -1
  327. data/spec/controllers/fe/admin/email_templates_controller_spec.rb +26 -1
  328. data/spec/controllers/fe/admin/question_pages_controller_spec.rb +8 -1
  329. data/spec/controllers/fe/admin/question_sheets_controller_spec.rb +48 -1
  330. data/spec/controllers/fe/answer_pages_controller_spec.rb +129 -1
  331. data/spec/controllers/fe/answer_sheets_controller_spec.rb +136 -1
  332. data/spec/controllers/fe/reference_pages_controller.rb +4 -0
  333. data/spec/controllers/fe/references_controller_spec.rb +4 -0
  334. data/spec/controllers/fe/submit_pages_controller_spec.rb +4 -0
  335. data/spec/dummy/app/assets/config/manifest.js +0 -0
  336. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  337. data/spec/dummy/app/helpers/application_helper.rb +9 -0
  338. data/spec/dummy/app/models/application.rb +3 -0
  339. data/spec/dummy/app/models/person.rb +11 -0
  340. data/spec/dummy/app/models/user.rb +3 -0
  341. data/spec/dummy/app/views/layouts/application.html.erb +2 -2
  342. data/spec/dummy/config/application.rb +3 -1
  343. data/spec/dummy/config/database.yml +20 -17
  344. data/spec/dummy/config/environments/production.rb +1 -5
  345. data/spec/dummy/config/environments/test.rb +4 -2
  346. data/spec/dummy/config/initializers/assets.rb +5 -2
  347. data/spec/dummy/config/initializers/fast_gettext.rb +5 -0
  348. data/spec/dummy/config/initializers/to_unsafe_h.rb +5 -0
  349. data/spec/dummy/config/initializers/to_unsafe_h.rb.new +5 -0
  350. data/spec/dummy/config/secrets.yml +2 -2
  351. data/spec/dummy/db/migrate/20141203214017_core.fe_engine.rb +92 -0
  352. data/spec/dummy/db/migrate/20141203214018_create_reference_sheets.fe_engine.rb +25 -0
  353. data/spec/dummy/db/migrate/20141203214019_add_element_and_answer_fields.fe_engine.rb +11 -0
  354. data/spec/dummy/db/migrate/20141203214020_create_email_templates.fe_engine.rb +18 -0
  355. data/spec/dummy/db/migrate/20141203214021_add_max_lengths.fe_engine.rb +9 -0
  356. data/spec/dummy/db/migrate/20141203214022_create_join_table.fe_engine.rb +12 -0
  357. data/spec/dummy/db/migrate/20141203214023_remove_question_id_from_element.fe_engine.rb +10 -0
  358. data/spec/dummy/db/migrate/20141203214024_create_fe_people.fe_engine.rb +13 -0
  359. data/spec/dummy/db/migrate/20141203214025_create_fe_addresses.fe_engine.rb +21 -0
  360. data/spec/dummy/db/migrate/20141203214027_create_fe_users.fe_engine.rb +13 -0
  361. data/spec/dummy/db/migrate/20141203214028_add_conditional_type_to_elements.fe_engine.rb +6 -0
  362. data/spec/dummy/db/migrate/20141203214029_add_conditional_answer_to_elements.fe_engine.rb +6 -0
  363. data/spec/dummy/db/migrate/20141203214030_remove_short_value_column.fe_engine.rb +6 -0
  364. data/spec/dummy/db/migrate/20141203214031_move_conditional_ids_used_for_choice_field_to_their_own_column.fe_engine.rb +8 -0
  365. data/spec/dummy/db/migrate/20150123215803_create_users.rb +9 -0
  366. data/spec/dummy/db/migrate/20150504222619_add_all_element_ids_to_pages.fe_engine.rb +6 -0
  367. data/spec/dummy/db/migrate/20150925192557_add_share_to_elements.fe_engine.rb +6 -0
  368. data/spec/dummy/db/migrate/20150930190001_create_fe_phone_numbers.fe_engine.rb +20 -0
  369. data/spec/dummy/db/migrate/20150930190002_add_locale_columns.fe_engine.rb +10 -0
  370. data/spec/dummy/db/migrate/20150930190003_add_locale_to_answer_sheet.fe_engine.rb +6 -0
  371. data/spec/dummy/db/migrate/20150930190004_change_pages_all_element_ids_to_text.fe_engine.rb +6 -0
  372. data/spec/dummy/db/migrate/20150930191756_add_locale_to_reference_sheets.fe_engine.rb +6 -0
  373. data/spec/dummy/db/migrate/20151021190027_add_question_sheet_id_in_refs.fe_engine.rb +13 -0
  374. data/spec/dummy/db/migrate/20160204164612_switch_conditional_answer_separator_to_semicolon.fe_engine.rb +8 -0
  375. data/spec/dummy/db/migrate/20160204164613_add_visible_and_visibility_cache_key_to_reference_sheets.fe_engine.rb +7 -0
  376. data/spec/dummy/db/migrate/20181108201746_create_versions.rb +80 -0
  377. data/spec/dummy/db/schema.rb +91 -69
  378. data/spec/dummy/log/test.log +101278 -419
  379. data/spec/factories/answer_sheet_question_sheets.rb +1 -1
  380. data/spec/factories/answer_sheets.rb +2 -2
  381. data/spec/factories/answers.rb +1 -1
  382. data/spec/factories/applications.rb +3 -4
  383. data/spec/factories/dummy_applications.rb +6 -0
  384. data/spec/factories/dummy_people.rb +6 -0
  385. data/spec/factories/dummy_users.rb +6 -0
  386. data/spec/factories/elements.rb +35 -10
  387. data/spec/factories/email_templates.rb +5 -0
  388. data/spec/factories/fe_addresses.rb +10 -10
  389. data/spec/factories/fe_email_addresses.rb +3 -3
  390. data/spec/factories/fe_email_templates.rb +9 -0
  391. data/spec/factories/fe_people.rb +5 -7
  392. data/spec/factories/fe_phone_numbers.rb +3 -3
  393. data/spec/factories/fe_user.rb +6 -0
  394. data/spec/factories/page.rb +1 -1
  395. data/spec/factories/page_elements.rb +1 -1
  396. data/spec/factories/paragraphs.rb +1 -1
  397. data/spec/factories/question_sheet.rb +3 -3
  398. data/spec/factories/reference_questions.rb +1 -1
  399. data/spec/factories/reference_sheets.rb +9 -0
  400. data/spec/jobs/fe/update_reference_sheet_visibility_job_spec.rb +40 -0
  401. data/spec/mailers/fe/notifier_spec.rb +39 -0
  402. data/spec/models/fe/answer_sheet_question_sheet_spec.rb +1 -1
  403. data/spec/models/fe/answer_spec.rb +2 -2
  404. data/spec/models/fe/application_spec.rb +94 -1
  405. data/spec/models/fe/choice_field_spec.rb +66 -0
  406. data/spec/models/fe/condition_spec.rb +2 -2
  407. data/spec/models/fe/element_spec.rb +414 -37
  408. data/spec/models/fe/email_template_spec.rb +1 -1
  409. data/spec/models/fe/page_element_spec.rb +1 -1
  410. data/spec/models/fe/page_spec.rb +168 -11
  411. data/spec/models/fe/person_spec.rb +1 -1
  412. data/spec/models/fe/question_set_spec.rb +91 -0
  413. data/spec/models/fe/question_sheet_spec.rb +103 -1
  414. data/spec/models/fe/question_spec.rb +73 -6
  415. data/spec/models/fe/reference_question_spec.rb +20 -0
  416. data/spec/models/fe/reference_sheet_spec.rb +305 -2
  417. data/spec/models/fe/text_field_spec.rb +22 -0
  418. data/spec/rails_helper.rb +85 -54
  419. data/spec/support/choices.xml +6 -0
  420. metadata +310 -84
  421. data/app/assets/javascripts/fe/fe.common.js +0 -57
  422. data/app/assets/javascripts/fe/jquery.scrollTo-min.js +0 -7
  423. data/app/assets/javascripts/fe/rails.extra.js +0 -6
  424. data/app/controllers/fe/applications_controller.rb +0 -183
  425. data/app/controllers/fe/concerns/answer_pages_controller_concern.rb +0 -83
  426. data/app/controllers/fe/payments_controller.rb +0 -184
  427. data/app/models/fe/concerns/answer_sheet_concern.rb +0 -55
  428. data/app/models/fe/payment.rb +0 -77
  429. data/app/models/fe/payment_question.rb +0 -22
  430. data/app/views/fe/admin/panels/_prop_payment_question.html.erb +0 -1
  431. data/app/views/fe/application/_logout.html.erb +0 -1
  432. data/app/views/fe/payment_pages/_credit.html.erb +0 -47
  433. data/app/views/fe/payment_pages/_mail.html.erb +0 -27
  434. data/app/views/fe/payment_pages/_payment.html.erb +0 -6
  435. data/app/views/fe/payment_pages/_staff.html.erb +0 -25
  436. data/app/views/fe/payment_pages/_staff_results.html.erb +0 -17
  437. data/app/views/fe/payment_pages/edit.html.erb +0 -75
  438. data/app/views/fe/payment_pages/staff_search.js.erb +0 -2
  439. data/app/views/fe/payments/_credit.html.erb +0 -47
  440. data/app/views/fe/payments/_errors.html.erb +0 -1
  441. data/app/views/fe/payments/_payment.html.erb +0 -13
  442. data/app/views/fe/payments/_staff.html.erb +0 -21
  443. data/app/views/fe/payments/_staff_results.html.erb +0 -18
  444. data/app/views/fe/payments/approve.js.erb +0 -3
  445. data/app/views/fe/payments/create.js.erb +0 -19
  446. data/app/views/fe/payments/destroy.js.erb +0 -7
  447. data/app/views/fe/payments/edit.html.erb +0 -56
  448. data/app/views/fe/payments/error.js.erb +0 -3
  449. data/app/views/fe/payments/no_access.html.erb +0 -7
  450. data/app/views/fe/payments/staff_search.js.erb +0 -1
  451. data/app/views/fe/payments/update.html.erb +0 -24
  452. data/app/views/fe/questions/fe/_payment_question.html.erb +0 -70
  453. data/db/migrate/20140828045339_create_payments.rb +0 -13
  454. data/spec/dummy/db/test.sqlite3 +0 -0
  455. data/spec/factories/payments.rb +0 -7
  456. data/spec/models/fe/payment_question_spec.rb +0 -65
@@ -1,9 +1,11 @@
1
1
  require 'net/http'
2
+ # :nocov:
2
3
  begin
3
4
  require 'xml/libxml'
4
5
  rescue LoadError
5
6
  require 'rexml/document'
6
7
  end
8
+ # :nocov:
7
9
 
8
10
  module Fe
9
11
  module ChoiceFieldConcern
@@ -11,28 +13,50 @@ module Fe
11
13
 
12
14
  begin
13
15
  included do
14
- has_many :elements, :class_name => "Element", :foreign_key => "choice_field_id", :dependent => :nullify#, :order => :position
16
+ has_many :elements, class_name: "Element", foreign_key: "choice_field_id", dependent: :nullify#, order: :position
17
+ serialize :rating_before_label_translations, Hash
18
+ serialize :rating_after_label_translations, Hash
19
+ serialize :rating_na_label_translations, Hash
15
20
  end
16
21
  rescue ActiveSupport::Concern::MultipleIncludedBlocks
17
22
  end
18
23
 
24
+ def rating_before_label(locale = nil)
25
+ rating_before_label_translations &&
26
+ rating_before_label_translations[locale].present? ?
27
+ rating_before_label_translations[locale] : self[:rating_before_label]
28
+ end
29
+
30
+ def rating_after_label(locale = nil)
31
+ rating_after_label_translations &&
32
+ rating_after_label_translations[locale].present? ?
33
+ rating_after_label_translations[locale] : self[:rating_after_label]
34
+ end
35
+
36
+ def rating_na_label(locale = nil)
37
+ rating_na_label_translations &&
38
+ rating_na_label_translations[locale].present? ?
39
+ rating_na_label_translations[locale] : self[:rating_na_label]
40
+ end
41
+
19
42
  # Returns choices stored one per line in content field
20
- def choices
43
+ def choices(locale = nil)
21
44
  retVal = Array.new
22
45
  if ['yes-no', 'acceptance'].include?(self.style)
23
- return [["Yes",1],["No",0]]
24
- elsif !source.blank?
46
+ return [[_('Yes'),1],[_('No'),0]]
47
+ elsif source.present?
25
48
  begin
26
49
  doc = XML::Document.file(source)
27
50
  options = doc.find(text_xpath).collect { |n| n.content }
28
51
  values = doc.find(value_xpath).collect { |n| n.content }
29
52
  retVal = [options, values].transpose
30
- rescue NameError
53
+ rescue NameError, LibXML::XML::Error
31
54
  doc = REXML::Document.new Net::HTTP.get_response(URI.parse(source)).body
32
- retVal = [ doc.elements.collect(text_xpath){|c|c.text}, doc.elements.collect(value_xpath){|c|c.text} ].transpose.sort
55
+ retVal = [ doc.elements.collect(text_xpath){|c|c.text}, doc.elements.collect(value_xpath){|c|c.text} ].transpose
33
56
  end
34
- elsif !content.nil?
35
- content.split("\n").each do |opt|
57
+ elsif content.present?
58
+ choices = content(locale)
59
+ choices.split("\n").each do |opt|
36
60
  pair = opt.strip.split(";").reverse!
37
61
  pair[1] ||= pair[0]
38
62
  retVal << pair
@@ -41,19 +65,19 @@ module Fe
41
65
  return retVal
42
66
  end
43
67
 
44
- def has_answer?(choice, app)
45
- responses(app).each do |r| # loop through Answers
46
- # legacy field type choices may be int or tinyint
47
- # raise r.inspect + ' - ' + choice.inspect if id == 1137 && r != 1
48
- # r = r.to_s
49
- return true if case true
50
- when is_true(r) then is_true(choice)
51
- when is_false(r) then is_false(choice)
52
- else
53
- r.to_s == choice.to_s
54
- end
68
+ # choices can be an array, in which case any match returns true
69
+ def has_answer?(choice, answer_sheet)
70
+ if choice.is_a?(Array)
71
+ return choice.any? { |c|
72
+ has_answer?(c, answer_sheet)
73
+ }
55
74
  end
56
- false
75
+
76
+ responses(answer_sheet).any? { |r|
77
+ is_true(r) && is_true(choice) ||
78
+ is_false(r) && is_false(choice) ||
79
+ r.to_s.strip == choice.to_s.strip
80
+ }
57
81
  end
58
82
 
59
83
  # which view to render this element?
@@ -86,8 +110,8 @@ module Fe
86
110
  end
87
111
 
88
112
  # css class names for javascript-based validation
89
- def validation_class(answer_sheet)
90
- if self.required?(answer_sheet)
113
+ def validation_class(answer_sheet, page = nil)
114
+ if self.required?(answer_sheet, page)
91
115
  if self.style == 'drop-down'
92
116
  'validate-selection required'
93
117
  elsif self.style == 'rating'
@@ -102,9 +126,8 @@ module Fe
102
126
  end
103
127
  end
104
128
 
105
- def display_response(app=nil)
129
+ def display_response(app = nil)
106
130
  r = responses(app)
107
- r.reject! {|a| a.class == Answer && a.value.blank?}
108
131
  if r.blank?
109
132
  ""
110
133
  elsif self.style == 'yes-no'
@@ -117,25 +140,27 @@ module Fe
117
140
  elsif self.style == 'acceptance'
118
141
  "Accepted" # if not blank, it's accepted
119
142
  else
120
- r.compact.join(", ")
143
+ r.compact.join(', ')
121
144
  end
122
145
  end
123
146
 
124
147
  def conditional_match(answer_sheet)
125
- displayed_response = display_response(answer_sheet)
126
- (is_true(displayed_response) && is_true(conditional_answer)) ||
127
- (is_false(displayed_response) && is_false(conditional_answer)) ||
128
- (displayed_response == conditional_answer)
148
+ has_answer?(conditional_answers, answer_sheet) ||
149
+ (responses(answer_sheet).empty? && conditional_answers.empty?)
150
+ end
151
+
152
+ def is_response_false(answer_sheet)
153
+ is_false(display_response(answer_sheet))
129
154
  end
130
155
 
131
156
  protected
132
157
  def is_true(val)
133
- [1,'1',true,'true','Yes','yes'].include?(val) # note: true = anything but false | nil
158
+ ['1','true','yes'].include?(val.to_s.downcase)
134
159
  end
135
160
 
136
161
  def is_false(val)
137
162
  # returns false if false (a bit odd)
138
- [0,'0',false,'false','No','no'].include?(val)
163
+ ['0','false','no'].include?(val.to_s.downcase)
139
164
  end
140
165
 
141
166
  end
@@ -1,3 +1,3 @@
1
- class Fe::Address < ActiveRecord::Base
2
- belongs_to :person
1
+ class Fe::Address < ApplicationRecord
2
+ belongs_to :person, optional: true
3
3
  end
@@ -7,7 +7,7 @@
7
7
  # may want special handling for ChoiceFields to store both id/slug and text representations
8
8
 
9
9
  module Fe
10
- class Answer < ActiveRecord::Base
10
+ class Answer < ApplicationRecord
11
11
  include Fe::AnswerConcern
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  module Fe
2
- class AnswerSheet < ActiveRecord::Base
2
+ class AnswerSheet < ApplicationRecord
3
3
  self.abstract_class = true
4
4
 
5
5
  =begin
@@ -1,7 +1,7 @@
1
1
  module Fe
2
- class AnswerSheetQuestionSheet < ActiveRecord::Base
2
+ class AnswerSheetQuestionSheet < ApplicationRecord
3
3
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
4
4
  belongs_to :answer_sheet, class_name: Fe.answer_sheet_class
5
- belongs_to :question_sheet, class_name: 'Fe::QuestionSheet'
5
+ belongs_to :question_sheet, optional: true, class_name: 'Fe::QuestionSheet'
6
6
  end
7
7
  end
@@ -2,14 +2,19 @@ require 'aasm'
2
2
 
3
3
  # a visitor applies to a sleeve (application)
4
4
  class Fe::Application < Fe::AnswerSheet
5
+
5
6
  self.table_name = "#{Fe.table_name_prefix}applications"
6
7
 
7
- belongs_to :applicant, :class_name => "Person", :foreign_key => "applicant_id"
8
- has_many :references, :class_name => 'ReferenceSheet', :foreign_key => :applicant_answer_sheet_id, :dependent => :destroy
9
- has_one :answer_sheet_question_sheet, :foreign_key => "answer_sheet_id"
10
- has_many :answer_sheet_question_sheets, :foreign_key => 'answer_sheet_id'
11
- has_many :question_sheets, :through => :answer_sheet_question_sheets
12
-
8
+ belongs_to :applicant, optional: true, foreign_key: 'person_id', class_name: "Person"
9
+ has_many :references, class_name: 'ReferenceSheet', foreign_key: :applicant_answer_sheet_id, dependent: :destroy
10
+ has_one :answer_sheet_question_sheet, foreign_key: "answer_sheet_id"
11
+ has_many :answer_sheet_question_sheets, foreign_key: 'answer_sheet_id'
12
+ has_many :question_sheets, through: :answer_sheet_question_sheets
13
+
14
+ has_paper_trail on: [], ignore: [:updated_at]
15
+
16
+ alias_method :all_references, :references
17
+
13
18
  # This will be overridden by the state machine defined in the enclosing app
14
19
  def completed?
15
20
  raise "completed? should be implemented by the extending class"
@@ -34,7 +39,7 @@ class Fe::Application < Fe::AnswerSheet
34
39
  end
35
40
  return Fe::ReferenceSheet.new()
36
41
  end
37
-
42
+
38
43
  def answer_sheets
39
44
  a_sheets = [self]
40
45
  references.each do |r|
@@ -42,7 +47,7 @@ class Fe::Application < Fe::AnswerSheet
42
47
  end
43
48
  a_sheets
44
49
  end
45
-
50
+
46
51
  def reference_answer_sheets
47
52
  r_sheets = Array.new()
48
53
  references.each do |r|
@@ -50,9 +55,9 @@ class Fe::Application < Fe::AnswerSheet
50
55
  end
51
56
  r_sheets
52
57
  end
53
-
58
+
54
59
  def has_references?
55
60
  self.references.size > 0
56
61
  end
57
-
62
+
58
63
  end
@@ -3,17 +3,17 @@
3
3
  # i.e. "answer == 'yes'"
4
4
  # a question can have more than one answer (choose many) in which case ANY answer will do (find)
5
5
  module Fe
6
- class Condition < ActiveRecord::Base
6
+ class Condition < ApplicationRecord
7
7
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
8
8
 
9
9
  belongs_to :question_sheet
10
10
 
11
11
  belongs_to :trigger,
12
- :class_name => "Question",
13
- :foreign_key => "trigger_id"
12
+ class_name: "Question",
13
+ foreign_key: "trigger_id"
14
14
 
15
15
  validates_presence_of :expression
16
- validates_length_of :expression, :maximum => 255, :allow_nil => true
16
+ validates_length_of :expression, maximum: 255, allow_nil: true
17
17
 
18
18
  # evaluate triggering element against expression and return match|nil
19
19
  def evaluate?
@@ -3,7 +3,7 @@
3
3
  module Fe
4
4
  class DateField < Question
5
5
 
6
- def validation_class(answer_sheet = nil)
6
+ def validation_class(answer_sheet = nil, page = nil)
7
7
  if self.style == 'mmyy'
8
8
  'validate-selection ' + super
9
9
  else
@@ -35,7 +35,7 @@ module Fe
35
35
 
36
36
  def format_date_response(answer_sheet = nil)
37
37
  r = response(answer_sheet)
38
- r = r.strftime("%m/%d/%Y") unless r.blank?
38
+ r = r.strftime("%Y-%m-%d") unless r.blank?
39
39
  r
40
40
  end
41
41
 
@@ -1,46 +1,79 @@
1
1
  # Element represents a section, question or content element on the question sheet
2
2
  module Fe
3
- class Element < ActiveRecord::Base
3
+ class Element < ApplicationRecord
4
4
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
5
5
 
6
+ attr_accessor :old_id
7
+
6
8
  belongs_to :question_grid,
7
- :class_name => "Fe::QuestionGrid",
8
- :foreign_key => "question_grid_id"
9
+ optional: true, class_name: "Fe::QuestionGrid"
10
+
11
+ belongs_to :question_grid_with_total,
12
+ optional: true, class_name: "Fe::QuestionGridWithTotal",
13
+ foreign_key: "question_grid_id"
9
14
 
10
15
  belongs_to :choice_field,
11
- :class_name => "Fe::ChoiceField",
12
- :foreign_key => "choice_field_id"
16
+ optional: true, class_name: "Fe::ChoiceField"
17
+
18
+ has_many :choice_field_children, foreign_key: 'choice_field_id',
19
+ class_name: 'Fe::Element'
13
20
 
14
- belongs_to :question_sheet, :foreign_key => "related_question_sheet_id"
21
+ belongs_to :question_sheet, optional: true, foreign_key: "related_question_sheet_id"
15
22
 
16
- belongs_to :conditional, :polymorphic => true
23
+ belongs_to :conditional, optional: true, polymorphic: true
17
24
 
18
25
  self.inheritance_column = :kind
19
26
 
20
- has_many :page_elements, :dependent => :destroy
21
- has_many :pages, :through => :page_elements
27
+ has_many :page_elements, dependent: :destroy
28
+ has_many :pages, through: :page_elements
22
29
 
23
- scope :active, -> { select("distinct(#{Fe::Element.table_name}.id), #{Fe::Element.table_name}.*").where(Fe::QuestionSheet.table_name + '.archived' => false).joins({:pages => :question_sheet}) }
30
+ scope :active, -> { select("distinct(#{Fe::Element.table_name}.id), #{Fe::Element.table_name}.*").where(Fe::QuestionSheet.table_name + '.archived' => false).joins({pages: :question_sheet}) }
31
+ scope :questions, -> { where("kind NOT IN('Fe::Paragraph', 'Fe::Section', 'Fe::QuestionGrid', 'Fe::QuestionGridWithTotal')") }
32
+ scope :shared, -> { where(share: true) }
33
+ scope :grid_kinds, -> { where(kind: ['Fe::QuestionGrid', 'Fe::QuestionGridWithTotal']) }
34
+ scope :reference_kinds, -> { where(kind: 'Fe::ReferenceQuestion') }
24
35
 
25
36
  validates_presence_of :kind
26
37
  validates_presence_of :style
27
- # validates_presence_of :label, :style, :on => :update
38
+ # validates_presence_of :label, :style, on: :update
28
39
 
29
- validates_length_of :kind, :maximum => 40, :allow_nil => true
30
- validates_length_of :style, :maximum => 40, :allow_nil => true
31
- # validates_length_of :label, :maximum => 255, :allow_nil => true
40
+ validates_length_of :kind, maximum: 40, allow_nil: true
41
+ validates_length_of :style, maximum: 40, allow_nil: true
42
+ # validates_length_of :label, maximum: 255, allow_nil: true
32
43
 
33
- before_validation :set_defaults, :on => :create
44
+ before_validation :set_defaults, on: :create
34
45
  before_save :set_conditional_element
46
+ after_save :update_page_all_element_ids
35
47
  after_save :update_any_previous_conditional_elements
36
48
 
49
+ serialize :label_translations, Hash
50
+ serialize :tip_translations, Hash
51
+ serialize :content_translations, Hash
52
+
37
53
  # HUMANIZED_ATTRIBUTES = {
38
- # :slug => "Variable"
54
+ # slug: "Variable"
39
55
  # }changed.include?('address1')
40
56
  #
41
57
  # def self.human_attrib_name(attr)
42
58
  # HUMANIZED_ATTRIBUTES[attr.to_sym] || super
43
59
  # end
60
+ def label(locale = nil)
61
+ label_translations[locale].present? ? label_translations[locale] : self[:label]
62
+ end
63
+
64
+ def content(locale = nil)
65
+ content_translations[locale].present? ? content_translations[locale] : self[:content]
66
+ end
67
+
68
+ def tooltip(locale = nil)
69
+ tip_translations[locale].present? ? tip_translations[locale] : self[:tooltip]
70
+ end
71
+
72
+ # returns all pages this element is on, whether that be directly, through a grid, or as a choice field conditional option
73
+ def pages_on
74
+ all_pages = pages.reload + [question_grid, question_grid_with_total, choice_field].compact.collect(&:pages_on)
75
+ all_pages.flatten.uniq
76
+ end
44
77
 
45
78
  def has_response?(answer_sheet = nil)
46
79
  false
@@ -52,7 +85,7 @@ module Fe
52
85
  unless eval("answer_sheet." + self.object_name + ".nil?")
53
86
  klass = eval("answer_sheet." + self.object_name + ".class")
54
87
  column = klass.columns_hash[self.attribute_name]
55
- column.limit
88
+ return column.limit
56
89
  end
57
90
  rescue
58
91
  nil
@@ -61,24 +94,85 @@ module Fe
61
94
  end
62
95
 
63
96
  # assume each element is on a question sheet only once to make things simpler. if not, just take the first one
64
- def previous_element(question_sheet)
65
- page_element = page_elements.joins(page: :question_sheet).where("#{Fe::QuestionSheet.table_name}.id" => question_sheet.id).first
66
- #binding.pry if page_element.nil?
67
- return unless page_element
68
- index = page_element.page.elements.index(self)
69
- if index > 0 && prev_el = page_element.page.elements[index-1]
70
- return prev_el
97
+ # NOTE: getting the previous_element isn't an expensive operation any more because of the all_elements_id cache
98
+ def previous_element(question_sheet, page = nil)
99
+ return false unless question_sheet
100
+ page ||= pages_on.detect{ |p| p.question_sheet == question_sheet }
101
+
102
+ index = page.all_element_ids_arr.index(self.id)
103
+ unless index
104
+ # this can happen for yesno options, since they're rendered as elements but aren't on the page or in a grid
105
+ # but just in case self is an element on the page and the element_ids got out of sync, rebuild the all_element_ids
106
+ # and try again
107
+ page.rebuild_all_element_ids
108
+ index = page.all_element_ids_arr.index(self.id)
109
+ end
110
+ if index && index > 0 && prev_el_id = page.all_element_ids_arr[index-1]
111
+ # occasionally the all_elements_ids_arr can get out of sync here, resulting in no element found
112
+ el = Fe::Element.find_by(id: prev_el_id)
113
+ unless el
114
+ page.rebuild_all_element_ids
115
+ index = page.all_element_ids_arr.index(self.id)
116
+ prev_el_id = page.all_element_ids_arr[index-1]
117
+ el = Fe::Element.find(prev_el_id) # give an error at this point if it's not found
118
+ end
119
+
120
+ return el
121
+ end
122
+ end
123
+
124
+ # return an array of all elements whose answers or visibility might affect
125
+ # the visibility of this element
126
+ def visibility_affecting_element_ids
127
+ return @visibility_affecting_element_ids if @visibility_affecting_element_ids
128
+
129
+ # the form doesn't change much so caching on the last updated element will
130
+ # provide a good balance of speed and cache invalidation
131
+ Rails.cache.fetch([self, 'element#visibility_affecting_element_ids', Fe::Element.order('updated_at desc, id desc').first]) do
132
+ elements = []
133
+
134
+ elements << question_grid if question_grid
135
+ elements << choice_field if choice_field
136
+ elements += Fe::Element.where(conditional_type: 'Fe::Element', conditional_id: id)
137
+ element_ids = elements.collect(&:id) +
138
+ elements.collect { |e| e.visibility_affecting_element_ids }.flatten
139
+ element_ids.uniq
71
140
  end
72
141
  end
73
142
 
74
- def required?(answer_sheet = nil)
75
- if answer_sheet &&
76
- self.question_grid.nil? &&
77
- (prev_el = previous_element(answer_sheet.question_sheet)) &&
78
- prev_el.is_a?(Fe::Question) &&
79
- prev_el.class != Fe::QuestionGrid &&
80
- prev_el.conditional_match(answer_sheet)
143
+ def visibility_affecting_questions
144
+ Fe::Question.where(id: visibility_affecting_element_ids)
145
+ end
146
+
147
+ def hidden_by_conditional?(answer_sheet, page)
148
+ return false unless answer_sheet.question_sheets.include?(page.question_sheet)
149
+ prev_el = previous_element(page.question_sheet, page)
150
+ prev_el.is_a?(Fe::Question) &&
151
+ prev_el.conditional == self &&
152
+ !prev_el.conditional_match(answer_sheet)
153
+ end
154
+
155
+ def hidden_by_choice_field?(answer_sheet)
156
+ choice_field.present? &&
157
+ choice_field.is_a?(Fe::ChoiceField) &&
158
+ choice_field.is_response_false(answer_sheet)
159
+ end
160
+
161
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
162
+ def visible?(answer_sheet = nil, page = nil)
163
+ !hidden?(answer_sheet, page)
164
+ end
81
165
 
166
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
167
+ def hidden?(answer_sheet = nil, page = nil)
168
+ page ||= pages_on.detect{ |p| answer_sheet.question_sheets.include?(p.question_sheet) }
169
+ return true if !page || page.hidden?(answer_sheet)
170
+ return page.all_hidden_elements(answer_sheet).include?(self)
171
+ end
172
+
173
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
174
+ def required?(answer_sheet = nil, page = nil)
175
+ if answer_sheet && hidden?(answer_sheet, page)
82
176
  return false
83
177
  else
84
178
  required == true
@@ -87,7 +181,7 @@ module Fe
87
181
 
88
182
  def position(page = nil)
89
183
  if page
90
- page_elements.where(:page_id => page.id).first.try(:position)
184
+ page_elements.where(page_id: page.id).first.try(:position)
91
185
  else
92
186
  self[:position]
93
187
  end
@@ -95,7 +189,7 @@ module Fe
95
189
 
96
190
  def set_position(position, page = nil)
97
191
  if page
98
- pe = page_elements.where(:page_id => page.id).first
192
+ pe = page_elements.where(page_id: page.id).first
99
193
  pe.update_attribute(:position, position) if pe
100
194
  else
101
195
  self[:position] = position
@@ -130,8 +224,9 @@ module Fe
130
224
  when "Fe::ChoiceField"
131
225
  new_element.choice_field_id = parent.id
132
226
  end
133
- new_element.save(:validate => false)
134
- Fe::PageElement.create(:element => new_element, :page => page) unless parent
227
+ new_element.position = parent.elements.maximum(:position).to_i + 1 if parent
228
+ new_element.save!(validate: false)
229
+ Fe::PageElement.create(element: new_element, page: page) unless parent
135
230
 
136
231
  # duplicate children
137
232
  if respond_to?(:elements) && elements.present?
@@ -144,20 +239,29 @@ module Fe
144
239
  # include nested elements
145
240
  def all_elements
146
241
  if respond_to?(:elements)
147
- (elements + elements.collect(&:all_elements)).flatten
242
+ elements.reload
243
+ #(elements + elements.collect(&:all_elements)).flatten
244
+ elements.collect{ |el|
245
+ [el, el.all_elements]
246
+ }.flatten
148
247
  else
149
248
  []
150
249
  end
151
250
  end
152
251
 
153
252
  def reuseable?
253
+ return false if Fe.never_reuse_elements
154
254
  (self.is_a?(Fe::Question) || self.is_a?(Fe::QuestionGrid) || self.is_a?(Fe::QuestionGridWithTotal))
155
255
  end
156
256
 
257
+ def conditional_answers
258
+ conditional_answer.split(';').collect(&:strip)
259
+ end
260
+
157
261
  def conditional_match(answer_sheet)
158
262
  displayed_response = display_response(answer_sheet)
159
263
  return false unless displayed_response && conditional_answer
160
- (displayed_response.split(',') & conditional_answer.split(',')).length > 0
264
+ conditional_answers.include?(displayed_response)
161
265
  end
162
266
 
163
267
  def self.max_label_length
@@ -167,27 +271,79 @@ module Fe
167
271
  def set_conditional_element
168
272
  case conditional_type
169
273
  when "Fe::Element"
170
- pages.reload.each do |page|
171
- index = page.elements.index(self)
172
- if index && index < page.elements.length - 1
173
- self.conditional_id = page.elements[index+1].id
274
+ pages_on.each do |page|
275
+
276
+ if index = page.all_element_ids_arr.index(self.id)
277
+ self.conditional_id = page.all_element_ids_arr[index+1]
278
+ else
279
+ self.conditional_id = nil
174
280
  end
175
281
  end
282
+ when ""
283
+ # keep conditional_type nil instead of empty to be consistent
284
+ self.conditional_type = nil
176
285
  end
177
286
  end
178
287
 
179
288
  def update_any_previous_conditional_elements
180
- pages.reload.each do |page|
181
- index = page.elements.index(self)
289
+ pages_on.each do |page|
290
+ index = page.all_element_ids_arr.index(self.id)
182
291
  if index && index > 0
183
- prev_el = page.elements[index-1]
292
+ prev_el = Fe::Element.find(page.all_element_ids_arr[index-1])
184
293
  if prev_el.conditional_type == "Fe::Element"
185
- prev_el.update_attribute(:conditional_id, id)
294
+ prev_el.update_column(:conditional_id, id)
186
295
  end
187
296
  end
188
297
  end
189
298
  end
190
299
 
300
+ def update_page_all_element_ids
301
+ [question_grid, question_grid_with_total, choice_field].compact.each do |field|
302
+ field.update_page_all_element_ids
303
+ end
304
+
305
+ pages.reload.each do |p| p.rebuild_all_element_ids end
306
+ end
307
+
308
+ # matches in an AND method; if requested we can add a second filter method later
309
+ # to match on an OR basis
310
+ def matches_filter(filter)
311
+ filter.all? { |method| self.send(method) }
312
+ end
313
+
314
+ def css_classes
315
+ css_class.to_s.split(' ').collect(&:strip)
316
+ end
317
+
318
+ def self.create_from_import(element_data, page, question_sheet)
319
+ element_data[:old_id] = element_data.delete('id')
320
+ children = element_data.delete(:children)
321
+ element = element_data['kind'].constantize.create!(element_data)
322
+ question_sheet.element_id_mappings[element.old_id] = element.id
323
+ children.each do |child|
324
+ byebug unless child.class == Hash
325
+ child_element = create_from_import(child, page, question_sheet)
326
+ if child['choice_field_id'].present?
327
+ child_element.choice_field_id = element.id
328
+ end
329
+ byebug if child_element.label == 'Your Name:'
330
+ if child['question_grid_id'].present?
331
+ child_element.question_grid_id = element.id
332
+ end
333
+ child_element.save!
334
+ end
335
+ element
336
+ end
337
+
338
+ def export_hash
339
+ children = choice_field_children.collect(&:export_hash)
340
+ self.attributes.to_hash.merge(children: children)
341
+ end
342
+
343
+ def export_to_yaml
344
+ export_hash.to_yaml
345
+ end
346
+
191
347
  protected
192
348
 
193
349
  def set_defaults
@@ -1,7 +1,7 @@
1
1
  require 'validates_email_format_of'
2
- class Fe::EmailAddress < ActiveRecord::Base
2
+ class Fe::EmailAddress < ApplicationRecord
3
3
  belongs_to :person
4
- validates :email, :email_format => { :message => "doesn't look right." }
4
+ validates :email, email_format: { message: "doesn't look right." }
5
5
 
6
6
  self.table_name = "email_addresses"
7
7
  end
@@ -1,5 +1,5 @@
1
1
  module Fe
2
- class EmailTemplate < ActiveRecord::Base
2
+ class EmailTemplate < ApplicationRecord
3
3
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
4
4
 
5
5
  validates_presence_of :name