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
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,42 @@
1
+ # Contributing
2
+
3
+ Contribute your code to ComfortableMediaSurfer in 5 easy steps:
4
+
5
+ ### 1. Fork it
6
+
7
+ Fork the project. Optionally, create a branch you want to work on.
8
+
9
+ ### 2. Get it running locally
10
+
11
+ - Install gem dependencies with `bundle install`
12
+ - There's nothing to configure, by default database is SQLite so it will be
13
+ created for you. Just run `bundle exec rake db:migrate`
14
+ - Prepare the environment by running `rails comfy:compile_assets`, and
15
+ `rake db:test:prepare`
16
+ - Make sure that existing tests are passing by running `bundle exec rake test`.
17
+ This uses six isolated workers by default, merges their coverage, and records
18
+ per-file runtimes in `tmp/parallel_runtime_test.log` for future balancing.
19
+ Set `PARALLEL_WORKERS` to tune the worker count, or run
20
+ `bundle exec rake test:serial` for a serial coverage run.
21
+ - There are system tests that can be run with `bundle exec rake test:system`.
22
+ You need to have `chromedriver` installed for that.
23
+ - You should be able to start the app via `bin/rails s` and navigate to http://localhost:3000/admin
24
+ and log in with username 'user' and password 'pass'
25
+
26
+ ### 3. Hack away
27
+
28
+ - Create a few small pull requests instead of a humoungous one. I can merge small stuff faster.
29
+ - When adding new code just make sure it follows the same style as the existing code.
30
+ - Avoid adding 3rd party dependencies if you can.
31
+ - Tests please, but nothing complicated. UnitTest / Fixtures all the way. Make sure all tests pass.
32
+ - Run `bundle exec rubocop` and fix any issues raised.
33
+
34
+ ### 4. Make a pull request
35
+
36
+ - If you never done it before read this: https://help.github.com/articles/using-pull-requests
37
+ - When PR is submitted check if Github actions CI ran all tests successfully
38
+
39
+ ### 5. Done!
40
+
41
+ If everything is good your changes will be merged into master branch. Eventually
42
+ a new version of gem will be published.
data/Gemfile ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ gemspec
7
+
8
+ gem 'rails', '~> 8.1.0'
9
+
10
+ group :development, :test do
11
+ gem 'autoprefixer-rails', '~> 10.4.21.0'
12
+ gem 'byebug', '~> 13.0.0', platforms: %i[mri mingw x64_mingw]
13
+ gem 'gem-release'
14
+ gem 'image_processing', '>= 1.12.2'
15
+ gem 'propshaft', '~> 1.3.1'
16
+ gem 'sqlite3', '>= 2.1'
17
+ # gem 'mysql2', '~> 0.5'
18
+ # gem 'pg', '~> 1.5.4'
19
+ end
20
+
21
+ group :development do
22
+ gem 'listen', '~> 3.10.0'
23
+ gem 'web-console', '~> 4.2'
24
+ end
25
+
26
+ group :test do
27
+ gem 'brakeman'
28
+ gem 'bundler-audit', '~> 0.9.1'
29
+ gem 'coveralls_reborn', '~> 0.29.0', require: false
30
+ gem 'cuprite', '>= 0.15'
31
+ gem 'diffy'
32
+ gem 'equivalent-xml', '~> 0.6.0'
33
+ gem 'minitest', '< 6.0.0'
34
+ gem 'minitest-reporters', '>= 1.6.1'
35
+ gem 'mocha', '>= 2.3.0', require: false
36
+ gem 'ostruct'
37
+ gem 'parallel_tests', '~> 5.7', require: false
38
+ gem 'puma'
39
+ gem 'rails-controller-testing', '~> 1.0.5'
40
+ gem 'rubocop', require: false
41
+ gem 'rubocop-minitest'
42
+ gem 'rubocop-rails'
43
+ gem 'simplecov', '~> 0.22.0', require: false
44
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2019 Oleg Khabarov, (c) 2024 Shakacode LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ [![Rails CI](https://github.com/avonderluft/comfy-middle-seat/actions/workflows/rubyonrails.yml/badge.svg)](https://github.com/avonderluft/comfy-middle-seat/actions/workflows/rubyonrails.yml)
2
+ [![Gem Version](https://img.shields.io/gem/v/comfy_middle_seat.svg?style=flat)](https://rubygems.org/gems/comfy_middle_seat)
3
+ [![Gem Downloads](https://img.shields.io/gem/dt/comfy_middle_seat.svg?style=flat)](https://rubygems.org/gems/comfy_middle_seat)
4
+ [![GitHub Release Date - Published_At](https://img.shields.io/github/release-date/avonderluft/comfy-middle-seat?label=last%20release&color=seagreen)](https://github.com/avonderluft/comfy-middle-seat/releases)
5
+
6
+ # Comfy Middle Seat
7
+
8
+ Comfy Middle Seat is a Rails CMS (Content Management System) engine, published as `comfy_middle_seat`. It keeps the Comfortable Media Surfer implementation and pays homage to the original Comfortable Mexican Sofa—with the same C.M.S. initials and a new place to sit.
9
+
10
+ The `ComfortableMediaSurfer` Ruby namespace, configuration, and internal paths remain unchanged. Only the gem's public name changes; there is no need to rename existing Surfer initializers or database tables.
11
+
12
+ ## Project History
13
+
14
+ - **[Comfortable Mexican Sofa](https://github.com/comfy/comfortable-mexican-sofa)**, created by Oleg Khabarov, is the original Rails CMS on which this project is built.
15
+ - **[Comfortable Media Surfer](https://github.com/shakacode/comfortable-media-surfer)** revived Sofa under ShakaCode, continuing its development for modern Rails applications.
16
+ - **Comfy Middle Seat** is a fork of Comfortable Media Surfer, published under a new gem name while retaining its implementation and API.
17
+
18
+ See the [CHANGELOG](CHANGELOG.md) for the inherited development history, and the original [Sofa releases](https://github.com/comfy/comfortable-mexican-sofa/releases) and [Surfer releases](https://github.com/shakacode/comfortable-media-surfer/releases) for earlier releases.
19
+
20
+ ## Features
21
+
22
+ - Simple drop-in integration with Rails 7.2+ apps with minimal configuration
23
+ * The CMS keeps clear from the rest of your application
24
+ * Powerful page templating capability using [Content Tags](https://github.com/shakacode/comfortable-media-surfer/wiki/Docs:-Content-Tags)
25
+ * [Multiple Sites](https://github.com/shakacode/comfortable-media-surfer/wiki/Docs:-Sites) from a single installation
26
+ * Multi-Language Support (i18n) (ca, cs, da, de, en, es, fi, fr, gr, hr, it, ja, nb, nl, pl, pt-BR, ru, sv, tr, uk, zh-CN, zh-TW) and page localization.
27
+ * [CMS Seeds](https://github.com/shakacode/comfortable-media-surfer/wiki/Docs:-CMS-Seeds) for initial content population
28
+ * [Revision History](https://github.com/shakacode/comfortable-media-surfer/wiki/Docs:-Revisions) to revert changes
29
+ * [Extendable Admin Area](https://github.com/shakacode/comfortable-media-surfer/wiki/HowTo:-Reusing-Admin-Area) built with [Bootstrap 4](http://getbootstrap.com) (responsive design). Using [CodeMirror](http://codemirror.net) for HTML and Markdown highlighing and [Redactor](http://imperavi.com/redactor) as the WYSIWYG editor.
30
+
31
+ ## Dependencies
32
+
33
+ - File attachments are handled by [ActiveStorage](https://github.com/rails/rails/tree/master/activestorage). Make sure that you can run appropriate migrations by running: `rails active_storage:install`
34
+ - Image resizing is done with with [ImageMagick](http://www.imagemagick.org/script/download.php), so make sure it's installed.
35
+ - Pagination is handled by [kaminari](https://github.com/amatsuda/kaminari).
36
+
37
+ ## Compatibility
38
+
39
+ On Ruby 3.2+, 4.x, Rails 7.2+, 8.x
40
+
41
+ ## Installation
42
+
43
+ Add gem definition to your Gemfile:
44
+
45
+ ```ruby
46
+ gem "comfy_middle_seat", "~> 3.1.0"
47
+ ```
48
+
49
+ Then from the Rails project's root run:
50
+
51
+ ```
52
+ bundle install
53
+ rails generate comfy:cms
54
+ rails db:migrate
55
+ rails comfy:compile_assets
56
+ ```
57
+
58
+ Now take a look inside your `config/routes.rb` file. You'll see where routes attach for the admin area and content serving. Make sure that content serving route appears as a very last item or it will make all other routes to be inaccessible.
59
+
60
+ ```ruby
61
+ comfy_route :cms_admin, path: "/admin"
62
+ comfy_route :cms, path: "/"
63
+ ```
64
+
65
+ ## Converting from Comfortable Media Surfer, Comfortable Mexican Sofa, or Occams
66
+
67
+ ### From Surfer to Middle Seat
68
+
69
+ Replace `comfortable_media_surfer` in your Gemfile with `comfy_middle_seat` and run `bundle install`. Do not include both gems. Existing `ComfortableMediaSurfer` configuration and database tables remain unchanged.
70
+
71
+ ### From Sofa to Middle Seat
72
+
73
+ The database structure is the same. Your Sofa project will also need to be upgraded to >= Rails 7.x
74
+ Then you should simply be able to update your Gemfile thus, and run bundle
75
+
76
+ ```ruby
77
+ gem 'comfy_middle_seat', '~> 3.1.0'
78
+ ```
79
+
80
+ ### From Occams to Middle Seat
81
+
82
+ Again the project must be >= Rails 7.x. Since the schema is different, executing this SQL should get you set for Middle Seat
83
+
84
+ ```sql
85
+ ALTER TABLE occams_cms_categories RENAME TO comfy_cms_categories;
86
+ ALTER TABLE occams_cms_categorizations RENAME TO comfy_cms_categorizations;
87
+ ALTER TABLE occams_cms_files RENAME TO comfy_cms_files;
88
+ ALTER TABLE occams_cms_fragments RENAME TO comfy_cms_fragments;
89
+ ALTER TABLE occams_cms_layouts RENAME TO comfy_cms_layouts;
90
+ ALTER TABLE occams_cms_pages RENAME TO comfy_cms_pages;
91
+ ALTER TABLE occams_cms_revisions RENAME TO comfy_cms_revisions;
92
+ ALTER TABLE occams_cms_sites RENAME TO comfy_cms_sites;
93
+ ALTER TABLE occams_cms_snippets RENAME TO comfy_cms_snippets;
94
+ ALTER TABLE occams_cms_translations RENAME TO comfy_cms_translations;
95
+ UPDATE comfy_cms_categories SET categorized_type = 'Comfy::Cms::Page' WHERE categorized_type = 'Occams::Cms::Page';
96
+ UPDATE comfy_cms_categorizations SET categorized_type = 'Comfy::Cms::Page' WHERE categorized_type = 'Occams::Cms::Page';
97
+ UPDATE comfy_cms_fragments SET record_type = 'Comfy::Cms::Page' WHERE record_type = 'Occams::Cms::Page';
98
+ UPDATE comfy_cms_fragments SET record_type = 'Comfy::Cms::Layout' WHERE record_type = 'Occams::Cms::Layout';
99
+ UPDATE comfy_cms_fragments SET record_type = 'Comfy::Cms::Snippet' WHERE record_type = 'Occams::Cms::Snippet';
100
+ UPDATE comfy_cms_revisions SET record_type = 'Comfy::Cms::Page' WHERE record_type = 'Occams::Cms::Page';
101
+ UPDATE comfy_cms_revisions SET record_type = 'Comfy::Cms::Layout' WHERE record_type = 'Occams::Cms::Layout';
102
+ UPDATE comfy_cms_revisions SET record_type = 'Comfy::Cms::Snippet' WHERE record_type = 'Occams::Cms::Snippet';
103
+ UPDATE active_storage_attachments SET record_type = 'Comfy::Cms::File' WHERE record_type = 'Occams::Cms::File';
104
+ ```
105
+
106
+ ## Quick Start Guide
107
+
108
+ After finishing installation you should be able to navigate to http://localhost:3000/admin
109
+
110
+ Default username and password is 'user' and 'pass'. You probably want to change it right away. Admin credentials (among other things) can be found and changed in your app's CMS initializer: `config/initializers/comfortable_media_surfer.rb`. The initializer retains its Surfer name for compatibility.
111
+
112
+ Before creating pages and populating them with content we need to create a Site. Site defines a hostname, content path and its language.
113
+
114
+ After creating a Site, you need to make a Layout. Layout is the template of your pages; it defines some reusable content (like header and footer, for example) and places where the content goes. A very simple layout can look like this:
115
+
116
+ ```html
117
+ <html>
118
+ <body>
119
+ <h1>{{ cms:text title }}</h1>
120
+ {{ cms:wysiwyg content }}
121
+ </body>
122
+ </html>
123
+ ```
124
+
125
+ Once you have a layout, you may start creating pages and populating content. It's that easy.
126
+
127
+ ## Documentation
128
+
129
+ The [Comfortable Media Surfer Wiki](https://github.com/shakacode/comfortable-media-surfer/wiki) remains the upstream reference for the inherited CMS features, including [Content Tags](https://github.com/shakacode/comfortable-media-surfer/wiki/Docs:-Content-Tags). The feature links above also point to that wiki. When following its installation instructions, use the `comfy_middle_seat` gem; Ruby examples using `ComfortableMediaSurfer` retain that namespace.
130
+
131
+ ## Add-ons
132
+
133
+ If you want to add a Blog functionality to your app take a look at
134
+ [ComfyBlog](https://github.com/comfy/comfy-blog).
135
+
136
+ ![Admin Area Preview](doc/preview.jpg)
137
+
138
+
139
+ #### Contributing
140
+
141
+ Comfy Middle Seat can run like any Rails application in development. It's as easy to work on as any other Rails app. For more detail see [CONTRIBUTING](CONTRIBUTING.md). The inherited development instructions may still refer to Comfortable Media Surfer.
142
+
143
+ #### Testing
144
+
145
+ - `bin/rails db:migrate RAILS_ENV=test`
146
+ - `rake db:test:prepare`
147
+ - `rake test`
148
+
149
+ #### Acknowledgements
150
+
151
+ - To [Oleg Khabarov](https://github.com/GBH) and the contributors to [Comfortable Mexican Sofa](https://github.com/comfy/comfortable-mexican-sofa), the original CMS.
152
+ - To [ShakaCode](https://github.com/shakacode) and the contributors to [Comfortable Media Surfer](https://github.com/shakacode/comfortable-media-surfer), whose revival this fork builds on.
153
+ - To [Roman Almeida](https://github.com/nasmorn) for contributing OEM License for [Redactor Text Editor](http://imperavi.com/redactor).
154
+
155
+ ---
156
+
157
+ Copyright 2010-2019 Oleg Khabarov, 2024-2026 ShakaCode LLC, 2026 Andrew vonderLuft
158
+ Released under the [MIT license](LICENSE)
data/Rakefile ADDED
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'config/application'
4
+ require 'open3'
5
+ require 'parallel_tests/tasks'
6
+ require 'rake/testtask'
7
+ require 'stringio'
8
+
9
+ Rails.application.load_tasks
10
+
11
+ module ComfortableMediaSurferTestOutput
12
+ SUMMARY = %r{(?<assertions>\d+) assertions, (?<errors>\d+) errors, (?<failures>\d+) failures, (?<skips>\d+) skips?, (?<tests>\d+) tests}
13
+ COLORS = { green: 32, yellow: 33, red: 31, cyan: 36 }.freeze
14
+
15
+ class Progress
16
+ def initialize(io)
17
+ @io = io
18
+ @line = +''
19
+ @pending = +''
20
+ @awaiting_blank = false
21
+ @expecting_progress = false
22
+ @printing_progress = false
23
+ end
24
+
25
+ def feed(chunk)
26
+ chunk.each_char { |character| consume(character) }
27
+ flush
28
+ end
29
+
30
+ def finish
31
+ flush
32
+ @io.puts if @printing_progress
33
+ end
34
+
35
+ private
36
+
37
+ def consume(character)
38
+ return if character == "\r"
39
+ return finish_line if character == "\n"
40
+
41
+ if @expecting_progress && character.match?(%r{[.EFS]})
42
+ @printing_progress = true
43
+ @pending << character
44
+ elsif @printing_progress
45
+ flush
46
+ @io.puts
47
+ @printing_progress = false
48
+ @expecting_progress = false
49
+ @line = +character
50
+ else
51
+ @expecting_progress = false if @expecting_progress
52
+ @line << character
53
+ end
54
+ end
55
+
56
+ def finish_line
57
+ flush
58
+ if @printing_progress
59
+ @io.puts
60
+ @expecting_progress = false
61
+ elsif @line.start_with?('# Running tests')
62
+ @awaiting_blank = true
63
+ elsif @awaiting_blank && @line.empty?
64
+ @expecting_progress = true
65
+ @awaiting_blank = false
66
+ end
67
+ @line.clear
68
+ @printing_progress = false
69
+ end
70
+
71
+ def flush
72
+ return if @pending.empty?
73
+
74
+ @io.print ComfortableMediaSurferTestOutput.progress_color(@pending)
75
+ @io.flush
76
+ @pending.clear
77
+ end
78
+ end
79
+
80
+ module_function
81
+
82
+ def capture(env, command)
83
+ output = +''
84
+ errors = +''
85
+ status = nil
86
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
87
+ progress = Progress.new($stdout)
88
+
89
+ Open3.popen3(env, *command) do |stdin, stdout, stderr, wait_thread|
90
+ stdin.close
91
+ error_reader = Thread.new { stderr.read }
92
+ loop do
93
+ chunk = stdout.readpartial(4096)
94
+ output << chunk
95
+ progress.feed(chunk)
96
+ end
97
+ rescue EOFError
98
+ errors = error_reader.value
99
+ status = wait_thread.value
100
+ ensure
101
+ progress.finish
102
+ end
103
+
104
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
105
+ [output, errors, status, elapsed]
106
+ end
107
+
108
+ def color(text, name)
109
+ return text unless $stdout.tty? && !ENV.key?('NO_COLOR')
110
+
111
+ "\e[#{COLORS.fetch(name)}m#{text}\e[0m"
112
+ end
113
+
114
+ def progress_color(text)
115
+ return text unless $stdout.tty? && !ENV.key?('NO_COLOR')
116
+
117
+ highlighted = text.gsub('S', "\e[33mS\e[32m")
118
+ .gsub(%r{[EF]}) { |character| "\e[31m#{character}\e[32m" }
119
+ "\e[32m#{highlighted}\e[0m"
120
+ end
121
+
122
+ def summary(output)
123
+ counts = output.match(SUMMARY)
124
+ raise 'Could not read the parallel test summary' unless counts
125
+
126
+ "#{counts[:tests]} tests, #{counts[:assertions]} assertions, " \
127
+ "#{counts[:failures]} failures, #{counts[:errors]} errors, " \
128
+ "#{counts[:skips]} #{counts[:skips] == '1' ? 'skip' : 'skips'}"
129
+ end
130
+
131
+ def run_parallel
132
+ workers = ENV.fetch('PARALLEL_WORKERS', '6')
133
+ skip_coverage = ENV.fetch('SKIP_COV', nil)
134
+ FileUtils.rm_rf('coverage')
135
+ FileUtils.mkdir_p('tmp')
136
+ Rake::Task['parallel:prepare'].invoke(workers)
137
+
138
+ env = {
139
+ 'PARALLEL_COVERAGE' => skip_coverage ? nil : 'true',
140
+ 'RECORD_RUNTIME' => 'true',
141
+ 'SKIP_COV' => skip_coverage,
142
+ 'NO_COLOR' => '1',
143
+ 'TMPDIR' => ENV.fetch('COMFY_TEST_TMPDIR', '/tmp')
144
+ }
145
+ command = [
146
+ 'bundle', 'exec', 'parallel_test', 'test',
147
+ '--type', 'test',
148
+ '--exclude-pattern', 'test/system/',
149
+ '-n', workers
150
+ ]
151
+
152
+ puts color("Running tests (#{workers} workers)…", :cyan)
153
+ stdout, stderr, status, elapsed = capture(env, command)
154
+ unless status.success?
155
+ $stdout.write(stdout)
156
+ $stderr.write(stderr)
157
+ raise "Parallel tests failed with status #{status.exitstatus}"
158
+ end
159
+ $stderr.write(stderr) unless stderr.empty?
160
+ puts color(summary(stdout), :green)
161
+ puts color(format('Completed in %.1f seconds', elapsed), :cyan)
162
+
163
+ report_coverage unless skip_coverage
164
+ end
165
+
166
+ def report_coverage
167
+ require 'simplecov'
168
+ SimpleCov.coverage_dir File.expand_path('coverage', __dir__)
169
+ result = SimpleCov::ResultMerger.merged_result
170
+ quietly { SimpleCov::Formatter::HTMLFormatter.new.format(result) }
171
+ coverage = format('Coverage: %<percent>.2f%% (%<covered>d/%<total>d) — coverage/index.html',
172
+ percent: result.covered_percent, covered: result.covered_lines, total: result.total_lines)
173
+ puts color(coverage, :green)
174
+ end
175
+
176
+ def quietly
177
+ previous = $stdout
178
+ $stdout = StringIO.new
179
+ yield
180
+ ensure
181
+ $stdout = previous
182
+ end
183
+ end
184
+
185
+ namespace :test do
186
+ task :prepare_serial do
187
+ FileUtils.rm_rf('coverage')
188
+ ENV['TMPDIR'] = ENV.fetch('COMFY_TEST_TMPDIR', '/tmp')
189
+ end
190
+
191
+ desc 'Run tests serially with coverage'
192
+ Rake::TestTask.new(:serial) do |task|
193
+ task.libs << 'test' << 'lib'
194
+ task.test_files = Rake::FileList['test/**/*_test.rb'].exclude('test/system/**/*_test.rb')
195
+ task.warning = false
196
+ end
197
+ Rake::Task['test:serial'].enhance(['test:prepare_serial'])
198
+
199
+ desc 'Run tests in isolated processes'
200
+ task(:parallel) { ComfortableMediaSurferTestOutput.run_parallel }
201
+ end
202
+
203
+ Rake::Task[:test].clear
204
+
205
+ desc 'Run tests in parallel (default: 6 workers)'
206
+ task test: 'test:parallel'
File without changes
@@ -0,0 +1 @@
1
+ //= link_tree ../builds
@@ -0,0 +1,27 @@
1
+ import jQuery from "jquery";
2
+ import ujs from "@rails/ujs";
3
+
4
+ window.jQuery = window.$ = jQuery;
5
+ ujs.start();
6
+
7
+ import "bootstrap/js/src/tab";
8
+ import "bootstrap/js/src/collapse";
9
+
10
+ import "../../vendor/diff/diff_match_patch.min";
11
+ import "../../vendor/diff/pretty_text_diff";
12
+ import "../../vendor/fontawesome";
13
+
14
+ import "./base";
15
+ import "./categories";
16
+ import "./codemirror";
17
+ import "./diff";
18
+ import "./file_link";
19
+ import "./file_upload";
20
+ import "./files_modal";
21
+ import "./page_fragments";
22
+ import "./sortable_list";
23
+ import "./slugify";
24
+ import "./timepicker";
25
+ import "./wysiwyg";
26
+
27
+ import "./custom";
@@ -0,0 +1,48 @@
1
+ (() => {
2
+ if (!window.CMS) window.CMS = {};
3
+ const CMS = window.CMS;
4
+
5
+ // TODO(glebm): Use the battle-tested universal onPageLoad code and enable Turbolinks+async in the demo app.
6
+ // See: https://gist.github.com/glebm/2496daf445877055447a6fac46509d9a
7
+ const isTurbolinks = "Turbolinks" in window && window.Turbolinks.supported;
8
+ if (isTurbolinks) {
9
+ document.addEventListener("turbolinks:load", () => {
10
+ window.CMS.init();
11
+ });
12
+ document.addEventListener("turbolinks:before-cache", () => {
13
+ window.CMS.dispose();
14
+ });
15
+ } else {
16
+ document.addEventListener("DOMContentLoaded", () => {
17
+ window.CMS.init();
18
+ });
19
+ }
20
+
21
+ CMS.init = () => {
22
+ CMS.current_path = window.location.pathname;
23
+ CMS.slugify();
24
+ CMS.codemirror.init();
25
+ CMS.wysiwyg.init();
26
+ CMS.sortableList.init();
27
+ CMS.timepicker.init();
28
+ CMS.pageFragments();
29
+ CMS.categories();
30
+ CMS.files.init();
31
+ CMS.fileLinks();
32
+ CMS.fileUpload.init();
33
+ CMS.diff();
34
+ };
35
+
36
+ CMS.dispose = () => {
37
+ CMS.codemirror.dispose();
38
+ CMS.wysiwyg.dispose();
39
+ CMS.files.dispose();
40
+ CMS.fileLinks.dispose();
41
+ CMS.fileUpload.dispose();
42
+ CMS.sortableList.dispose();
43
+ CMS.timepicker.dispose();
44
+ };
45
+
46
+ CMS.getLocale = () =>
47
+ document.querySelector('meta[name="cms-locale"]').content;
48
+ })();
@@ -0,0 +1,17 @@
1
+ (() => {
2
+ window.CMS.categories = (root = document) => {
3
+ const widget = root.querySelector('.categories-widget');
4
+ if (widget === null) return;
5
+ const readSection = widget.querySelector('.read');
6
+ const editSection = widget.querySelector('.editable');
7
+ widget.querySelector('.read button.toggle-cat-edit').addEventListener('click', () => {
8
+ readSection.style.display = 'none';
9
+ editSection.style.display = 'block';
10
+ });
11
+ widget.querySelector('.editable button.toggle-cat-edit').addEventListener('click', () => {
12
+ editSection.style.display = 'none';
13
+ readSection.style.display = 'block';
14
+ });
15
+ };
16
+ })();
17
+
@@ -0,0 +1,43 @@
1
+ import jQuery from "jquery";
2
+ import CodeMirror from "codemirror";
3
+ import "codemirror/mode/markdown/markdown";
4
+ import "codemirror/mode/htmlmixed/htmlmixed";
5
+
6
+ (() => {
7
+ const codeMirrorInstances = [];
8
+ window.CMS.codemirror = {
9
+ init(root = document) {
10
+ for (const textarea of root.querySelectorAll(
11
+ "textarea[data-cms-cm-mode]"
12
+ )) {
13
+ const codemirror = CodeMirror.fromTextArea(textarea, {
14
+ mode: textarea.dataset.cmsCmMode,
15
+ tabSize: 2,
16
+ lineWrapping: true,
17
+ autoCloseTags: true,
18
+ lineNumbers: true,
19
+ viewportMargin: Infinity,
20
+ });
21
+ codeMirrorInstances.push(codemirror);
22
+ }
23
+
24
+ const tabsRoot =
25
+ root.id === "form-fragments"
26
+ ? root
27
+ : root.querySelector("#form-fragments");
28
+ jQuery(tabsRoot)
29
+ .find('a[data-toggle="tab"]')
30
+ .on("shown.bs.tab", () => {
31
+ for (const codemirror of codeMirrorInstances) {
32
+ codemirror.refresh();
33
+ }
34
+ });
35
+ },
36
+ dispose() {
37
+ for (const codemirror of codeMirrorInstances) {
38
+ codemirror.toTextArea();
39
+ }
40
+ codeMirrorInstances.length = 0;
41
+ },
42
+ };
43
+ })();
@@ -0,0 +1 @@
1
+ // Custom JS for the admin area
@@ -0,0 +1,12 @@
1
+ import jQuery from "jquery";
2
+
3
+ (() => {
4
+ window.CMS.diff = () => {
5
+ jQuery(".revision").prettyTextDiff({
6
+ cleanup: true,
7
+ originalContainer: ".original",
8
+ changedContainer: ".current",
9
+ diffContainer: ".diff .content",
10
+ });
11
+ };
12
+ })();