fe 1.0.0 → 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 (402) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -4
  3. data/app/assets/config/fe/manifest.js +3 -0
  4. data/app/assets/config/manifest.js +3 -0
  5. data/app/assets/javascripts/application.js.erb +1 -1
  6. data/app/assets/javascripts/fe/admin.js +0 -1
  7. data/app/assets/javascripts/fe/fe.admin.js +2 -6
  8. data/app/assets/javascripts/fe/{fe.common.js → fe.common.js.erb} +14 -7
  9. data/app/assets/javascripts/fe/fe.public.js +1 -2
  10. data/app/assets/javascripts/fe/{fe.public.nojquery.js → fe.public.nojquery.js.erb} +152 -76
  11. data/app/assets/javascripts/fe/jquery.html5_upload.js +258 -0
  12. data/app/assets/stylesheets/fe/fe.screen.css.scss.erb +22 -1
  13. data/app/assets/stylesheets/fe/validation.css +5 -1
  14. data/app/controllers/{fe/concerns → concerns/fe}/admin/question_sheets_controller_concern.rb +15 -16
  15. data/app/controllers/concerns/fe/answer_pages_controller_concern.rb +131 -0
  16. data/app/controllers/{fe/concerns → concerns/fe}/answer_sheets_controller_concern.rb +14 -6
  17. data/app/controllers/{fe/concerns → concerns/fe}/application_controller_concern.rb +3 -3
  18. data/app/controllers/fe/admin/elements_controller.rb +46 -24
  19. data/app/controllers/fe/admin/email_templates_controller.rb +5 -5
  20. data/app/controllers/fe/admin/question_pages_controller.rb +8 -8
  21. data/app/controllers/fe/reference_pages_controller.rb +11 -11
  22. data/app/controllers/fe/reference_sheets_controller.rb +4 -4
  23. data/app/controllers/fe/references_controller.rb +19 -19
  24. data/app/controllers/fe/submit_pages_controller.rb +5 -5
  25. data/app/helpers/fe/answer_pages_helper.rb +1 -1
  26. data/app/helpers/fe/application_helper.rb +3 -3
  27. data/app/jobs/fe/update_reference_sheet_visibility_job.rb +11 -0
  28. data/app/mailers/fe/notifier.rb +4 -4
  29. data/app/models/answer_sheet.rb +1 -1
  30. data/app/models/application_record.rb +3 -0
  31. data/app/models/{fe/concerns → concerns/fe}/answer_concern.rb +2 -2
  32. data/app/models/{fe/concerns → concerns/fe}/answer_pages_presenter_concern.rb +6 -5
  33. data/app/models/concerns/fe/answer_sheet_concern.rb +125 -0
  34. data/app/models/{fe/concerns → concerns/fe}/choice_field_concern.rb +41 -23
  35. data/app/models/fe/address.rb +2 -2
  36. data/app/models/fe/answer.rb +1 -1
  37. data/app/models/fe/answer_sheet.rb +1 -1
  38. data/app/models/fe/answer_sheet_question_sheet.rb +2 -2
  39. data/app/models/fe/application.rb +14 -10
  40. data/app/models/fe/condition.rb +4 -4
  41. data/app/models/fe/date_field.rb +1 -1
  42. data/app/models/fe/element.rb +126 -44
  43. data/app/models/fe/email_address.rb +2 -2
  44. data/app/models/fe/email_template.rb +1 -1
  45. data/app/models/fe/page.rb +94 -19
  46. data/app/models/fe/page_element.rb +2 -2
  47. data/app/models/fe/paragraph.rb +1 -1
  48. data/app/models/fe/person.rb +5 -5
  49. data/app/models/fe/phone_number.rb +1 -1
  50. data/app/models/fe/question.rb +62 -38
  51. data/app/models/fe/question_grid.rb +15 -3
  52. data/app/models/fe/question_set.rb +53 -13
  53. data/app/models/fe/question_sheet.rb +49 -16
  54. data/app/models/fe/reference_question.rb +9 -3
  55. data/app/models/fe/reference_sheet.rb +102 -47
  56. data/app/models/fe/state_chooser.rb +2 -2
  57. data/app/models/fe/text_field.rb +2 -2
  58. data/app/models/fe/user.rb +1 -1
  59. data/app/models/staff.rb +6 -6
  60. data/app/views/fe/admin/elements/create.js.erb +3 -3
  61. data/app/views/fe/admin/elements/destroy.js.erb +1 -1
  62. data/app/views/fe/admin/elements/drop.js.erb +1 -1
  63. data/app/views/fe/admin/elements/duplicate.js.erb +1 -1
  64. data/app/views/fe/admin/elements/edit.js.erb +1 -1
  65. data/app/views/fe/admin/elements/error.js.erb +1 -1
  66. data/app/views/fe/admin/elements/new.js.erb +13 -6
  67. data/app/views/fe/admin/elements/update.js.erb +1 -1
  68. data/app/views/fe/admin/email_templates/_form.html.erb +3 -3
  69. data/app/views/fe/admin/email_templates/edit.html.erb +3 -3
  70. data/app/views/fe/admin/email_templates/index.html.erb +3 -3
  71. data/app/views/fe/admin/email_templates/new.html.erb +3 -3
  72. data/app/views/fe/admin/panels/_advanced_options.html.erb +8 -8
  73. data/app/views/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  74. data/app/views/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  75. data/app/views/fe/admin/panels/_common_fields.html.erb +2 -17
  76. data/app/views/fe/admin/panels/_condition.html.erb +1 -1
  77. data/app/views/fe/admin/panels/_insert.html.erb +25 -25
  78. data/app/views/fe/admin/panels/_nav_controls.html.erb +4 -4
  79. data/app/views/fe/admin/panels/_page.html.erb +1 -1
  80. data/app/views/fe/admin/panels/_pages_list.html.erb +3 -3
  81. data/app/views/fe/admin/panels/_prop_attachment_field.html.erb +2 -2
  82. data/app/views/fe/admin/panels/_prop_choice_field.html.erb +36 -17
  83. data/app/views/fe/admin/panels/_prop_date_field.html.erb +1 -1
  84. data/app/views/fe/admin/panels/_prop_element.html.erb +5 -5
  85. data/app/views/fe/admin/panels/_prop_page.html.erb +3 -3
  86. data/app/views/fe/admin/panels/_prop_paragraph.html.erb +8 -8
  87. data/app/views/fe/admin/panels/_prop_question_grid.html.erb +7 -2
  88. data/app/views/fe/admin/panels/_prop_question_grid_with_total.html.erb +2 -2
  89. data/app/views/fe/admin/panels/_prop_reference_question.html.erb +3 -3
  90. data/app/views/fe/admin/panels/_prop_section.html.erb +1 -1
  91. data/app/views/fe/admin/panels/_prop_sheet.html.erb +3 -3
  92. data/app/views/fe/admin/panels/_prop_text_field.html.erb +12 -12
  93. data/app/views/fe/admin/question_pages/_element.html.erb +7 -7
  94. data/app/views/fe/admin/question_pages/_element_show.html.erb +2 -2
  95. data/app/views/fe/admin/question_pages/_question_page.html.erb +4 -4
  96. data/app/views/fe/admin/question_pages/create.js.erb +3 -3
  97. data/app/views/fe/admin/question_pages/destroy.js.erb +3 -3
  98. data/app/views/fe/admin/question_pages/edit.js.erb +1 -1
  99. data/app/views/fe/admin/question_pages/error.js.erb +1 -1
  100. data/app/views/fe/admin/question_pages/show.js.erb +2 -2
  101. data/app/views/fe/admin/question_pages/show_panel.js.erb +1 -1
  102. data/app/views/fe/admin/question_pages/update.js.erb +1 -1
  103. data/app/views/fe/admin/question_sheets/edit.js.erb +1 -1
  104. data/app/views/fe/admin/question_sheets/error.js.erb +1 -1
  105. data/app/views/fe/admin/question_sheets/index.html.erb +8 -8
  106. data/app/views/fe/admin/question_sheets/new.html.erb +3 -3
  107. data/app/views/fe/admin/question_sheets/show.html.erb +5 -5
  108. data/app/views/fe/admin/question_sheets/update.js.erb +1 -1
  109. data/app/views/fe/answer_pages/_answer_page.html.erb +13 -14
  110. data/app/views/fe/answer_pages/_element.html.erb +22 -2
  111. data/app/views/fe/answer_pages/_page_name.html.erb +1 -1
  112. data/app/views/fe/answer_pages/show.html.erb +39 -0
  113. data/app/views/fe/answer_pages/update.js.erb +10 -2
  114. data/app/views/fe/answer_sheets/_answer_sheet.html.erb +4 -4
  115. data/app/views/fe/answer_sheets/_element.html.erb +9 -7
  116. data/app/views/fe/answer_sheets/_incomplete.html.erb +1 -1
  117. data/app/views/fe/answer_sheets/_page_link.html.erb +9 -7
  118. data/app/views/fe/answer_sheets/_pages_list.html.erb +3 -3
  119. data/app/views/fe/answer_sheets/_submit_to.html.erb +1 -1
  120. data/app/views/fe/answer_sheets/_title.html.erb +1 -1
  121. data/app/views/fe/answer_sheets/edit.html.erb +18 -18
  122. data/app/views/fe/answer_sheets/incomplete.js.erb +7 -4
  123. data/app/views/fe/answer_sheets/index.html.erb +1 -1
  124. data/app/views/fe/answer_sheets/show.html.erb +1 -1
  125. data/app/views/fe/applications/_logout.html.erb +1 -1
  126. data/app/views/fe/questions/fe/_acceptance.html.erb +10 -9
  127. data/app/views/fe/questions/fe/_attachment_field.html.erb +129 -10
  128. data/app/views/fe/questions/fe/_checkbox_field.html.erb +18 -16
  129. data/app/views/fe/questions/fe/_country.html.erb +6 -6
  130. data/app/views/fe/questions/fe/_date_field.html.erb +5 -4
  131. data/app/views/fe/questions/fe/_date_field_mmyy.html.erb +8 -8
  132. data/app/views/fe/questions/fe/_drop_down_field.html.erb +5 -5
  133. data/app/views/fe/questions/fe/_question_grid.html.erb +10 -10
  134. data/app/views/fe/questions/fe/_question_grid_with_total.html.erb +8 -8
  135. data/app/views/fe/questions/fe/_questions.html.erb +11 -5
  136. data/app/views/fe/questions/fe/_radio_button_field.html.erb +35 -19
  137. data/app/views/fe/questions/fe/_rating.html.erb +56 -18
  138. data/app/views/fe/questions/fe/_reference_discipler.html.erb +1 -1
  139. data/app/views/fe/questions/fe/_reference_friend.html.erb +1 -1
  140. data/app/views/fe/questions/fe/_reference_parent.html.erb +1 -1
  141. data/app/views/fe/questions/fe/_reference_peer.html.erb +1 -1
  142. data/app/views/fe/questions/fe/_reference_question.html.erb +40 -24
  143. data/app/views/fe/questions/fe/_reference_roommate.html.erb +1 -1
  144. data/app/views/fe/questions/fe/_reference_spiritual.html.erb +1 -1
  145. data/app/views/fe/questions/fe/_reference_staff.html.erb +1 -1
  146. data/app/views/fe/questions/fe/_state_chooser.html.erb +6 -6
  147. data/app/views/fe/questions/fe/_text_area_field.html.erb +14 -10
  148. data/app/views/fe/questions/fe/_text_field.html.erb +7 -6
  149. data/app/views/fe/questions/fe/_yes_no.html.erb +8 -8
  150. data/app/views/fe/questions/fe/_yes_no_field.erb +11 -8
  151. data/app/views/fe/reference_pages/_reference.html.erb +12 -12
  152. data/app/views/fe/reference_pages/edit.html.erb +4 -4
  153. data/app/views/fe/reference_sheets/done.html.erb +2 -2
  154. data/app/views/fe/reference_sheets/not_found.html.erb +3 -3
  155. data/app/views/fe/references/edit.html.erb +5 -5
  156. data/app/views/fe/references/show.html.erb +3 -3
  157. data/app/views/fe/references/submit.js.erb +3 -3
  158. data/app/views/fe/submit_pages/_thankyou.html.erb +1 -1
  159. data/app/views/fe/submit_pages/edit.html.erb +9 -9
  160. data/app/views/fe/submit_pages/error.js.erb +1 -1
  161. data/app/views/fe/submit_pages/submit.js.erb +2 -2
  162. data/app/views/layouts/fe/_error_messages_for.html.erb +1 -1
  163. data/app/views/layouts/fe/application.html.erb +3 -4
  164. data/app/views/layouts/fe/fe_admin.html.erb +30 -0
  165. data/app/views.current/fe/admin/elements/_errors.html.erb +11 -0
  166. data/app/views.current/fe/admin/elements/create.js.erb +12 -0
  167. data/app/views.current/fe/admin/elements/destroy.js.erb +4 -0
  168. data/app/views.current/fe/admin/elements/drop.js.erb +3 -0
  169. data/app/views.current/fe/admin/elements/duplicate.js.erb +3 -0
  170. data/app/views.current/fe/admin/elements/edit.js.erb +4 -0
  171. data/app/views.current/fe/admin/elements/error.js.erb +4 -0
  172. data/app/views.current/fe/admin/elements/new.js.erb +17 -0
  173. data/app/views.current/fe/admin/elements/update.js.erb +9 -0
  174. data/app/views.current/fe/admin/email_templates/_form.html.erb +8 -0
  175. data/app/views.current/fe/admin/email_templates/edit.html.erb +13 -0
  176. data/app/views.current/fe/admin/email_templates/index.html.erb +20 -0
  177. data/app/views.current/fe/admin/email_templates/new.html.erb +11 -0
  178. data/app/views.current/fe/admin/panels/_advanced_options.html.erb +49 -0
  179. data/app/views.current/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  180. data/app/views.current/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  181. data/app/views.current/fe/admin/panels/_common_fields.html.erb +23 -0
  182. data/app/views.current/fe/admin/panels/_condition.html.erb +6 -0
  183. data/app/views.current/fe/admin/panels/_insert.html.erb +39 -0
  184. data/app/views.current/fe/admin/panels/_nav_controls.html.erb +6 -0
  185. data/app/views.current/fe/admin/panels/_page.html.erb +3 -0
  186. data/app/views.current/fe/admin/panels/_pages_list.html.erb +16 -0
  187. data/app/views.current/fe/admin/panels/_prop_attachment_field.html.erb +2 -0
  188. data/app/views.current/fe/admin/panels/_prop_choice_field.html.erb +74 -0
  189. data/app/views.current/fe/admin/panels/_prop_date_field.html.erb +7 -0
  190. data/app/views.current/fe/admin/panels/_prop_element.html.erb +23 -0
  191. data/app/views.current/fe/admin/panels/_prop_page.html.erb +26 -0
  192. data/app/views.current/fe/admin/panels/_prop_paragraph.html.erb +46 -0
  193. data/app/views.current/fe/admin/panels/_prop_question_grid.html.erb +28 -0
  194. data/app/views.current/fe/admin/panels/_prop_question_grid_with_total.html.erb +14 -0
  195. data/app/views.current/fe/admin/panels/_prop_reference_question.html.erb +12 -0
  196. data/app/views.current/fe/admin/panels/_prop_section.html.erb +8 -0
  197. data/app/views.current/fe/admin/panels/_prop_sheet.html.erb +20 -0
  198. data/app/views.current/fe/admin/panels/_prop_text_field.html.erb +20 -0
  199. data/app/views.current/fe/admin/question_pages/_element.html.erb +28 -0
  200. data/app/views.current/fe/admin/question_pages/_element_show.html.erb +10 -0
  201. data/app/views.current/fe/admin/question_pages/_errors.html.erb +10 -0
  202. data/app/views.current/fe/admin/question_pages/_question_page.html.erb +13 -0
  203. data/app/views.current/fe/admin/question_pages/create.js.erb +11 -0
  204. data/app/views.current/fe/admin/question_pages/destroy.js.erb +5 -0
  205. data/app/views.current/fe/admin/question_pages/edit.js.erb +3 -0
  206. data/app/views.current/fe/admin/question_pages/error.js.erb +4 -0
  207. data/app/views.current/fe/admin/question_pages/show.js.erb +9 -0
  208. data/app/views.current/fe/admin/question_pages/show_panel.js.erb +3 -0
  209. data/app/views.current/fe/admin/question_pages/update.js.erb +2 -0
  210. data/app/views.current/fe/admin/question_sheets/_errors.html.erb +11 -0
  211. data/app/views.current/fe/admin/question_sheets/edit.js.erb +3 -0
  212. data/app/views.current/fe/admin/question_sheets/error.js.erb +5 -0
  213. data/app/views.current/fe/admin/question_sheets/index.html.erb +41 -0
  214. data/app/views.current/fe/admin/question_sheets/new.html.erb +15 -0
  215. data/app/views.current/fe/admin/question_sheets/show.html.erb +27 -0
  216. data/app/views.current/fe/admin/question_sheets/update.js.erb +2 -0
  217. data/app/views.current/fe/answer_pages/_answer_page.html.erb +53 -0
  218. data/app/views.current/fe/answer_pages/_element.html.erb +32 -0
  219. data/app/views.current/fe/answer_pages/_page_name.html.erb +1 -0
  220. data/app/views.current/fe/answer_pages/show.html.erb +39 -0
  221. data/app/views.current/fe/answer_pages/update.js.erb +13 -0
  222. data/app/views.current/fe/answer_sheets/_answer_sheet.html.erb +26 -0
  223. data/app/views.current/fe/answer_sheets/_element.html.erb +74 -0
  224. data/app/views.current/fe/answer_sheets/_incomplete.html.erb +10 -0
  225. data/app/views.current/fe/answer_sheets/_page_link.html.erb +9 -0
  226. data/app/views.current/fe/answer_sheets/_pages_list.html.erb +11 -0
  227. data/app/views.current/fe/answer_sheets/_submit_to.html.erb +1 -0
  228. data/app/views.current/fe/answer_sheets/_title.html.erb +1 -0
  229. data/app/views.current/fe/answer_sheets/edit.html.erb +66 -0
  230. data/app/views.current/fe/answer_sheets/incomplete.js.erb +11 -0
  231. data/app/views.current/fe/answer_sheets/index.html.erb +18 -0
  232. data/app/views.current/fe/answer_sheets/send_reference_invite.js.erb +8 -0
  233. data/app/views.current/fe/answer_sheets/show.html.erb +13 -0
  234. data/app/views.current/fe/applications/_logout.html.erb +1 -0
  235. data/app/views.current/fe/applications/show.html.erb +1 -0
  236. data/app/views.current/fe/help/builder.html +33 -0
  237. data/app/views.current/fe/help/question_grid.html +18 -0
  238. data/app/views.current/fe/questions/fe/_acceptance.html.erb +14 -0
  239. data/app/views.current/fe/questions/fe/_attachment_field.html.erb +165 -0
  240. data/app/views.current/fe/questions/fe/_checkbox_field.html.erb +53 -0
  241. data/app/views.current/fe/questions/fe/_country.html.erb +7 -0
  242. data/app/views.current/fe/questions/fe/_date_field.html.erb +7 -0
  243. data/app/views.current/fe/questions/fe/_date_field_mmyy.html.erb +9 -0
  244. data/app/views.current/fe/questions/fe/_drop_down_field.html.erb +8 -0
  245. data/app/views.current/fe/questions/fe/_paragraph.html.erb +1 -0
  246. data/app/views.current/fe/questions/fe/_question_grid.html.erb +70 -0
  247. data/app/views.current/fe/questions/fe/_question_grid_with_total.html.erb +64 -0
  248. data/app/views.current/fe/questions/fe/_questions.html.erb +15 -0
  249. data/app/views.current/fe/questions/fe/_radio_button_field.html.erb +60 -0
  250. data/app/views.current/fe/questions/fe/_rating.html.erb +64 -0
  251. data/app/views.current/fe/questions/fe/_reference_discipler.html.erb +1 -0
  252. data/app/views.current/fe/questions/fe/_reference_friend.html.erb +1 -0
  253. data/app/views.current/fe/questions/fe/_reference_parent.html.erb +1 -0
  254. data/app/views.current/fe/questions/fe/_reference_peer.html.erb +1 -0
  255. data/app/views.current/fe/questions/fe/_reference_question.html.erb +61 -0
  256. data/app/views.current/fe/questions/fe/_reference_roommate.html.erb +1 -0
  257. data/app/views.current/fe/questions/fe/_reference_spiritual.html.erb +1 -0
  258. data/app/views.current/fe/questions/fe/_reference_staff.html.erb +1 -0
  259. data/app/views.current/fe/questions/fe/_section.html.erb +1 -0
  260. data/app/views.current/fe/questions/fe/_state_chooser.html.erb +7 -0
  261. data/app/views.current/fe/questions/fe/_text_area_field.html.erb +17 -0
  262. data/app/views.current/fe/questions/fe/_text_field.html.erb +9 -0
  263. data/app/views.current/fe/questions/fe/_yes_no.html.erb +17 -0
  264. data/app/views.current/fe/questions/fe/_yes_no_field.erb +20 -0
  265. data/app/views.current/fe/reference_pages/_reference.html.erb +31 -0
  266. data/app/views.current/fe/reference_pages/edit.html.erb +24 -0
  267. data/app/views.current/fe/reference_sheets/done.html.erb +2 -0
  268. data/app/views.current/fe/reference_sheets/not_found.html.erb +5 -0
  269. data/app/views.current/fe/reference_sheets/submitted.js.erb +1 -0
  270. data/app/views.current/fe/references/edit.html.erb +8 -0
  271. data/app/views.current/fe/references/send_invite.js.erb +7 -0
  272. data/app/views.current/fe/references/show.html.erb +18 -0
  273. data/app/views.current/fe/references/submit.js.erb +3 -0
  274. data/app/views.current/fe/submit_pages/_errors.html.erb +1 -0
  275. data/app/views.current/fe/submit_pages/_thankyou.html.erb +2 -0
  276. data/app/views.current/fe/submit_pages/edit.html.erb +36 -0
  277. data/app/views.current/fe/submit_pages/error.js.erb +1 -0
  278. data/app/views.current/fe/submit_pages/submit.js.erb +3 -0
  279. data/app/views.current/layouts/fe/_error_messages_for.html.erb +7 -0
  280. data/app/views.current/layouts/fe/application.html.erb +47 -0
  281. data/app/{views/layouts/fe/fe.admin.html.erb → views.current/layouts/fe/fe_admin.html.erb} +4 -4
  282. data/config/initializers/paper_trail.rb +3 -0
  283. data/config/routes.rb +3 -1
  284. data/db/migrate/20131003041856_core.rb +23 -23
  285. data/db/migrate/20131003044250_create_reference_sheets.rb +1 -1
  286. data/db/migrate/20131003044436_add_element_and_answer_fields.rb +3 -3
  287. data/db/migrate/20131003044518_create_email_templates.rb +2 -2
  288. data/db/migrate/20131003044621_add_max_lengths.rb +1 -1
  289. data/db/migrate/20131003044714_create_join_table.rb +1 -1
  290. data/db/migrate/20131016162128_remove_question_id_from_element.rb +1 -1
  291. data/db/migrate/20140623153424_create_fe_people.rb +1 -1
  292. data/db/migrate/20140624180246_create_fe_addresses.rb +1 -1
  293. data/db/migrate/20140624182216_create_fe_phone_numbers.rb +1 -1
  294. data/db/migrate/20140625160545_create_fe_users.rb +1 -1
  295. data/db/migrate/20140808202507_add_conditional_type_to_elements.rb +1 -1
  296. data/db/migrate/20140808203609_add_conditional_answer_to_elements.rb +1 -1
  297. data/db/migrate/20141103204704_remove_short_value_column.rb +1 -1
  298. data/db/migrate/20141109154522_move_conditional_ids_used_for_choice_field_to_their_own_column.rb +1 -1
  299. data/db/migrate/20150504221439_add_all_element_ids_to_pages.rb +1 -1
  300. data/db/migrate/20150713022326_add_locale_columns.rb +1 -1
  301. data/db/migrate/20150714220730_add_locale_to_answer_sheet.rb +1 -1
  302. data/db/migrate/20150925181652_add_share_to_elements.rb +5 -0
  303. data/db/migrate/20150928085325_change_pages_all_element_ids_to_text.rb +1 -1
  304. data/db/migrate/20150930191538_add_locale_to_reference_sheets.rb +1 -1
  305. data/db/migrate/20151021181928_switch_conditional_answer_separator_to_semicolon.rb +7 -0
  306. data/db/migrate/20151021184250_add_question_sheet_id_in_refs.rb +12 -0
  307. data/db/migrate/20160201185838_add_visible_and_visibility_cache_key_to_reference_sheets.rb +6 -0
  308. data/db/migrate/20160805221415_add_rating_extra_labels.rb +10 -0
  309. data/db/migrate/20181108201746_create_versions.rb +80 -0
  310. data/db/migrate/20181218201130_increase_slug_length.rb +5 -0
  311. data/lib/fe/engine.rb +10 -10
  312. data/lib/fe/version.rb +1 -1
  313. data/lib/fe.rb +7 -1
  314. data/spec/controllers/fe/admin/elements_controller_spec.rb +30 -20
  315. data/spec/controllers/fe/admin/email_templates_controller_spec.rb +2 -2
  316. data/spec/controllers/fe/admin/question_pages_controller_spec.rb +1 -1
  317. data/spec/controllers/fe/admin/question_sheets_controller_spec.rb +4 -4
  318. data/spec/controllers/fe/answer_pages_controller_spec.rb +86 -30
  319. data/spec/controllers/fe/answer_sheets_controller_spec.rb +65 -9
  320. data/spec/dummy/app/assets/config/manifest.js +0 -0
  321. data/spec/dummy/app/models/user.rb +1 -1
  322. data/spec/dummy/app/views/layouts/application.html.erb +2 -2
  323. data/spec/dummy/config/application.rb +1 -0
  324. data/spec/dummy/config/environments/test.rb +3 -3
  325. data/spec/dummy/config/initializers/assets.rb +5 -2
  326. data/spec/dummy/config/initializers/fast_gettext.rb +1 -1
  327. data/spec/dummy/config/initializers/to_unsafe_h.rb +5 -0
  328. data/spec/dummy/config/initializers/to_unsafe_h.rb.new +5 -0
  329. data/spec/dummy/db/migrate/20141203214017_core.fe_engine.rb +23 -23
  330. data/spec/dummy/db/migrate/20141203214018_create_reference_sheets.fe_engine.rb +1 -1
  331. data/spec/dummy/db/migrate/20141203214019_add_element_and_answer_fields.fe_engine.rb +3 -3
  332. data/spec/dummy/db/migrate/20141203214020_create_email_templates.fe_engine.rb +2 -2
  333. data/spec/dummy/db/migrate/20141203214021_add_max_lengths.fe_engine.rb +1 -1
  334. data/spec/dummy/db/migrate/20141203214022_create_join_table.fe_engine.rb +1 -1
  335. data/spec/dummy/db/migrate/20141203214023_remove_question_id_from_element.fe_engine.rb +1 -1
  336. data/spec/dummy/db/migrate/20141203214024_create_fe_people.fe_engine.rb +1 -1
  337. data/spec/dummy/db/migrate/20141203214025_create_fe_addresses.fe_engine.rb +1 -1
  338. data/spec/dummy/db/migrate/20141203214027_create_fe_users.fe_engine.rb +1 -1
  339. data/spec/dummy/db/migrate/20141203214028_add_conditional_type_to_elements.fe_engine.rb +1 -1
  340. data/spec/dummy/db/migrate/20141203214029_add_conditional_answer_to_elements.fe_engine.rb +1 -1
  341. data/spec/dummy/db/migrate/20141203214030_remove_short_value_column.fe_engine.rb +1 -1
  342. data/spec/dummy/db/migrate/20141203214031_move_conditional_ids_used_for_choice_field_to_their_own_column.fe_engine.rb +1 -1
  343. data/spec/dummy/db/migrate/20150123215803_create_users.rb +1 -1
  344. data/spec/dummy/db/migrate/20150504222619_add_all_element_ids_to_pages.fe_engine.rb +1 -1
  345. data/spec/dummy/db/migrate/20150925192557_add_share_to_elements.fe_engine.rb +6 -0
  346. data/spec/dummy/db/migrate/20150930190001_create_fe_phone_numbers.fe_engine.rb +1 -1
  347. data/spec/dummy/db/migrate/20150930190002_add_locale_columns.fe_engine.rb +1 -1
  348. data/spec/dummy/db/migrate/20150930190003_add_locale_to_answer_sheet.fe_engine.rb +1 -1
  349. data/spec/dummy/db/migrate/20150930190004_change_pages_all_element_ids_to_text.fe_engine.rb +1 -1
  350. data/spec/dummy/db/migrate/20150930191756_add_locale_to_reference_sheets.fe_engine.rb +1 -1
  351. data/spec/dummy/db/migrate/20151021190027_add_question_sheet_id_in_refs.fe_engine.rb +13 -0
  352. data/spec/dummy/db/migrate/20160204164612_switch_conditional_answer_separator_to_semicolon.fe_engine.rb +8 -0
  353. data/spec/dummy/db/migrate/20160204164613_add_visible_and_visibility_cache_key_to_reference_sheets.fe_engine.rb +7 -0
  354. data/spec/dummy/db/migrate/20181108201746_create_versions.rb +80 -0
  355. data/spec/dummy/db/schema.rb +71 -79
  356. data/spec/dummy/log/test.log +101278 -419
  357. data/spec/factories/answer_sheet_question_sheets.rb +1 -1
  358. data/spec/factories/answer_sheets.rb +2 -2
  359. data/spec/factories/answers.rb +1 -1
  360. data/spec/factories/applications.rb +3 -3
  361. data/spec/factories/dummy_applications.rb +3 -3
  362. data/spec/factories/dummy_people.rb +3 -3
  363. data/spec/factories/dummy_users.rb +3 -3
  364. data/spec/factories/elements.rb +21 -21
  365. data/spec/factories/email_templates.rb +3 -3
  366. data/spec/factories/fe_addresses.rb +10 -10
  367. data/spec/factories/fe_email_addresses.rb +3 -3
  368. data/spec/factories/fe_email_templates.rb +4 -4
  369. data/spec/factories/fe_people.rb +5 -5
  370. data/spec/factories/fe_phone_numbers.rb +3 -3
  371. data/spec/factories/fe_user.rb +3 -3
  372. data/spec/factories/page.rb +1 -1
  373. data/spec/factories/page_elements.rb +1 -1
  374. data/spec/factories/paragraphs.rb +1 -1
  375. data/spec/factories/question_sheet.rb +2 -2
  376. data/spec/factories/reference_questions.rb +1 -1
  377. data/spec/factories/reference_sheets.rb +1 -1
  378. data/spec/jobs/fe/update_reference_sheet_visibility_job_spec.rb +40 -0
  379. data/spec/models/fe/answer_sheet_question_sheet_spec.rb +1 -1
  380. data/spec/models/fe/answer_spec.rb +2 -2
  381. data/spec/models/fe/application_spec.rb +94 -1
  382. data/spec/models/fe/choice_field_spec.rb +47 -1
  383. data/spec/models/fe/condition_spec.rb +2 -2
  384. data/spec/models/fe/element_spec.rb +252 -60
  385. data/spec/models/fe/email_template_spec.rb +1 -1
  386. data/spec/models/fe/page_element_spec.rb +1 -1
  387. data/spec/models/fe/page_spec.rb +57 -12
  388. data/spec/models/fe/person_spec.rb +1 -1
  389. data/spec/models/fe/question_set_spec.rb +91 -0
  390. data/spec/models/fe/question_sheet_spec.rb +11 -15
  391. data/spec/models/fe/question_spec.rb +73 -6
  392. data/spec/models/fe/reference_question_spec.rb +20 -0
  393. data/spec/models/fe/reference_sheet_spec.rb +287 -4
  394. data/spec/models/fe/text_field_spec.rb +22 -0
  395. data/spec/rails_helper.rb +79 -76
  396. metadata +195 -57
  397. data/app/assets/javascripts/fe/jquery.scrollTo-min.js +0 -7
  398. data/app/controllers/fe/concerns/answer_pages_controller_concern.rb +0 -84
  399. data/app/models/fe/concerns/answer_sheet_concern.rb +0 -85
  400. data/spec/dummy/db/migrate/20141203214026_create_create_fe_phone_numbers.fe_engine.rb +0 -17
  401. data/spec/dummy/log/development.log +0 -5
  402. /data/app/{assets/stylesheets/360front.css → views.current/fe/admin/elements/reorder.js.erb} +0 -0
