comfy_middle_seat 3.2.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 (436) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +4 -0
  3. data/.gitattributes +3 -0
  4. data/.github/dependabot.yml +7 -0
  5. data/.github/issue_template.md +19 -0
  6. data/.github/pull_request_template.md +6 -0
  7. data/.github/workflows/claude-code-review.yml +46 -0
  8. data/.github/workflows/claude.yml +148 -0
  9. data/.github/workflows/coveralls.yml +53 -0
  10. data/.github/workflows/rubyonrails.yml +94 -0
  11. data/.gitignore +25 -0
  12. data/.rubocop.yml +82 -0
  13. data/.travis.yml +44 -0
  14. data/CHANGELOG.md +147 -0
  15. data/CONTRIBUTING.md +42 -0
  16. data/Gemfile +44 -0
  17. data/LICENSE +20 -0
  18. data/README.md +158 -0
  19. data/Rakefile +206 -0
  20. data/app/assets/builds/.keep +0 -0
  21. data/app/assets/config/manifest.js +1 -0
  22. data/app/assets/fonts/comfy/admin/cms/lib/redactor-font.eot +0 -0
  23. data/app/assets/javascripts/comfy/admin/cms/application.js +27 -0
  24. data/app/assets/javascripts/comfy/admin/cms/base.js +48 -0
  25. data/app/assets/javascripts/comfy/admin/cms/categories.js +17 -0
  26. data/app/assets/javascripts/comfy/admin/cms/codemirror.js +43 -0
  27. data/app/assets/javascripts/comfy/admin/cms/custom.js +1 -0
  28. data/app/assets/javascripts/comfy/admin/cms/diff.js +12 -0
  29. data/app/assets/javascripts/comfy/admin/cms/file_link.js +96 -0
  30. data/app/assets/javascripts/comfy/admin/cms/file_upload.js +229 -0
  31. data/app/assets/javascripts/comfy/admin/cms/files_modal.js +97 -0
  32. data/app/assets/javascripts/comfy/admin/cms/page_fragments.js +22 -0
  33. data/app/assets/javascripts/comfy/admin/cms/slugify.js +34 -0
  34. data/app/assets/javascripts/comfy/admin/cms/sortable_list.js +49 -0
  35. data/app/assets/javascripts/comfy/admin/cms/timepicker.js +38 -0
  36. data/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +91 -0
  37. data/app/assets/javascripts/comfy/vendor/diff/diff_match_patch.min.js +49 -0
  38. data/app/assets/javascripts/comfy/vendor/diff/pretty_text_diff.js +88 -0
  39. data/app/assets/javascripts/comfy/vendor/flatpickr.min.js +2 -0
  40. data/app/assets/javascripts/comfy/vendor/fontawesome.js +5 -0
  41. data/app/assets/javascripts/comfy/vendor/redactor/definedlinks.js +50 -0
  42. data/app/assets/javascripts/comfy/vendor/redactor/filemanager.js +79 -0
  43. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ar.js +76 -0
  44. data/app/assets/javascripts/comfy/vendor/redactor/i18n/az.js +75 -0
  45. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ba.js +79 -0
  46. data/app/assets/javascripts/comfy/vendor/redactor/i18n/bg.js +77 -0
  47. data/app/assets/javascripts/comfy/vendor/redactor/i18n/by.js +77 -0
  48. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ca.js +75 -0
  49. data/app/assets/javascripts/comfy/vendor/redactor/i18n/cs.js +87 -0
  50. data/app/assets/javascripts/comfy/vendor/redactor/i18n/da.js +75 -0
  51. data/app/assets/javascripts/comfy/vendor/redactor/i18n/de.js +80 -0
  52. data/app/assets/javascripts/comfy/vendor/redactor/i18n/el.js +76 -0
  53. data/app/assets/javascripts/comfy/vendor/redactor/i18n/en.js +75 -0
  54. data/app/assets/javascripts/comfy/vendor/redactor/i18n/eo.js +76 -0
  55. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es.js +75 -0
  56. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es_AR.js +76 -0
  57. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fa.js +76 -0
  58. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fi.js +75 -0
  59. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fr.js +75 -0
  60. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ge.js +75 -0
  61. data/app/assets/javascripts/comfy/vendor/redactor/i18n/gr.js +75 -0
  62. data/app/assets/javascripts/comfy/vendor/redactor/i18n/he.js +75 -0
  63. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hr.js +78 -0
  64. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hu.js +77 -0
  65. data/app/assets/javascripts/comfy/vendor/redactor/i18n/id.js +76 -0
  66. data/app/assets/javascripts/comfy/vendor/redactor/i18n/it.js +78 -0
  67. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ja.js +76 -0
  68. data/app/assets/javascripts/comfy/vendor/redactor/i18n/kn.js +75 -0
  69. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ko.js +76 -0
  70. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lt.js +75 -0
  71. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lv.js +75 -0
  72. data/app/assets/javascripts/comfy/vendor/redactor/i18n/mk.js +75 -0
  73. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nb.js +77 -0
  74. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nl.js +80 -0
  75. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pl.js +78 -0
  76. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt-BR.js +75 -0
  77. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt.js +75 -0
  78. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ro.js +76 -0
  79. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ru.js +76 -0
  80. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sk.js +76 -0
  81. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sl.js +79 -0
  82. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sq.js +79 -0
  83. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-CIR.js +79 -0
  84. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-LAT.js +79 -0
  85. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sv.js +74 -0
  86. data/app/assets/javascripts/comfy/vendor/redactor/i18n/th.js +74 -0
  87. data/app/assets/javascripts/comfy/vendor/redactor/i18n/tr.js +75 -0
  88. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ua.js +77 -0
  89. data/app/assets/javascripts/comfy/vendor/redactor/i18n/uk.js +77 -0
  90. data/app/assets/javascripts/comfy/vendor/redactor/i18n/vi.js +74 -0
  91. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-CN.js +75 -0
  92. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-TW.js +75 -0
  93. data/app/assets/javascripts/comfy/vendor/redactor/imagemanager.js +68 -0
  94. data/app/assets/javascripts/comfy/vendor/redactor/table.js +348 -0
  95. data/app/assets/javascripts/comfy/vendor/redactor/video.js +82 -0
  96. data/app/assets/javascripts/comfy/vendor/redactor.js +9563 -0
  97. data/app/assets/stylesheets/comfy/admin/cms/application.sass +11 -0
  98. data/app/assets/stylesheets/comfy/admin/cms/base.sass +256 -0
  99. data/app/assets/stylesheets/comfy/admin/cms/codemirror_overrides.sass +19 -0
  100. data/app/assets/stylesheets/comfy/admin/cms/custom.sass +1 -0
  101. data/app/assets/stylesheets/comfy/admin/cms/redactor_overrides.sass +22 -0
  102. data/app/assets/stylesheets/comfy/vendor/bootstrap/_alert.scss +51 -0
  103. data/app/assets/stylesheets/comfy/vendor/bootstrap/_badge.scss +54 -0
  104. data/app/assets/stylesheets/comfy/vendor/bootstrap/_breadcrumb.scss +41 -0
  105. data/app/assets/stylesheets/comfy/vendor/bootstrap/_button-group.scss +163 -0
  106. data/app/assets/stylesheets/comfy/vendor/bootstrap/_buttons.scss +137 -0
  107. data/app/assets/stylesheets/comfy/vendor/bootstrap/_card.scss +289 -0
  108. data/app/assets/stylesheets/comfy/vendor/bootstrap/_carousel.scss +197 -0
  109. data/app/assets/stylesheets/comfy/vendor/bootstrap/_close.scss +41 -0
  110. data/app/assets/stylesheets/comfy/vendor/bootstrap/_code.scss +48 -0
  111. data/app/assets/stylesheets/comfy/vendor/bootstrap/_custom-forms.scss +507 -0
  112. data/app/assets/stylesheets/comfy/vendor/bootstrap/_dropdown.scss +191 -0
  113. data/app/assets/stylesheets/comfy/vendor/bootstrap/_forms.scss +330 -0
  114. data/app/assets/stylesheets/comfy/vendor/bootstrap/_functions.scss +86 -0
  115. data/app/assets/stylesheets/comfy/vendor/bootstrap/_grid.scss +52 -0
  116. data/app/assets/stylesheets/comfy/vendor/bootstrap/_images.scss +42 -0
  117. data/app/assets/stylesheets/comfy/vendor/bootstrap/_input-group.scss +193 -0
  118. data/app/assets/stylesheets/comfy/vendor/bootstrap/_jumbotron.scss +17 -0
  119. data/app/assets/stylesheets/comfy/vendor/bootstrap/_list-group.scss +149 -0
  120. data/app/assets/stylesheets/comfy/vendor/bootstrap/_media.scss +8 -0
  121. data/app/assets/stylesheets/comfy/vendor/bootstrap/_mixins.scss +47 -0
  122. data/app/assets/stylesheets/comfy/vendor/bootstrap/_modal.scss +229 -0
  123. data/app/assets/stylesheets/comfy/vendor/bootstrap/_nav.scss +120 -0
  124. data/app/assets/stylesheets/comfy/vendor/bootstrap/_navbar.scss +294 -0
  125. data/app/assets/stylesheets/comfy/vendor/bootstrap/_pagination.scss +73 -0
  126. data/app/assets/stylesheets/comfy/vendor/bootstrap/_popover.scss +171 -0
  127. data/app/assets/stylesheets/comfy/vendor/bootstrap/_print.scss +141 -0
  128. data/app/assets/stylesheets/comfy/vendor/bootstrap/_progress.scss +43 -0
  129. data/app/assets/stylesheets/comfy/vendor/bootstrap/_reboot.scss +483 -0
  130. data/app/assets/stylesheets/comfy/vendor/bootstrap/_root.scss +19 -0
  131. data/app/assets/stylesheets/comfy/vendor/bootstrap/_spinners.scss +55 -0
  132. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tables.scss +185 -0
  133. data/app/assets/stylesheets/comfy/vendor/bootstrap/_toasts.scss +44 -0
  134. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tooltip.scss +115 -0
  135. data/app/assets/stylesheets/comfy/vendor/bootstrap/_transitions.scss +20 -0
  136. data/app/assets/stylesheets/comfy/vendor/bootstrap/_type.scss +125 -0
  137. data/app/assets/stylesheets/comfy/vendor/bootstrap/_utilities.scss +17 -0
  138. data/app/assets/stylesheets/comfy/vendor/bootstrap/_variables.scss +1123 -0
  139. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-grid.scss +29 -0
  140. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-reboot.scss +12 -0
  141. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap.scss +44 -0
  142. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_alert.scss +13 -0
  143. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_background-variant.scss +21 -0
  144. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_badge.scss +17 -0
  145. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_border-radius.scss +63 -0
  146. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_box-shadow.scss +20 -0
  147. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_breakpoints.scss +123 -0
  148. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_buttons.scss +107 -0
  149. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_caret.scss +62 -0
  150. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_clearfix.scss +7 -0
  151. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_deprecate.scss +10 -0
  152. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_float.scss +14 -0
  153. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_forms.scss +192 -0
  154. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_gradients.scss +45 -0
  155. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid-framework.scss +66 -0
  156. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid.scss +51 -0
  157. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_hover.scss +37 -0
  158. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_image.scss +36 -0
  159. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_list-group.scss +21 -0
  160. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_lists.scss +7 -0
  161. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_nav-divider.scss +10 -0
  162. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_pagination.scss +22 -0
  163. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_reset-text.scss +17 -0
  164. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_resize.scss +6 -0
  165. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_screen-reader.scss +33 -0
  166. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_size.scss +7 -0
  167. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_table-row.scss +39 -0
  168. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-emphasis.scss +16 -0
  169. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-hide.scss +11 -0
  170. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-truncate.scss +8 -0
  171. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_transition.scss +16 -0
  172. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_visibility.scss +8 -0
  173. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_align.scss +8 -0
  174. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_background.scss +19 -0
  175. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_borders.scss +75 -0
  176. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_clearfix.scss +3 -0
  177. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_display.scss +26 -0
  178. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_embed.scss +39 -0
  179. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_flex.scss +51 -0
  180. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_float.scss +11 -0
  181. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_overflow.scss +5 -0
  182. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_position.scss +32 -0
  183. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_screenreaders.scss +11 -0
  184. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_shadows.scss +6 -0
  185. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_sizing.scss +20 -0
  186. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_spacing.scss +73 -0
  187. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_stretched-link.scss +19 -0
  188. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_text.scss +72 -0
  189. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_visibility.scss +13 -0
  190. data/app/assets/stylesheets/comfy/vendor/bootstrap/vendor/_rfs.scss +204 -0
  191. data/app/assets/stylesheets/comfy/vendor/fontawesome.css +343 -0
  192. data/app/assets/stylesheets/comfy/vendor/redactor.css +948 -0
  193. data/app/controllers/application_controller.rb +5 -0
  194. data/app/controllers/comfy/admin/base_controller.rb +20 -0
  195. data/app/controllers/comfy/admin/cms/base_controller.rb +54 -0
  196. data/app/controllers/comfy/admin/cms/categories_controller.rb +38 -0
  197. data/app/controllers/comfy/admin/cms/files_controller.rb +140 -0
  198. data/app/controllers/comfy/admin/cms/layouts_controller.rb +69 -0
  199. data/app/controllers/comfy/admin/cms/pages_controller.rb +193 -0
  200. data/app/controllers/comfy/admin/cms/revisions/base_controller.rb +48 -0
  201. data/app/controllers/comfy/admin/cms/revisions/layout_controller.rb +16 -0
  202. data/app/controllers/comfy/admin/cms/revisions/page_controller.rb +28 -0
  203. data/app/controllers/comfy/admin/cms/revisions/snippet_controller.rb +16 -0
  204. data/app/controllers/comfy/admin/cms/revisions/translation_controller.rb +29 -0
  205. data/app/controllers/comfy/admin/cms/sites_controller.rb +68 -0
  206. data/app/controllers/comfy/admin/cms/snippets_controller.rb +66 -0
  207. data/app/controllers/comfy/admin/cms/translations_controller.rb +117 -0
  208. data/app/controllers/comfy/cms/assets_controller.rb +38 -0
  209. data/app/controllers/comfy/cms/base_controller.rb +35 -0
  210. data/app/controllers/comfy/cms/content_controller.rb +90 -0
  211. data/app/controllers/concerns/comfy/paginate.rb +11 -0
  212. data/app/controllers/concerns/comfy/reorder_action.rb +19 -0
  213. data/app/helpers/comfy/admin/cms_helper.rb +50 -0
  214. data/app/helpers/comfy/cms_helper.rb +71 -0
  215. data/app/models/comfy/cms/categorization.rb +23 -0
  216. data/app/models/comfy/cms/category.rb +22 -0
  217. data/app/models/comfy/cms/file.rb +65 -0
  218. data/app/models/comfy/cms/fragment.rb +65 -0
  219. data/app/models/comfy/cms/layout.rb +119 -0
  220. data/app/models/comfy/cms/page.rb +182 -0
  221. data/app/models/comfy/cms/revision.rb +10 -0
  222. data/app/models/comfy/cms/site.rb +93 -0
  223. data/app/models/comfy/cms/snippet.rb +43 -0
  224. data/app/models/comfy/cms/translation.rb +47 -0
  225. data/app/models/concerns/comfy/cms/with_categories.rb +51 -0
  226. data/app/models/concerns/comfy/cms/with_fragments.rb +123 -0
  227. data/app/views/comfy/admin/cms/categories/_categories.html.haml +4 -0
  228. data/app/views/comfy/admin/cms/categories/_edit.html.haml +7 -0
  229. data/app/views/comfy/admin/cms/categories/_form.html.haml +5 -0
  230. data/app/views/comfy/admin/cms/categories/_index.html.haml +28 -0
  231. data/app/views/comfy/admin/cms/categories/_show.html.haml +15 -0
  232. data/app/views/comfy/admin/cms/categories/create.js.erb +7 -0
  233. data/app/views/comfy/admin/cms/categories/destroy.js.erb +8 -0
  234. data/app/views/comfy/admin/cms/categories/edit.js.erb +3 -0
  235. data/app/views/comfy/admin/cms/categories/update.js.erb +6 -0
  236. data/app/views/comfy/admin/cms/files/_file.html.haml +31 -0
  237. data/app/views/comfy/admin/cms/files/_form.html.haml +26 -0
  238. data/app/views/comfy/admin/cms/files/_modal.html.haml +3 -0
  239. data/app/views/comfy/admin/cms/files/destroy.js.erb +4 -0
  240. data/app/views/comfy/admin/cms/files/edit.html.haml +5 -0
  241. data/app/views/comfy/admin/cms/files/index.html.haml +31 -0
  242. data/app/views/comfy/admin/cms/files/new.html.haml +5 -0
  243. data/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml +23 -0
  244. data/app/views/comfy/admin/cms/fragments/_form_fragments.html.haml +38 -0
  245. data/app/views/comfy/admin/cms/layouts/_form.html.haml +20 -0
  246. data/app/views/comfy/admin/cms/layouts/_index_branch.html.haml +26 -0
  247. data/app/views/comfy/admin/cms/layouts/edit.html.haml +9 -0
  248. data/app/views/comfy/admin/cms/layouts/index.html.haml +10 -0
  249. data/app/views/comfy/admin/cms/layouts/new.html.haml +5 -0
  250. data/app/views/comfy/admin/cms/pages/_form.html.haml +31 -0
  251. data/app/views/comfy/admin/cms/pages/_index_branch.html.haml +44 -0
  252. data/app/views/comfy/admin/cms/pages/edit.html.haml +14 -0
  253. data/app/views/comfy/admin/cms/pages/index.html.haml +13 -0
  254. data/app/views/comfy/admin/cms/pages/new.html.haml +5 -0
  255. data/app/views/comfy/admin/cms/pages/toggle_branch.js.erb +19 -0
  256. data/app/views/comfy/admin/cms/revisions/_sidebar.html.haml +2 -0
  257. data/app/views/comfy/admin/cms/revisions/show.html.haml +36 -0
  258. data/app/views/comfy/admin/cms/sites/_form.html.haml +15 -0
  259. data/app/views/comfy/admin/cms/sites/edit.html.haml +5 -0
  260. data/app/views/comfy/admin/cms/sites/index.html.haml +28 -0
  261. data/app/views/comfy/admin/cms/sites/new.html.haml +5 -0
  262. data/app/views/comfy/admin/cms/snippets/_form.html.haml +19 -0
  263. data/app/views/comfy/admin/cms/snippets/edit.html.haml +9 -0
  264. data/app/views/comfy/admin/cms/snippets/index.html.haml +33 -0
  265. data/app/views/comfy/admin/cms/snippets/new.html.haml +5 -0
  266. data/app/views/comfy/admin/cms/translations/_form.html.haml +20 -0
  267. data/app/views/comfy/admin/cms/translations/_sidebar.html.haml +19 -0
  268. data/app/views/comfy/admin/cms/translations/edit.html.haml +10 -0
  269. data/app/views/comfy/admin/cms/translations/new.html.haml +5 -0
  270. data/app/views/kaminari/comfy/_first_page.html.haml +3 -0
  271. data/app/views/kaminari/comfy/_gap.html.haml +2 -0
  272. data/app/views/kaminari/comfy/_last_page.html.haml +3 -0
  273. data/app/views/kaminari/comfy/_next_page.html.haml +2 -0
  274. data/app/views/kaminari/comfy/_page.html.haml +2 -0
  275. data/app/views/kaminari/comfy/_paginator.html.haml +11 -0
  276. data/app/views/kaminari/comfy/_prev_page.html.haml +2 -0
  277. data/app/views/layouts/comfy/admin/cms/_body.html.haml +14 -0
  278. data/app/views/layouts/comfy/admin/cms/_flash.html.haml +4 -0
  279. data/app/views/layouts/comfy/admin/cms/_footer_js.html.haml +6 -0
  280. data/app/views/layouts/comfy/admin/cms/_head.html.haml +19 -0
  281. data/app/views/layouts/comfy/admin/cms/_left.html.haml +46 -0
  282. data/app/views/layouts/comfy/admin/cms/_right.html.haml +5 -0
  283. data/app/views/layouts/comfy/admin/cms.html.haml +4 -0
  284. data/bin/bundle +3 -0
  285. data/bin/rails +4 -0
  286. data/bin/rake +4 -0
  287. data/bin/setup +36 -0
  288. data/bin/update +31 -0
  289. data/bin/yarn +11 -0
  290. data/comfy_middle_seat.gemspec +35 -0
  291. data/config/application.rb +62 -0
  292. data/config/boot.rb +5 -0
  293. data/config/cable.yml +10 -0
  294. data/config/cms_routes.rb +8 -0
  295. data/config/database.yml +40 -0
  296. data/config/environment.rb +7 -0
  297. data/config/environments/development.rb +81 -0
  298. data/config/environments/production.rb +93 -0
  299. data/config/environments/test.rb +74 -0
  300. data/config/initializers/comfortable_media_surfer.rb +122 -0
  301. data/config/initializers/content_security_policy.rb +27 -0
  302. data/config/initializers/filter_parameter_logging.rb +10 -0
  303. data/config/initializers/inflections.rb +18 -0
  304. data/config/initializers/json.rb +11 -0
  305. data/config/initializers/new_framework_defaults_7_0.rb +144 -0
  306. data/config/initializers/new_framework_defaults_7_1.rb +225 -0
  307. data/config/initializers/permissions_policy.rb +15 -0
  308. data/config/locales/ar.yml +268 -0
  309. data/config/locales/ca.yml +268 -0
  310. data/config/locales/cs.yml +268 -0
  311. data/config/locales/da.yml +268 -0
  312. data/config/locales/de.yml +268 -0
  313. data/config/locales/en.yml +268 -0
  314. data/config/locales/es.yml +268 -0
  315. data/config/locales/fi.yml +268 -0
  316. data/config/locales/fr.yml +268 -0
  317. data/config/locales/gr.yml +268 -0
  318. data/config/locales/hr.yml +268 -0
  319. data/config/locales/it.yml +268 -0
  320. data/config/locales/ja.yml +268 -0
  321. data/config/locales/nb.yml +268 -0
  322. data/config/locales/nl.yml +268 -0
  323. data/config/locales/pl.yml +268 -0
  324. data/config/locales/pt-BR.yml +268 -0
  325. data/config/locales/ru.yml +268 -0
  326. data/config/locales/sk.yml +268 -0
  327. data/config/locales/sv.yml +268 -0
  328. data/config/locales/tr.yml +268 -0
  329. data/config/locales/uk.yml +268 -0
  330. data/config/locales/zh-CN.yml +268 -0
  331. data/config/locales/zh-TW.yml +268 -0
  332. data/config/puma.rb +43 -0
  333. data/config/storage.yml +35 -0
  334. data/config.ru +6 -0
  335. data/db/cms_seeds/sample-site/files/_default.jpg.yml +5 -0
  336. data/db/cms_seeds/sample-site/files/default.jpg +0 -0
  337. data/db/cms_seeds/sample-site/layouts/default/content.html +16 -0
  338. data/db/cms_seeds/sample-site/layouts/default/nested/content.html +14 -0
  339. data/db/cms_seeds/sample-site/pages/index/child_a/content.html +7 -0
  340. data/db/cms_seeds/sample-site/pages/index/child_b/child_a/content.html +6 -0
  341. data/db/cms_seeds/sample-site/pages/index/child_b/content.html +6 -0
  342. data/db/cms_seeds/sample-site/pages/index/cms logo.png +0 -0
  343. data/db/cms_seeds/sample-site/pages/index/content.es.html +6 -0
  344. data/db/cms_seeds/sample-site/pages/index/content.fr.html +6 -0
  345. data/db/cms_seeds/sample-site/pages/index/content.html +25 -0
  346. data/db/cms_seeds/sample-site/pages/index/footer.png +0 -0
  347. data/db/cms_seeds/sample-site/pages/index/header.png +0 -0
  348. data/db/cms_seeds/sample-site/snippets/default.html +8 -0
  349. data/db/migrate/00_create_active_storage_tables.active_storage.rb +50 -0
  350. data/db/migrate/01_create_cms.rb +142 -0
  351. data/db/migrate/02_add_markdown_to_snippets.rb +5 -0
  352. data/gemfiles/7.2.gemfile +36 -0
  353. data/gemfiles/8.0.gemfile +36 -0
  354. data/gemfiles/8.1.gemfile +36 -0
  355. data/lib/comfortable_media_surfer/access_control/admin_authentication.rb +22 -0
  356. data/lib/comfortable_media_surfer/access_control/admin_authorization.rb +10 -0
  357. data/lib/comfortable_media_surfer/access_control/public_authentication.rb +10 -0
  358. data/lib/comfortable_media_surfer/access_control/public_authorization.rb +10 -0
  359. data/lib/comfortable_media_surfer/configuration.rb +138 -0
  360. data/lib/comfortable_media_surfer/content/block.rb +12 -0
  361. data/lib/comfortable_media_surfer/content/params_parser.rb +149 -0
  362. data/lib/comfortable_media_surfer/content/renderer.rb +146 -0
  363. data/lib/comfortable_media_surfer/content/tag.rb +48 -0
  364. data/lib/comfortable_media_surfer/content/tags/asset.rb +57 -0
  365. data/lib/comfortable_media_surfer/content/tags/audio.rb +39 -0
  366. data/lib/comfortable_media_surfer/content/tags/breadcrumbs.rb +43 -0
  367. data/lib/comfortable_media_surfer/content/tags/checkbox.rb +27 -0
  368. data/lib/comfortable_media_surfer/content/tags/children.rb +52 -0
  369. data/lib/comfortable_media_surfer/content/tags/date.rb +19 -0
  370. data/lib/comfortable_media_surfer/content/tags/datetime.rb +42 -0
  371. data/lib/comfortable_media_surfer/content/tags/file.rb +70 -0
  372. data/lib/comfortable_media_surfer/content/tags/file_link.rb +58 -0
  373. data/lib/comfortable_media_surfer/content/tags/files.rb +41 -0
  374. data/lib/comfortable_media_surfer/content/tags/fragment.rb +64 -0
  375. data/lib/comfortable_media_surfer/content/tags/helper.rb +53 -0
  376. data/lib/comfortable_media_surfer/content/tags/image.rb +56 -0
  377. data/lib/comfortable_media_surfer/content/tags/markdown.rb +22 -0
  378. data/lib/comfortable_media_surfer/content/tags/mixins/file_content.rb +42 -0
  379. data/lib/comfortable_media_surfer/content/tags/number.rb +18 -0
  380. data/lib/comfortable_media_surfer/content/tags/page_file_link.rb +86 -0
  381. data/lib/comfortable_media_surfer/content/tags/partial.rb +47 -0
  382. data/lib/comfortable_media_surfer/content/tags/siblings.rb +66 -0
  383. data/lib/comfortable_media_surfer/content/tags/snippet.rb +36 -0
  384. data/lib/comfortable_media_surfer/content/tags/template.rb +42 -0
  385. data/lib/comfortable_media_surfer/content/tags/text.rb +18 -0
  386. data/lib/comfortable_media_surfer/content/tags/textarea.rb +19 -0
  387. data/lib/comfortable_media_surfer/content/tags/wysiwyg.rb +19 -0
  388. data/lib/comfortable_media_surfer/content/tags.rb +29 -0
  389. data/lib/comfortable_media_surfer/content.rb +11 -0
  390. data/lib/comfortable_media_surfer/engine.rb +24 -0
  391. data/lib/comfortable_media_surfer/error.rb +24 -0
  392. data/lib/comfortable_media_surfer/extensions/acts_as_tree.rb +114 -0
  393. data/lib/comfortable_media_surfer/extensions/has_revisions.rb +68 -0
  394. data/lib/comfortable_media_surfer/extensions.rb +8 -0
  395. data/lib/comfortable_media_surfer/form_builder.rb +43 -0
  396. data/lib/comfortable_media_surfer/render_methods.rb +106 -0
  397. data/lib/comfortable_media_surfer/routing.rb +87 -0
  398. data/lib/comfortable_media_surfer/seeds/file/exporter.rb +37 -0
  399. data/lib/comfortable_media_surfer/seeds/file/importer.rb +55 -0
  400. data/lib/comfortable_media_surfer/seeds/layout/exporter.rb +38 -0
  401. data/lib/comfortable_media_surfer/seeds/layout/importer.rb +60 -0
  402. data/lib/comfortable_media_surfer/seeds/page/exporter.rb +88 -0
  403. data/lib/comfortable_media_surfer/seeds/page/importer.rb +245 -0
  404. data/lib/comfortable_media_surfer/seeds/snippet/exporter.rb +32 -0
  405. data/lib/comfortable_media_surfer/seeds/snippet/importer.rb +47 -0
  406. data/lib/comfortable_media_surfer/seeds.rb +110 -0
  407. data/lib/comfortable_media_surfer/version.rb +3 -0
  408. data/lib/comfortable_media_surfer/view_hooks.rb +33 -0
  409. data/lib/comfortable_media_surfer.rb +56 -0
  410. data/lib/comfy_middle_seat/version.rb +5 -0
  411. data/lib/comfy_middle_seat.rb +3 -0
  412. data/lib/generators/comfy/cms/README +21 -0
  413. data/lib/generators/comfy/cms/assets_generator.rb +16 -0
  414. data/lib/generators/comfy/cms/cms_generator.rb +69 -0
  415. data/lib/generators/comfy/cms/controllers_generator.rb +15 -0
  416. data/lib/generators/comfy/cms/models_generator.rb +15 -0
  417. data/lib/generators/comfy/cms/views_generator.rb +15 -0
  418. data/lib/generators/comfy/scaffold/scaffold_generator.rb +79 -0
  419. data/lib/generators/comfy/scaffold/templates/controller.rb.tt +62 -0
  420. data/lib/generators/comfy/scaffold/templates/migration.rb.tt +11 -0
  421. data/lib/generators/comfy/scaffold/templates/model.rb.tt +21 -0
  422. data/lib/generators/comfy/scaffold/templates/tests/controller.rb.tt +115 -0
  423. data/lib/generators/comfy/scaffold/templates/tests/fixture.yml.tt +4 -0
  424. data/lib/generators/comfy/scaffold/templates/tests/model.rb.tt +26 -0
  425. data/lib/generators/comfy/scaffold/templates/views/_form.haml.tt +7 -0
  426. data/lib/generators/comfy/scaffold/templates/views/edit.haml.tt +5 -0
  427. data/lib/generators/comfy/scaffold/templates/views/index.haml.tt +21 -0
  428. data/lib/generators/comfy/scaffold/templates/views/new.haml.tt +5 -0
  429. data/lib/generators/comfy/scaffold/templates/views/show.haml.tt +4 -0
  430. data/lib/tasks/cms_assets.rake +19 -0
  431. data/lib/tasks/cms_seeds.rake +40 -0
  432. data/package-lock.json +989 -0
  433. data/package.json +23 -0
  434. data/public/favicon.ico +0 -0
  435. data/rakelib/create_release.rake +82 -0
  436. metadata +665 -0
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Admin::Cms::SitesController < Comfy::Admin::Cms::BaseController
4
+ skip_before_action :load_admin_site,
5
+ :load_seeds
6
+
7
+ before_action :build_site, only: %i[new create]
8
+ before_action :load_site, only: %i[edit update destroy]
9
+ before_action :authorize
10
+
11
+ def index
12
+ return redirect_to action: :new if ::Comfy::Cms::Site.none?
13
+
14
+ @site = ::Comfy::Cms::Site.find_by_id(session[:site_id])
15
+ @sites = ::Comfy::Cms::Site.all
16
+ end
17
+
18
+ def new
19
+ render
20
+ end
21
+
22
+ def edit
23
+ render
24
+ end
25
+
26
+ def create
27
+ @site.save!
28
+ flash[:success] = I18n.t('comfy.admin.cms.sites.created')
29
+ redirect_to comfy_admin_cms_site_layouts_path(@site)
30
+ rescue ActiveRecord::RecordInvalid
31
+ flash.now[:danger] = I18n.t('comfy.admin.cms.sites.creation_failure')
32
+ render action: :new
33
+ end
34
+
35
+ def update
36
+ @site.update!(site_params)
37
+ flash[:success] = I18n.t('comfy.admin.cms.sites.updated')
38
+ redirect_to action: :edit, id: @site
39
+ rescue ActiveRecord::RecordInvalid
40
+ flash.now[:danger] = I18n.t('comfy.admin.cms.sites.update_failure')
41
+ render action: :edit
42
+ end
43
+
44
+ def destroy
45
+ @site.destroy
46
+ flash[:success] = I18n.t('comfy.admin.cms.sites.deleted')
47
+ redirect_to action: :index
48
+ end
49
+
50
+ protected
51
+
52
+ def build_site
53
+ @site = ::Comfy::Cms::Site.new(site_params)
54
+ @site.hostname ||= request.host.downcase
55
+ end
56
+
57
+ def load_site
58
+ @site = ::Comfy::Cms::Site.find(params[:id])
59
+ I18n.locale = ComfortableMediaSurfer.config.admin_locale || @site.locale
60
+ rescue ActiveRecord::RecordNotFound
61
+ flash[:danger] = I18n.t('comfy.admin.cms.sites.not_found')
62
+ redirect_to action: :index
63
+ end
64
+
65
+ def site_params
66
+ params.fetch(:site, {}).permit(:label, :identifier, :hostname, :path, :locale)
67
+ end
68
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Admin::Cms::SnippetsController < Comfy::Admin::Cms::BaseController
4
+ include ::Comfy::ReorderAction
5
+
6
+ self.reorder_action_resource = ::Comfy::Cms::Snippet
7
+
8
+ before_action :build_snippet, only: %i[new create]
9
+ before_action :load_snippet, only: %i[edit update destroy]
10
+ before_action :authorize
11
+
12
+ def index
13
+ return redirect_to action: :new if @site.snippets.none?
14
+
15
+ @snippets = @site.snippets.includes(:categories).for_category(params[:categories]).order(:position)
16
+ end
17
+
18
+ def new
19
+ render
20
+ end
21
+
22
+ def edit
23
+ @snippet.attributes = snippet_params
24
+ end
25
+
26
+ def create
27
+ @snippet.save!
28
+ flash[:success] = I18n.t('comfy.admin.cms.snippets.created')
29
+ redirect_to action: :edit, id: @snippet
30
+ rescue ActiveRecord::RecordInvalid
31
+ flash.now[:danger] = I18n.t('comfy.admin.cms.snippets.creation_failure')
32
+ render action: :new
33
+ end
34
+
35
+ def update
36
+ @snippet.update!(snippet_params)
37
+ flash[:success] = I18n.t('comfy.admin.cms.snippets.updated')
38
+ redirect_to action: :edit, id: @snippet
39
+ rescue ActiveRecord::RecordInvalid
40
+ flash.now[:danger] = I18n.t('comfy.admin.cms.snippets.update_failure')
41
+ render action: :edit
42
+ end
43
+
44
+ def destroy
45
+ @snippet.destroy
46
+ flash[:success] = I18n.t('comfy.admin.cms.snippets.deleted')
47
+ redirect_to action: :index
48
+ end
49
+
50
+ protected
51
+
52
+ def build_snippet
53
+ @snippet = @site.snippets.new(snippet_params)
54
+ end
55
+
56
+ def load_snippet
57
+ @snippet = @site.snippets.find(params[:id])
58
+ rescue ActiveRecord::RecordNotFound
59
+ flash[:danger] = I18n.t('comfy.admin.cms.snippets.not_found')
60
+ redirect_to action: :index
61
+ end
62
+
63
+ def snippet_params
64
+ params.fetch(:snippet, {}).permit(:label, :identifier, :markdown, :content, category_ids: [])
65
+ end
66
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Admin::Cms::TranslationsController < Comfy::Admin::Cms::BaseController
4
+ FRAGMENT_PARAMS = [
5
+ :identifier, :tag, :content, :datetime, :boolean, :files,
6
+ { files: [], file_ids_destroy: [] }
7
+ ].freeze
8
+
9
+ helper_method :translation_select_options
10
+
11
+ before_action :load_page
12
+ before_action :build_translation, only: %i[new create]
13
+ before_action :load_translation, only: %i[edit update destroy]
14
+ before_action :authorize
15
+ before_action :assign_translation_params, only: :update
16
+ before_action :preview_translation, only: %i[create update]
17
+
18
+ def new
19
+ render
20
+ end
21
+
22
+ def edit
23
+ render
24
+ end
25
+
26
+ def create
27
+ @translation.save!
28
+ flash[:success] = I18n.t('comfy.admin.cms.translations.created')
29
+ redirect_to action: :edit, id: @translation
30
+ rescue ActiveRecord::RecordInvalid
31
+ flash.now[:danger] = I18n.t('comfy.admin.cms.translations.creation_failure')
32
+ render action: :new
33
+ end
34
+
35
+ def update
36
+ @translation.save!
37
+ flash[:success] = I18n.t('comfy.admin.cms.translations.updated')
38
+ redirect_to action: :edit, id: @translation
39
+ rescue ActiveRecord::RecordInvalid
40
+ flash.now[:danger] = I18n.t('comfy.admin.cms.translations.update_failure')
41
+ render action: :edit
42
+ end
43
+
44
+ def destroy
45
+ @translation.destroy
46
+ flash[:success] = I18n.t('comfy.admin.cms.translations.deleted')
47
+ redirect_to edit_comfy_admin_cms_site_page_path(@site, @page)
48
+ end
49
+
50
+ def form_fragments
51
+ @translation = @page.translations.find_by(id: params[:id]) || @page.translations.new
52
+ @translation.layout = @site.layouts.find_by(id: params[:layout_id])
53
+
54
+ render(
55
+ partial: 'comfy/admin/cms/fragments/form_fragments',
56
+ locals: { record: @translation, scope: :translation },
57
+ layout: false
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def translation_select_options
64
+ all = ComfortableMediaSurfer.config.locales
65
+ taken = @page.translations.pluck(:locale) + [@site.locale] - [@translation.locale]
66
+ all.except(*taken).map { |k, v| ["#{v} (#{k})", k] }
67
+ end
68
+
69
+ def load_page
70
+ @page = @site.pages.find(params[:page_id])
71
+ rescue ActiveRecord::RecordNotFound
72
+ flash[:danger] = I18n.t('comfy.admin.cms.pages.not_found')
73
+ redirect_to comfy_admin_cms_site_pages_path(@site)
74
+ end
75
+
76
+ def build_translation
77
+ @translation = @page.translations.new(translation_params)
78
+ @translation.layout ||= @page.layout
79
+ end
80
+
81
+ def load_translation
82
+ @translation = @page.translations.find(params[:id])
83
+ rescue ActiveRecord::RecordNotFound
84
+ flash[:danger] = I18n.t('comfy.admin.cms.translations.not_found')
85
+ redirect_to edit_comfy_admin_cms_site_page_path(@site, @page)
86
+ end
87
+
88
+ def assign_translation_params
89
+ @translation.assign_attributes(translation_params)
90
+ end
91
+
92
+ def translation_params
93
+ params.fetch(:translation, {}).permit(
94
+ :locale, :label, :layout_id, :is_published,
95
+ fragments_attributes: FRAGMENT_PARAMS
96
+ )
97
+ end
98
+
99
+ def preview_translation
100
+ return unless params[:preview]
101
+
102
+ layout = @translation.layout.app_layout.blank? ? false : @translation.layout.app_layout
103
+ @cms_site = @page.site
104
+ @cms_layout = @translation.layout
105
+ @cms_page = @page
106
+
107
+ # Make sure to use the site locale to render the preview becaue it might
108
+ # be different from the admin locale.
109
+ I18n.locale = @translation.locale
110
+
111
+ # Chrome chokes on content with iframes. Issue #434
112
+ response.headers['X-XSS-Protection'] = '0'
113
+
114
+ # raise
115
+ render inline: @translation.render, layout: layout, content_type: 'text/html'
116
+ end
117
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::AssetsController < Comfy::Cms::BaseController
4
+ skip_before_action :verify_authenticity_token, raise: false
5
+
6
+ before_action :load_cms_layout,
7
+ :use_null_session
8
+
9
+ after_action :set_cache_control_header
10
+
11
+ def render_css
12
+ render body: @cms_layout.css, content_type: 'text/css'
13
+ end
14
+
15
+ def render_js
16
+ render body: @cms_layout.js, content_type: 'application/javascript'
17
+ end
18
+
19
+ protected
20
+
21
+ def load_cms_layout
22
+ @cms_layout = @cms_site.layouts.find_by_identifier!(params[:identifier])
23
+ rescue ActiveRecord::RecordNotFound
24
+ render body: nil, status: 404
25
+ end
26
+
27
+ # null_session avoids cookies and flash updates
28
+ def use_null_session
29
+ ActionController::RequestForgeryProtection::ProtectionMethods::NullSession.new(self)
30
+ .handle_unverified_request
31
+ end
32
+
33
+ def set_cache_control_header
34
+ return unless params[:cache_buster].present?
35
+
36
+ response.headers['Cache-Control'] = "public, max-age=#{1.year.to_i}"
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::BaseController < ComfortableMediaSurfer.config.public_base_controller.to_s.constantize
4
+ before_action :load_cms_site
5
+
6
+ helper Comfy::CmsHelper
7
+
8
+ protected
9
+
10
+ def load_cms_site
11
+ route_site_id = request.path_parameters[:site_id]
12
+ @cms_site ||= if route_site_id
13
+ find_cms_site_for_asset(route_site_id)
14
+ else
15
+ ::Comfy::Cms::Site.find_site(request.host_with_port.downcase, request.fullpath)
16
+ end
17
+
18
+ raise ActionController::RoutingError, 'Site Not Found' unless @cms_site
19
+
20
+ return unless @cms_site.path.present? && !route_site_id
21
+ raise ActionController::RoutingError, 'Site Not Found' unless params[:cms_path]&.match(%r{\A#{@cms_site.path}})
22
+
23
+ params[:cms_path].gsub!(%r{\A#{@cms_site.path}}, '')
24
+ params[:cms_path]&.gsub!(%r{\A/}, '')
25
+ end
26
+
27
+ def find_cms_site_for_asset(site_id)
28
+ site = ::Comfy::Cms::Site.find_by_id(site_id)
29
+ return site if site && ::Comfy::Cms::Site.one?
30
+ return unless site
31
+
32
+ request_host = ::Comfy::Cms::Site.real_host_from_aliases(request.host_with_port.downcase)
33
+ site if site.hostname.downcase == request_host
34
+ end
35
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::ContentController < Comfy::Cms::BaseController
4
+ # Authentication module must have `authenticate` method
5
+ include ComfortableMediaSurfer.config.public_auth.to_s.constantize
6
+
7
+ # Authorization module must have `authorize` method
8
+ include ComfortableMediaSurfer.config.public_authorization.to_s.constantize
9
+
10
+ before_action :load_seeds
11
+ before_action :load_cms_page,
12
+ :authenticate,
13
+ :authorize,
14
+ only: :show
15
+
16
+ def show
17
+ if @cms_page.target_page.present?
18
+ redirect_to @cms_page.target_page.url(relative: true)
19
+ else
20
+ respond_to do |format|
21
+ format.html { render_page }
22
+ format.json do
23
+ @cms_page.content = render_to_string(
24
+ inline: @cms_page.content_cache,
25
+ layout: false
26
+ )
27
+ json_page = @cms_page.as_json(ComfortableMediaSurfer.config.page_to_json_options)
28
+ render json: json_page
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def render_page(status = :ok)
37
+ render inline: @cms_page.content_cache,
38
+ layout: app_layout,
39
+ status: status,
40
+ content_type: mime_type
41
+ end
42
+
43
+ # it's possible to control mimetype of a page by creating a `mime_type` field
44
+ def mime_type
45
+ mime_block = @cms_page.fragments.detect { |f| f.identifier == 'mime_type' }
46
+ mime_block&.content&.strip || 'text/html'
47
+ end
48
+
49
+ def app_layout
50
+ return false if request.xhr? || !@cms_layout
51
+
52
+ @cms_layout.app_layout.present? ? @cms_layout.app_layout : false
53
+ end
54
+
55
+ def load_seeds
56
+ return unless ComfortableMediaSurfer.config.enable_seeds
57
+
58
+ ComfortableMediaSurfer::Seeds::Importer.new(@cms_site.identifier).import!
59
+ end
60
+
61
+ # Attempting to populate @cms_page and @cms_layout instance variables so they
62
+ # can be used in view helpers/partials
63
+ def load_cms_page
64
+ return if find_cms_page_by_full_path("/#{params[:cms_path]}")
65
+
66
+ if find_cms_page_by_full_path('/404')
67
+ render_page(:not_found)
68
+ else
69
+ message = "Page Not Found at: \"#{params[:cms_path]}\""
70
+ raise ActionController::RoutingError, message
71
+ end
72
+ end
73
+
74
+ # Getting page and setting content_cache and fragments data if we need to
75
+ # serve translation data
76
+ def find_cms_page_by_full_path(full_path)
77
+ @cms_page = if Rails.env == 'development'
78
+ @cms_site.pages.find_by!(full_path: full_path)
79
+ else
80
+ @cms_site.pages.published.find_by!(full_path: full_path)
81
+ end
82
+
83
+ @cms_page.translate!
84
+ @cms_layout = @cms_page.layout
85
+
86
+ @cms_page
87
+ rescue ActiveRecord::RecordNotFound
88
+ nil
89
+ end
90
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy::Paginate
4
+ def comfy_paginate(scope, per_page: 50)
5
+ if defined?(Kaminari)
6
+ scope.page(params[:page]).per(per_page)
7
+ else
8
+ scope
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy::ReorderAction
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ mattr_accessor :reorder_action_resource
8
+ end
9
+
10
+ def reorder
11
+ resource_class = self.class.reorder_action_resource
12
+ site_resources = resource_class.where(site_id: @site.id)
13
+ (params.permit(order: [])[:order] || []).each_with_index do |id, index|
14
+ site_resources.where(id: id).update_all(position: index)
15
+ end
16
+ @site.pages.each(&:save!) if resource_class == ::Comfy::Cms::Page
17
+ head :ok
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy
4
+ module Admin
5
+ module CmsHelper
6
+ # Wrapper around Comfy::FormBuilder
7
+ def comfy_form_with(**options, &)
8
+ form_options = options.merge(builder: ComfortableMediaSurfer::FormBuilder)
9
+ form_options[:bootstrap] = { layout: :horizontal }
10
+ form_options[:local] = true
11
+ bootstrap_form_with(**form_options, &)
12
+ end
13
+
14
+ def comfy_admin_partial(path, params = {})
15
+ render path, params
16
+ rescue ActionView::MissingTemplate
17
+ if ComfortableMediaSurfer.config.reveal_cms_partials
18
+ content_tag(:div, class: 'comfy-admin-partial') do
19
+ path
20
+ end
21
+ end
22
+ end
23
+
24
+ # Injects some content somewhere inside cms admin area
25
+ def cms_hook(name, options = {})
26
+ ComfortableMediaSurfer::ViewHooks.render(name, self, options)
27
+ end
28
+
29
+ # @param [String] fragment_id
30
+ # @param [ActiveStorage::Blob] attachment
31
+ # @param [Boolean] multiple
32
+ # @return [String] {{ cms:page_file_link #{fragment_id}, ... }}
33
+ def cms_page_file_link_tag(fragment_id:, attachment:, multiple:)
34
+ filename = ", filename: \"#{attachment.filename}\"" if multiple
35
+ as = ', as: image' if attachment.image?
36
+ "{{ cms:page_file_link #{fragment_id}#{filename}#{as} }}"
37
+ end
38
+
39
+ # @param [Comfy::Cms::File] file
40
+ # @return [String] {{ cms:file_link #{file.id}, ... }}
41
+ def cms_file_link_tag(file)
42
+ if file.attachment.image?
43
+ "{{ cms:image #{file.label} }}"
44
+ else
45
+ "{{ cms:file_link #{file.id} }}"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy
4
+ module CmsHelper
5
+ # Raw content of a page fragment. This is how you get content from unrenderable
6
+ # tags like {{cms:fragment meta, render: false}}
7
+ # Example:
8
+ # cms_fragment_content(:left_column, CmsPage.first)
9
+ # cms_fragment_content(:left_column) # if @cms_page is present
10
+ def cms_fragment_content(identifier, page = @cms_page)
11
+ frag = page&.fragments&.detect { |f| f.identifier == identifier.to_s }
12
+ return '' unless frag
13
+
14
+ case frag.tag
15
+ when 'date', 'datetime'
16
+ frag.datetime
17
+ when 'checkbox'
18
+ frag.boolean
19
+ when 'file', 'files'
20
+ frag.attachments
21
+ else
22
+ frag.content
23
+ end
24
+ end
25
+
26
+ # Same as cms_fragment_content but with cms tags expanded and rendered. Use
27
+ # it only if you know you got more stuff in the fragment content other than
28
+ # text because this is a potentially expensive call.
29
+ def cms_fragment_render(identifier, page = @cms_page)
30
+ node = page.fragment_nodes.detect { |n| n.identifier == identifier.to_s }
31
+ return '' unless node
32
+
33
+ node.renderable = true
34
+ render inline: page.render([node])
35
+ end
36
+
37
+ # Raw content of a snippet.
38
+ # Example:
39
+ # cms_snippet_content(:my_snippet)
40
+ def cms_snippet_content(identifier, cms_site = @cms_site)
41
+ cms_site ||= cms_site_detect
42
+ snippet = cms_site&.snippets&.find_by_identifier(identifier)
43
+ return '' unless snippet
44
+
45
+ snippet.content
46
+ end
47
+
48
+ # Same as cms_snippet_content but cms tags will be expanded. Note that there
49
+ # is no page context, so snippet cannot contain fragment tags.
50
+ def cms_snippet_render(identifier, cms_site = @cms_site)
51
+ cms_site ||= cms_site_detect
52
+ snippet = cms_site&.snippets&.find_by_identifier(identifier)
53
+ return '' unless snippet
54
+
55
+ r = ComfortableMediaSurfer::Content::Renderer.new(snippet)
56
+ render inline: r.render(r.nodes(r.tokenize(snippet.content)))
57
+ end
58
+
59
+ def cms_site_detect
60
+ host = request.host_with_port.downcase
61
+ path = request.fullpath
62
+ Comfy::Cms::Site.find_site(host, path)
63
+ end
64
+
65
+ def comfy_paginate(collection)
66
+ return unless collection
67
+
68
+ paginate collection, theme: 'comfy' if defined?(Kaminari)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Categorization < ActiveRecord::Base
4
+ self.table_name = 'comfy_cms_categorizations'
5
+
6
+ # -- Relationships -----------------------------------------------------------
7
+ belongs_to :category
8
+ belongs_to :categorized,
9
+ polymorphic: true
10
+
11
+ # -- Validations -------------------------------------------------------------
12
+ validates :category_id,
13
+ uniqueness: { scope: %i[categorized_type categorized_id] }
14
+ validate :category_belongs_to_site
15
+
16
+ private
17
+
18
+ def category_belongs_to_site
19
+ return unless category && categorized.respond_to?(:site_id)
20
+
21
+ errors.add(:category_id, :invalid) if category.site_id != categorized.site_id
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Category < ActiveRecord::Base
4
+ self.table_name = 'comfy_cms_categories'
5
+
6
+ # -- Relationships --------------------------------------------------------
7
+ belongs_to :site
8
+ has_many :categorizations,
9
+ dependent: :destroy
10
+
11
+ # -- Validations ----------------------------------------------------------
12
+ validates :label,
13
+ presence: true,
14
+ uniqueness: { scope: %i[categorized_type site_id] }
15
+ validates :categorized_type,
16
+ presence: true
17
+
18
+ # -- Scopes ---------------------------------------------------------------
19
+ scope :of_type, ->(type) {
20
+ where(categorized_type: type)
21
+ }
22
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::File < ActiveRecord::Base
4
+ self.table_name = 'comfy_cms_files'
5
+
6
+ include Comfy::Cms::WithCategories
7
+
8
+ VARIANT_SIZE = {
9
+ redactor: { resize: '100x75^', gravity: 'center', crop: '100x75+0+0' },
10
+ thumb: { resize: '200x150^', gravity: 'center', crop: '200x150+0+0' },
11
+ icon: { resize: '28x28^', gravity: 'center', crop: '28x28+0+0' }
12
+ }.freeze
13
+
14
+ # temporary place to store attachment
15
+ attr_accessor :file
16
+
17
+ has_one_attached :attachment
18
+
19
+ # -- Relationships -----------------------------------------------------------
20
+ belongs_to :site
21
+
22
+ # -- Callbacks ---------------------------------------------------------------
23
+ before_validation :assign_label, on: :create
24
+ before_create :assign_position
25
+ # active_storage attachment behavior changed in rails 6
26
+ before_save :process_attachment
27
+ after_save :clear_page_content_cache
28
+
29
+ # -- Validations -------------------------------------------------------------
30
+ validates :label, presence: true
31
+ validates :file, presence: true, on: :create
32
+
33
+ # -- Scopes ------------------------------------------------------------------
34
+ # When we need to grab only files with image attachments.
35
+ # Don't forget to include `with_attached_attachment` before calling this
36
+ scope :with_images, -> {
37
+ where("active_storage_blobs.content_type LIKE 'image/%'").references(:blob)
38
+ }
39
+
40
+ private
41
+
42
+ def clear_page_content_cache
43
+ Comfy::Cms::Page.where(id: site.pages.pluck(:id)).update_all(content_cache: nil)
44
+ end
45
+
46
+ protected
47
+
48
+ def assign_position
49
+ max = Comfy::Cms::File.maximum(:position)
50
+ self.position = max ? max + 1 : 0
51
+ end
52
+
53
+ # TODO: Change db schema not to set blank string
54
+ def assign_label
55
+ return if label.present?
56
+
57
+ self.label = file&.original_filename
58
+ end
59
+
60
+ def process_attachment
61
+ return if @file.blank?
62
+
63
+ self.attachment = @file
64
+ end
65
+ end