forest_cms 0.98.1

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 (411) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +204 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/forest_manifest.js +2 -0
  6. data/app/assets/fonts/FontAwesome.otf +0 -0
  7. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  8. data/app/assets/fonts/fontawesome-webfont.svg +2671 -0
  9. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  10. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  11. data/app/assets/fonts/fontawesome-webfont.woff2 +0 -0
  12. data/app/assets/images/forest/favicons/android-chrome-192x192.png +0 -0
  13. data/app/assets/images/forest/favicons/android-chrome-512x512.png +0 -0
  14. data/app/assets/images/forest/favicons/apple-touch-icon.png +0 -0
  15. data/app/assets/images/forest/favicons/browserconfig.xml +9 -0
  16. data/app/assets/images/forest/favicons/favicon-16x16.png +0 -0
  17. data/app/assets/images/forest/favicons/favicon-32x32.png +0 -0
  18. data/app/assets/images/forest/favicons/favicon.ico +0 -0
  19. data/app/assets/images/forest/favicons/manifest.json.erb +18 -0
  20. data/app/assets/images/forest/favicons/mstile-150x150.png +0 -0
  21. data/app/assets/images/forest/favicons/safari-pinned-tab.svg +21 -0
  22. data/app/assets/images/forest/forest-low.jpg +0 -0
  23. data/app/assets/images/forest/forest.jpg +0 -0
  24. data/app/assets/javascripts/forest/admin/lib/jquery_plugins.js +32 -0
  25. data/app/assets/javascripts/forest/admin/main.js +59 -0
  26. data/app/assets/javascripts/forest/admin/partials/add_block.js +22 -0
  27. data/app/assets/javascripts/forest/admin/partials/block_collapser.js +49 -0
  28. data/app/assets/javascripts/forest/admin/partials/block_errors.js +11 -0
  29. data/app/assets/javascripts/forest/admin/partials/block_selector_inputs.js +61 -0
  30. data/app/assets/javascripts/forest/admin/partials/cocoon_events.js +3 -0
  31. data/app/assets/javascripts/forest/admin/partials/cocoon_sortable_field_sets.js +55 -0
  32. data/app/assets/javascripts/forest/admin/partials/collage_inputs.js +286 -0
  33. data/app/assets/javascripts/forest/admin/partials/color_inputs.js +24 -0
  34. data/app/assets/javascripts/forest/admin/partials/datepicker_init.js +70 -0
  35. data/app/assets/javascripts/forest/admin/partials/documentation_sidebar_affix.js +22 -0
  36. data/app/assets/javascripts/forest/admin/partials/dropdown_block_selector.js +106 -0
  37. data/app/assets/javascripts/forest/admin/partials/edit_header.js +22 -0
  38. data/app/assets/javascripts/forest/admin/partials/file_upload_init.js +111 -0
  39. data/app/assets/javascripts/forest/admin/partials/forest_tables.js +42 -0
  40. data/app/assets/javascripts/forest/admin/partials/infinite_loader.js +89 -0
  41. data/app/assets/javascripts/forest/admin/partials/markdown_text_editor_init.js +89 -0
  42. data/app/assets/javascripts/forest/admin/partials/media_item_chooser_modal.js.erb +259 -0
  43. data/app/assets/javascripts/forest/admin/partials/menus.js +79 -0
  44. data/app/assets/javascripts/forest/admin/partials/multi_checkbox_selector.js +27 -0
  45. data/app/assets/javascripts/forest/admin/partials/persisted_value_warnings.js +38 -0
  46. data/app/assets/javascripts/forest/admin/partials/repeater_inputs.js +24 -0
  47. data/app/assets/javascripts/forest/admin/partials/select2_init.js +165 -0
  48. data/app/assets/javascripts/forest/admin/partials/select_tag_form_submitter.js +40 -0
  49. data/app/assets/javascripts/forest/admin/partials/sortable_gallery.js +45 -0
  50. data/app/assets/javascripts/forest/admin/partials/sortable_page_block_position_setter.js +37 -0
  51. data/app/assets/javascripts/forest/admin/partials/status_scheduler.js +16 -0
  52. data/app/assets/javascripts/forest/admin/partials/tab_templates.js +93 -0
  53. data/app/assets/javascripts/forest/admin/partials/tables.js +7 -0
  54. data/app/assets/javascripts/forest/admin/partials/tooltips.js +7 -0
  55. data/app/assets/javascripts/forest/admin/partials/turboform_form_submitter.js +15 -0
  56. data/app/assets/javascripts/forest/admin/vendor/imagesloaded.pkgd.min.js +7 -0
  57. data/app/assets/javascripts/forest/admin/vendor/jquery-ui-timepicker-addon.js +2291 -0
  58. data/app/assets/javascripts/forest/admin/vendor/jquery.ba-throttle-debounce.min.js +9 -0
  59. data/app/assets/javascripts/forest/admin/vendor/jquery.nestable.js +756 -0
  60. data/app/assets/javascripts/forest/admin/vendor/select2.full.min.js +3 -0
  61. data/app/assets/javascripts/forest/admin/vendor/simplemde.min.js +15 -0
  62. data/app/assets/javascripts/forest/application_admin.js +29 -0
  63. data/app/assets/javascripts/forest/application_admin_from_host_app.js +1 -0
  64. data/app/assets/javascripts/forest/application_public.js +19 -0
  65. data/app/assets/javascripts/forest/cable.js +13 -0
  66. data/app/assets/javascripts/forest/lib/jquery-3.3.1.min.js +2 -0
  67. data/app/assets/javascripts/forest/main.js +59 -0
  68. data/app/assets/javascripts/forest/public/partials/analytics.js +16 -0
  69. data/app/assets/stylesheets/forest/admin/_bootstrap-variables.scss +877 -0
  70. data/app/assets/stylesheets/forest/admin/_variables.scss +13 -0
  71. data/app/assets/stylesheets/forest/admin/base/_foundation.scss +17 -0
  72. data/app/assets/stylesheets/forest/admin/mixins/_breakpoints.scss +26 -0
  73. data/app/assets/stylesheets/forest/admin/mixins/_glyphicons.scss +278 -0
  74. data/app/assets/stylesheets/forest/admin/mixins/_layout.scss +13 -0
  75. data/app/assets/stylesheets/forest/admin/mixins/_links.scss +10 -0
  76. data/app/assets/stylesheets/forest/admin/mixins/_lists.scss +10 -0
  77. data/app/assets/stylesheets/forest/admin/mixins/_mixins.scss +6 -0
  78. data/app/assets/stylesheets/forest/admin/mixins/_triangles.scss +21 -0
  79. data/app/assets/stylesheets/forest/admin/partials/_admin.scss +57 -0
  80. data/app/assets/stylesheets/forest/admin/partials/_affix.scss +5 -0
  81. data/app/assets/stylesheets/forest/admin/partials/_block_errors.scss +15 -0
  82. data/app/assets/stylesheets/forest/admin/partials/_block_layout.scss +48 -0
  83. data/app/assets/stylesheets/forest/admin/partials/_block_type_edit_ui.scss +15 -0
  84. data/app/assets/stylesheets/forest/admin/partials/_bootstrap_customizations.scss +12 -0
  85. data/app/assets/stylesheets/forest/admin/partials/_collage_inputs.scss +122 -0
  86. data/app/assets/stylesheets/forest/admin/partials/_documentation.scss +7 -0
  87. data/app/assets/stylesheets/forest/admin/partials/_dropdown_block_selector.scss +19 -0
  88. data/app/assets/stylesheets/forest/admin/partials/_edit_header.scss +16 -0
  89. data/app/assets/stylesheets/forest/admin/partials/_file_upload.scss +3 -0
  90. data/app/assets/stylesheets/forest/admin/partials/_forest_tables.scss +20 -0
  91. data/app/assets/stylesheets/forest/admin/partials/_forms_and_inputs.scss +72 -0
  92. data/app/assets/stylesheets/forest/admin/partials/_helper_classes.scss +24 -0
  93. data/app/assets/stylesheets/forest/admin/partials/_infinite_loader.scss +8 -0
  94. data/app/assets/stylesheets/forest/admin/partials/_markdown_editor.scss +12 -0
  95. data/app/assets/stylesheets/forest/admin/partials/_media_item_chooser.scss +168 -0
  96. data/app/assets/stylesheets/forest/admin/partials/_media_library.scss +109 -0
  97. data/app/assets/stylesheets/forest/admin/partials/_pages.scss +6 -0
  98. data/app/assets/stylesheets/forest/admin/partials/_pagination.scss +5 -0
  99. data/app/assets/stylesheets/forest/admin/partials/_repeater_inputs.scss +57 -0
  100. data/app/assets/stylesheets/forest/admin/partials/_resizable_ui.scss +1 -0
  101. data/app/assets/stylesheets/forest/admin/partials/_select2_custom.scss +20 -0
  102. data/app/assets/stylesheets/forest/admin/partials/_sessions.scss +13 -0
  103. data/app/assets/stylesheets/forest/admin/partials/_slug_attributes.scss +9 -0
  104. data/app/assets/stylesheets/forest/admin/partials/_sortable_field_sets.scss +22 -0
  105. data/app/assets/stylesheets/forest/admin/partials/_sortable_select2.scss +18 -0
  106. data/app/assets/stylesheets/forest/admin/partials/_sortable_ui.scss +15 -0
  107. data/app/assets/stylesheets/forest/admin/partials/_status_indicators.scss +33 -0
  108. data/app/assets/stylesheets/forest/admin/partials/_status_scheduler.scss +9 -0
  109. data/app/assets/stylesheets/forest/admin/partials/_tooltips.scss +5 -0
  110. data/app/assets/stylesheets/forest/admin/partials/_turbolinks.scss +5 -0
  111. data/app/assets/stylesheets/forest/admin/partials/_typography.scss +19 -0
  112. data/app/assets/stylesheets/forest/admin/partials/_ui.scss +32 -0
  113. data/app/assets/stylesheets/forest/admin/sprockets/font-awesome.scss +2337 -0
  114. data/app/assets/stylesheets/forest/admin/sprockets/main_bootstrap.scss +3 -0
  115. data/app/assets/stylesheets/forest/admin/vendor/_jquery-ui-timepicker-addon.scss +30 -0
  116. data/app/assets/stylesheets/forest/admin/vendor/_nestable.scss +347 -0
  117. data/app/assets/stylesheets/forest/admin/vendor/_select2_1_core.scss +484 -0
  118. data/app/assets/stylesheets/forest/admin/vendor/_select2_2_bootstrap.scss +612 -0
  119. data/app/assets/stylesheets/forest/admin/vendor/_simplemde.min.scss +7 -0
  120. data/app/assets/stylesheets/forest/admin_overrides.scss +1 -0
  121. data/app/assets/stylesheets/forest/application_admin.css +23 -0
  122. data/app/assets/stylesheets/forest/application_public.css +16 -0
  123. data/app/assets/stylesheets/forest/main_admin.scss +16 -0
  124. data/app/assets/stylesheets/forest/main_public.scss +4 -0
  125. data/app/assets/stylesheets/forest/public/base/_admin_error_messages.scss +15 -0
  126. data/app/assets/stylesheets/forest/public/base/_foundation.scss +24 -0
  127. data/app/assets/stylesheets/forest/public/base/_images.scss +14 -0
  128. data/app/assets/stylesheets/forest/scaffolds.scss +87 -0
  129. data/app/assets/stylesheets/jquery.fileupload.scss +37 -0
  130. data/app/classes/forest/block_set.rb +26 -0
  131. data/app/classes/forest/error.rb +15 -0
  132. data/app/classes/forest/markdown_renderer.rb +31 -0
  133. data/app/classes/search_index_manager.rb +83 -0
  134. data/app/controllers/admin/block_kinds_controller.rb +31 -0
  135. data/app/controllers/admin/block_layouts_controller.rb +47 -0
  136. data/app/controllers/admin/cache_purge_controller.rb +11 -0
  137. data/app/controllers/admin/dashboard_controller.rb +6 -0
  138. data/app/controllers/admin/documentation_controller.rb +5 -0
  139. data/app/controllers/admin/errors_controller.rb +9 -0
  140. data/app/controllers/admin/forest_controller.rb +96 -0
  141. data/app/controllers/admin/imports_controller.rb +34 -0
  142. data/app/controllers/admin/media_items_controller.rb +107 -0
  143. data/app/controllers/admin/menus_controller.rb +69 -0
  144. data/app/controllers/admin/pages_controller.rb +105 -0
  145. data/app/controllers/admin/settings_controller.rb +66 -0
  146. data/app/controllers/admin/translations_controller.rb +58 -0
  147. data/app/controllers/admin/user_groups_controller.rb +69 -0
  148. data/app/controllers/admin/users_controller.rb +88 -0
  149. data/app/controllers/application_controller.rb +2 -0
  150. data/app/controllers/errors_controller.rb +26 -0
  151. data/app/controllers/forest_controller.rb +39 -0
  152. data/app/controllers/media_items_controller.rb +12 -0
  153. data/app/controllers/pages_controller.rb +39 -0
  154. data/app/controllers/public_controller.rb +5 -0
  155. data/app/helpers/admin_helper.rb +95 -0
  156. data/app/helpers/admin_info_helper.rb +12 -0
  157. data/app/helpers/admin_settings_helper.rb +16 -0
  158. data/app/helpers/application_helper.rb +2 -0
  159. data/app/helpers/block_helper.rb +25 -0
  160. data/app/helpers/cache_helper.rb +23 -0
  161. data/app/helpers/forest_errors_helper.rb +34 -0
  162. data/app/helpers/form_helper.rb +93 -0
  163. data/app/helpers/image_helper.rb +65 -0
  164. data/app/helpers/markdown_helper.rb +25 -0
  165. data/app/helpers/meta_helper.rb +238 -0
  166. data/app/helpers/navigation_helper.rb +18 -0
  167. data/app/helpers/status_indicator_helper.rb +7 -0
  168. data/app/inputs/block_selector_input.rb +16 -0
  169. data/app/inputs/collage_input.rb +63 -0
  170. data/app/inputs/color_input.rb +30 -0
  171. data/app/inputs/currency_input.rb +22 -0
  172. data/app/inputs/datepicker_input.rb +19 -0
  173. data/app/inputs/gallery_input.rb +67 -0
  174. data/app/inputs/grouped_collection_select_input.rb +19 -0
  175. data/app/inputs/image_input.rb +82 -0
  176. data/app/inputs/repeater_input.rb +58 -0
  177. data/app/jobs/forest/application_job.rb +4 -0
  178. data/app/mailers/forest/application_mailer.rb +6 -0
  179. data/app/models/application_record.rb +13 -0
  180. data/app/models/block_kind.rb +54 -0
  181. data/app/models/block_layout.rb +19 -0
  182. data/app/models/block_slot.rb +49 -0
  183. data/app/models/blocks/base_block.rb +134 -0
  184. data/app/models/cache_record.rb +2 -0
  185. data/app/models/concerns/attachable.rb +126 -0
  186. data/app/models/concerns/blockable.rb +65 -0
  187. data/app/models/concerns/filter_controller_scopes.rb +13 -0
  188. data/app/models/concerns/forest/cache_buster.rb +112 -0
  189. data/app/models/concerns/searchable.rb +200 -0
  190. data/app/models/concerns/sluggable.rb +70 -0
  191. data/app/models/concerns/statusable.rb +71 -0
  192. data/app/models/concerns/versionable.rb +13 -0
  193. data/app/models/forest/application_record.rb +111 -0
  194. data/app/models/import.rb +43 -0
  195. data/app/models/media_item.rb +136 -0
  196. data/app/models/menu.rb +65 -0
  197. data/app/models/page.rb +154 -0
  198. data/app/models/setting.rb +142 -0
  199. data/app/models/translation.rb +97 -0
  200. data/app/models/user.rb +45 -0
  201. data/app/models/user_group.rb +17 -0
  202. data/app/policies/application_policy.rb +71 -0
  203. data/app/policies/block_kind_policy.rb +2 -0
  204. data/app/policies/block_layout_policy.rb +2 -0
  205. data/app/policies/block_record_policy.rb +13 -0
  206. data/app/policies/dashboard_policy.rb +19 -0
  207. data/app/policies/media_item_policy.rb +5 -0
  208. data/app/policies/menu_policy.rb +2 -0
  209. data/app/policies/page_policy.rb +9 -0
  210. data/app/policies/setting_policy.rb +2 -0
  211. data/app/policies/translation_policy.rb +2 -0
  212. data/app/policies/user_group_policy.rb +2 -0
  213. data/app/policies/user_policy.rb +17 -0
  214. data/app/views/admin/_file_upload.html.erb +75 -0
  215. data/app/views/admin/_flash_notifications.html.erb +20 -0
  216. data/app/views/admin/_navigation.html.erb +61 -0
  217. data/app/views/admin/api/index/_header.json.jbuilder +2 -0
  218. data/app/views/admin/api/index/_records.json.jbuilder +6 -0
  219. data/app/views/admin/block_kinds/_form.html.erb +34 -0
  220. data/app/views/admin/block_kinds/edit.html.erb +3 -0
  221. data/app/views/admin/block_kinds/index.html.erb +31 -0
  222. data/app/views/admin/block_layouts/_form.html.erb +33 -0
  223. data/app/views/admin/block_layouts/edit.html.erb +3 -0
  224. data/app/views/admin/block_layouts/index.html.erb +29 -0
  225. data/app/views/admin/block_layouts/new.html.erb +3 -0
  226. data/app/views/admin/block_layouts/show.html.erb +24 -0
  227. data/app/views/admin/block_record/_block_layout.html.erb +67 -0
  228. data/app/views/admin/block_record/_block_slot_field_buttons.html.erb +15 -0
  229. data/app/views/admin/block_record/_block_slot_fields.html.erb +37 -0
  230. data/app/views/admin/block_record/_block_slot_fields_template.html.erb +50 -0
  231. data/app/views/admin/dashboard/_panel.html.erb +30 -0
  232. data/app/views/admin/dashboard/index.html.erb +18 -0
  233. data/app/views/admin/documentation/_body.html.erb +9 -0
  234. data/app/views/admin/documentation/_sidebar.html.erb +17 -0
  235. data/app/views/admin/documentation/index.html.erb +87 -0
  236. data/app/views/admin/form_inputs/_slug.html.erb +11 -0
  237. data/app/views/admin/form_inputs/_status.html.erb +20 -0
  238. data/app/views/admin/form_inputs/collage/_media_item_fields.html.erb +41 -0
  239. data/app/views/admin/headers/_edit.html.erb +68 -0
  240. data/app/views/admin/headers/_index.html.erb +61 -0
  241. data/app/views/admin/headers/_new.html.erb +14 -0
  242. data/app/views/admin/headers/_show.html.erb +37 -0
  243. data/app/views/admin/imports/edit.html.erb +60 -0
  244. data/app/views/admin/media_items/_form.html.erb +54 -0
  245. data/app/views/admin/media_items/_media_item_grid_layout.html.erb +23 -0
  246. data/app/views/admin/media_items/_media_item_list_layout.html.erb +32 -0
  247. data/app/views/admin/media_items/_modal.html.erb +62 -0
  248. data/app/views/admin/media_items/edit.html.erb +3 -0
  249. data/app/views/admin/media_items/index.html.erb +58 -0
  250. data/app/views/admin/media_items/index.json.jbuilder +2 -0
  251. data/app/views/admin/media_items/new.html.erb +3 -0
  252. data/app/views/admin/menus/_form.html.erb +92 -0
  253. data/app/views/admin/menus/_list_item.html.erb +39 -0
  254. data/app/views/admin/menus/edit.html.erb +3 -0
  255. data/app/views/admin/menus/index.html.erb +29 -0
  256. data/app/views/admin/menus/new.html.erb +3 -0
  257. data/app/views/admin/menus/show.html.erb +3 -0
  258. data/app/views/admin/navigation/_resources_dropdown.html.erb +12 -0
  259. data/app/views/admin/pages/_additional_inputs.html.erb +1 -0
  260. data/app/views/admin/pages/_form.html.erb +52 -0
  261. data/app/views/admin/pages/_table_row.html.erb +14 -0
  262. data/app/views/admin/pages/edit.html.erb +3 -0
  263. data/app/views/admin/pages/index.html.erb +33 -0
  264. data/app/views/admin/pages/index.json.jbuilder +2 -0
  265. data/app/views/admin/pages/new.html.erb +3 -0
  266. data/app/views/admin/pages/show.html.erb +3 -0
  267. data/app/views/admin/settings/_form.html.erb +25 -0
  268. data/app/views/admin/settings/edit.html.erb +3 -0
  269. data/app/views/admin/settings/index.html.erb +38 -0
  270. data/app/views/admin/settings/new.html.erb +3 -0
  271. data/app/views/admin/settings/show.html.erb +19 -0
  272. data/app/views/admin/settings/types/_boolean.html.erb +1 -0
  273. data/app/views/admin/settings/types/_image.html.erb +10 -0
  274. data/app/views/admin/settings/types/_integer.html.erb +5 -0
  275. data/app/views/admin/settings/types/_string.html.erb +5 -0
  276. data/app/views/admin/settings/types/_text.html.erb +5 -0
  277. data/app/views/admin/shared/_admin_head_additions.html.erb +1 -0
  278. data/app/views/admin/shared/navigation/_navbar.html.erb +8 -0
  279. data/app/views/admin/translations/_form.html.erb +31 -0
  280. data/app/views/admin/translations/edit.html.erb +3 -0
  281. data/app/views/admin/translations/index.html.erb +41 -0
  282. data/app/views/admin/translations/new.html.erb +3 -0
  283. data/app/views/admin/translations/show.html.erb +19 -0
  284. data/app/views/admin/user_groups/_form.html.erb +11 -0
  285. data/app/views/admin/user_groups/edit.html.erb +3 -0
  286. data/app/views/admin/user_groups/index.html.erb +29 -0
  287. data/app/views/admin/user_groups/new.html.erb +3 -0
  288. data/app/views/admin/user_groups/show.html.erb +9 -0
  289. data/app/views/admin/users/_form.html.erb +41 -0
  290. data/app/views/admin/users/edit.html.erb +3 -0
  291. data/app/views/admin/users/index.html.erb +35 -0
  292. data/app/views/admin/users/new.html.erb +3 -0
  293. data/app/views/admin/users/show.html.erb +4 -0
  294. data/app/views/blocks/_show.html.erb +21 -0
  295. data/app/views/devise/confirmations/new.html.erb +28 -0
  296. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  297. data/app/views/devise/mailer/password_change.html.erb +3 -0
  298. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  299. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  300. data/app/views/devise/passwords/edit.html.erb +31 -0
  301. data/app/views/devise/passwords/new.html.erb +27 -0
  302. data/app/views/devise/registrations/edit.html.erb +39 -0
  303. data/app/views/devise/registrations/new.html.erb +1 -0
  304. data/app/views/devise/sessions/_default_login.html.erb +25 -0
  305. data/app/views/devise/sessions/_first_time_login.html.erb +22 -0
  306. data/app/views/devise/sessions/new.html.erb +12 -0
  307. data/app/views/devise/shared/_flash_notifications.html.erb +3 -0
  308. data/app/views/devise/shared/_links.html.erb +25 -0
  309. data/app/views/devise/unlocks/new.html.erb +28 -0
  310. data/app/views/errors/internal_server_error.html.erb +2 -0
  311. data/app/views/errors/internal_server_error.json.jbuilder +4 -0
  312. data/app/views/errors/not_found.html.erb +2 -0
  313. data/app/views/errors/not_found.json.jbuilder +4 -0
  314. data/app/views/errors/unprocessable.html.erb +2 -0
  315. data/app/views/errors/unprocessable.json.jbuilder +4 -0
  316. data/app/views/forest/_analytics.html.erb +10 -0
  317. data/app/views/forest/_favicons.html.erb +7 -0
  318. data/app/views/kaminari/_first_page.html.erb +11 -0
  319. data/app/views/kaminari/_gap.html.erb +8 -0
  320. data/app/views/kaminari/_last_page.html.erb +11 -0
  321. data/app/views/kaminari/_next_page.html.erb +11 -0
  322. data/app/views/kaminari/_page.html.erb +12 -0
  323. data/app/views/kaminari/_paginator.html.erb +25 -0
  324. data/app/views/kaminari/_prev_page.html.erb +11 -0
  325. data/app/views/layouts/_shared_head.html.erb +48 -0
  326. data/app/views/layouts/admin.html.erb +19 -0
  327. data/app/views/layouts/application.html.erb +15 -0
  328. data/app/views/layouts/devise.html.erb +12 -0
  329. data/app/views/layouts/mailer.html.erb +13 -0
  330. data/app/views/layouts/mailer.text.erb +1 -0
  331. data/app/views/media_items/show.html.erb +29 -0
  332. data/app/views/pages/show.html.erb +1 -0
  333. data/app/views/public/index.html.erb +2 -0
  334. data/app/views/shared/_footer.html.erb +9 -0
  335. data/app/views/shared/_header.html.erb +9 -0
  336. data/app/views/shared/_header_colophon.html.erb +7 -0
  337. data/app/views/shared/_menu.html.erb +5 -0
  338. data/app/views/shared/_menu_item.html.erb +30 -0
  339. data/app/views/shared/_subnav.html.erb +7 -0
  340. data/config/initializers/aws.rb +17 -0
  341. data/config/initializers/devise.rb +275 -0
  342. data/config/initializers/forest.rb +5 -0
  343. data/config/initializers/paperclip.rb +1 -0
  344. data/config/initializers/rails_extensions.rb +6 -0
  345. data/config/initializers/simple_form.rb +164 -0
  346. data/config/initializers/simple_form/markdown.rb +21 -0
  347. data/config/initializers/simple_form/remote.rb +38 -0
  348. data/config/initializers/simple_form/sortable.rb +28 -0
  349. data/config/initializers/simple_form_bootstrap.rb +155 -0
  350. data/config/locales/devise.en.yml +62 -0
  351. data/config/locales/en.yml +24 -0
  352. data/config/locales/simple_form.en.yml +31 -0
  353. data/config/routes.rb +61 -0
  354. data/db/migrate/20161217191000_devise_create_users.rb +48 -0
  355. data/db/migrate/20161217200300_create_pages.rb +21 -0
  356. data/db/migrate/20161217205353_create_menus.rb +12 -0
  357. data/db/migrate/20161219033747_create_settings.rb +14 -0
  358. data/db/migrate/20161219231741_create_media_items.rb +18 -0
  359. data/db/migrate/20161225212203_create_user_groups.rb +10 -0
  360. data/db/migrate/20161225212258_create_join_table_user_groups_users.rb +8 -0
  361. data/db/migrate/20170109175932_create_block_kinds.rb +14 -0
  362. data/db/migrate/20170109180818_create_block_slots.rb +17 -0
  363. data/db/migrate/20170109185414_create_base_blocks.rb +8 -0
  364. data/db/migrate/20170818025124_create_block_layouts.rb +13 -0
  365. data/db/migrate/20170818025229_add_block_layout_to_block_slots.rb +5 -0
  366. data/db/migrate/20170818025500_remove_layout_from_block_slots.rb +5 -0
  367. data/db/migrate/20170821161755_add_unique_index_to_block_layouts.rb +6 -0
  368. data/db/migrate/20170826173346_create_translations.rb +12 -0
  369. data/db/migrate/20170910204934_index_attachment_content_type_on_media_items.rb +5 -0
  370. data/db/migrate/20180206035546_create_cache_records.rb +14 -0
  371. data/db/migrate/20180415034427_add_redirect_to_pages.rb +5 -0
  372. data/db/migrate/20180618190248_add_index_created_at_to_media_items.rb +5 -0
  373. data/db/migrate/20180625172226_add_blockable_metadata_to_pages.rb +6 -0
  374. data/db/migrate/20180626182949_remove_attachable_from_media_items.rb +7 -0
  375. data/db/migrate/20180712162414_add_media_item_status_to_media_items.rb +6 -0
  376. data/db/migrate/20190223155013_add_type_to_block_kinds.rb +6 -0
  377. data/db/migrate/20190423173909_add_seo_title_to_pages.rb +5 -0
  378. data/lib/forest.rb +9 -0
  379. data/lib/forest/engine.rb +51 -0
  380. data/lib/forest/migrations.rb +86 -0
  381. data/lib/forest/rails/active_record/has_many_ordered.rb +30 -0
  382. data/lib/forest/rails/active_record/has_repeatable.rb +19 -0
  383. data/lib/forest/rails/core_ext/enumerable.rb +17 -0
  384. data/lib/forest/version.rb +3 -0
  385. data/lib/generators/forest/block/USAGE +8 -0
  386. data/lib/generators/forest/block/block_generator.rb +103 -0
  387. data/lib/generators/forest/block/templates/_block.html.erb +2 -0
  388. data/lib/generators/forest/block/templates/_block.scss.erb +5 -0
  389. data/lib/generators/forest/block/templates/_block_edit_fields.html.erb +21 -0
  390. data/lib/generators/forest/block/templates/create_table_migration.rb +31 -0
  391. data/lib/generators/forest/block/templates/model.rb +41 -0
  392. data/lib/generators/forest/install/USAGE +2 -0
  393. data/lib/generators/forest/install/install_generator.rb +72 -0
  394. data/lib/generators/forest/scaffold/USAGE +8 -0
  395. data/lib/generators/forest/scaffold/scaffold_generator.rb +133 -0
  396. data/lib/generators/forest/scaffold/templates/admin_controller.rb +67 -0
  397. data/lib/generators/forest/scaffold/templates/controller.rb +20 -0
  398. data/lib/generators/forest/scaffold/templates/create_table_migration.rb +35 -0
  399. data/lib/generators/forest/scaffold/templates/model.rb +21 -0
  400. data/lib/generators/forest/scaffold/templates/policy.rb +2 -0
  401. data/lib/generators/forest/scaffold/templates/views/admin/_form.html.erb +56 -0
  402. data/lib/generators/forest/scaffold/templates/views/admin/edit.html.erb +3 -0
  403. data/lib/generators/forest/scaffold/templates/views/admin/index.html.erb +58 -0
  404. data/lib/generators/forest/scaffold/templates/views/admin/index.json.jbuilder +2 -0
  405. data/lib/generators/forest/scaffold/templates/views/admin/new.html.erb +3 -0
  406. data/lib/generators/forest/scaffold/templates/views/admin/show.html.erb +12 -0
  407. data/lib/generators/forest/scaffold/templates/views/public/index.html.erb +1 -0
  408. data/lib/generators/forest/scaffold/templates/views/public/show.html.erb +17 -0
  409. data/lib/tasks/forest_db_tasks.rake +176 -0
  410. data/lib/tasks/forest_elasticsearch_tasks.rake +22 -0
  411. metadata +719 -0