@@ -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
@@ -1,8 +1,10 @@
1
1
  require 'acts_as_list'
2
2
  module Fe
3
- class Page < ActiveRecord::Base
3
+ class Page < 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_sheet
7
9
 
8
10
  has_many :page_elements, -> { order(:position) },
@@ -23,24 +25,26 @@ module Fe
23
25
  through: :page_elements,
24
26
  source: :element
25
27
 
26
- # has_many :conditions, :class_name => "Condition", :foreign_key => "toggle_page_id", # conditions associated with page as a whole
27
- # conditions: 'toggle_id is NULL', :dependent => :nullify
28
+ # has_many :conditions, class_name: "Condition", foreign_key: "toggle_page_id", # conditions associated with page as a whole
29
+ # conditions: 'toggle_id is NULL', dependent: :nullify
28
30
 
29
- acts_as_list :column => :number, :scope => :question_sheet_id
31
+ acts_as_list column: :number, scope: :question_sheet_id
30
32
 
31
- scope :visible, -> { where(:hidden => false) }
33
+ scope :visible, -> { where(hidden: false) }
32
34
 
33
35
  # callbacks
34
- before_validation :set_default_label, :on => :create # Page x
36
+ before_validation :set_default_label, on: :create # Page x
35
37
 
36
38
  # validation
