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,12 @@
1
+ module AdminInfoHelper
2
+ def info_message(message)
3
+ [
4
+ '<div class="well">',
5
+ '<div class="text-muted">',
6
+ '<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> ',
7
+ message,
8
+ '</div>',
9
+ '</div>'
10
+ ].join.html_safe
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module AdminSettingsHelper
2
+
3
+ def admin_setting_value(setting)
4
+ return setting.value if setting.value.blank?
5
+
6
+ case setting.value_type
7
+ when 'boolean'
8
+ setting.value == '1' ? 'True' : 'False'
9
+ when 'image'
10
+ URI(setting.value).path.split('/').last
11
+ else
12
+ setting.value
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,25 @@
1
+ module BlockHelper
2
+
3
+ def render_blocks(record, options = {})
4
+ return if record.blank?
5
+
6
+ begin
7
+ block_layout = options[:block_layout].presence || BlockLayout.default_layout
8
+
9
+ if block_layout.is_a?(Symbol)
10
+ block_layout = BlockLayout.find_by_slug(block_layout)
11
+ end
12
+
13
+ collection = record.blocks(block_layout: block_layout).reject(&:hidden?)
14
+
15
+ if collection.present?
16
+ content_tag :div, class: "blocks block-layout--#{block_layout.slug}" do
17
+ render partial: 'blocks/show', collection: collection, as: 'block'
18
+ end
19
+ end
20
+ rescue Exception => e
21
+ forest_admin_error(e)
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,23 @@
1
+ module CacheHelper
2
+ def cache_key_for(collection)
3
+ count = collection.try(:count) || 1
4
+ if collection.is_a? Array
5
+ return if collection.blank?
6
+ max_updated_at = collection.reject(&:blank?).max_by(&:updated_at).updated_at.try(:utc).try(:to_s, :number)
7
+ "#{collection.first.model_name.plural}/all-#{count}-#{max_updated_at}"
8
+ elsif collection.try :updated_at
9
+ "#{collection.model_name.plural}/all-#{count}-#{collection.updated_at.try(:utc).try(:to_s, :number)}"
10
+ else
11
+ max_updated_at = collection.maximum(:updated_at).try(:utc).try(:to_s, :number)
12
+ "#{collection.model_name.plural}/all-#{count}-#{max_updated_at}"
13
+ end
14
+ end
15
+
16
+ def cache_digest_for(stringable)
17
+ Digest::MD5.hexdigest(stringable.to_s)
18
+ end
19
+
20
+ def application_cache_key
21
+ @_application_cache_key ||= Setting.application_cache_key
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module ForestErrorsHelper
2
+ # Display errors that are only visible to admin users
3
+ def forest_admin_error(error, options = {})
4
+ if Rails.env.production?
5
+ if current_user&.admin?
6
+ logger.error("[Forest] forest_admin_error")
7
+ logger.error(error.message)
8
+ logger.error(error.backtrace.join("\n"))
9
+
10
+ error_messages = []
11
+ error_messages << "Error on #{Rails.application.class.parent.to_s.titleize}\n"
12
+ error_messages << "Path: #{request.fullpath.force_encoding('utf-8')}\n"
13
+ error_messages << "#{error.message}\n"
14
+ error_messages << error.backtrace.first(20).join("\n")
15
+
16
+ visible_errors_for_admin = []
17
+ visible_errors_for_admin << options.fetch(:message, "Warning: this code block has errors and is not visible to the public.\nYou are seeing this message because you are an admin.\n")
18
+ visible_errors_for_admin << mail_to('mailto:hi@dylanfisher.com',
19
+ "Click here to email this error to the developers.\n",
20
+ subject: "Error on #{Rails.application.class.parent.to_s.titleize} - #{request.path.force_encoding('utf-8')}",
21
+ body: error_messages.join("\n").html_safe,
22
+ target: '_blank')
23
+ visible_errors_for_admin << "#{error.message}\n"
24
+ visible_errors_for_admin << error.backtrace.join("\n")
25
+
26
+ content_tag :pre, class: 'admin-error-message' do
27
+ visible_errors_for_admin.join("\n").html_safe
28
+ end
29
+ end
30
+ else
31
+ raise error
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,93 @@
1
+ module FormHelper
2
+
3
+ # TODO:
4
+ # - enable multiple selections at the same time, e.g. merge the current_scopes
5
+
6
+ def dropdown_select_form(options = {})
7
+ url = options.fetch :url
8
+ id = options.fetch :id
9
+ select_options = options.fetch :select_options
10
+ title = options.fetch :title, nil
11
+ placeholder = options.fetch :placeholder, nil
12
+ include_blank = options.fetch :include_blank, nil
13
+ css_class = options.fetch :css_class, nil
14
+ remote_parent = options.fetch :remote_parent, nil
15
+ remote_target = options.fetch :remote_target, nil
16
+ remote_response_target = options.fetch :remote_response_target, nil
17
+
18
+ if remote_parent && remote_target && remote_response_target
19
+ remote = true
20
+ end
21
+
22
+ if remote
23
+ form_tag_base_css_class = 'select-tag-remote-form'
24
+ else
25
+ form_tag_base_css_class = 'select-tag-form'
26
+ end
27
+
28
+ form_tag url, method: :get, enforce_utf8: false, class: "#{form_tag_base_css_class} #{css_class}" do
29
+ # concat params_as_hidden_fields id
30
+ concat select_tag_dropdown id: id,
31
+ select_options: select_options,
32
+ title: title,
33
+ placeholder: placeholder,
34
+ include_blank: include_blank,
35
+ selected: params[id],
36
+ remote_parent: remote_parent,
37
+ remote_target: remote_target,
38
+ remote_response_target: remote_response_target
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def select_tag_dropdown(options = {})
45
+ id = options.fetch :id
46
+ select_options = options.fetch :select_options
47
+ title = options.fetch :title, nil
48
+ placeholder = options.fetch :placeholder, nil
49
+ include_blank = options.fetch :include_blank, nil
50
+ selected = options.fetch :selected, nil
51
+ remote_parent = options.fetch :remote_parent, nil
52
+ remote_target = options.fetch :remote_target, nil
53
+ remote_response_target = options.fetch :remote_response_target, nil
54
+ remote_attributes = {}
55
+ options_for_select = title ? select_options.unshift([title, '']) : select_options
56
+
57
+ if remote_parent && remote_target && remote_response_target
58
+ remote = true
59
+ end
60
+
61
+ if remote
62
+ css_class = 'select-tag--remote'
63
+ remote_attributes = {
64
+ remote_parent: remote_parent,
65
+ remote_target: remote_target,
66
+ remote_response_target: remote_response_target
67
+ }
68
+ else
69
+ css_class = 'select-tag--default'
70
+ end
71
+
72
+ select_tag id,
73
+ options_for_select(options_for_select, selected),
74
+ placeholder: placeholder.presence || 'false',
75
+ id: id,
76
+ class: css_class,
77
+ include_blank: include_blank,
78
+ style: 'width: 100%',
79
+ data: {
80
+ form_title: title,
81
+ **remote_attributes
82
+ }
83
+ end
84
+
85
+ def params_as_hidden_fields(additional_params_to_ignore)
86
+ params_to_ignore = :controller, :action, :locale, :utf8
87
+ params_to_process = params_to_ignore.reject { |param| param == additional_params_to_ignore.to_s }
88
+ params_to_process.collect do |param|
89
+ hidden_field_tag param
90
+ end.join.html_safe
91
+ end
92
+
93
+ end
@@ -0,0 +1,65 @@
1
+ module ImageHelper
2
+ # Embed an SVG file inline, allowing it to be styled with CSS.
3
+ def embedded_svg(filename, options={})
4
+ file = File.read(Rails.root.join('app', 'assets', 'images', 'svg', "#{filename}.svg"))
5
+ doc = Nokogiri::HTML::DocumentFragment.parse file
6
+ svg = doc.at_css 'svg'
7
+ if options[:class].present?
8
+ svg['class'] = "#{svg['class']} #{options[:class]}"
9
+ end
10
+ if options[:id].present?
11
+ svg['id'] = options[:id]
12
+ end
13
+ if options[:style].present?
14
+ svg['style'] = "#{svg['style']} #{options[:style]}"
15
+ end
16
+ doc.to_html.html_safe
17
+ end
18
+
19
+ def responsive_image(media_item, options = {})
20
+ # TODO: DF 08/04/17 - return a missing image if media item is blank?
21
+ # return if media_item.blank?
22
+ css_class = options.fetch :class, nil
23
+ image_set_tag media_item.attachment.url(:small), {
24
+ media_item.attachment.url(:medium) => '1200w',
25
+ media_item.attachment.url(:large) => '2000w'
26
+ },
27
+ options.merge(
28
+ sizes: options.fetch(:sizes, '100vw'),
29
+ class: "responsive-image #{css_class}"
30
+ )
31
+ end
32
+
33
+ # https://gist.github.com/mrreynolds/4fc71c8d09646567111f
34
+ # Acts as a thin wrapper for image_tag and generates an srcset attribute for regular image tags
35
+ # for usage with responsive images polyfills like picturefill.js, supports asset pipeline path helpers.
36
+ #
37
+ # image_set_tag 'pic_1980.jpg', { 'pic_640.jpg' => '640w', 'pic_1024.jpg' => '1024w', 'pic_1980.jpg' => '1980w' }, sizes: '100vw', class: 'my-image'
38
+ #
39
+ # => <img src="/assets/ants_1980.jpg" srcset="/assets/pic_640.jpg 640w, /assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw" class="my-image">
40
+ def image_set_tag(source, srcset = {}, options = {})
41
+ srcset = srcset.map { |src, size| "#{asset_path(src)} #{size}" }.join(', ')
42
+ image_tag source, options.merge(srcset: srcset)
43
+ end
44
+
45
+ # Prevent images from thrashing your page's layout with the image_jump_fix helper.
46
+ #
47
+ # <%= image_jump_fix block.media_item do %>
48
+ # <%= image_tag block.media_item.attachment.url(:medium) %>
49
+ # <% end %>
50
+ def image_jump_fix(media_item, options = {})
51
+ width = media_item.try(:dimensions).try(:[], :width) || media_item.try(:attachment_width)
52
+ height = media_item.try(:dimensions).try(:[], :height) || media_item.try(:attachment_height)
53
+ tag_type = options.delete(:tag) || :div
54
+ css_class = options[:class]
55
+
56
+ if [width, height].all?
57
+ ratio = height.to_f / width.to_f * 100
58
+ padding_bottom = "padding-bottom: #{ratio}%;"
59
+ end
60
+
61
+ content_tag tag_type, class: "forest-image-jump-fix #{('forest-image-jump-fix--' + media_item.attachment_content_type.parameterize) if media_item.try(:attachment_content_type).present?} #{css_class}", style: padding_bottom do
62
+ yield
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ require 'redcarpet/render_strip'
2
+
3
+ module MarkdownHelper
4
+ def md(text)
5
+ parser.render(text.to_s).html_safe
6
+ end
7
+
8
+ def stripdown(text)
9
+ stripper.render(text.to_s).squish
10
+ end
11
+
12
+ private
13
+
14
+ def parser
15
+ @parser ||= Redcarpet::Markdown.new(renderer, Forest::MarkdownRenderer.options)
16
+ end
17
+
18
+ def stripper
19
+ @stripper ||= Redcarpet::Markdown.new(Redcarpet::Render::StripDown)
20
+ end
21
+
22
+ def renderer
23
+ @renderer ||= Forest::MarkdownRenderer.new(Forest::MarkdownRenderer.render_options)
24
+ end
25
+ end
@@ -0,0 +1,238 @@
1
+ module MetaHelper
2
+ def meta_page_title
3
+ @_meta_page_title ||= begin
4
+ title = []
5
+
6
+ if record_to_build_from.try(:seo_title).present?
7
+ title << record_to_build_from.seo_title
8
+ else
9
+ title << page_title unless controller_name == 'home_pages'
10
+ title << site_title
11
+ end
12
+
13
+ title = title.reject(&:blank?).join(divider)
14
+ stripdown(title).squish
15
+ end
16
+ end
17
+
18
+ def page_title
19
+ @_page_title ||= begin
20
+ return if @disable_page_title.present?
21
+ title = [
22
+ content_for(:page_title),
23
+ @page_title,
24
+ build_page_title_from_record,
25
+ build_page_title_from_controller
26
+ ].reject(&:blank?).first
27
+ stripdown(title).squish
28
+ end
29
+ end
30
+
31
+ def site_title
32
+ @_site_title ||= begin
33
+ title = Setting.for('site_title') || default_site_title
34
+ stripdown(title).squish
35
+ end
36
+ end
37
+
38
+ def page_description
39
+ @_page_description ||= begin
40
+ description = [
41
+ content_for(:page_description),
42
+ @page_description,
43
+ build_page_description_from_record,
44
+ site_description
45
+ ].reject(&:blank?).first.try(:squish)
46
+ stripdown(description).squish
47
+ end
48
+ end
49
+
50
+ def page_keywords
51
+ @_page_keywords ||= begin
52
+ [
53
+ content_for(:page_keywords),
54
+ Setting.for('page_keywords')
55
+ ].reject(&:blank?).first.try(:squish)
56
+ end
57
+ end
58
+
59
+ def page_featured_image
60
+ @_page_featured_image ||= begin
61
+ image = [
62
+ content_for(:page_featured_image),
63
+ @page_featured_image,
64
+ build_page_featured_image_from_record,
65
+ site_featured_image
66
+ ].reject(&:blank?).first
67
+
68
+ if image.respond_to?(:attachment)
69
+ image = image.attachment
70
+ end
71
+
72
+ image
73
+ end
74
+ end
75
+
76
+ def page_featured_image_url
77
+ begin
78
+ @_page_featured_image_url ||= begin
79
+ if page_featured_image.is_a?(String)
80
+ url = page_featured_image
81
+ end
82
+
83
+ if page_featured_image.is_a?(Paperclip::Attachment)
84
+ if page_featured_image.options[:styles].keys.include?(:large)
85
+ style = :large
86
+ else
87
+ style = page_featured_image.options[:styles].keys.last
88
+ end
89
+ url = page_featured_image.url(style)
90
+ end
91
+
92
+ url
93
+ end
94
+ rescue Exception => e
95
+ end
96
+ end
97
+
98
+ def page_featured_image_width
99
+ @_page_featured_image_width ||= begin
100
+ return unless page_featured_image.is_a?(Paperclip::Attachment)
101
+ @page_featured_image_width ||
102
+ page_featured_image.instance.try(:dimensions).try(:[], :width) ||
103
+ page_featured_image.instance.try(:attachment_width)
104
+ end
105
+ end
106
+
107
+ def page_featured_image_height
108
+ @_page_featured_image_height ||= begin
109
+ return unless page_featured_image.is_a?(Paperclip::Attachment)
110
+ @page_featured_image_height ||
111
+ page_featured_image.instance.try(:dimensions).try(:[], :height) ||
112
+ page_featured_image.instance.try(:attachment_height)
113
+ end
114
+ end
115
+
116
+ def page_featured_image_type
117
+ @_page_featured_image_type ||= begin
118
+ return unless page_featured_image.is_a?(Paperclip::Attachment)
119
+ page_featured_image.try(:attachment_content_type)
120
+ end
121
+ end
122
+
123
+ def page_featured_image_alt
124
+ @_page_featured_image_alt ||= begin
125
+ return unless page_featured_image.is_a?(Paperclip::Attachment)
126
+ page_featured_image.try(:instance).try(:alternative_text)
127
+ end
128
+ end
129
+
130
+ def site_description
131
+ @_site_description ||= begin
132
+ (Setting.for('description') || default_site_description).try(:squish)
133
+ end
134
+ end
135
+
136
+ def site_featured_image
137
+ @_site_featured_image ||= begin
138
+ Setting.for('featured_image')
139
+ end
140
+ end
141
+
142
+ def meta_see_also
143
+ @_meta_see_also ||= begin
144
+ [
145
+ content_for(:meta_see_also),
146
+ root_path
147
+ ].reject(&:blank?).first
148
+ end
149
+ end
150
+
151
+ def meta_type
152
+ @_meta_type ||= begin
153
+ [
154
+ content_for(:meta_type),
155
+ default_meta_type
156
+ ].reject(&:blank?).first
157
+ end
158
+ end
159
+
160
+ def meta_url
161
+ @_meta_url ||= begin
162
+ [
163
+ content_for(:meta_url),
164
+ (request.base_url.force_encoding('utf-8') + request.path.force_encoding('utf-8'))
165
+ ].reject(&:blank?).first
166
+ end
167
+ end
168
+
169
+ def meta_viewport
170
+ @_meta_viewport ||= begin
171
+ [
172
+ content_for(:meta_viewport),
173
+ 'width=device-width, initial-scale=1.0'
174
+ ].reject(&:blank?).first
175
+ end
176
+ end
177
+
178
+ def additional_meta_tags
179
+ content_for(:additional_meta_tags)
180
+ end
181
+
182
+ private
183
+
184
+ def default_site_title
185
+ 'Forest CMS'
186
+ end
187
+
188
+ def default_site_description
189
+ 'This site was built with Forest, a Ruby on Rails CMS.'
190
+ end
191
+
192
+ def default_meta_type
193
+ 'website'
194
+ end
195
+
196
+ def divider(spacer = ' - ')
197
+ spacer
198
+ end
199
+
200
+ def build_page_title_from_record
201
+ record_to_build_from.try(:to_page_title) ||
202
+ record_to_build_from.try(:title) ||
203
+ record_to_build_from.try(:name)
204
+ end
205
+
206
+ def build_page_title_from_controller
207
+ if controller_name == 'home_pages'
208
+ 'Home'
209
+ else
210
+ controller_name.titleize
211
+ end
212
+ end
213
+
214
+ def build_page_description_from_record
215
+ @_build_page_description_from_record ||= begin
216
+ description = record_to_build_from.try(:to_page_description) ||
217
+ record_to_build_from.try(:description)
218
+ truncate(description, length: 240)
219
+ end
220
+ end
221
+
222
+ def build_page_featured_image_from_record
223
+ @_build_page_featured_image_from_record ||= begin
224
+ record_to_build_from.try(:featured_image).presence ||
225
+ record_to_build_from.try(:media_item).presence ||
226
+ record_to_build_from.try(:blockable_metadata).try(:[], 'featured_image_url').presence
227
+ end
228
+ end
229
+
230
+ def record_to_build_from(record = nil)
231
+ @_record_to_build_from ||= begin
232
+ record.presence ||
233
+ @page.presence ||
234
+ @record.presence ||
235
+ instance_variable_get("@#{controller_name.singularize}").presence
236
+ end
237
+ end
238
+ end