PixelForce_ETS 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (467) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +4 -0
  3. data/.gitattributes +2 -0
  4. data/.github/issue_template.md +22 -0
  5. data/.github/pull_request_template.md +6 -0
  6. data/.gitignore +21 -0
  7. data/.rubocop.yml +100 -0
  8. data/.travis.yml +43 -0
  9. data/CONTRIBUTING.md +35 -0
  10. data/Gemfile +29 -0
  11. data/LICENSE +20 -0
  12. data/README.md +116 -0
  13. data/Rakefile +5 -0
  14. data/app/assets/fonts/comfy/admin/cms/lib/redactor-font.eot +0 -0
  15. data/app/assets/javascripts/comfy/admin/cms/application.js +39 -0
  16. data/app/assets/javascripts/comfy/admin/cms/base.js +46 -0
  17. data/app/assets/javascripts/comfy/admin/cms/categories.js +17 -0
  18. data/app/assets/javascripts/comfy/admin/cms/codemirror.js +31 -0
  19. data/app/assets/javascripts/comfy/admin/cms/custom.js +1 -0
  20. data/app/assets/javascripts/comfy/admin/cms/diff.js +10 -0
  21. data/app/assets/javascripts/comfy/admin/cms/file_link.js +67 -0
  22. data/app/assets/javascripts/comfy/admin/cms/file_upload.js +194 -0
  23. data/app/assets/javascripts/comfy/admin/cms/files_modal.js +40 -0
  24. data/app/assets/javascripts/comfy/admin/cms/page_fragments.js +22 -0
  25. data/app/assets/javascripts/comfy/admin/cms/slugify.js +34 -0
  26. data/app/assets/javascripts/comfy/admin/cms/sortable_list.js +40 -0
  27. data/app/assets/javascripts/comfy/admin/cms/timepicker.js +30 -0
  28. data/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +65 -0
  29. data/app/assets/javascripts/comfy/vendor/Moxie.swf +0 -0
  30. data/app/assets/javascripts/comfy/vendor/Moxie.xap +0 -0
  31. data/app/assets/javascripts/comfy/vendor/bootstrap.bundle.min.js +7 -0
  32. data/app/assets/javascripts/comfy/vendor/bootstrap.bundle.min.js.map +1 -0
  33. data/app/assets/javascripts/comfy/vendor/codemirror.js +9653 -0
  34. data/app/assets/javascripts/comfy/vendor/codemirror/addon/edit/closetag.js +169 -0
  35. data/app/assets/javascripts/comfy/vendor/codemirror/mode/css/css.js +832 -0
  36. data/app/assets/javascripts/comfy/vendor/codemirror/mode/htmlmixed/htmlmixed.js +152 -0
  37. data/app/assets/javascripts/comfy/vendor/codemirror/mode/javascript/javascript.js +875 -0
  38. data/app/assets/javascripts/comfy/vendor/codemirror/mode/markdown/markdown.js +861 -0
  39. data/app/assets/javascripts/comfy/vendor/codemirror/mode/xml/xml.js +394 -0
  40. data/app/assets/javascripts/comfy/vendor/diff/diff_match_patch.min.js +49 -0
  41. data/app/assets/javascripts/comfy/vendor/diff/pretty_text_diff.js +76 -0
  42. data/app/assets/javascripts/comfy/vendor/flatpickr.min.js +2 -0
  43. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ar.js +52 -0
  44. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/bg.js +65 -0
  45. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/bn.js +65 -0
  46. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ca.js +83 -0
  47. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/cs.js +70 -0
  48. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/cy.js +92 -0
  49. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/da.js +71 -0
  50. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/de.js +70 -0
  51. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/default.js +80 -0
  52. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/eo.js +73 -0
  53. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/es.js +69 -0
  54. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/et.js +73 -0
  55. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fa.js +68 -0
  56. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fi.js +69 -0
  57. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/fr.js +75 -0
  58. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/gr.js +74 -0
  59. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/he.js +57 -0
  60. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hi.js +65 -0
  61. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hr.js +66 -0
  62. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/hu.js +72 -0
  63. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/id.js +61 -0
  64. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/it.js +70 -0
  65. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ja.js +57 -0
  66. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ko.js +60 -0
  67. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/lt.js +72 -0
  68. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/lv.js +67 -0
  69. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/mk.js +69 -0
  70. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/mn.js +67 -0
  71. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ms.js +68 -0
  72. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/my.js +69 -0
  73. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/nl.js +75 -0
  74. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/no.js +73 -0
  75. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pa.js +65 -0
  76. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pl.js +68 -0
  77. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pt-BR.js +66 -0
  78. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/pt.js +66 -0
  79. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ro.js +69 -0
  80. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/ru.js +66 -0
  81. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/si.js +65 -0
  82. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sk.js +70 -0
  83. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sl.js +70 -0
  84. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sq.js +65 -0
  85. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sr.js +69 -0
  86. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/sv.js +70 -0
  87. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/th.js +72 -0
  88. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/tr.js +66 -0
  89. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/uk.js +66 -0
  90. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/vn.js +66 -0
  91. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/zh-CN.js +61 -0
  92. data/app/assets/javascripts/comfy/vendor/flatpickr/i18n/zh-TW.js +61 -0
  93. data/app/assets/javascripts/comfy/vendor/fontawesome.js +5 -0
  94. data/app/assets/javascripts/comfy/vendor/moxie.min.js +16 -0
  95. data/app/assets/javascripts/comfy/vendor/plupload.dev.js +2497 -0
  96. data/app/assets/javascripts/comfy/vendor/redactor.js +9561 -0
  97. data/app/assets/javascripts/comfy/vendor/redactor/definedlinks.js +53 -0
  98. data/app/assets/javascripts/comfy/vendor/redactor/filemanager.js +64 -0
  99. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ar.js +76 -0
  100. data/app/assets/javascripts/comfy/vendor/redactor/i18n/az.js +75 -0
  101. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ba.js +79 -0
  102. data/app/assets/javascripts/comfy/vendor/redactor/i18n/bg.js +77 -0
  103. data/app/assets/javascripts/comfy/vendor/redactor/i18n/by.js +77 -0
  104. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ca.js +75 -0
  105. data/app/assets/javascripts/comfy/vendor/redactor/i18n/cs.js +87 -0
  106. data/app/assets/javascripts/comfy/vendor/redactor/i18n/da.js +75 -0
  107. data/app/assets/javascripts/comfy/vendor/redactor/i18n/de.js +80 -0
  108. data/app/assets/javascripts/comfy/vendor/redactor/i18n/el.js +76 -0
  109. data/app/assets/javascripts/comfy/vendor/redactor/i18n/en.js +75 -0
  110. data/app/assets/javascripts/comfy/vendor/redactor/i18n/eo.js +76 -0
  111. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es.js +75 -0
  112. data/app/assets/javascripts/comfy/vendor/redactor/i18n/es_AR.js +76 -0
  113. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fa.js +76 -0
  114. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fi.js +75 -0
  115. data/app/assets/javascripts/comfy/vendor/redactor/i18n/fr.js +75 -0
  116. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ge.js +75 -0
  117. data/app/assets/javascripts/comfy/vendor/redactor/i18n/gr.js +75 -0
  118. data/app/assets/javascripts/comfy/vendor/redactor/i18n/he.js +75 -0
  119. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hr.js +76 -0
  120. data/app/assets/javascripts/comfy/vendor/redactor/i18n/hu.js +77 -0
  121. data/app/assets/javascripts/comfy/vendor/redactor/i18n/id.js +76 -0
  122. data/app/assets/javascripts/comfy/vendor/redactor/i18n/it.js +78 -0
  123. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ja.js +76 -0
  124. data/app/assets/javascripts/comfy/vendor/redactor/i18n/kn.js +75 -0
  125. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ko.js +76 -0
  126. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lt.js +75 -0
  127. data/app/assets/javascripts/comfy/vendor/redactor/i18n/lv.js +75 -0
  128. data/app/assets/javascripts/comfy/vendor/redactor/i18n/mk.js +75 -0
  129. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nb.js +75 -0
  130. data/app/assets/javascripts/comfy/vendor/redactor/i18n/nl.js +80 -0
  131. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pl.js +76 -0
  132. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt-BR.js +75 -0
  133. data/app/assets/javascripts/comfy/vendor/redactor/i18n/pt.js +75 -0
  134. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ro.js +76 -0
  135. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ru.js +76 -0
  136. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sk.js +76 -0
  137. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sl.js +79 -0
  138. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sq.js +79 -0
  139. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-CIR.js +79 -0
  140. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sr-LAT.js +79 -0
  141. data/app/assets/javascripts/comfy/vendor/redactor/i18n/sv.js +74 -0
  142. data/app/assets/javascripts/comfy/vendor/redactor/i18n/th.js +74 -0
  143. data/app/assets/javascripts/comfy/vendor/redactor/i18n/tr.js +75 -0
  144. data/app/assets/javascripts/comfy/vendor/redactor/i18n/ua.js +77 -0
  145. data/app/assets/javascripts/comfy/vendor/redactor/i18n/uk.js +77 -0
  146. data/app/assets/javascripts/comfy/vendor/redactor/i18n/vi.js +74 -0
  147. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-CN.js +75 -0
  148. data/app/assets/javascripts/comfy/vendor/redactor/i18n/zh-TW.js +75 -0
  149. data/app/assets/javascripts/comfy/vendor/redactor/imagemanager.js +57 -0
  150. data/app/assets/javascripts/comfy/vendor/redactor/table.js +337 -0
  151. data/app/assets/javascripts/comfy/vendor/redactor/video.js +77 -0
  152. data/app/assets/javascripts/comfy/vendor/sortable.min.js +5 -0
  153. data/app/assets/stylesheets/comfy/admin/cms/application.sass +11 -0
  154. data/app/assets/stylesheets/comfy/admin/cms/base.sass +256 -0
  155. data/app/assets/stylesheets/comfy/admin/cms/codemirror_overrides.sass +19 -0
  156. data/app/assets/stylesheets/comfy/admin/cms/custom.sass +1 -0
  157. data/app/assets/stylesheets/comfy/admin/cms/redactor_overrides.sass +22 -0
  158. data/app/assets/stylesheets/comfy/vendor/bootstrap/_alert.scss +51 -0
  159. data/app/assets/stylesheets/comfy/vendor/bootstrap/_badge.scss +54 -0
  160. data/app/assets/stylesheets/comfy/vendor/bootstrap/_breadcrumb.scss +41 -0
  161. data/app/assets/stylesheets/comfy/vendor/bootstrap/_button-group.scss +163 -0
  162. data/app/assets/stylesheets/comfy/vendor/bootstrap/_buttons.scss +137 -0
  163. data/app/assets/stylesheets/comfy/vendor/bootstrap/_card.scss +289 -0
  164. data/app/assets/stylesheets/comfy/vendor/bootstrap/_carousel.scss +197 -0
  165. data/app/assets/stylesheets/comfy/vendor/bootstrap/_close.scss +41 -0
  166. data/app/assets/stylesheets/comfy/vendor/bootstrap/_code.scss +48 -0
  167. data/app/assets/stylesheets/comfy/vendor/bootstrap/_custom-forms.scss +507 -0
  168. data/app/assets/stylesheets/comfy/vendor/bootstrap/_dropdown.scss +191 -0
  169. data/app/assets/stylesheets/comfy/vendor/bootstrap/_forms.scss +330 -0
  170. data/app/assets/stylesheets/comfy/vendor/bootstrap/_functions.scss +86 -0
  171. data/app/assets/stylesheets/comfy/vendor/bootstrap/_grid.scss +52 -0
  172. data/app/assets/stylesheets/comfy/vendor/bootstrap/_images.scss +42 -0
  173. data/app/assets/stylesheets/comfy/vendor/bootstrap/_input-group.scss +193 -0
  174. data/app/assets/stylesheets/comfy/vendor/bootstrap/_jumbotron.scss +17 -0
  175. data/app/assets/stylesheets/comfy/vendor/bootstrap/_list-group.scss +149 -0
  176. data/app/assets/stylesheets/comfy/vendor/bootstrap/_media.scss +8 -0
  177. data/app/assets/stylesheets/comfy/vendor/bootstrap/_mixins.scss +47 -0
  178. data/app/assets/stylesheets/comfy/vendor/bootstrap/_modal.scss +229 -0
  179. data/app/assets/stylesheets/comfy/vendor/bootstrap/_nav.scss +120 -0
  180. data/app/assets/stylesheets/comfy/vendor/bootstrap/_navbar.scss +294 -0
  181. data/app/assets/stylesheets/comfy/vendor/bootstrap/_pagination.scss +73 -0
  182. data/app/assets/stylesheets/comfy/vendor/bootstrap/_popover.scss +171 -0
  183. data/app/assets/stylesheets/comfy/vendor/bootstrap/_print.scss +141 -0
  184. data/app/assets/stylesheets/comfy/vendor/bootstrap/_progress.scss +43 -0
  185. data/app/assets/stylesheets/comfy/vendor/bootstrap/_reboot.scss +483 -0
  186. data/app/assets/stylesheets/comfy/vendor/bootstrap/_root.scss +19 -0
  187. data/app/assets/stylesheets/comfy/vendor/bootstrap/_spinners.scss +55 -0
  188. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tables.scss +185 -0
  189. data/app/assets/stylesheets/comfy/vendor/bootstrap/_toasts.scss +44 -0
  190. data/app/assets/stylesheets/comfy/vendor/bootstrap/_tooltip.scss +115 -0
  191. data/app/assets/stylesheets/comfy/vendor/bootstrap/_transitions.scss +20 -0
  192. data/app/assets/stylesheets/comfy/vendor/bootstrap/_type.scss +125 -0
  193. data/app/assets/stylesheets/comfy/vendor/bootstrap/_utilities.scss +17 -0
  194. data/app/assets/stylesheets/comfy/vendor/bootstrap/_variables.scss +1123 -0
  195. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-grid.scss +29 -0
  196. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap-reboot.scss +12 -0
  197. data/app/assets/stylesheets/comfy/vendor/bootstrap/bootstrap.scss +44 -0
  198. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_alert.scss +13 -0
  199. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_background-variant.scss +21 -0
  200. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_badge.scss +17 -0
  201. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_border-radius.scss +63 -0
  202. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_box-shadow.scss +20 -0
  203. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_breakpoints.scss +123 -0
  204. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_buttons.scss +107 -0
  205. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_caret.scss +62 -0
  206. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_clearfix.scss +7 -0
  207. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_deprecate.scss +10 -0
  208. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_float.scss +14 -0
  209. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_forms.scss +192 -0
  210. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_gradients.scss +45 -0
  211. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid-framework.scss +66 -0
  212. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_grid.scss +51 -0
  213. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_hover.scss +37 -0
  214. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_image.scss +36 -0
  215. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_list-group.scss +21 -0
  216. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_lists.scss +7 -0
  217. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_nav-divider.scss +10 -0
  218. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_pagination.scss +22 -0
  219. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_reset-text.scss +17 -0
  220. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_resize.scss +6 -0
  221. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_screen-reader.scss +33 -0
  222. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_size.scss +7 -0
  223. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_table-row.scss +39 -0
  224. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-emphasis.scss +16 -0
  225. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-hide.scss +11 -0
  226. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_text-truncate.scss +8 -0
  227. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_transition.scss +16 -0
  228. data/app/assets/stylesheets/comfy/vendor/bootstrap/mixins/_visibility.scss +8 -0
  229. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_align.scss +8 -0
  230. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_background.scss +19 -0
  231. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_borders.scss +75 -0
  232. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_clearfix.scss +3 -0
  233. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_display.scss +26 -0
  234. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_embed.scss +39 -0
  235. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_flex.scss +51 -0
  236. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_float.scss +11 -0
  237. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_overflow.scss +5 -0
  238. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_position.scss +32 -0
  239. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_screenreaders.scss +11 -0
  240. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_shadows.scss +6 -0
  241. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_sizing.scss +20 -0
  242. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_spacing.scss +73 -0
  243. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_stretched-link.scss +19 -0
  244. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_text.scss +72 -0
  245. data/app/assets/stylesheets/comfy/vendor/bootstrap/utilities/_visibility.scss +13 -0
  246. data/app/assets/stylesheets/comfy/vendor/bootstrap/vendor/_rfs.scss +204 -0
  247. data/app/assets/stylesheets/comfy/vendor/codemirror.css +346 -0
  248. data/app/assets/stylesheets/comfy/vendor/flatpickr.min.css +13 -0
  249. data/app/assets/stylesheets/comfy/vendor/fontawesome.css +343 -0
  250. data/app/assets/stylesheets/comfy/vendor/redactor.css +948 -0
  251. data/app/controllers/application_controller.rb +7 -0
  252. data/app/controllers/comfy/admin/base_controller.rb +22 -0
  253. data/app/controllers/comfy/admin/cms/base_controller.rb +55 -0
  254. data/app/controllers/comfy/admin/cms/categories_controller.rb +40 -0
  255. data/app/controllers/comfy/admin/cms/files_controller.rb +142 -0
  256. data/app/controllers/comfy/admin/cms/layouts_controller.rb +69 -0
  257. data/app/controllers/comfy/admin/cms/pages_controller.rb +156 -0
  258. data/app/controllers/comfy/admin/cms/revisions/base_controller.rb +50 -0
  259. data/app/controllers/comfy/admin/cms/revisions/layout_controller.rb +18 -0
  260. data/app/controllers/comfy/admin/cms/revisions/page_controller.rb +30 -0
  261. data/app/controllers/comfy/admin/cms/revisions/snippet_controller.rb +18 -0
  262. data/app/controllers/comfy/admin/cms/revisions/translation_controller.rb +31 -0
  263. data/app/controllers/comfy/admin/cms/sites_controller.rb +69 -0
  264. data/app/controllers/comfy/admin/cms/snippets_controller.rb +66 -0
  265. data/app/controllers/comfy/admin/cms/translations_controller.rb +107 -0
  266. data/app/controllers/comfy/cms/assets_controller.rb +40 -0
  267. data/app/controllers/comfy/cms/base_controller.rb +33 -0
  268. data/app/controllers/comfy/cms/content_controller.rb +87 -0
  269. data/app/controllers/concerns/comfy/paginate.rb +16 -0
  270. data/app/controllers/concerns/comfy/reorder_action.rb +19 -0
  271. data/app/helpers/comfy/admin/cms_helper.rb +49 -0
  272. data/app/helpers/comfy/cms_helper.rb +73 -0
  273. data/app/models/comfy/cms/categorization.rb +16 -0
  274. data/app/models/comfy/cms/category.rb +24 -0
  275. data/app/models/comfy/cms/file.rb +57 -0
  276. data/app/models/comfy/cms/fragment.rb +59 -0
  277. data/app/models/comfy/cms/layout.rb +113 -0
  278. data/app/models/comfy/cms/page.rb +169 -0
  279. data/app/models/comfy/cms/revision.rb +12 -0
  280. data/app/models/comfy/cms/site.rb +94 -0
  281. data/app/models/comfy/cms/snippet.rb +45 -0
  282. data/app/models/comfy/cms/translation.rb +43 -0
  283. data/app/models/concerns/comfy/cms/with_categories.rb +53 -0
  284. data/app/models/concerns/comfy/cms/with_fragments.rb +125 -0
  285. data/app/views/comfy/admin/cms/categories/_categories.html.haml +4 -0
  286. data/app/views/comfy/admin/cms/categories/_edit.html.haml +7 -0
  287. data/app/views/comfy/admin/cms/categories/_form.html.haml +5 -0
  288. data/app/views/comfy/admin/cms/categories/_index.html.haml +28 -0
  289. data/app/views/comfy/admin/cms/categories/_show.html.haml +15 -0
  290. data/app/views/comfy/admin/cms/categories/create.js.erb +7 -0
  291. data/app/views/comfy/admin/cms/categories/destroy.js.erb +8 -0
  292. data/app/views/comfy/admin/cms/categories/edit.js.erb +3 -0
  293. data/app/views/comfy/admin/cms/categories/update.js.erb +6 -0
  294. data/app/views/comfy/admin/cms/files/_file.html.haml +31 -0
  295. data/app/views/comfy/admin/cms/files/_form.html.haml +26 -0
  296. data/app/views/comfy/admin/cms/files/_modal.html.haml +3 -0
  297. data/app/views/comfy/admin/cms/files/destroy.js.erb +4 -0
  298. data/app/views/comfy/admin/cms/files/edit.html.haml +5 -0
  299. data/app/views/comfy/admin/cms/files/index.html.haml +29 -0
  300. data/app/views/comfy/admin/cms/files/new.html.haml +5 -0
  301. data/app/views/comfy/admin/cms/fragments/_form_fragment_attachments.html.haml +23 -0
  302. data/app/views/comfy/admin/cms/fragments/_form_fragments.html.haml +38 -0
  303. data/app/views/comfy/admin/cms/layouts/_form.html.haml +20 -0
  304. data/app/views/comfy/admin/cms/layouts/_index_branch.html.haml +26 -0
  305. data/app/views/comfy/admin/cms/layouts/edit.html.haml +9 -0
  306. data/app/views/comfy/admin/cms/layouts/index.html.haml +10 -0
  307. data/app/views/comfy/admin/cms/layouts/new.html.haml +5 -0
  308. data/app/views/comfy/admin/cms/pages/_form.html.haml +31 -0
  309. data/app/views/comfy/admin/cms/pages/_index_branch.html.haml +44 -0
  310. data/app/views/comfy/admin/cms/pages/edit.html.haml +10 -0
  311. data/app/views/comfy/admin/cms/pages/index.html.haml +13 -0
  312. data/app/views/comfy/admin/cms/pages/new.html.haml +5 -0
  313. data/app/views/comfy/admin/cms/pages/toggle_branch.js.erb +13 -0
  314. data/app/views/comfy/admin/cms/revisions/_sidebar.html.haml +2 -0
  315. data/app/views/comfy/admin/cms/revisions/show.html.haml +36 -0
  316. data/app/views/comfy/admin/cms/sites/_form.html.haml +15 -0
  317. data/app/views/comfy/admin/cms/sites/edit.html.haml +5 -0
  318. data/app/views/comfy/admin/cms/sites/index.html.haml +28 -0
  319. data/app/views/comfy/admin/cms/sites/new.html.haml +5 -0
  320. data/app/views/comfy/admin/cms/snippets/_form.html.haml +13 -0
  321. data/app/views/comfy/admin/cms/snippets/edit.html.haml +9 -0
  322. data/app/views/comfy/admin/cms/snippets/index.html.haml +33 -0
  323. data/app/views/comfy/admin/cms/snippets/new.html.haml +5 -0
  324. data/app/views/comfy/admin/cms/translations/_form.html.haml +20 -0
  325. data/app/views/comfy/admin/cms/translations/_sidebar.html.haml +19 -0
  326. data/app/views/comfy/admin/cms/translations/edit.html.haml +10 -0
  327. data/app/views/comfy/admin/cms/translations/new.html.haml +5 -0
  328. data/app/views/kaminari/comfy/_first_page.html.haml +3 -0
  329. data/app/views/kaminari/comfy/_gap.html.haml +2 -0
  330. data/app/views/kaminari/comfy/_last_page.html.haml +3 -0
  331. data/app/views/kaminari/comfy/_next_page.html.haml +2 -0
  332. data/app/views/kaminari/comfy/_page.html.haml +2 -0
  333. data/app/views/kaminari/comfy/_paginator.html.haml +11 -0
  334. data/app/views/kaminari/comfy/_prev_page.html.haml +2 -0
  335. data/app/views/layouts/comfy/admin/cms.html.haml +4 -0
  336. data/app/views/layouts/comfy/admin/cms/_body.html.haml +14 -0
  337. data/app/views/layouts/comfy/admin/cms/_flash.html.haml +4 -0
  338. data/app/views/layouts/comfy/admin/cms/_footer_js.html.haml +6 -0
  339. data/app/views/layouts/comfy/admin/cms/_head.html.haml +19 -0
  340. data/app/views/layouts/comfy/admin/cms/_left.html.haml +39 -0
  341. data/app/views/layouts/comfy/admin/cms/_right.html.haml +5 -0
  342. data/bin/bundle +3 -0
  343. data/bin/rails +4 -0
  344. data/bin/rake +4 -0
  345. data/bin/setup +36 -0
  346. data/bin/update +31 -0
  347. data/bin/yarn +11 -0
  348. data/comfortable_mexican_sofa.gemspec +34 -0
  349. data/config.ru +6 -0
  350. data/config/application.rb +39 -0
  351. data/config/boot.rb +7 -0
  352. data/config/cms_routes.rb +8 -0
  353. data/config/database.yml +11 -0
  354. data/config/environment.rb +7 -0
  355. data/config/environments/development.rb +64 -0
  356. data/config/environments/test.rb +55 -0
  357. data/config/initializers/comfortable_mexican_sofa.rb +122 -0
  358. data/config/locales/ca.yml +263 -0
  359. data/config/locales/cs.yml +263 -0
  360. data/config/locales/da.yml +263 -0
  361. data/config/locales/de.yml +263 -0
  362. data/config/locales/en.yml +263 -0
  363. data/config/locales/es.yml +263 -0
  364. data/config/locales/fi.yml +263 -0
  365. data/config/locales/fr.yml +263 -0
  366. data/config/locales/gr.yml +263 -0
  367. data/config/locales/hr.yml +263 -0
  368. data/config/locales/it.yml +263 -0
  369. data/config/locales/ja.yml +263 -0
  370. data/config/locales/nb.yml +263 -0
  371. data/config/locales/nl.yml +263 -0
  372. data/config/locales/pl.yml +263 -0
  373. data/config/locales/pt-BR.yml +263 -0
  374. data/config/locales/ru.yml +263 -0
  375. data/config/locales/sk.yml +263 -0
  376. data/config/locales/sv.yml +263 -0
  377. data/config/locales/tr.yml +263 -0
  378. data/config/locales/uk.yml +263 -0
  379. data/config/locales/zh-CN.yml +263 -0
  380. data/config/locales/zh-TW.yml +263 -0
  381. data/config/storage.yml +35 -0
  382. data/db/cms_seeds/sample-site/files/_default.jpg.yml +5 -0
  383. data/db/cms_seeds/sample-site/files/default.jpg +0 -0
  384. data/db/cms_seeds/sample-site/layouts/default/content.html +16 -0
  385. data/db/cms_seeds/sample-site/layouts/default/nested/content.html +14 -0
  386. data/db/cms_seeds/sample-site/pages/index/child_a/content.html +7 -0
  387. data/db/cms_seeds/sample-site/pages/index/child_b/content.html +6 -0
  388. data/db/cms_seeds/sample-site/pages/index/cms logo.png +0 -0
  389. data/db/cms_seeds/sample-site/pages/index/content.es.html +6 -0
  390. data/db/cms_seeds/sample-site/pages/index/content.fr.html +6 -0
  391. data/db/cms_seeds/sample-site/pages/index/content.html +25 -0
  392. data/db/cms_seeds/sample-site/pages/index/footer.png +0 -0
  393. data/db/cms_seeds/sample-site/pages/index/header.png +0 -0
  394. data/db/cms_seeds/sample-site/snippets/default.html +8 -0
  395. data/db/migrate/00_create_active_storage_tables.active_storage.rb +26 -0
  396. data/db/migrate/01_create_cms.rb +142 -0
  397. data/lib/comfortable_mexican_sofa.rb +60 -0
  398. data/lib/comfortable_mexican_sofa/access_control/admin_authentication.rb +24 -0
  399. data/lib/comfortable_mexican_sofa/access_control/admin_authorization.rb +12 -0
  400. data/lib/comfortable_mexican_sofa/access_control/public_authentication.rb +12 -0
  401. data/lib/comfortable_mexican_sofa/access_control/public_authorization.rb +12 -0
  402. data/lib/comfortable_mexican_sofa/configuration.rb +139 -0
  403. data/lib/comfortable_mexican_sofa/content.rb +31 -0
  404. data/lib/comfortable_mexican_sofa/content/block.rb +14 -0
  405. data/lib/comfortable_mexican_sofa/content/params_parser.rb +149 -0
  406. data/lib/comfortable_mexican_sofa/content/renderer.rb +149 -0
  407. data/lib/comfortable_mexican_sofa/content/tag.rb +50 -0
  408. data/lib/comfortable_mexican_sofa/content/tags/asset.rb +59 -0
  409. data/lib/comfortable_mexican_sofa/content/tags/checkbox.rb +29 -0
  410. data/lib/comfortable_mexican_sofa/content/tags/date.rb +21 -0
  411. data/lib/comfortable_mexican_sofa/content/tags/datetime.rb +44 -0
  412. data/lib/comfortable_mexican_sofa/content/tags/file.rb +72 -0
  413. data/lib/comfortable_mexican_sofa/content/tags/file_link.rb +63 -0
  414. data/lib/comfortable_mexican_sofa/content/tags/files.rb +43 -0
  415. data/lib/comfortable_mexican_sofa/content/tags/fragment.rb +66 -0
  416. data/lib/comfortable_mexican_sofa/content/tags/helper.rb +55 -0
  417. data/lib/comfortable_mexican_sofa/content/tags/markdown.rb +24 -0
  418. data/lib/comfortable_mexican_sofa/content/tags/mixins/file_content.rb +43 -0
  419. data/lib/comfortable_mexican_sofa/content/tags/number.rb +20 -0
  420. data/lib/comfortable_mexican_sofa/content/tags/page_file_link.rb +84 -0
  421. data/lib/comfortable_mexican_sofa/content/tags/partial.rb +49 -0
  422. data/lib/comfortable_mexican_sofa/content/tags/snippet.rb +34 -0
  423. data/lib/comfortable_mexican_sofa/content/tags/template.rb +44 -0
  424. data/lib/comfortable_mexican_sofa/content/tags/text.rb +20 -0
  425. data/lib/comfortable_mexican_sofa/content/tags/textarea.rb +20 -0
  426. data/lib/comfortable_mexican_sofa/content/tags/wysiwyg.rb +21 -0
  427. data/lib/comfortable_mexican_sofa/engine.rb +31 -0
  428. data/lib/comfortable_mexican_sofa/error.rb +32 -0
  429. data/lib/comfortable_mexican_sofa/extensions/acts_as_tree.rb +120 -0
  430. data/lib/comfortable_mexican_sofa/extensions/has_revisions.rb +72 -0
  431. data/lib/comfortable_mexican_sofa/form_builder.rb +47 -0
  432. data/lib/comfortable_mexican_sofa/render_methods.rb +112 -0
  433. data/lib/comfortable_mexican_sofa/routes/cms.rb +18 -0
  434. data/lib/comfortable_mexican_sofa/routes/cms_admin.rb +58 -0
  435. data/lib/comfortable_mexican_sofa/routing.rb +12 -0
  436. data/lib/comfortable_mexican_sofa/seeds.rb +114 -0
  437. data/lib/comfortable_mexican_sofa/seeds/file/exporter.rb +43 -0
  438. data/lib/comfortable_mexican_sofa/seeds/file/importer.rb +63 -0
  439. data/lib/comfortable_mexican_sofa/seeds/layout/exporter.rb +40 -0
  440. data/lib/comfortable_mexican_sofa/seeds/layout/importer.rb +62 -0
  441. data/lib/comfortable_mexican_sofa/seeds/page/exporter.rb +92 -0
  442. data/lib/comfortable_mexican_sofa/seeds/page/importer.rb +219 -0
  443. data/lib/comfortable_mexican_sofa/seeds/snippet/exporter.rb +34 -0
  444. data/lib/comfortable_mexican_sofa/seeds/snippet/importer.rb +51 -0
  445. data/lib/comfortable_mexican_sofa/version.rb +9 -0
  446. data/lib/comfortable_mexican_sofa/view_hooks.rb +35 -0
  447. data/lib/generators/comfy/cms/README +17 -0
  448. data/lib/generators/comfy/cms/assets_generator.rb +18 -0
  449. data/lib/generators/comfy/cms/cms_generator.rb +69 -0
  450. data/lib/generators/comfy/cms/controllers_generator.rb +17 -0
  451. data/lib/generators/comfy/cms/models_generator.rb +17 -0
  452. data/lib/generators/comfy/cms/views_generator.rb +17 -0
  453. data/lib/generators/comfy/scaffold/scaffold_generator.rb +80 -0
  454. data/lib/generators/comfy/scaffold/templates/controller.rb.tt +62 -0
  455. data/lib/generators/comfy/scaffold/templates/migration.rb.tt +11 -0
  456. data/lib/generators/comfy/scaffold/templates/model.rb.tt +21 -0
  457. data/lib/generators/comfy/scaffold/templates/tests/controller.rb.tt +115 -0
  458. data/lib/generators/comfy/scaffold/templates/tests/fixture.yml.tt +4 -0
  459. data/lib/generators/comfy/scaffold/templates/tests/model.rb.tt +26 -0
  460. data/lib/generators/comfy/scaffold/templates/views/_form.haml.tt +7 -0
  461. data/lib/generators/comfy/scaffold/templates/views/edit.haml.tt +5 -0
  462. data/lib/generators/comfy/scaffold/templates/views/index.haml.tt +21 -0
  463. data/lib/generators/comfy/scaffold/templates/views/new.haml.tt +5 -0
  464. data/lib/generators/comfy/scaffold/templates/views/show.haml.tt +4 -0
  465. data/lib/tasks/cms_seeds.rake +40 -0
  466. data/public/favicon.ico +0 -0
  467. metadata +648 -0
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Page < ActiveRecord::Base
4
+
5
+ self.table_name = "comfy_cms_pages"
6
+
7
+ include Comfy::Cms::WithFragments
8
+ include Comfy::Cms::WithCategories
9
+
10
+ cms_acts_as_tree counter_cache: :children_count, order: :position
11
+ cms_has_revisions_for :fragments_attributes
12
+
13
+ attr_accessor :content
14
+
15
+ # -- Relationships -----------------------------------------------------------
16
+ belongs_to :site
17
+ belongs_to :target_page,
18
+ class_name: "Comfy::Cms::Page",
19
+ optional: true
20
+
21
+ has_many :translations,
22
+ dependent: :destroy
23
+
24
+ # -- Callbacks ---------------------------------------------------------------
25
+ before_validation :assigns_label,
26
+ :assign_parent,
27
+ :escape_slug,
28
+ :assign_full_path
29
+ before_create :assign_position
30
+ after_save :sync_child_full_paths!
31
+ after_find :unescape_slug_and_path
32
+
33
+ # -- Validations -------------------------------------------------------------
34
+ validates :label,
35
+ presence: true
36
+ validates :slug,
37
+ presence: true,
38
+ uniqueness: { scope: :parent_id },
39
+ unless: ->(p) {
40
+ p.site && (p.site.pages.count.zero? || p.site.pages.root == self)
41
+ }
42
+ validate :validate_target_page
43
+ validate :validate_format_of_unescaped_slug
44
+
45
+ # -- Scopes ------------------------------------------------------------------
46
+ scope :published, -> { where(is_published: true) }
47
+
48
+ # -- Class Methods -----------------------------------------------------------
49
+ # Tree-like structure for pages
50
+ def self.options_for_select(site:, current_page: nil, exclude_self: false)
51
+ options = []
52
+
53
+ options_for_page = ->(page, depth = 0) do
54
+ return if exclude_self && page == current_page
55
+
56
+ options << ["#{'. . ' * depth}#{page.label}", page.id]
57
+
58
+ page.children.order(:position).each do |child_page|
59
+ options_for_page.call(child_page, depth + 1)
60
+ end
61
+ end
62
+
63
+ options_for_page.call(site.pages.root)
64
+
65
+ options
66
+ end
67
+
68
+ # -- Instance Methods --------------------------------------------------------
69
+ # For previewing purposes sometimes we need to have full_path set. This
70
+ # full path take care of the pages and its childs but not of the site path
71
+ def full_path
72
+ read_attribute(:full_path) || assign_full_path
73
+ end
74
+
75
+ # Somewhat unique method of identifying a page that is not a full_path
76
+ def identifier
77
+ parent_id.blank? ? "index" : full_path[1..-1].parameterize
78
+ end
79
+
80
+ # Full url for a page
81
+ def url(relative: false)
82
+ [site.url(relative: relative), full_path].compact.join
83
+ end
84
+
85
+ # This method will mutate page object by transfering attributes from translation
86
+ # for a given locale.
87
+ def translate!
88
+ # If site locale same as page's or there's no translastions, we do nothing
89
+ if site.locale == I18n.locale.to_s || translations.blank?
90
+ return
91
+ end
92
+
93
+ translation = translations.published.find_by!(locale: I18n.locale)
94
+ self.layout = translation.layout
95
+ self.label = translation.label
96
+ self.content_cache = translation.content_cache
97
+
98
+ # We can't just assign fragments as it's a relation and will write to DB
99
+ # This has odd side-effect of preserving page's fragments and just replacing
100
+ # them from the translation. Not an issue if all fragments match.
101
+ self.fragments_attributes = translation.fragments_attributes
102
+ readonly!
103
+
104
+ self
105
+ end
106
+
107
+ protected
108
+
109
+ def assigns_label
110
+ self.label = label.blank? ? slug.try(:titleize) : label
111
+ end
112
+
113
+ def assign_parent
114
+ return unless site
115
+ self.parent ||= site.pages.root unless self == site.pages.root || site.pages.count.zero?
116
+ end
117
+
118
+ def assign_full_path
119
+ self.full_path =
120
+ if self.parent
121
+ [CGI.escape(self.parent.full_path).gsub("%2F", "/"), slug].join("/").squeeze("/")
122
+ else
123
+ "/"
124
+ end
125
+ end
126
+
127
+ def assign_position
128
+ return unless self.parent
129
+ return if position.to_i.positive?
130
+ max = self.parent.children.maximum(:position)
131
+ self.position = max ? max + 1 : 0
132
+ end
133
+
134
+ def validate_target_page
135
+ return unless target_page
136
+ p = self
137
+ while p.target_page
138
+ if (p = p.target_page) == self
139
+ return errors.add(:target_page_id, "Invalid Redirect")
140
+ end
141
+ end
142
+ end
143
+
144
+ def validate_format_of_unescaped_slug
145
+ return unless slug.present?
146
+ unescaped_slug = CGI.unescape(slug)
147
+ errors.add(:slug, :invalid) unless unescaped_slug =~ %r{^\p{Alnum}[\.\p{Alnum}\p{Mark}_-]*$}i
148
+ end
149
+
150
+ # Forcing re-saves for child pages so they can update full_paths
151
+ def sync_child_full_paths!
152
+ return unless full_path_previously_changed?
153
+ children.each do |p|
154
+ p.update_attribute(:full_path, p.send(:assign_full_path))
155
+ end
156
+ end
157
+
158
+ # Escape slug unless it's nonexistent (root)
159
+ def escape_slug
160
+ self.slug = CGI.escape(slug) unless slug.nil?
161
+ end
162
+
163
+ # Unescape the slug and full path back into their original forms unless they're nonexistent
164
+ def unescape_slug_and_path
165
+ self.slug = CGI.unescape(slug) unless slug.nil?
166
+ self.full_path = CGI.unescape(full_path) unless full_path.nil?
167
+ end
168
+
169
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Revision < ActiveRecord::Base
4
+
5
+ self.table_name = "comfy_cms_revisions"
6
+
7
+ serialize :data
8
+
9
+ # -- Relationships --------------------------------------------------------
10
+ belongs_to :record, polymorphic: true
11
+
12
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Site < ActiveRecord::Base
4
+
5
+ self.table_name = "comfy_cms_sites"
6
+
7
+ # -- Relationships -----------------------------------------------------------
8
+ with_options dependent: :destroy do |site|
9
+ site.has_many :layouts
10
+ site.has_many :pages
11
+ site.has_many :snippets
12
+ site.has_many :files
13
+ site.has_many :categories
14
+ end
15
+
16
+ # -- Callbacks ---------------------------------------------------------------
17
+ before_validation :assign_identifier,
18
+ :assign_hostname,
19
+ :assign_label,
20
+ :clean_path
21
+
22
+ # -- Validations -------------------------------------------------------------
23
+ validates :identifier,
24
+ presence: true,
25
+ uniqueness: true,
26
+ format: { with: %r{\A\w[a-z0-9_-]*\z}i }
27
+ validates :label,
28
+ presence: true
29
+ validates :hostname,
30
+ presence: true,
31
+ uniqueness: { scope: :path },
32
+ format: { with: %r{\A[\w.-]+(?:\:\d+)?\z} }
33
+
34
+ # -- Class Methods -----------------------------------------------------------
35
+ # returning the Comfy::Cms::Site instance based on host and path
36
+ def self.find_site(host, path = nil)
37
+ return Comfy::Cms::Site.first if Comfy::Cms::Site.count == 1
38
+ cms_site = nil
39
+
40
+ public_cms_path = ComfortableMexicanSofa.configuration.public_cms_path
41
+ if path && public_cms_path != "/"
42
+ path = path.sub(%r{\A#{public_cms_path}}, "")
43
+ end
44
+
45
+ Comfy::Cms::Site.where(hostname: real_host_from_aliases(host)).each do |site|
46
+ if site.path.blank?
47
+ cms_site = site
48
+ elsif "#{path.to_s.split('?')[0]}/" =~ %r{^/#{Regexp.escape(site.path.to_s)}/}
49
+ cms_site = site
50
+ break
51
+ end
52
+ end
53
+ cms_site
54
+ end
55
+
56
+ def self.real_host_from_aliases(host)
57
+ if (aliases = ComfortableMexicanSofa.config.hostname_aliases)
58
+ aliases.each do |alias_host, hosts|
59
+ return alias_host if hosts.include?(host)
60
+ end
61
+ end
62
+ host
63
+ end
64
+
65
+ # -- Instance Methods --------------------------------------------------------
66
+ def url(relative: false)
67
+ public_cms_path = ComfortableMexicanSofa.config.public_cms_path || "/"
68
+ host = "//#{hostname}"
69
+ path = ["/", public_cms_path, self.path].compact.join("/").squeeze("/").chomp("/")
70
+ relative ? path.presence : [host, path].join
71
+ end
72
+
73
+ protected
74
+
75
+ def assign_identifier
76
+ self.identifier = identifier.blank? ? hostname.try(:parameterize) : identifier
77
+ end
78
+
79
+ def assign_hostname
80
+ self.hostname ||= identifier
81
+ end
82
+
83
+ def assign_label
84
+ self.label = label.blank? ? identifier.try(:titleize) : label
85
+ end
86
+
87
+ def clean_path
88
+ self.path ||= ""
89
+ self.path.squeeze!("/")
90
+ self.path.gsub!(%r{/$}, "")
91
+ self.path = nil if self.path.blank?
92
+ end
93
+
94
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Snippet < ActiveRecord::Base
4
+
5
+ self.table_name = "comfy_cms_snippets"
6
+
7
+ include Comfy::Cms::WithCategories
8
+
9
+ cms_has_revisions_for :content
10
+
11
+ # -- Relationships -----------------------------------------------------------
12
+ belongs_to :site
13
+
14
+ # -- Callbacks ---------------------------------------------------------------
15
+ before_validation :assign_label
16
+ before_create :assign_position
17
+ after_save :clear_page_content_cache
18
+ after_destroy :clear_page_content_cache
19
+
20
+ # -- Validations -------------------------------------------------------------
21
+ validates :label,
22
+ presence: true
23
+ validates :identifier,
24
+ presence: true,
25
+ uniqueness: { scope: :site_id },
26
+ format: { with: %r{\A\w[a-z0-9_-]*\z}i }
27
+
28
+ protected
29
+
30
+ def assign_label
31
+ self.label = label.blank? ? identifier.try(:titleize) : label
32
+ end
33
+
34
+ # When snippet is changed or removed we need to blow away all page caches as
35
+ # we don't know where it was used.
36
+ def clear_page_content_cache
37
+ Comfy::Cms::Page.where(id: site.pages.pluck(:id)).update_all(content_cache: nil)
38
+ end
39
+
40
+ def assign_position
41
+ max = site.snippets.maximum(:position)
42
+ self.position = max ? max + 1 : 0
43
+ end
44
+
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Comfy::Cms::Translation < ActiveRecord::Base
4
+
5
+ self.table_name = "comfy_cms_translations"
6
+
7
+ include Comfy::Cms::WithFragments
8
+
9
+ cms_has_revisions_for :fragments_attributes
10
+
11
+ delegate :site, to: :page
12
+
13
+ # -- Relationships -----------------------------------------------------------
14
+ belongs_to :page
15
+
16
+ # -- Callbacks ---------------------------------------------------------------
17
+ before_validation :assign_layout
18
+
19
+ # -- Scopes ------------------------------------------------------------------
20
+ scope :published, -> { where(is_published: true) }
21
+
22
+ # -- Validations -------------------------------------------------------------
23
+ validates :label,
24
+ presence: true
25
+
26
+ validates :locale,
27
+ presence: true,
28
+ uniqueness: { scope: :page_id }
29
+
30
+ validate :validate_locale
31
+
32
+ private
33
+
34
+ def validate_locale
35
+ return unless page
36
+ errors.add(:locale) if locale == page.site.locale
37
+ end
38
+
39
+ def assign_layout
40
+ self.layout ||= page.layout if page.present?
41
+ end
42
+
43
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy::Cms::WithCategories
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_many :categorizations,
9
+ as: :categorized,
10
+ class_name: "Comfy::Cms::Categorization",
11
+ dependent: :destroy
12
+ has_many :categories,
13
+ through: :categorizations,
14
+ class_name: "Comfy::Cms::Category"
15
+
16
+ attr_writer :category_ids
17
+
18
+ after_save :sync_categories
19
+
20
+ scope :for_category, ->(*categories) {
21
+ if (categories = [categories].flatten.compact).present?
22
+ distinct
23
+ .joins(categorizations: :category)
24
+ .where("comfy_cms_categories.label" => categories)
25
+ end
26
+ }
27
+ end
28
+
29
+ def category_ids
30
+ @category_ids ||= categories.pluck(:id)
31
+ end
32
+
33
+ def sync_categories
34
+ return unless category_ids.is_a?(Array)
35
+
36
+ scope = Comfy::Cms::Category.of_type(self.class.to_s)
37
+ existing_ids = scope.pluck(:id)
38
+
39
+ ids_to_add = category_ids.map(&:to_i)
40
+
41
+ # adding categorizations
42
+ ids_to_add.each do |id|
43
+ if (category = scope.find_by_id(id))
44
+ category.categorizations.create(categorized: self)
45
+ end
46
+ end
47
+
48
+ # removing categorizations
49
+ ids_to_remove = existing_ids - ids_to_add
50
+ categorizations.where(category_id: ids_to_remove).destroy_all
51
+ end
52
+
53
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Comfy::Cms::WithFragments
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_accessor :fragments_attributes_changed
9
+
10
+ belongs_to :layout,
11
+ class_name: "Comfy::Cms::Layout"
12
+
13
+ has_many :fragments,
14
+ class_name: "Comfy::Cms::Fragment",
15
+ as: :record,
16
+ autosave: true,
17
+ dependent: :destroy
18
+
19
+ before_save :clear_content_cache
20
+
21
+ validates :layout,
22
+ presence: true
23
+ end
24
+
25
+ # Array of fragment hashes in the following format:
26
+ # [
27
+ # {identifier: "frag_a", format: "text", content: "fragment a content"},
28
+ # {identifier: "frag_b", format: "file", files: [{file_a}, {file_b}]}
29
+ # ]
30
+ # It also handles when frag hashes come in as a hash:
31
+ # {
32
+ # "0" => {identifer: "foo", content: "bar"},
33
+ # "1" => {identifier: "bar", content: "foo"}
34
+ # }
35
+ def fragments_attributes=(frag_hashes = [])
36
+ frag_hashes = frag_hashes.values if frag_hashes.is_a?(Hash)
37
+
38
+ frag_hashes.each do |frag_attrs|
39
+ unless frag_attrs.is_a?(HashWithIndifferentAccess)
40
+ frag_attrs.symbolize_keys!
41
+ end
42
+
43
+ identifier = frag_attrs.delete(:identifier)
44
+
45
+ fragment =
46
+ fragments.detect { |f| f.identifier == identifier } ||
47
+ fragments.build(identifier: identifier)
48
+
49
+ fragment.attributes = frag_attrs
50
+
51
+ # tracking dirty
52
+ self.fragments_attributes_changed ||= fragment.changed?
53
+ end
54
+ end
55
+
56
+ # Snapshop of page fragments data used primarily for saving revisions
57
+ def fragments_attributes(was = false)
58
+ fragments.collect do |frag|
59
+ attrs = {}
60
+ %i[identifier tag content datetime boolean].each do |column|
61
+ attrs[column] = frag.send(was ? "#{column}_was" : column)
62
+ end
63
+ # TODO: save files against revision (not on db though)
64
+ # attrs[:files] = frag.attachments.collect do |a|
65
+ # {io: a.download, filename: a.filename.to_s, content_type: a.content_type}
66
+ # end
67
+ attrs
68
+ end
69
+ end
70
+
71
+ # Method to collect prevous state of blocks for revisions
72
+ def fragments_attributes_was
73
+ fragments_attributes(:previous_values)
74
+ end
75
+
76
+ # Grabbing nodes that we need to render form elements in the admin area
77
+ # Rejecting duplicates as we'd need to render only one form field. Don't declare
78
+ # duplicate tags on the layout. That's wierd (but still works).
79
+ def fragment_nodes
80
+ nodes
81
+ .select { |n| n.is_a?(ComfortableMexicanSofa::Content::Tag::Fragment) }
82
+ .uniq(&:identifier)
83
+ end
84
+
85
+ # Rendered content of the page. We grab whatever layout is associated with the
86
+ # page and feed its content tokens to the renderer while passing this page as
87
+ # context.
88
+ def render(n = nodes)
89
+ renderer.render(n)
90
+ end
91
+
92
+ # If content_cache column is populated we don't need to call render for this
93
+ # page.
94
+ def content_cache
95
+ if (cache = read_attribute(:content_cache)).nil?
96
+ cache = render
97
+ update_column(:content_cache, cache) unless new_record?
98
+ end
99
+ cache
100
+ end
101
+
102
+ # Nuking content cache so it can be regenerated.
103
+ def clear_content_cache!
104
+ update_column(:content_cache, nil)
105
+ end
106
+
107
+ # Blanking cache on page saves so it can be regenerated on access
108
+ def clear_content_cache
109
+ write_attribute(:content_cache, nil)
110
+ end
111
+
112
+ protected
113
+
114
+ def renderer
115
+ ComfortableMexicanSofa::Content::Renderer.new(self)
116
+ end
117
+
118
+ def nodes
119
+ return [] unless layout.present?
120
+
121
+ tokens = layout.content_tokens
122
+ renderer.nodes(tokens)
123
+ end
124
+
125
+ end