37
39
  validates_presence_of :label, :number
38
- validates_length_of :label, :maximum => 100, :allow_nil => true
40
+ validates_length_of :label, maximum: 100, allow_nil: true
39
41
 
40
- # validates_uniqueness_of :number, :scope => :question_sheet_id
42
+ # validates_uniqueness_of :number, scope: :question_sheet_id
41
43
 
42
- validates_numericality_of :number, :only_integer => true
44
+ validates_numericality_of :number, only_integer: true
43
45
 
46
+ # NOTE: You may need config.active_record.yaml_column_permitted_classes = [Hash, ActiveSupport::HashWithIndifferentAccess]
47
+ # in config/application.rb or you may get Psych::DisallowedClass trying to use label_translations
44
48
  serialize :label_translations, Hash
45
49
 
46
50
  # a page is disabled if there is a condition, and that condition evaluates to false
@@ -54,6 +58,16 @@ module Fe
54
58
  # false
55
59
  # end
56
60
 
61
+ def conditionally_visible?
62
+ question_sheet&.all_elements&.where(conditional_type: 'Fe::Page', conditional_id: self)&.any?
63
+ end
64
+
65
+ # any page that's conditionally visible should not use cache, there are race conditions otherwise
66
+ # that happen when the conditional value is set and the now visible page loaded in ajax
67
+ def no_cache
68
+ conditionally_visible? || self[:no_cache]
69
+ end
70
+
57
71
  def label(locale = nil)
