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