@@ -0,0 +1,47 @@
1
+ class Admin::BlockLayoutsController < Admin::ForestController
2
+ before_action :set_block_layout, only: [:edit, :update]
3
+
4
+ def index
5
+ @block_layouts = apply_scopes(BlockLayout).by_id.page params[:page]
6
+ end
7
+
8
+ def new
9
+ @block_layout = BlockLayout.new
10
+ authorize @block_layout
11
+ end
12
+
13
+ def edit
14
+ authorize @block_layout
15
+ end
16
+
17
+ def create
18
+ @block_layout = BlockLayout.new(block_layout_params)
19
+ authorize @block_layout
20
+
21
+ if @block_layout.save
22
+ redirect_to edit_admin_block_layout_path(@block_layout), notice: 'BlockLayout was successfully created.'
23
+ else
24
+ render :new
25
+ end
26
+ end
27
+
28
+ def update
29
+ authorize @block_layout
30
+
31
+ if @block_layout.update(block_layout_params)
32
+ redirect_to edit_admin_block_layout_path(@block_layout), notice: 'BlockLayout was successfully updated.'
33
+ else
34
+ render :edit
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def block_layout_params
41
+ params.require(:block_layout).permit(:slug, :display_name, :description)
42
+ end
43
+
44
+ def set_block_layout
45
+ @block_layout = BlockLayout.find(params[:id])
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ class Admin::CachePurgeController < Admin::ForestController
2
+ def index
3
+ authorize :dashboard, :index?
4
+
5
+ logger.info "[Forest] Performing user-issued cache clear"
6
+
7
+ Rails.cache.clear
8
+
9
+ redirect_to params.delete(:return_to) || admin_path, notice: "Cache has been cleared."
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class Admin::DashboardController < Admin::ForestController
2
+ def index
3
+ authorize :dashboard, :index?
4
+ @page_title = 'Dashboard'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class Admin::DocumentationController < Admin::ForestController
2
+ def index
3
+ authorize :dashboard, :index?
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class ErrorsController < ForestController
2
+ def not_found
3
+ render status: 404
4
+ end
5
+
6
+ def internal_server_error
7
+ render status: 500
8
+ end
9
+ end
@@ -0,0 +1,96 @@
1
+ class Admin::ForestController < ApplicationController
2
+ include Pundit
3
+
4
+ protect_from_forgery with: :exception, prepend: true
5
+
6
+ layout 'admin'
7
+
8
+ before_action :authenticate_user!
9
+ before_action :set_body_classes, :set_page_title
10
+ before_action :reset_class_method_cache
11
+ before_action :set_admin_resources
12
+
13
+ rescue_from ActiveRecord::InvalidForeignKey, with: :foreign_key_contraint
14
+ rescue_from ActiveRecord::RecordNotUnique, with: :record_not_unique
15
+
16
+ # rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
17
+
18
+ has_scope :by_status
19
+ has_scope :by_id
20
+ has_scope :by_title
21
+ has_scope :by_slug
22
+ has_scope :by_created_at
23
+ has_scope :by_updated_at
24
+ has_scope :by_status
25
+ has_scope :fuzzy_search
26
+
27
+ private
28
+
29
+ def set_body_classes
30
+ controller_heirarchy = self.class.name.split('::').reject(&:blank?)
31
+ controller_heirarchy.pop if controller_heirarchy.length > 1
32
+ controller_heirarchy = controller_heirarchy.collect { |a| "controller-class--#{a.underscore}" }.join(' ')
33
+
34
+ classes = []
35
+ classes << controller_heirarchy
36
+ classes << "controller--#{controller_name}"
37
+ classes << "action--#{action_name}"
38
+
39
+ @body_classes = classes.join ' '
40
+ end
41
+
42
+ def set_page_title
43
+ @page_title = controller_name.titleize
44
+ end
45
+
46
+ def reset_class_method_cache
47
+ Menu.reset_method_cache!
48
+ Setting.reset_method_cache!
49
+ end
50
+
51
+ # def user_not_authorized
52
+ # if Rails.env.production?
53
+ # redirect_to root_path
54
+ # else
55
+ # raise Pundit::NotAuthorizedError
56
+ # end
57
+ # end
58
+
59
+ def set_admin_resources
60
+ @admin_resources ||= admin_resource_names.collect do |resource_name|
61
+ resource_name.sub(/^admin_/, '').classify.safe_constantize
62
+ end.reject(&:blank?)
63
+ .reject { |r| r.class == Module }
64
+ end
65
+
66
+ def admin_resource_names
67
+ @admin_resource_names ||= Rails.application.routes.routes.collect do |route|
68
+ if route.path.spec.to_s.starts_with?('/admin') && route.requirements[:action] == 'index'
69
+ route.name
70
+ end
71
+ end.reject(&:blank?)
72
+ .uniq
73
+ .sort
74
+ end
75
+
76
+ def record
77
+ instance_variable_get("@#{controller_name.singularize}")
78
+ end
79
+
80
+ def foreign_key_contraint(exception)
81
+ if record
82
+ statusable_message = record.try(:statusable?) ? ' Alternatively, you may want to set this record\'s status to hidden instead.' : ''
83
+ redirect_to edit_polymorphic_path([:admin, record]), flash: { error: ["This record can't be deleted because another record depends on it. First remove the association to the other record before deleting this one.#{statusable_message}", "<code>#{exception.message}</code>"] }
84
+ else
85
+ raise
86
+ end
87
+ end
88
+
89
+ def record_not_unique(exception)
90
+ if record && record.try(:sluggable?)
91
+ redirect_to edit_polymorphic_path([:admin, record]), flash: { error: ["A record with this slug already exists. Please create a unique slug.", "<code>#{exception.message}</code>"] }
92
+ else
93
+ raise
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,34 @@
1
+ class Admin::ImportsController < Admin::ForestController
2
+ before_action :set_import, only: [:edit]
3
+
4
+ def edit
5
+ authorize @import
6
+
7
+ respond_to do |format|
8
+ format.html
9
+ format.csv {
10
+ send_data @import.to_csv_template, filename: "#{@import.to_s.parameterize.underscore}-import-template.csv"
11
+ }
12
+ end
13
+ end
14
+
15
+ def create
16
+ klass = params[:import][:model_name].constantize
17
+ file = params[:import][:file]
18
+
19
+ authorize klass
20
+
21
+ Import.new(klass, file)
22
+
23
+ redirect_to edit_admin_import_path(klass.model_name.singular), notice: "Import has been started and is being processed in the background."
24
+ end
25
+
26
+ private
27
+
28
+ def import_params
29
+ end
30
+
31
+ def set_import
32
+ @import = params[:id].camelcase.constantize
33
+ end
34
+ end
@@ -0,0 +1,107 @@
1
+ class Admin::MediaItemsController < Admin::ForestController
2
+ before_action :set_media_item, only: [:show, :edit, :update, :destroy]
3
+ before_action :set_s3_direct_post, only: [:new, :edit, :create, :update]
4
+
5
+ has_scope :by_date
6
+ has_scope :by_content_type
7
+ has_scope :videos, type: :boolean
8
+ has_scope :images, type: :boolean
9
+ has_scope :pdfs, type: :boolean
10
+ has_scope :fuzzy_search
11
+
12
+ # GET /media_items
13
+ def index
14
+ @media_items = apply_scopes(MediaItem.all).by_id.not_hidden.page(params[:page]).per(36)
15
+ authorize @media_items
16
+
17
+ if params[:layout].blank? || params[:layout] != 'list'
18
+ @layout = :grid
19
+ else
20
+ @layout = :list
21
+ end
22
+
23
+ respond_to :html, :json
24
+ end
25
+
26
+ # GET /media_items/1
27
+ def show
28
+ end
29
+
30
+ # GET /media_items/new
31
+ def new
32
+ @media_item = MediaItem.new
33
+ authorize @media_item
34
+ end
35
+
36
+ # GET /media_items/1/edit
37
+ def edit
38
+ authorize @media_item
39
+ end
40
+
41
+ # POST /media_items
42
+ def create
43
+ @media_item = MediaItem.new(media_item_params)
44
+ authorize @media_item
45
+
46
+ respond_to do |format|
47
+ if @media_item.save
48
+ format.html { redirect_to edit_admin_media_item_path(@media_item), notice: 'Media item was successfully created.' }
49
+ format.json { render json: { files: [@media_item.to_jq_upload]}, status: :created, location: [:admin, @media_item] }
50
+ else
51
+ format.html { render :new }
52
+ format.json { render json: @media_item.errors, status: :unprocessable_entity }
53
+ end
54
+ end
55
+ end
56
+
57
+ # PATCH/PUT /media_items/1
58
+ def update
59
+ authorize @media_item
60
+ if @media_item.update(media_item_params)
61
+ redirect_to edit_admin_media_item_path(@media_item), notice: 'Media item was successfully updated.'
62
+ else
63
+ render :edit
64
+ end
65
+ end
66
+
67
+ # PATCH/PUT /media_items
68
+ def update_multiple
69
+ if params['selected']&.any?
70
+ @media_items = MediaItem.where(id: params[:selected])
71
+ authorize @media_items
72
+ if params[:bulk_actions] == 'delete'
73
+ @media_items.destroy_all
74
+ notice = 'Media items were successfully destroyed.'
75
+ else
76
+ notice = 'Please select a bulk action.'
77
+ end
78
+ else
79
+ notice = 'Please select a media item.'
80
+ end
81
+ redirect_to admin_media_items_url, notice: notice
82
+ end
83
+
84
+ # DELETE /media_items/1
85
+ def destroy
86
+ authorize @media_item
87
+ @media_item.destroy
88
+ redirect_to admin_media_items_url, notice: 'Media item was successfully destroyed.'
89
+ end
90
+
91
+ private
92
+ # Use callbacks to share common setup or constraints between actions.
93
+ def set_media_item
94
+ @media_item = MediaItem.find(params[:id])
95
+ end
96
+
97
+ # Only allow a trusted parameter "white list" through.
98
+ def media_item_params
99
+ params.require(:media_item).permit(:title, :slug, :caption, :alternative_text, :description, :attachment, :selected, :point_of_interest_x, :point_of_interest_y)
100
+ end
101
+
102
+ def set_s3_direct_post
103
+ if defined? S3_BUCKET
104
+ @s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,69 @@
1
+ class Admin::MenusController < Admin::ForestController
2
+ before_action :set_menu, only: [:show, :edit, :update, :destroy]
3
+ before_action :set_pages, only: [:edit, :update, :new]
4
+
5
+ # GET /menus
6
+ def index
7
+ @menus = apply_scopes(Menu.order(id: :asc)).page(params[:page])
8
+ authorize @menus
9
+ end
10
+
11
+ # GET /menus/1
12
+ def show
13
+ end
14
+
15
+ # GET /menus/new
16
+ def new
17
+ @menu = Menu.new
18
+ authorize @menu
19
+ end
20
+
21
+ # GET /menus/1/edit
22
+ def edit
23
+ authorize @menu
24
+ end
25
+
26
+ # POST /menus
27
+ def create
28
+ @menu = Menu.new(menu_params)
29
+ authorize @menu
30
+
31
+ if @menu.save
32
+ redirect_to edit_admin_menu_path(@menu), notice: 'Menu was successfully created.'
33
+ else
34
+ render :new
35
+ end
36
+ end
37
+
38
+ # PATCH/PUT /menus/1
39
+ def update
40
+ authorize @menu
41
+ if @menu.update(menu_params)
42
+ redirect_to edit_admin_menu_path(@menu), notice: 'Menu was successfully updated.'
43
+ else
44
+ render :edit
45
+ end
46
+ end
47
+
48
+ # DELETE /menus/1
49
+ def destroy
50
+ authorize @menu
51
+ @menu.destroy
52
+ redirect_to admin_menus_url, notice: 'Menu was successfully destroyed.'
53
+ end
54
+
55
+ private
56
+ # Use callbacks to share common setup or constraints between actions.
57
+ def set_menu
58
+ @menu = Menu.find(params[:id])
59
+ end
60
+
61
+ def set_pages
62
+ @pages = Page.page(1)
63
+ end
64
+
65
+ # Only allow a trusted parameter "white list" through.
66
+ def menu_params
67
+ params.require(:menu).permit(:title, :slug, :structure)
68
+ end
69
+ end
@@ -0,0 +1,105 @@
1
+ class Admin::PagesController < Admin::ForestController
2
+ before_action :set_page, only: [:show, :edit, :update, :destroy]
3
+ before_action :set_block_layout, only: [:create, :edit, :new]
4
+ before_action :set_block_kinds, only: [:create, :edit, :new]
5
+
6
+ has_scope :by_parent_page
7
+ has_scope :title_like
8
+
9
+ def index
10
+ if request.format.json?
11
+ @pages = apply_scopes(Page).by_title.where.not(id: params[:current_record]).page(params[:page])
12
+ else
13
+ # TODO: fuzzy searching doesn't work properly with page hierarchy
14
+ @parent_pages = apply_scopes(Page.includes(:immediate_children)).parent_pages.page(params[:page])
15
+ @pages = apply_scopes(Page).by_title.page params[:page]
16
+ end
17
+
18
+ authorize @pages
19
+ respond_to :html, :json
20
+ end
21
+
22
+ # def show
23
+ # unless @page
24
+ # raise ActionController::RoutingError.new('Not Found')
25
+ # end
26
+ # authorize @page
27
+ # redirect_to edit_admin_page_path(@page)
28
+ # end
29
+
30
+ def new
31
+ @page = Page.new
32
+ authorize @page
33
+ end
34
+
35
+ def edit
36
+ authorize @page
37
+ end
38
+
39
+ def create
40
+ @page = Page.new
41
+ authorize @page
42
+
43
+ respond_to do |format|
44
+ if @page.update(page_params)
45
+ format.html { redirect_to edit_admin_page_path(@page), notice: 'Page was successfully created.' }
46
+ format.json { render :show, status: :created, location: @page }
47
+ else
48
+ format.html { render :new }
49
+ format.json { render json: @page.errors, status: :unprocessable_entity }
50
+ end
51
+ end
52
+ end
53
+
54
+ def update
55
+ authorize @page
56
+
57
+ respond_to do |format|
58
+ if @page.update(page_params)
59
+ format.html { redirect_to edit_admin_page_path(@page), notice: 'Page was successfully updated.' }
60
+ format.json { render :show, status: :ok, location: @page }
61
+ else
62
+ format.html { render :edit }
63
+ format.json { render json: @page.errors, status: :unprocessable_entity }
64
+ end
65
+ end
66
+ end
67
+
68
+ def destroy
69
+ authorize @page
70
+ @page.destroy
71
+ respond_to do |format|
72
+ format.html { redirect_to admin_pages_url, notice: 'Page was successfully destroyed.' }
73
+ format.json { head :no_content }
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def page_params
80
+ page_attributes = [
81
+ :title, :slug, :description, :status, :featured_image_id, :redirect,
82
+ :media_item_ids, :parent_page_id, :ancestor_page_id, :scheduled_date, :path,
83
+ **BlockSlot.blockable_params
84
+ ]
85
+ # TODO: come up with a better pattern for adding additional params via the host app, rather than
86
+ # just permitting all other column_names of the class.
87
+ column_names = (@page.class.column_names.collect(&:to_sym) - page_attributes).reject { |a| [:id, :created_at, :updated_at].include?(a) }
88
+ # Attributes from store_accessors
89
+ stored_attributes = @page.class.stored_attributes.values.flatten
90
+ permitted_attributes = page_attributes + column_names + stored_attributes
91
+ params.require(:page).permit(permitted_attributes)
92
+ end
93
+
94
+ def set_page
95
+ @page = Page.includes(block_slots: [:block_kind, :block]).find(params[:id])
96
+ end
97
+
98
+ def set_block_layout
99
+ @block_layout = BlockLayout.find_by_slug('default')
100
+ end
101
+
102
+ def set_block_kinds
103
+ @block_kinds = BlockKind.all
104
+ end
105
+ end