58
72
  label_translations[locale] || self[:label]
59
73
  end
@@ -75,7 +89,7 @@ module Fe
75
89
  def all_elements
76
90
  ids = all_element_ids_arr
77
91
  order = ids.collect{ |id| "id=#{id} DESC" }.join(', ')
78
- ids.present? ? Element.where(id: ids).order(order) : Element.where("1 = 0")
92
+ ids.present? ? Element.where(id: ids).order(Arel.sql(order)) : Element.where("1 = 0")
79
93
  end
80
94
 
81
95
  def all_element_ids
@@ -94,33 +108,46 @@ module Fe
94
108
  def copy_to(question_sheet)
95
109
  new_page = Fe::Page.new(self.attributes.merge(id: nil))
96
110
  new_page.question_sheet_id = question_sheet.id
97
- new_page.save(:validate => false)
111
+ new_page.save(validate: false)
98
112
  self.elements.each do |element|
99
113
  if !question_sheet.archived? && element.reuseable?
100
- Fe::PageElement.create(:element => element, :page => new_page)
114
+ Fe::PageElement.create(element: element, page: new_page)
101
115
  else
102
116
  element.duplicate(new_page)
103
117
  end
104
118
  end
105
119
  new_page.rebuild_all_element_ids
120
+ new_page
106
121
  end
