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,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::Seeds::Page
4
+ class Importer < ComfortableMediaSurfer::Seeds::Importer
5
+ # tracking target page linking. Since we might be linking to something that
6
+ # doesn't exist yet, we'll defer linking to the end of import
7
+ attr_accessor :target_pages
8
+
9
+ def initialize(from, to = from)
10
+ super
11
+ self.path = ::File.join(ComfortableMediaSurfer.config.seeds_path, from, 'pages/')
12
+ end
13
+
14
+ def import!
15
+ import_page(File.join(path, 'index/'), nil)
16
+
17
+ link_target_pages
18
+
19
+ # Remove pages not found in seeds
20
+ site.pages.where('id NOT IN (?)', seed_ids).destroy_all
21
+ end
22
+
23
+ private
24
+
25
+ # Recursive function that will be called for each child page (subfolder)
26
+ def import_page(path, parent)
27
+ slug = path.split('/').last
28
+
29
+ # setting page record
30
+ page =
31
+ if parent.present?
32
+ child = site.pages.where(slug: slug, parent_id: parent.id).first_or_initialize
33
+ child.parent = parent
34
+ child
35
+ else
36
+ site.pages.root || site.pages.new(slug: slug)
37
+ end
38
+
39
+ content_path = File.join(path, 'content.html')
40
+
41
+ # If file is newer than page record we'll process it
42
+ if fresh_seed?(page, content_path)
43
+
44
+ # reading file content in, resulting in a hash
45
+ fragments_hash = parse_file_content(content_path)
46
+
47
+ # parsing attributes section
48
+ attributes_yaml = fragments_hash.delete('attributes')
49
+ attrs = YAML.safe_load(attributes_yaml)
50
+
51
+ # applying attributes
52
+ layout = site.layouts.find_by(identifier: attrs.delete('layout')) || parent.try(:layout)
53
+ category_ids = category_names_to_ids(page, attrs.delete('categories'))
54
+ target_page = attrs.delete('target_page')
55
+
56
+ page.attributes = attrs.merge(
57
+ layout: layout,
58
+ category_ids: category_ids
59
+ )
60
+
61
+ # applying fragments
62
+ old_frag_identifiers = page.fragments.pluck(:identifier)
63
+
64
+ new_frag_identifiers, fragments_attributes =
65
+ construct_fragments_attributes(fragments_hash, page, path)
66
+ page_saved = begin
67
+ page.fragments_attributes = fragments_attributes
68
+ page.save
69
+ ensure
70
+ close_fragment_files(fragments_attributes)
71
+ end
72
+
73
+ if page_saved
74
+ message = "[CMS SEEDS] Imported Page \t #{page.full_path}"
75
+ ComfortableMediaSurfer.logger.info(message)
76
+
77
+ # defering target page linking
78
+ if target_page.present?
79
+ self.target_pages ||= {}
80
+ target_pages[page.id] = target_page
81
+ end
82
+
83
+ # cleaning up old fragments
84
+ page.fragments.where(identifier: old_frag_identifiers - new_frag_identifiers).destroy_all
85
+
86
+ else
87
+ message = "[CMS SEEDS] Failed to import Page \n#{page.errors.inspect}"
88
+ ComfortableMediaSurfer.logger.warn(message)
89
+ end
90
+ end
91
+
92
+ import_translations(path, page)
93
+
94
+ # Tracking what page from seeds we're working with. So we can remove pages
95
+ # that are no longer in seeds
96
+ seed_ids << page.id
97
+
98
+ # importing child pages (if there are any)
99
+ Dir["#{path}*/"].each do |page_path|
100
+ import_page(page_path, page)
101
+ end
102
+ end
103
+
104
+ # Importing translations for given page. They look like `content.locale.html`
105
+ def import_translations(path, page)
106
+ old_translations = page.translations.pluck(:locale)
107
+ new_translations = []
108
+
109
+ Dir["#{path}content.*.html"].each do |file_path|
110
+ locale = File.basename(file_path).match(%r{content\.(\w+)\.html})[1]
111
+ new_translations << locale
112
+
113
+ translation = page.translations.where(locale: locale).first_or_initialize
114
+
115
+ next unless fresh_seed?(translation, file_path)
116
+
117
+ # reading file content in, resulting in a hash
118
+ fragments_hash = parse_file_content(file_path)
119
+
120
+ # parsing attributes section
121
+ attributes_yaml = fragments_hash.delete('attributes')
122
+ attrs = YAML.safe_load(attributes_yaml)
123
+
124
+ # applying attributes
125
+ layout = site.layouts.find_by(identifier: attrs.delete('layout')) || page.try(:layout)
126
+ translation.attributes = attrs.merge(
127
+ layout: layout
128
+ )
129
+
130
+ # applying fragments
131
+ old_frag_identifiers = translation.fragments.pluck(:identifier)
132
+
133
+ new_frag_identifiers, fragments_attributes =
134
+ construct_fragments_attributes(fragments_hash, translation, path)
135
+ translation_saved = begin
136
+ translation.fragments_attributes = fragments_attributes
137
+ translation.save
138
+ ensure
139
+ close_fragment_files(fragments_attributes)
140
+ end
141
+
142
+ if translation_saved
143
+ message = "[CMS SEEDS] Imported Translation \t #{locale}"
144
+ ComfortableMediaSurfer.logger.info(message)
145
+
146
+ # cleaning up old fragments
147
+ frags_to_remove = old_frag_identifiers - new_frag_identifiers
148
+ translation.fragments.where(identifier: frags_to_remove).destroy_all
149
+
150
+ else
151
+ message = "[CMS SEEDS] Failed to import Translation \n#{locale}"
152
+ ComfortableMediaSurfer.logger.warn(message)
153
+ end
154
+ end
155
+
156
+ # Cleaning up removed translations
157
+ translations_to_remove = old_translations - new_translations
158
+ page.translations.where(locale: translations_to_remove).destroy_all
159
+ end
160
+
161
+ # Constructing frag attributes hash that can be assigned to page or translation
162
+ # also returning list of frag identifiers so we can destroy old ones
163
+ def construct_fragments_attributes(hash, record, path)
164
+ frag_identifiers = []
165
+ frag_attributes = hash.collect do |frag_header, frag_content|
166
+ tag, identifier = frag_header.split
167
+ frag_hash = {
168
+ identifier: identifier,
169
+ tag: tag
170
+ }
171
+
172
+ # tracking fragments that need removing later
173
+ frag_identifiers << identifier
174
+
175
+ # based on tag we need to cram content in proper place and proper format
176
+ case tag
177
+ when 'date', 'datetime'
178
+ frag_hash[:datetime] = frag_content
179
+ when 'checkbox'
180
+ frag_hash[:boolean] = frag_content
181
+ when 'file', 'files'
182
+ files, file_ids_destroy = files_content(record, identifier, path, frag_content)
183
+ frag_hash[:files] = files
184
+ frag_hash[:file_ids_destroy] = file_ids_destroy
185
+ else
186
+ frag_hash[:content] = frag_content
187
+ end
188
+
189
+ frag_hash
190
+ end
191
+
192
+ [frag_identifiers, frag_attributes]
193
+ end
194
+
195
+ # Preparing fragment attachments. Returns hashes with file data for
196
+ # ActiveStorage and a list of ids of old attachements to destroy
197
+ def files_content(record, identifier, path, frag_content)
198
+ # preparing attachments
199
+ files = []
200
+ frag_content.split("\n").each do |filename|
201
+ file_handler = File.new(File.join(path, filename), 'rb')
202
+ attachment = {
203
+ io: file_handler,
204
+ filename: filename
205
+ }
206
+ files << attachment
207
+ attachment[:content_type] = MimeMagic.by_magic(file_handler)
208
+ end
209
+
210
+ # ensuring that old attachments get removed
211
+ ids_destroy = []
212
+ if (frag = record.fragments.find_by(identifier: identifier))
213
+ ids_destroy = frag.attachments.pluck(:id)
214
+ end
215
+
216
+ [files, ids_destroy]
217
+ rescue StandardError
218
+ close_files(files)
219
+ raise
220
+ end
221
+
222
+ def close_fragment_files(fragment_attributes)
223
+ fragment_attributes.each do |attributes|
224
+ close_files(attributes[:files])
225
+ end
226
+ end
227
+
228
+ def close_files(files)
229
+ Array(files).each do |file|
230
+ io = file[:io]
231
+ io.close unless io.closed?
232
+ end
233
+ end
234
+
235
+ def link_target_pages
236
+ return unless target_pages.present?
237
+
238
+ target_pages.each do |page_id, target|
239
+ if (target = site.pages.find_by(full_path: target))
240
+ @site.pages.find(page_id).update_column(:target_page_id, target.id)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::Seeds::Snippet
4
+ class Exporter < ComfortableMediaSurfer::Seeds::Exporter
5
+ def initialize(from, to = from)
6
+ super
7
+ self.path = ::File.join(ComfortableMediaSurfer.config.seeds_path, to, 'snippets/')
8
+ end
9
+
10
+ def export!
11
+ prepare_folder!(path)
12
+
13
+ site.snippets.each do |snippet|
14
+ attrs = {
15
+ 'label' => snippet.label,
16
+ 'categories' => snippet.categories.map(&:label),
17
+ 'position' => snippet.position
18
+ }.to_yaml
19
+
20
+ data = []
21
+ data << { header: 'attributes', content: attrs }
22
+ data << { header: 'content', content: snippet.content }
23
+
24
+ snippet_path = File.join(path, "#{snippet.identifier}.html")
25
+ write_file_content(snippet_path, data)
26
+
27
+ message = "[CMS SEEDS] Exported Snippet \t #{snippet.identifier}"
28
+ ComfortableMediaSurfer.logger.info(message)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::Seeds::Snippet
4
+ class Importer < ComfortableMediaSurfer::Seeds::Importer
5
+ def initialize(from, to = from)
6
+ super
7
+ self.path = ::File.join(ComfortableMediaSurfer.config.seeds_path, from, 'snippets/')
8
+ end
9
+
10
+ def import!
11
+ Dir.glob("#{path}/*.html").each do |path|
12
+ identifier = File.basename(path, '.html')
13
+
14
+ # reading file content in, resulting in a hash
15
+ content_hash = parse_file_content(path)
16
+
17
+ # parsing attributes section
18
+ attributes_yaml = content_hash.delete('attributes')
19
+ attrs = YAML.safe_load(attributes_yaml)
20
+
21
+ snippet = site.snippets.where(identifier: identifier).first_or_initialize
22
+
23
+ if fresh_seed?(snippet, path)
24
+ category_ids = category_names_to_ids(snippet, attrs.delete('categories'))
25
+
26
+ snippet.attributes = attrs.merge(
27
+ category_ids: category_ids,
28
+ content: content_hash['content']
29
+ )
30
+
31
+ if snippet.save
32
+ ComfortableMediaSurfer.logger.info("[CMS SEEDS] Imported Snippet \t #{snippet.identifier}")
33
+ else
34
+ ComfortableMediaSurfer.logger.warn("[CMS SEEDS] Failed to import Snippet \n#{snippet.errors.inspect}")
35
+ end
36
+ end
37
+
38
+ # Tracking what page from seeds we're working with. So we can remove pages
39
+ # that are no longer in seeds
40
+ seed_ids << snippet.id
41
+ end
42
+
43
+ # cleaning up
44
+ site.snippets.where('id NOT IN (?)', seed_ids).destroy_all
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::Seeds
4
+ SEED_CLASSES = %w[Layout Page Snippet File].freeze
5
+
6
+ class Error < StandardError; end
7
+
8
+ require 'mimemagic'
9
+
10
+ class Importer
11
+ attr_accessor :site,
12
+ :path,
13
+ :from,
14
+ :to,
15
+ :seed_ids
16
+
17
+ # `from` and `to` indicate site identifier and folder name
18
+ def initialize(from, to = from)
19
+ self.from = from
20
+ self.to = to
21
+ self.site = Comfy::Cms::Site.where(identifier: to).first!
22
+ self.seed_ids = []
23
+
24
+ return if ::File.exist?(path = ::File.join(ComfortableMediaSurfer.config.seeds_path, from))
25
+
26
+ raise Error, "Folder for import: '#{path}' is not found"
27
+ end
28
+
29
+ # if passed nil will use default seed classes
30
+ def import!(classes = nil)
31
+ classes ||= SEED_CLASSES
32
+ classes.each do |klass|
33
+ klass = "ComfortableMediaSurfer::Seeds::#{klass}::Importer"
34
+ klass.constantize.new(from, to).import!
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Splitting file content in sections delimited by headers that look like this:
41
+ # [header]
42
+ # some content
43
+ # [header 2]
44
+ # some more content
45
+ def parse_file_content(file_path)
46
+ text = ::File.read(file_path)
47
+ tokens = text.split(%r{^\[(.*?)\]\r?\n})
48
+ tokens.shift # first item should be blank
49
+ tokens.in_groups_of(2).to_h
50
+ end
51
+
52
+ def fresh_seed?(object, file_path)
53
+ object.new_record? || ::File.mtime(file_path) > object.updated_at
54
+ end
55
+
56
+ def category_names_to_ids(record, names)
57
+ [names].flatten.map do |name|
58
+ category = site.categories.find_or_create_by(
59
+ label: name,
60
+ categorized_type: record.class.to_s
61
+ )
62
+ category.id
63
+ end
64
+ end
65
+ end
66
+
67
+ class Exporter
68
+ attr_accessor :site,
69
+ :path,
70
+ :from,
71
+ :to
72
+
73
+ def initialize(from, to = from)
74
+ self.from = from
75
+ self.to = to
76
+ self.site = Comfy::Cms::Site.where(identifier: from).first!
77
+ end
78
+
79
+ # if passed nil will use default seed classes
80
+ def export!(classes = nil)
81
+ classes ||= SEED_CLASSES
82
+ classes.each do |klass|
83
+ klass = "ComfortableMediaSurfer::Seeds::#{klass}::Exporter"
84
+ klass.constantize.new(from, to).export!
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ # Writing to the seed file. Takes in file handler and array of hashes with
91
+ # `header` and `content` keys
92
+ def write_file_content(path, data)
93
+ ::File.open(::File.join(path), 'wb') do |f|
94
+ data.each do |item|
95
+ f.write("[#{item[:header]}]\n#{item[:content]}")
96
+ # adds a newline between items if not already there
97
+ # makes sure last item does not have a newline
98
+ if data.last != item && !item[:content].to_s.end_with?("\n")
99
+ f.write("\n")
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def prepare_folder!(path)
106
+ FileUtils.rm_rf(path)
107
+ FileUtils.mkdir_p(path)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../comfy_middle_seat/version'
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This mechanism is used by 3rd party plugins.
4
+ # Normally you'd use partials from your own app
5
+ module ComfortableMediaSurfer::ViewHooks
6
+ # Array of declared hooks
7
+ def self.hooks
8
+ @hooks ||= {}
9
+ end
10
+
11
+ # Renders hook content
12
+ def self.render(name, template, options = {})
13
+ out = ''
14
+ (hooks[name.to_sym] || []).each do |path|
15
+ out += template.render({ partial: path.first }.merge(options))
16
+ end
17
+ out.html_safe
18
+ end
19
+
20
+ # Will declare a partial that will be rendered for this hook
21
+ # Example:
22
+ # ComfortableMediaSurfer::ViewHooks.add(:navigation, 'shared/navigation')
23
+ def self.add(name, partial_path, position = 0)
24
+ hooks[name.to_sym] ||= []
25
+ hooks[name.to_sym] << [partial_path, position]
26
+ hooks[name.to_sym].sort_by!(&:last)
27
+ end
28
+
29
+ # Removing previously declared hook
30
+ def self.remove(name)
31
+ hooks.delete(name)
32
+ end
33
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Loading engine only if this is not a standalone installation
4
+ unless defined? ComfortableMediaSurfer::Application
5
+ require_relative 'comfortable_media_surfer/engine'
6
+ end
7
+
8
+ require_relative 'comfortable_media_surfer/version'
9
+ require_relative 'comfortable_media_surfer/error'
10
+ require_relative 'comfortable_media_surfer/configuration'
11
+ require_relative 'comfortable_media_surfer/routing'
12
+ require_relative 'comfortable_media_surfer/access_control/admin_authentication'
13
+ require_relative 'comfortable_media_surfer/access_control/admin_authorization'
14
+ require_relative 'comfortable_media_surfer/access_control/public_authentication'
15
+ require_relative 'comfortable_media_surfer/access_control/public_authorization'
16
+ require_relative 'comfortable_media_surfer/render_methods'
17
+ require_relative 'comfortable_media_surfer/view_hooks'
18
+ require_relative 'comfortable_media_surfer/form_builder'
19
+ require_relative 'comfortable_media_surfer/seeds'
20
+ require_relative 'comfortable_media_surfer/seeds/layout/importer'
21
+ require_relative 'comfortable_media_surfer/seeds/layout/exporter'
22
+ require_relative 'comfortable_media_surfer/seeds/page/importer'
23
+ require_relative 'comfortable_media_surfer/seeds/page/exporter'
24
+ require_relative 'comfortable_media_surfer/seeds/snippet/importer'
25
+ require_relative 'comfortable_media_surfer/seeds/snippet/exporter'
26
+ require_relative 'comfortable_media_surfer/seeds/file/importer'
27
+ require_relative 'comfortable_media_surfer/seeds/file/exporter'
28
+ require_relative 'comfortable_media_surfer/content'
29
+ require_relative 'comfortable_media_surfer/extensions'
30
+
31
+ module ComfortableMediaSurfer
32
+ Version = ComfortableMediaSurfer::VERSION
33
+
34
+ class << self
35
+ attr_writer :logger
36
+
37
+ # Modify CMS configuration
38
+ # Example:
39
+ # ComfortableMediaSurfer.configure do |config|
40
+ # config.cms_title = 'ComfortableMediaSurfer'
41
+ # end
42
+ def configure
43
+ yield configuration
44
+ end
45
+
46
+ # Accessor for ComfortableMediaSurfer::Configuration
47
+ def configuration
48
+ @configuration ||= Configuration.new
49
+ end
50
+ alias config configuration
51
+
52
+ def logger
53
+ @logger ||= Rails.logger
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer
4
+ VERSION = '3.2.0'
5
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'comfortable_media_surfer'
@@ -0,0 +1,21 @@
1
+ ____ __ _ _ _
2
+ / ___|___ _ __ ___ / _| ___ _ __| |_ __ _| |__ | | ___
3
+ | | / _ \| '_ ` _ \| |_ / _ \| '__| __/ _` | '_ \| |/ _ \
4
+ | |__| (_) | | | | | | _| (_) | | | || (_| | |_) | | __/
5
+ \____\___/|_| |_| |_|_| \___/|_| \__\__,_|_.__/|_|\___|
6
+ __ __ _ _ ____ __
7
+ | \/ | ___ __| (_) __ _ / ___| _ _ _ __ / _| ___ _ __
8
+ | |\/| |/ _ \/ _` | |/ _` | \___ \| | | | '__| |_ / _ \ '__|
9
+ | | | | __/ (_| | | (_| | ___) | |_| | | | _| __/ |
10
+ |_| |_|\___|\__,_|_|\__,_| |____/ \__,_|_| |_| \___|_|
11
+
12
+ You are almost done. Don't forget to:
13
+
14
+ [Optional] - If you want to store files in your CMS with ActiveStorage: `rails active_storage:install`
15
+ [Mandatory] - Create the tables in your database: `rails db:migrate`
16
+ [Mandatory] - Run `rails comfy:compile_assets`
17
+
18
+ Then go to http://your-rails-app/admin to start adding content.
19
+
20
+ Default username and password are: user / pass
21
+ Change these defaults in config/initializers/comfortable_media_surfer.rb
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy
4
+ module Generators
5
+ module Cms
6
+ class AssetsGenerator < Rails::Generators::Base
7
+ source_root File.expand_path(File.join(File.dirname(__FILE__), '../../../../app/assets'))
8
+
9
+ def generate_assets
10
+ directory 'javascripts/comfy/admin/cms', 'app/assets/javascripts/comfy/admin/cms'
11
+ directory 'stylesheets/comfy/admin/cms', 'app/assets/stylesheets/comfy/admin/cms'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/active_record'
4
+
5
+ module Comfy
6
+ module Generators
7
+ class CmsGenerator < Rails::Generators::Base
8
+ include Rails::Generators::Migration
9
+ include Thor::Actions
10
+
11
+ source_root File.expand_path('../../../..', __dir__)
12
+
13
+ def generate_migration
14
+ %w[01_create_cms 02_add_markdown_to_snippets].each do |migration|
15
+ destination = File.expand_path("db/migrate/#{migration}.rb", destination_root)
16
+ migration_dir = File.dirname(destination)
17
+ destination = self.class.migration_exists?(migration_dir, migration)
18
+
19
+ if destination
20
+ puts "\e[0m\e[31mFound existing #{migration}.rb migration. Remove it if you want to regenerate.\e[0m"
21
+ else
22
+ migration_template "db/migrate/#{migration}.rb", "db/migrate/#{migration[3..]}.rb"
23
+ end
24
+ end
25
+ end
26
+
27
+ def generate_initializer
28
+ copy_file 'config/initializers/comfortable_media_surfer.rb',
29
+ 'config/initializers/comfortable_media_surfer.rb'
30
+ end
31
+
32
+ def generate_railties_order
33
+ application <<~RUBY
34
+ # Ensuring that ActiveStorage routes are loaded before Comfy's globbing
35
+ # route. Without this file serving routes are inaccessible.
36
+ config.railties_order = [ActiveStorage::Engine, :main_app, :all]
37
+ RUBY
38
+ end
39
+
40
+ def generate_routing
41
+ route_string = <<~RUBY
42
+ comfy_route :cms_admin, path: "/admin"
43
+ # Ensure that this route is defined last
44
+ comfy_route :cms, path: "/"
45
+ RUBY
46
+ route route_string
47
+ end
48
+
49
+ def generate_cms_seeds
50
+ directory 'db/cms_seeds', 'db/cms_seeds'
51
+ end
52
+
53
+ def generate_assets
54
+ copy_file 'app/assets/javascripts/comfy/admin/cms/custom.js',
55
+ 'app/assets/javascripts/comfy/admin/cms/custom.js'
56
+ copy_file 'app/assets/stylesheets/comfy/admin/cms/custom.sass',
57
+ 'app/assets/stylesheets/comfy/admin/cms/custom.sass'
58
+ end
59
+
60
+ def show_readme
61
+ readme 'lib/generators/comfy/cms/README'
62
+ end
63
+
64
+ def self.next_migration_number(dirname)
65
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy
4
+ module Generators
5
+ module Cms
6
+ class ControllersGenerator < Rails::Generators::Base
7
+ source_root File.expand_path(File.join(File.dirname(__FILE__), '../../../../app/controllers'))
8
+
9
+ def generate_controllers
10
+ directory 'comfy', 'app/controllers/comfy'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy
4
+ module Generators
5
+ module Cms
6
+ class ModelsGenerator < Rails::Generators::Base
7
+ source_root File.expand_path(File.join(File.dirname(__FILE__), '../../../../app/models'))
8
+
9
+ def generate_models
10
+ directory 'comfy', 'app/models/comfy'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end