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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ gemspec path: '../'
7
+
8
+ gem 'rails', '~> 7.2.0', '>= 7.2.2.1'
9
+
10
+ group :development, :test do
11
+ gem 'autoprefixer-rails', '~> 10.4.16.0'
12
+ gem 'byebug', '~> 11.1.0', platforms: %i[mri mingw x64_mingw]
13
+ gem 'image_processing', '>= 1.12.0'
14
+ gem 'propshaft', '~> 1.1.0'
15
+ gem 'sqlite3', '>= 2.1'
16
+ end
17
+
18
+ group :test do
19
+ gem 'brakeman', '~> 7.1.0'
20
+ gem 'bundler-audit', '~> 0.9.1'
21
+ gem 'coveralls_reborn', '~> 0.29.0', require: false
22
+ gem 'cuprite', '>= 0.15'
23
+ gem 'diffy'
24
+ gem 'equivalent-xml', '~> 0.6.0'
25
+ gem 'minitest', '< 6.0.0'
26
+ gem 'minitest-reporters', '>= 1.6.1'
27
+ gem 'mocha', '>= 2.3.0', require: false
28
+ gem 'ostruct'
29
+ gem 'parallel_tests', '~> 5.7', require: false
30
+ gem 'puma'
31
+ gem 'rails-controller-testing', '~> 1.0.5'
32
+ gem 'rubocop', '~> 1.81.1', require: false
33
+ gem 'rubocop-minitest'
34
+ gem 'rubocop-rails'
35
+ gem 'simplecov', '~> 0.22.0', require: false
36
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ gemspec path: '../'
7
+
8
+ gem 'rails', '~> 8.0.1'
9
+
10
+ group :development, :test do
11
+ gem 'autoprefixer-rails', '~> 10.4.16.0'
12
+ gem 'byebug', '~> 11.1.0', platforms: %i[mri mingw x64_mingw]
13
+ gem 'image_processing', '>= 1.12.0'
14
+ gem 'propshaft', '~> 1.1.0'
15
+ gem 'sqlite3', '>= 2.1'
16
+ end
17
+
18
+ group :test do
19
+ gem 'brakeman', '~> 7.1.0'
20
+ gem 'bundler-audit', '~> 0.9.1'
21
+ gem 'coveralls_reborn', '~> 0.29.0', require: false
22
+ gem 'cuprite', '>= 0.15'
23
+ gem 'diffy'
24
+ gem 'equivalent-xml', '~> 0.6.0'
25
+ gem 'minitest', '< 6.0.0'
26
+ gem 'minitest-reporters', '>= 1.6.1'
27
+ gem 'mocha', '>= 2.3.0', require: false
28
+ gem 'ostruct'
29
+ gem 'parallel_tests', '~> 5.7', require: false
30
+ gem 'puma'
31
+ gem 'rails-controller-testing', '~> 1.0.5'
32
+ gem 'rubocop', '~> 1.81.1', require: false
33
+ gem 'rubocop-minitest'
34
+ gem 'rubocop-rails'
35
+ gem 'simplecov', '~> 0.22.0', require: false
36
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ gemspec path: '../'
7
+
8
+ gem 'rails', '~> 8.1.0'
9
+
10
+ group :development, :test do
11
+ gem 'autoprefixer-rails', '~> 10.4.16.0'
12
+ gem 'byebug', '~> 11.1.0', platforms: %i[mri mingw x64_mingw]
13
+ gem 'image_processing', '>= 1.12.0'
14
+ gem 'propshaft', '~> 1.1.0'
15
+ gem 'sqlite3', '>= 2.1'
16
+ end
17
+
18
+ group :test do
19
+ gem 'brakeman', '~> 7.1.0'
20
+ gem 'bundler-audit', '~> 0.9.1'
21
+ gem 'coveralls_reborn', '~> 0.29.0', require: false
22
+ gem 'cuprite', '>= 0.15'
23
+ gem 'diffy'
24
+ gem 'equivalent-xml', '~> 0.6.0'
25
+ gem 'minitest', '< 6.0.0'
26
+ gem 'minitest-reporters', '>= 1.6.1'
27
+ gem 'mocha', '>= 2.3.0', require: false
28
+ gem 'ostruct'
29
+ gem 'parallel_tests', '~> 5.7', require: false
30
+ gem 'puma'
31
+ gem 'rails-controller-testing', '~> 1.0.5'
32
+ gem 'rubocop', '~> 1.81.1', require: false
33
+ gem 'rubocop-minitest'
34
+ gem 'rubocop-rails'
35
+ gem 'simplecov', '~> 0.22.0', require: false
36
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::AccessControl
4
+ module AdminAuthentication
5
+ # Set username and password in config/initializers/comfortable_media_surfer.rb
6
+ # Like this:
7
+ # ComfortableMediaSurfer::AccessControl::AdminAuthentication.username = 'myname'
8
+ # ComfortableMediaSurfer::AccessControl::AdminAuthentication.password = 'mypassword'
9
+
10
+ mattr_accessor :username,
11
+ :password
12
+
13
+ # Simple http_auth. When implementing some other form of authentication
14
+ # this method should return +true+ if everything is great, or redirect user
15
+ # to some other page, thus denying access to cms admin section.
16
+ def authenticate
17
+ authenticate_or_request_with_http_basic do |username, password|
18
+ self.username == username && self.password == password
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::AccessControl
4
+ module AdminAuthorization
5
+ # By default there's no authorization of any kind
6
+ def authorize
7
+ true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::AccessControl
4
+ module PublicAuthentication
5
+ # By defaut all published pages are accessible
6
+ def authenticate
7
+ true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ComfortableMediaSurfer::AccessControl
4
+ module PublicAuthorization
5
+ # By default there's no authorization of any kind
6
+ def authorize
7
+ true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ComfortableMediaSurfer::Configuration
4
+ # Don't like ComfortableMediaSurfer? Set it to whatever you like. :(
5
+ attr_accessor :cms_title
6
+
7
+ # Controller that is inherited from CmsAdmin::BaseController
8
+ # 'ApplicationController' is the default
9
+ attr_accessor :admin_base_controller
10
+
11
+ # Controller that Comfy::Cms::BaseController will inherit from
12
+ # 'ApplicationController' is the default
13
+ attr_accessor :public_base_controller
14
+
15
+ # Module that will handle authentication to access cms-admin area
16
+ attr_accessor :admin_auth
17
+
18
+ # Module that will handle authorization against admin cms resources
19
+ attr_accessor :admin_authorization
20
+
21
+ # Module that will handle authentication for public pages
22
+ attr_accessor :public_auth
23
+
24
+ # Module that will handle authorization against public resources
25
+ attr_accessor :public_authorization
26
+
27
+ # When arriving at /cms-admin you may chose to redirect to arbirtary path,
28
+ # for example '/cms-admin/users'
29
+ attr_accessor :admin_route_redirect
30
+
31
+ # With each page load, files will be synched with the database. Database entries are
32
+ # destroyed if there's no corresponding file. Seeds are disabled by default.
33
+ attr_accessor :enable_seeds
34
+
35
+ # Path where seeds can be located.
36
+ attr_accessor :seeds_path
37
+
38
+ # Number of revisions kept. Default is 25. If you wish to disable: set this to 0.
39
+ attr_accessor :revisions_limit
40
+
41
+ # Locale definitions. If you want to define your own locale merge
42
+ # {:locale => 'Locale Title'} with this.
43
+ attr_accessor :locales
44
+
45
+ # Admin interface will respect the locale of the site being managed. However you can
46
+ # force it to English by setting this to `:en`
47
+ attr_accessor :admin_locale
48
+
49
+ # A class that is included as a sweeper to admin base controller if it's set
50
+ attr_accessor :admin_cache_sweeper
51
+
52
+ # Not allowing erb code to be run inside page content. False by default.
53
+ # @return [Boolean]
54
+ attr_accessor :allow_erb
55
+
56
+ # Whitelist of all helper methods that can be used via {{cms:helper}} tag. By default
57
+ # all helpers are allowed except `eval`, `send`, `call` and few others. Empty array
58
+ # will prevent rendering of all helpers.
59
+ attr_accessor :allowed_helpers
60
+
61
+ # Whitelist of partials paths that can be used via {{cms:partial}} tag. All partials
62
+ # are accessible by default. Empty array will prevent rendering of all partials.
63
+ attr_accessor :allowed_partials
64
+
65
+ # Whitelist of template paths that can be used via {{cms:template}} tag. All templates
66
+ # are accessible by default. Empty array will prevent rendering of all templates.
67
+ attr_accessor :allowed_templates
68
+
69
+ # Site aliases, if you want to have aliases for your site. Good for harmonizing
70
+ # production env with dev/testing envs.
71
+ # e.g. config.site_aliases = {'host.com' => 'host.inv', 'host_a.com' => ['host.lvh.me', 'host.dev']}
72
+ # Default is nil (not used)
73
+ attr_accessor :hostname_aliases
74
+
75
+ # Reveal partials that can be overwritten in the admin area.
76
+ # Default is false.
77
+ attr_accessor :reveal_cms_partials
78
+
79
+ # Auto-setting parameter derived from the routes
80
+ attr_accessor :public_cms_path
81
+
82
+ # Customize returned content from `page.to_json`
83
+ # Default is set to `methods: [:content], except: [:content_cache]`
84
+ # For example: include fragments into json data with `config.page_to_json_options = { include: [:fragments] }`
85
+ attr_accessor :page_to_json_options
86
+
87
+ # Configuration defaults
88
+ def initialize
89
+ @cms_title = 'ComfortableMediaSurfer CMS Engine'
90
+ @admin_base_controller = 'ApplicationController'
91
+ @public_base_controller = 'ApplicationController'
92
+ @admin_auth = 'ComfortableMediaSurfer::AccessControl::AdminAuthentication'
93
+ @admin_authorization = 'ComfortableMediaSurfer::AccessControl::AdminAuthorization'
94
+ @public_auth = 'ComfortableMediaSurfer::AccessControl::PublicAuthentication'
95
+ @public_authorization = 'ComfortableMediaSurfer::AccessControl::PublicAuthorization'
96
+ @seed_data_path = nil
97
+ @admin_route_redirect = ''
98
+ @enable_sitemap = true
99
+ @enable_seeds = false
100
+ @seeds_path = File.expand_path('db/cms_seeds', Rails.root)
101
+ @revisions_limit = 25
102
+ @locales = {
103
+ 'ar' => 'عربي',
104
+ 'ca' => 'Català',
105
+ 'cs' => 'Česky',
106
+ 'da' => 'Dansk',
107
+ 'de' => 'Deutsch',
108
+ 'en' => 'English',
109
+ 'es' => 'Español',
110
+ 'fi' => 'Suomi',
111
+ 'fr' => 'Français',
112
+ 'gr' => 'Ελληνικά',
113
+ 'hr' => 'Hrvatski',
114
+ 'it' => 'Italiano',
115
+ 'ja' => '日本語',
116
+ 'nb' => 'Norsk',
117
+ 'nl' => 'Nederlands',
118
+ 'pl' => 'Polski',
119
+ 'pt-BR' => 'Português Brasileiro',
120
+ 'ru' => 'Русский',
121
+ 'sk' => 'Slovensky',
122
+ 'sv' => 'Svenska',
123
+ 'tr' => 'Türkçe',
124
+ 'uk' => 'Українська',
125
+ 'zh-CN' => '简体中文',
126
+ 'zh-TW' => '正體中文'
127
+ }
128
+ @admin_locale = nil
129
+ @admin_cache_sweeper = nil
130
+ @allow_erb = false
131
+ @allowed_helpers = nil
132
+ @allowed_partials = nil
133
+ @hostname_aliases = nil
134
+ @reveal_cms_partials = false
135
+ @public_cms_path = nil
136
+ @page_to_json_options = { methods: [:content], except: [:content_cache] }
137
+ end
138
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # wtf does this mean
4
+ # it means that all nodes between this must be moved into here
5
+ # {{cms:block}} some content {{cms:end_block}}
6
+ class ComfortableMediaSurfer::Content::Block < ComfortableMediaSurfer::Content::Tag
7
+ attr_writer :nodes
8
+
9
+ def nodes
10
+ @nodes ||= []
11
+ end
12
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ class ComfortableMediaSurfer::Content::ParamsParser
6
+ class Error < StandardError; end
7
+
8
+ STRING_LITERAL = %r{'[^']*'|"[^"]*"}
9
+ IDENTIFIER = %r{[a-z0-9][\w\-/.]*}i
10
+ HASH_KEY = %r{#{IDENTIFIER}:}
11
+ COMMA = %r{,}
12
+ HASH_OPEN = %r{\{}
13
+ HASH_CLOSE = %r{\}}
14
+ ARRAY_OPEN = %r{\[}
15
+ ARRAY_CLOSE = %r{\]}
16
+ INTEGER = %r{\b[0-9]+\b}i
17
+
18
+ # @param <String> string
19
+ def initialize(string = '')
20
+ @string = string
21
+ end
22
+
23
+ # Takes CMS content tag parameters and converts them into array of strings,
24
+ # hashes and arrays.
25
+ #
26
+ # @return [Array<String, {String => String}>]
27
+ # @raise [Error] if the given `text` is malformed.
28
+ #
29
+ # @example
30
+ # new("a, b, c").parse
31
+ # #=> ["a", "b", "c"]
32
+ #
33
+ # new("a, b: c, d: e").parse
34
+ # #=> ["a", {"b" => "c", "d" => "e"}]
35
+ #
36
+ # new("a, b: {c: [d, e]}").parse
37
+ # #=> ["a", {"b" => {"c" => ["d", "e"]}}]
38
+ #
39
+ def params
40
+ @tokens = tokenize(@string)
41
+ parse_params
42
+ end
43
+
44
+ private
45
+
46
+ # Contructs root-level list of arguments sent via content tag
47
+ def parse_params
48
+ params = []
49
+ while (token = @tokens.shift)
50
+ params << parse_value(token)
51
+ end
52
+ params
53
+ end
54
+
55
+ # Gets token value. Will trigger parsing of hash and array structures
56
+ def parse_value(token)
57
+ case token&.first
58
+ when :string
59
+ token[1]
60
+ when :hash_key
61
+ @tokens.unshift(token)
62
+ parse_hash
63
+ when :hash_open
64
+ parse_hash
65
+ when :array_open
66
+ parse_array
67
+ else
68
+ raise Error, "Invalid params: #{@string}"
69
+ end
70
+ end
71
+
72
+ # Hash constructor method. Will handle nested hashes as well
73
+ def parse_hash
74
+ opens = 1
75
+ hash = {}
76
+
77
+ while (token = @tokens.shift)
78
+ case token&.first
79
+ when :hash_key
80
+ hash[token[1]] = parse_value(@tokens.shift)
81
+ when :hash_close
82
+ opens -= 1
83
+ when :hash_open
84
+ opens += 1
85
+ else
86
+ raise Error, "Invalid params: #{@string}"
87
+ end
88
+
89
+ return hash if opens.zero?
90
+ end
91
+
92
+ # We're can't really detect unclosed hashes as we can construct them without
93
+ # opening brakets. For example, `a: b, c: d` is same as `{a: b, c: d}` and
94
+ # `{a: b, c: d` is also ends up to be a valid hash. It will error out if
95
+ # unclosed hash is followed by some other unexpected token. Like: `{a: b, c`
96
+ hash
97
+ end
98
+
99
+ # Array construction method. Will handle nested arrays
100
+ def parse_array
101
+ opens = 1
102
+ array = []
103
+ while (token = @tokens.shift)
104
+ case token&.first
105
+ when :array_close
106
+ opens -= 1
107
+ else
108
+ array << parse_value(token)
109
+ end
110
+
111
+ return array if opens.zero?
112
+ end
113
+
114
+ raise Error, "Unclosed array param: #{@string}"
115
+ end
116
+
117
+ # Tokenizing input string into a list of touples
118
+ # Also args_string is stripped of "smart" quotes coming from wysiwyg
119
+ #
120
+ # @param [String] args_string
121
+ # @return [Array<String>] tokens
122
+ def tokenize(args_string)
123
+ args_string = args_string.tr('“”‘’', %q(""''))
124
+ ss = StringScanner.new(args_string)
125
+ tokens = []
126
+ loop do
127
+ ss.skip(%r{\s*})
128
+ break if ss.eos?
129
+
130
+ # commas are just separators like spaces
131
+ next if ss.scan(COMMA)
132
+
133
+ tokens <<
134
+ if (t = ss.scan(STRING_LITERAL)) then [:string, t[1...-1]]
135
+ elsif (t = ss.scan(HASH_KEY)) then [:hash_key, t[0...-1]]
136
+ elsif (t = ss.scan(INTEGER)) then [:string, t.to_i]
137
+ elsif (t = ss.scan(IDENTIFIER)) then [:string, t]
138
+ elsif (t = ss.scan(HASH_OPEN)) then [:hash_open, t]
139
+ elsif (t = ss.scan(HASH_CLOSE)) then [:hash_close, t]
140
+ elsif (t = ss.scan(ARRAY_OPEN)) then [:array_open, t]
141
+ elsif (t = ss.scan(ARRAY_CLOSE)) then [:array_close, t]
142
+ else
143
+ raise Error, "Unexpected char: #{ss.getch}"
144
+ end
145
+ end
146
+
147
+ tokens
148
+ end
149
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ # Processing content follows these stages:
6
+ #
7
+ # string - Text with tags. like this: "some {{cms:fragment content}} text"
8
+ # tokenization - Splits string into a list of strings and hashes that define tags
9
+ # Example: ["some ", {tag_class: "fragment", tag_params: ""}, " text"]
10
+ # nodefying - Initializes Tag instances from tag hashes and returns list
11
+ # like this: ["some ", (FragmentTagInstance), " text"]
12
+ # rendering - Recursively iterates through nodes. Tag instances get their
13
+ # `render` method called. Result of that is tokenized, nodefied
14
+ # and rendered once again until there are no tags to expand.
15
+ # Resulting list is flattened and joined into a final rendered string.
16
+ #
17
+ class ComfortableMediaSurfer::Content::Renderer
18
+ class SyntaxError < StandardError; end
19
+ class Error < StandardError; end
20
+
21
+ MAX_DEPTH = 100
22
+
23
+ # tags are in this format: {{ cms:tag_class params }}
24
+ TAG_REGEX = %r{\{\{\s*?cms:(?<class>\w+)(?<params>.*?)\}\}}
25
+
26
+ class << self
27
+ # @return [Hash<String, Class<ComfortableMediaSurfer::Content::Tag>>]
28
+ def tags
29
+ @tags ||= {}
30
+ end
31
+
32
+ # @param [String] name
33
+ # @param [Class<ComfortableMediaSurfer::Content::Tag>] klass
34
+ def register_tag(name, klass)
35
+ tags[name.to_s] = klass
36
+ end
37
+ end
38
+
39
+ # @param [Comfy::Cms::WithFragments, nil] context
40
+ def initialize(context)
41
+ @context = context
42
+ end
43
+
44
+ # This is how we render content out. Takes context (cms page) and content
45
+ # nodes
46
+ # @param [Array<String, ComfortableMediaSurfer::Content::Tag>]
47
+ # @param [Boolean] allow_erb
48
+ # @param [Integer] depth current tag nesting position
49
+ def render(nodes, allow_erb = ComfortableMediaSurfer.config.allow_erb, depth = 0)
50
+ if depth >= MAX_DEPTH
51
+ raise Error, 'Deep tag nesting or recursive nesting detected'
52
+ end
53
+
54
+ nodes.map do |node|
55
+ case node
56
+ when String
57
+ sanitize_erb(node, allow_erb)
58
+ else
59
+ tokens = tokenize(node.render)
60
+ nodes = nodes(tokens)
61
+ render(nodes, allow_erb || node.allow_erb?, depth.next)
62
+ end
63
+ end.join
64
+ end
65
+
66
+ def sanitize_erb(string, allow_erb)
67
+ if allow_erb
68
+ string.to_s
69
+ else
70
+ string.to_s.gsub('<%', '&lt;%').gsub('%>', '%&gt;')
71
+ end
72
+ end
73
+
74
+ # Splitting text with tags into tokens we can process down the line
75
+ # @return [Array<String, {Symbol => String}>]
76
+ def tokenize(string)
77
+ tokens = []
78
+ ss = StringScanner.new(string.to_s)
79
+ while (string = ss.scan_until(TAG_REGEX))
80
+ text = string.sub(ss[0], '')
81
+ tokens << text unless text.empty?
82
+ tokens << {
83
+ tag_class: ss[:class],
84
+ tag_params: ss[:params].strip,
85
+ source: ss[0]
86
+ }
87
+ end
88
+ text = ss.rest
89
+ tokens << text if text.present?
90
+ tokens
91
+ end
92
+
93
+ # Constructing node tree for content. It's a list of strings and tags with
94
+ # their own `nodes` method that has array of strings and tags with their own
95
+ # `nodes` method that... you get the idea.
96
+ # @param [Array<String, {Symbol => String}>] tokens
97
+ # @return [Array<String, ComfortableMediaSurfer::Content::Tag>]
98
+ def nodes(tokens)
99
+ nodes = [[]]
100
+ tokens.each do |token|
101
+ case token
102
+
103
+ # tag signature
104
+ when Hash
105
+ case tag_class = token[:tag_class]
106
+
107
+ # This handles {{cms:end}} tag. Stopping collecting block nodes.
108
+ when 'end'
109
+ if nodes.one?
110
+ raise SyntaxError, 'closing unopened block'
111
+ end
112
+
113
+ nodes.pop
114
+
115
+ else
116
+ # @type [Class<ComfortableMediaSurfer::Content::Tag>]
117
+ klass = self.class.tags[tag_class] ||
118
+ raise(SyntaxError, "Unrecognized tag: #{token[:source]}")
119
+
120
+ # @type [ComfortableMediaSurfer::Content::Tag]
121
+ tag = klass.new(
122
+ context: @context,
123
+ params: ComfortableMediaSurfer::Content::ParamsParser.new(token[:tag_params]).params,
124
+ source: token[:source]
125
+ )
126
+ nodes.last << tag
127
+
128
+ # If it's a block tag we start collecting nodes into it
129
+ if tag.is_a?(ComfortableMediaSurfer::Content::Block)
130
+ nodes << tag.nodes
131
+ end
132
+ end
133
+
134
+ # text
135
+ else
136
+ nodes.last << token
137
+ end
138
+ end
139
+
140
+ if nodes.count > 1
141
+ raise SyntaxError, 'unclosed block detected'
142
+ end
143
+
144
+ nodes.flatten
145
+ end
146
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ComfortableMediaSurfer::Content::Tag
4
+ class Error < StandardError; end
5
+
6
+ # @type [Comfy::Cms::WithFragments]
7
+ attr_reader :context
8
+
9
+ # @type [Array<String, {String => String}>] params
10
+ attr_reader :params
11
+
12
+ # @type [String, nil] source
13
+ attr_reader :source
14
+
15
+ # @param [Comfy::Cms::WithFragments] context
16
+ # @param [Array<String, {String => String}>] params
17
+ # @param [String, nil] source
18
+ def initialize(context:, params: [], source: nil)
19
+ @context = context
20
+ @params = params
21
+ @source = source
22
+ end
23
+
24
+ # Making sure we don't leak erb from tags by accident.
25
+ # Tag classes can override this, like partials/helpers tags.
26
+ def allow_erb?
27
+ ComfortableMediaSurfer.config.allow_erb
28
+ end
29
+
30
+ # Normally it's a {(String)}. However, if tag content has tags,
31
+ # we need to expand them and that produces potentially more stuff.
32
+ # @return [Array<String, ComfortableMediaSurfer::Content::Tag>]
33
+ def nodes
34
+ template = ComfortableMediaSurfer::Content::Renderer.new(@context)
35
+ tokens = template.tokenize(content)
36
+ template.nodes(tokens)
37
+ end
38
+
39
+ # @return [String]
40
+ def content
41
+ raise Error, 'This is a base class. It holds no content'
42
+ end
43
+
44
+ # @return [String]
45
+ def render
46
+ content
47
+ end
48
+ end