107
122
 
108
123
  def hidden?(answer_sheet)
109
- return false unless question_sheet.all_elements.where(conditional_type: 'Fe::Page', conditional_id: self).any?
124
+ return true if hidden
125
+
126
+ @hidden_cache ||= {}
127
+ return @hidden_cache[answer_sheet] if !@hidden_cache[answer_sheet].nil?
128
+
129
+ unless conditionally_visible?
130
+ @hidden_cache[answer_sheet] = false
131
+ return false
132
+ end
110
133
 
111
134
  # if any of the conditional questions matches, it's visible
112
- !question_sheet.all_elements.where(conditional_type: 'Fe::Page', conditional_id: self).any?{ |e|
113
- e.conditional_match(answer_sheet)
135
+ r = !question_sheet.all_elements.where(conditional_type: 'Fe::Page', conditional_id: self).any?{ |e|
136
+ e.visible?(answer_sheet) && e.conditional_match(answer_sheet)
114
137
  }
138
+ @hidden_cache[answer_sheet] = r
139
+ return r
140
+ end
141
+
142
+ def clear_hidden_cache
143
+ @hidden_cache = nil
115
144
  end
116
145
 
117
146
  def complete?(answer_sheet)
118
147
  return true if hidden?(answer_sheet)
119
- prev_el = nil
148
+
120
149
  all_elements.all? {|e|
121
- complete = !e.required?(answer_sheet, self, prev_el) || e.has_response?(answer_sheet)
122
- prev_el = e
123
- complete
150
+ e.hidden?(answer_sheet, self) || !e.required?(answer_sheet, self) || e.has_response?(answer_sheet)
124
151
  }
125
152
  end
126
153
 
@@ -128,6 +155,54 @@ module Fe
128
155
  all_questions.any? {|e| e.has_response?(answer_sheet)}
129
156
  end
130
157
 
158
+ def all_hidden_elements(answer_sheet)
159
+ @all_hidden_elements ||= {}
160
+ @all_hidden_elements[answer_sheet.cache_key] ||= build_all_hidden_elements(answer_sheet)
161
+ end
162
+
163
+ def build_all_hidden_elements(answer_sheet)
164
+ @all_hidden_elements ||= {}
165
+ @all_hidden_elements[answer_sheet.cache_key] = []
166
+ all_elements.each do |e|
167
+ next if @all_hidden_elements[answer_sheet.cache_key].include?(e)
168
+ if e.hidden_by_choice_field?(answer_sheet) || e.hidden_by_conditional?(answer_sheet, self)
169
+ @all_hidden_elements[answer_sheet.cache_key] += ([e] + e.all_elements)
170
+ @all_hidden_elements[answer_sheet.cache_key].uniq!
171
+ end
172
+ end
173
+ @all_hidden_elements[answer_sheet.cache_key]
174
+ end
175
+
176
+ def clear_all_hidden_elements
177
+ @all_hidden_elements = nil
178
+ end
179
+
180
+ def export_hash
181
+ base_attributes = self.attributes.to_hash
182
+ base_attributes[:elements] = elements.collect(&:export_hash)
183
+ base_attributes.delete(:id)
184
+ base_attributes[:question_sheet_id] = :question_sheet_id
185
+ base_attributes
186
+ end
187
+
188
+ def export_to_yaml
189
+ export_hash.to_yaml
190
+ end
191
+
192
+ def self.create_from_import(page_data, question_sheet)
193
+ elements = page_data.delete(:elements)
194
+ page_data.delete(:all_element_ids) # this can get build again
195
+ page_data[:old_id] = page_data.delete('id')
196
+ page_data[:question_sheet_id] = question_sheet.id
197
+ puts("Import page from data #{page_data}")
198
+ page = Fe::Page.create!(page_data)
199
+ elements.each do |el|
200
+ page.elements << Fe::Element.create_from_import(el, page, question_sheet)
201
+ end
202
+ page.rebuild_all_element_ids
203
+ page
204
+ end
205
+
131
206
  private
132
207
 
133
208
  # next unused label with "Page" prefix
@@ -1,8 +1,8 @@
1
1
  require 'acts_as_list'
2
2
  module Fe
3
- class PageElement < ActiveRecord::Base
3
+ class PageElement < ApplicationRecord
4
4
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
5
- acts_as_list :scope => :page_id
5
+ acts_as_list scope: :page_id
6
6
  belongs_to :page, touch: true
7
7
  belongs_to :element
8
8
 
@@ -5,6 +5,6 @@
5
5
  # :content - instructions, agreements, etc. to display
6
6
  module Fe
7
7
  class Paragraph < Element
8
- validates_presence_of :content, :on => :update
8
+ validates_presence_of :content, on: :update
9
9
  end
10
10
  end
@@ -1,6 +1,6 @@
1
1
  module Fe
2
- class Person < ActiveRecord::Base
3
- belongs_to :user, :foreign_key => "fk_ssmUserId" # TODO need to migrate person columns to be more rails-like
2
+ class Person < ApplicationRecord
3
+ belongs_to :user, optional: true, foreign_key: "fk_ssmUserId" # TODO need to migrate person columns to be more rails-like
4
4
  has_many :email_addresses, class_name: '::EmailAddress', dependent: :destroy
5
5
  has_many :phone_numbers, class_name: '::PhoneNumber', dependent: :destroy
6
6
  has_one :current_address, -> { where("address_type = 'current'") }, class_name: '::Fe::Address', dependent: :destroy
@@ -17,15 +17,15 @@ module Fe
17
17
  end
18
18
 
19
19
  def create_emergency_address
20
- Address.create(:person_id => self.id, :address_type => 'emergency1')
20
+ Address.create(person_id: self.id, address_type: 'emergency1')
21
21
  end
22
22
 
23
23
  def create_current_address
24
- Address.create(:person_id => self.id, :address_type => 'current')
24
+ Address.create(person_id: self.id, address_type: 'current')
25
25
  end
26
26
 
27
27
  def create_permanent_address
28
- Address.create(:person_id => self.id, :address_type => 'permanent')
28
+ Address.create(person_id: self.id, address_type: 'permanent')
29
29
  end
30
30
 
31
31
  def name
@@ -1,4 +1,4 @@
1
- class Fe::PhoneNumber < ActiveRecord::Base
1
+ class Fe::PhoneNumber < ApplicationRecord
2
2
  belongs_to :person
3
3
 
4
4
  self.table_name = "phone_numbers"
@@ -12,34 +12,40 @@ module Fe
12
12
  class Question < Element
13
13
  include ActionView::RecordIdentifier # dom_id
14
14
  has_many :conditions,
15
- :class_name => "Condition",
16
- :foreign_key => "toggle_id",
17
- :dependent => :nullify
15
+ class_name: "Condition",
16
+ foreign_key: "toggle_id",
17
+ dependent: :nullify
18
18
 
19
19
  has_many :dependents,
20
- :class_name => "Condition",
21
- :foreign_key => "trigger_id",
22
- :dependent => :nullify
20
+ class_name: "Condition",
21
+ foreign_key: "trigger_id",
22
+ dependent: :nullify
23
23
 
24
24
  has_many :sheet_answers,
25
- :class_name => "Answer",
26
- :foreign_key => "question_id",
27
- :dependent => :destroy
25
+ class_name: "Answer",
26
+ foreign_key: "question_id",
27
+ dependent: :destroy
28
28
 
29
29
  belongs_to :related_question_sheet,
30
- :class_name => "QuestionSheet",
31
- :foreign_key => "related_question_sheet_id"
30
+ optional: true,
31
+ class_name: "QuestionSheet",
32
+ foreign_key: "related_question_sheet_id"
32
33
 
33
- # validates_inclusion_of :required, :in => [false, true]
34
+ # validates_inclusion_of :required, in: [false, true]
34
35
 
35
- validates_format_of :slug, :with => /\A[a-z_][a-z0-9_]*\z/,
36
- :allow_nil => true, :if => Proc.new { |q| !q.slug.blank? },
37
- :message => 'may only contain lowercase letters, digits and underscores; and cannot begin with a digit.' # enforcing lowercase because javascript is case-sensitive
38
- validates_length_of :slug, :in => 4..36,
39
- :allow_nil => true, :if => Proc.new { |q| !q.slug.blank? }
40
- validates_uniqueness_of :slug,
41
- :allow_nil => true, :if => Proc.new { |q| !q.slug.blank? },
42
- :message => 'must be unique.'
36
+ validates_format_of :slug, with: /\A[a-z_][a-z0-9_]*\z/,
37
+ allow_nil: true, if: Proc.new { |q| !q.slug.blank? },
38
+ message: 'may only contain lowercase letters, digits and underscores; and cannot begin with a digit.' # enforcing lowercase because javascript is case-sensitive
39
+ validates_length_of :slug, in: 4..128,
40
+ allow_nil: true, if: Proc.new { |q| !q.slug.blank? }
41
+
42
+ validates_each :slug, allow_nil: true, allow_blank: true do |record, attr, value|
43
+ record.pages_on.collect(&:question_sheet).uniq.each do |qs|
44
+ if qs.all_elements.where(slug: record.slug).where("id != ?", record.id).any?
45
+ record.errors.add(attr, "must be unique (within the question sheet)")
46
+ end
47
+ end
48
+ end
43
49
 
44
50
  # a question has one response per AnswerSheet (that is, an instance of a user filling out the question)
45
51
  # generally the response is a single answer
@@ -66,9 +72,9 @@ module Fe
66
72
  # expression = new_conditions[i]["expression"]
67
73
  # trigger_id = new_conditions[i]["trigger_id"].to_i
68
74
  # unless expression.blank? || !page.questions.collect(&:id).include?(trigger_id) || conditions.collect(&:trigger_id).include?(trigger_id)
69
- # conditions.create(:question_sheet_id => question_sheet_id, :trigger_id => trigger_id,
70
- # :expression => expression, :toggle_page_id => page_id,
71
- # :toggle_id => self.id)
75
+ # conditions.create(question_sheet_id: question_sheet_id, trigger_id: trigger_id,
76
+ # expression: expression, toggle_page_id: page_id,
77
+ # toggle_id: self.id)
72
78
  # end
73
79
  # end
74
80
  # end
@@ -78,7 +84,9 @@ module Fe
78
84
  true
79
85
  end
80
86
 
81
- def locked?(params, answer_sheet, presenter)
87
+ # NOTE: current_person is passed in for the benefit of enclosing apps that override locked?
88
+ # and need to lock an element depending on who the current person is
89
+ def locked?(params, answer_sheet, presenter, current_person)
82
90
  return true unless params['action'] == 'edit'
83
91
  if self.object_name == 'person.current_address' && ['address1','address2','city','zip','email','state','country'].include?(self.attribute_name)
84
92
  # Billing Address
@@ -90,13 +98,14 @@ module Fe
90
98
  # Relationship & Country & Email Address
91
99
  return false
92
100
  else
93
- return answer_sheet.frozen? && !presenter.reference?
101
+ return answer_sheet.frozen? && !presenter.reference? &&
102
+ !@answer_sheet.try(:reference?)
94
103
  end
95
104
  end
96
105
 
97
106
  # css class names for javascript-based validation
98
- def validation_class(answer_sheet = nil)
99
- if required?(answer_sheet)
107
+ def validation_class(answer_sheet = nil, page = nil)
108
+ if required?(answer_sheet, page)
100
109
  ' required '
101
110
  else
102
111
  ''
@@ -147,8 +156,9 @@ module Fe
147
156
  [eval("obj." + attribute_name)]
148
157
  end
149
158
  else
150
- #answer_sheet.answers_by_question[id] || []
151
- Fe::Answer.where(:answer_sheet_id => answer_sheet.id, :question_id => self.id).to_a
159
+ answers = sheet_answers.where(answer_sheet: answer_sheet)
160
+ answers = answers.where("value IS NOT NULL AND value != ''")
161
+ answers.to_a
152
162
  end
153
163
  end
154
164
 
@@ -180,7 +190,8 @@ module Fe
180
190
  # raise object_name.inspect + ' == ' + attribute_name.inspect
181
191
  # end
182
192
  else
183
- @answers ||= []
193
+ @answers = sheet_answers.where(answer_sheet_id: answer_sheet.id).to_a
194
+ @answer_sheet_answers_are_for = answer_sheet
184
195
  @mark_for_destroy ||= []
185
196
  # go through existing answers (in reverse order, as we delete)
186
197
  (@answers.length - 1).downto(0) do |index|
@@ -195,7 +206,7 @@ module Fe
195
206
  # insert any new answers
196
207
  for value in values
197
208
  if @mark_for_destroy.empty?
198
- answer = Fe::Answer.new(:question_id => self.id)
209
+ answer = Fe::Answer.new(question_id: self.id)
199
210
  else
200
211
  # re-use marked answers (an update vs. a delete+insert)
201
212
  answer = @mark_for_destroy.pop
@@ -206,13 +217,26 @@ module Fe
206
217
  end
207
218
  end
208
219
 
220
+ def check_answer_sheet_matches_set_response_answer_sheet(answer_sheet)
221
+ if @answer_sheet_answers_are_for && @answer_sheet_answers_are_for != answer_sheet
222
+ fail("Trying to save answers to a different answer sheet than the one given in set_response")
223
+ end
224
+ end
225
+
209
226
  def save_file(answer_sheet, file)
227
+ check_answer_sheet_matches_set_response_answer_sheet(answer_sheet)
210
228
  @answers.collect(&:destroy) if @answers
211
- Fe::Answer.create!(:question_id => self.id, :answer_sheet_id => answer_sheet.id, :attachment => file)
229
+ Fe::Answer.create!(question_id: self.id, answer_sheet_id: answer_sheet.id, attachment: file)
230
+ end
231
+
232
+ def delete_file(answer_sheet, answer)
233
+ check_answer_sheet_matches_set_response_answer_sheet(answer_sheet)
234
+ answer.destroy
212
235
  end
213
236
 
214
237
  # save this question's @answers to database
215
238
  def save_response(answer_sheet)
239
+ check_answer_sheet_matches_set_response_answer_sheet(answer_sheet)
216
240
  unless @answers.nil?
217
241
  for answer in @answers
218
242
  if answer.is_a?(Fe::Answer)
@@ -229,19 +253,19 @@ module Fe
229
253
  end
230
254
  @mark_for_destroy.clear
231
255
  end
256
+
257
+ # clear hidden elements cache on page since this answer might modify which elements are hidden
258
+ pages_on.each do |p| p.clear_all_hidden_elements; end
259
+
232
260
  rescue TypeError
233
261
  raise answer.inspect
234
262
  end
235
263
 
236
264
  # has any sort of non-empty response?
237
265
  def has_response?(answer_sheet = nil)
238
- if answer_sheet.present?
239
- answers = responses(answer_sheet)
240
- else
241
- answers = Fe::Answer.where(:question_id => self.id)
242
- end
266
+ answers = answer_sheet.present? ? responses(answer_sheet) : sheet_answers
243
267
  return false if answers.length == 0
244
- answers.each do |answer| # loop through Answers
268
+ answers.each do |answer|
245
269
  value = answer.is_a?(Fe::Answer) ? answer.value : answer
246
270
  return true if (value.is_a?(FalseClass) && value === false) || value.present?
247
271
  end
@@ -8,9 +8,9 @@ module Fe
8
8
  class QuestionGrid < Element
9
9
 
10
10
  has_many :elements, -> { order('position asc, id asc') },
11
- :class_name => "Element",
12
- :foreign_key => "question_grid_id",
13
- :dependent => :nullify
11
+ class_name: "Element",
12
+ foreign_key: "question_grid_id",
13
+ dependent: :nullify
14
14
 
15
15
  def num_cols
16
16
  num = cols.to_s.split(';').length
@@ -21,5 +21,17 @@ module Fe
21
21
  def has_response?(answer_sheet = nil)
22
22
  elements.any? {|e| e.has_response?(answer_sheet)}
23
23
  end
24
+
25
+ def export_hash
26
+ super_hash = super
27
+ children = elements.collect do |e|
28
+ h = e.export_hash
29
+ h[:question_grid_id] = :parent_id
30
+ h
31
+ end
32
+
33
+ super_hash[:children] += children
34
+ super_hash
35
+ end
24
36
  end
25
37
  end
@@ -3,30 +3,24 @@
3
3
  module Fe
4
4
  class QuestionSet
5
5
 
6
- attr_reader :elements
6
+ attr_reader :elements, :questions
7
7
 
8
8
  # associate answers from database with a set of elements
9
9
  def initialize(elements, answer_sheet)
10
10
  @elements = elements
11
11
  @answer_sheet = answer_sheet
12
-
13
12
  @questions = elements.select { |e| e.question? }
14
-
15
- # answers = @answer_sheet.answers_by_question
16
-
17
- @questions.each do |question|
18
- question.answers = question.responses(answer_sheet) #answers[question.id]
19
- end
20
- @questions
21
13
  end
22
14
 
23
15
  # update with responses from form
24
16
  def post(params, answer_sheet)
25
- questions_indexed = @questions.index_by {|q| q.id}
17
+ questions_indexed = @questions.index_by(&:id)
26
18
 
27
19
  # loop over form values
28
20
  params ||= {}
29
- params.each do |question_id, response|
21
+ # we only assign answers to questions from this set, so it's fine to use to_unsafe_h here. If we don't,
22
+ # the kind_of?(Hash) checks will fail as per rails 5
23
+ params.to_unsafe_h.each do |question_id, response|
30
24
  next if questions_indexed[question_id.to_i].nil? # the rare case where a question was removed after the app was opened.
31
25
  # update each question with the posted response
32
26
  questions_indexed[question_id.to_i].set_response(posted_values(response), answer_sheet)
@@ -53,6 +47,51 @@ module Fe
53
47
  end
54
48
  end
55
49
 
50
+ # options should contain:
51
+ #
52
+ # :filter - Array of symbols, ex [ :confidential ]
53
+ #
54
+ # These will be called on each element to determine if they match the filter
55
+ # An element matches the filter using an AND condition, ie. if all the methods
56
+ # in the array return true
57
+ #
58
+ # :filter_default - Either :show or :hide
59
+ #
60
+ # If show, all elements are shown by default and hidden if they match the filter.
61
+ # If hide, all elements are hidden by default and shown if they match the filter.
62
+ #
63
+ def set_filter(options = {})
64
+ return if options.nil? || options.empty?
65
+
66
+ filter = options.delete(:filter)
67
+ unless filter && filter.is_a?(Array)
68
+ raise("expect options[:filter] to be an array")
69
+ end
70
+ filter_default = options.delete(:filter_default)
71
+ unless filter_default && [:show, :hide].include?(filter_default)
72
+ raise("expect options[:filter_default] to be either :show or :hide")
73
+ end
74
+
75
+ @filter = filter
76
+ @filter_default = filter_default
77
+
78
+ matching_ids = []
79
+ @elements.each do |e|
80
+ if e.matches_filter(@filter) && !matching_ids.include?(e.id)
81
+ matching_ids << e.id
82
+ matching_ids += e.all_elements.collect(&:id)
83
+ end
84
+ end
85
+
86
+ case filter_default
87
+ when :show
88
+ @elements = @elements.to_a.reject{ |e| matching_ids.include?(e.id) }
89
+ when :hide
90
+ @elements = @elements.to_a.select{ |e| matching_ids.include?(e.id) }
91
+ end
92
+
93
+ initialize(@elements, @answer_sheet)
94
+ end
56
95
 
57
96
  private
58
97
 
@@ -69,7 +108,9 @@ module Fe
69
108
  end
70
109
  elsif param.kind_of?(Hash)
71
110
  # from Hash with multiple answers per question
72
- values = param.values.map {|v| CGI.unescape(v)}
111
+ # If value is also a hash, use the value hash without escaping or anything,
112
+ # so that custom elements can be implemented by handling set_response.
113
+ values = param.values.map {|v| v.is_a?(Hash) ? v : CGI.unescape(v)}
73
114
  elsif param.kind_of?(String)
74
115
  values = [CGI.unescape(param)]
75
116
  end
@@ -77,6 +118,5 @@ module Fe
77
118
  # Hash may contain empty string to force post for no checkboxes
78
119
  # values = values.reject {|r| r == ''}
79
120
  end
80
-
81
121
  end
82
122
  end