macron-cms 2.0.0 → 3.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +376 -14
  3. data/app/assets/javascripts/macron/cms/codemirror.js +3 -0
  4. data/app/assets/javascripts/macron/cms/editor.js +34 -6
  5. data/app/assets/javascripts/macron/cms/katex.min.js +1 -0
  6. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  7. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  8. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  9. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  10. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  11. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Bold.woff2 +0 -0
  12. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  13. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Italic.woff2 +0 -0
  14. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Regular.woff2 +0 -0
  15. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  16. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-Italic.woff2 +0 -0
  17. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  18. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  19. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  20. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Script-Regular.woff2 +0 -0
  21. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  22. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  23. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  24. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  25. data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  26. data/app/assets/stylesheets/macron/cms/katex.min.css +1 -0
  27. data/app/controllers/macron/cms/api/v1/articles_controller.rb +186 -0
  28. data/app/controllers/macron/cms/api/v1/base_controller.rb +35 -0
  29. data/app/controllers/macron/cms/api/v1/content_types_controller.rb +21 -0
  30. data/app/controllers/macron/cms/article_contents_controller.rb +14 -3
  31. data/app/helpers/macron/cms/fields_helper.rb +1 -1
  32. data/app/services/macron/cms/remote_asset_attacher.rb +86 -0
  33. data/app/views/layouts/macron/cms/application.html.erb +2 -0
  34. data/app/views/macron/cms/articles/_content_blocks_section.html.erb +5 -1
  35. data/app/views/macron/cms/fields/_file.html.erb +4 -0
  36. data/app/views/macron/cms/fields/_latex.html.erb +17 -0
  37. data/config/routes.rb +7 -0
  38. data/lib/macron/cms/configuration.rb +2 -1
  39. data/lib/macron/cms/version.rb +1 -1
  40. metadata +28 -1
@@ -0,0 +1 @@
1
+ @font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.16.21"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}
@@ -0,0 +1,186 @@
1
+ module Macron
2
+ module Cms
3
+ module Api
4
+ module V1
5
+ class ArticlesController < BaseController
6
+ before_action :set_article, only: %i[show update]
7
+
8
+ def show
9
+ render json: serialize_article_with_blocks(@article)
10
+ end
11
+
12
+ def create
13
+ upsert_mode = ActiveModel::Type::Boolean.new.cast(params[:upsert])
14
+ article_attributes = article_params
15
+
16
+ @article = if upsert_mode
17
+ find_existing_article(article_attributes) || Article.new
18
+ else
19
+ Article.new
20
+ end
21
+
22
+ @article.assign_attributes(article_attributes)
23
+
24
+ ActiveRecord::Base.transaction do
25
+ unless @article.save
26
+ render json: { errors: @article.errors.full_messages }, status: :unprocessable_entity
27
+ return
28
+ end
29
+
30
+ if params.key?(:content_blocks)
31
+ sync_content_blocks!(@article, params[:content_blocks])
32
+ end
33
+ end
34
+
35
+ status = @article.previously_new_record? ? :created : :ok
36
+ render json: serialize_article_with_blocks(@article), status: status
37
+ rescue ArgumentError => e
38
+ render json: { errors: [ e.message ] }, status: :unprocessable_entity
39
+ rescue ActiveRecord::RecordInvalid => e
40
+ render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
41
+ end
42
+
43
+ def update
44
+ ActiveRecord::Base.transaction do
45
+ unless @article.update(article_params)
46
+ render json: { errors: @article.errors.full_messages }, status: :unprocessable_entity
47
+ return
48
+ end
49
+
50
+ if params.key?(:content_blocks)
51
+ sync_content_blocks!(@article, params[:content_blocks])
52
+ end
53
+ end
54
+
55
+ render json: serialize_article_with_blocks(@article), status: :ok
56
+ rescue ArgumentError => e
57
+ render json: { errors: [ e.message ] }, status: :unprocessable_entity
58
+ rescue ActiveRecord::RecordInvalid => e
59
+ render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
60
+ end
61
+
62
+ private
63
+
64
+ def set_article
65
+ @article = Article.friendly.find(params[:id])
66
+ rescue ActiveRecord::RecordNotFound
67
+ render json: { error: "Article not found" }, status: :not_found
68
+ end
69
+
70
+ def find_existing_article(attrs)
71
+ if attrs[:name].present?
72
+ existing = Article.find_by(name: attrs[:name])
73
+ return existing if existing
74
+ end
75
+
76
+ if attrs[:slug].present?
77
+ existing = Article.find_by(slug: attrs[:slug])
78
+ return existing if existing
79
+ end
80
+
81
+ if attrs[:title].present?
82
+ existing = Article.find_by(title: attrs[:title])
83
+ return existing if existing
84
+ end
85
+
86
+ nil
87
+ end
88
+
89
+ def article_params
90
+ raw = params[:article].presence || params
91
+ raw.permit(
92
+ :title,
93
+ :name,
94
+ :slug,
95
+ :meta_title,
96
+ :meta_description,
97
+ :background_color,
98
+ :text_color,
99
+ background_options: %i[repeat size position attachment]
100
+ )
101
+ end
102
+
103
+ def sync_content_blocks!(article, blocks_params)
104
+ return unless blocks_params.is_a?(Array)
105
+
106
+ # Cleanly replace existing content blocks
107
+ article.article_contents.destroy_all
108
+
109
+ blocks_params.each_with_index do |block_data, index|
110
+ raw_block = block_data.respond_to?(:to_unsafe_h) ? block_data.to_unsafe_h : block_data.to_h
111
+ content_type_name = raw_block["content_type"] || raw_block[:content_type]
112
+ content_type = Content.find_by(name: content_type_name.to_s.strip)
113
+
114
+ if content_type.nil?
115
+ raise ArgumentError, "Unknown content type: '#{content_type_name}'. Available types: #{Content.pluck(:name).join(', ')}"
116
+ end
117
+
118
+ raw_payload = raw_block["payload"] || raw_block[:payload] || {}
119
+ remote_url = raw_block["image_url"] || raw_block[:image_url] ||
120
+ raw_block["file_url"] || raw_block[:file_url] ||
121
+ raw_block["upload_url"] || raw_block[:upload_url] ||
122
+ raw_payload["upload"]
123
+
124
+ order = (raw_block["sort_order"] || raw_block[:sort_order]).presence || (index + 1)
125
+
126
+ article_content = article.article_contents.build(
127
+ content: content_type,
128
+ sort_order: order.to_i
129
+ )
130
+ article_content.payload = article_content.filter_payload(raw_payload)
131
+
132
+ if remote_url.present?
133
+ article_content.payload["upload"] = remote_url
134
+ end
135
+
136
+ article_content.save!
137
+
138
+ if remote_url.present? && (remote_url.start_with?("http://") || remote_url.start_with?("https://"))
139
+ RemoteAssetAttacher.attach(article_content: article_content, url: remote_url)
140
+ end
141
+ end
142
+ end
143
+
144
+ def serialize_article_with_blocks(article)
145
+ article.reload
146
+ {
147
+ article: {
148
+ id: article.id,
149
+ title: article.title,
150
+ name: article.name,
151
+ slug: article.slug,
152
+ meta_title: article.meta_title,
153
+ meta_description: article.meta_description,
154
+ background_color: article.background_color,
155
+ text_color: article.text_color,
156
+ background_options: article.background_options,
157
+ created_at: article.created_at&.iso8601,
158
+ updated_at: article.updated_at&.iso8601
159
+ },
160
+ content_blocks: article.article_contents.includes(:content).order(:sort_order, :id).map do |ac|
161
+ block_data = {
162
+ id: ac.id,
163
+ content_type: ac.content.name,
164
+ sort_order: ac.sort_order,
165
+ payload: ac.payload
166
+ }
167
+
168
+ if ac.files.attached?
169
+ file = ac.files.first
170
+ block_data[:attached_file] = {
171
+ filename: file.filename.to_s,
172
+ content_type: file.content_type,
173
+ byte_size: file.byte_size,
174
+ url: Rails.application.routes.url_helpers.rails_blob_path(file, only_path: true)
175
+ }
176
+ end
177
+
178
+ block_data
179
+ end
180
+ }
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,35 @@
1
+ module Macron
2
+ module Cms
3
+ module Api
4
+ module V1
5
+ class BaseController < ActionController::API
6
+ before_action :authenticate_api_key!
7
+
8
+ private
9
+
10
+ def authenticate_api_key!
11
+ expected_key = Macron::Cms.configuration.api_key.presence
12
+
13
+ # In development and test, if no API key is configured, allow requests to facilitate local setup.
14
+ return if expected_key.blank? && (Rails.env.development? || Rails.env.test?)
15
+
16
+ token = extracted_token
17
+
18
+ if expected_key.blank? || token.blank? || !ActiveSupport::SecurityUtils.secure_compare(token, expected_key)
19
+ render json: { error: "Unauthorized. Missing or invalid API key." }, status: :unauthorized
20
+ end
21
+ end
22
+
23
+ def extracted_token
24
+ auth_header = request.headers["Authorization"].to_s
25
+ if auth_header.downcase.start_with?("bearer ")
26
+ auth_header.split(" ", 2).last.to_s.strip
27
+ else
28
+ request.headers["X-API-Key"].to_s.strip
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module Macron
2
+ module Cms
3
+ module Api
4
+ module V1
5
+ class ContentTypesController < BaseController
6
+ def index
7
+ content_types = Content.order(:name).map do |ct|
8
+ {
9
+ id: ct.id,
10
+ name: ct.name,
11
+ fields: ct.normalized_fields
12
+ }
13
+ end
14
+
15
+ render json: { content_types: content_types }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -54,11 +54,22 @@ module Macron
54
54
 
55
55
  def update
56
56
  filtered = @article_content.filter_payload(payload_param)
57
- if filtered.key?("upload") && filtered["upload"].blank? && @article_content.payload["upload"].present?
58
- filtered["upload"] = @article_content.payload["upload"]
57
+ remove_files = Array(params.dig(:article_content, :remove_files)).map(&:to_s)
58
+
59
+ remove_files.each do |fname|
60
+ filtered[fname] = nil
61
+ if %w[upload file].include?(fname) && @article_content.files.attached?
62
+ @article_content.files.purge
63
+ end
64
+ end
65
+
66
+ @article_content.payload.each do |k, v|
67
+ if filtered.key?(k) && filtered[k].blank? && v.present? && !remove_files.include?(k)
68
+ filtered[k] = v
69
+ end
59
70
  end
60
71
 
61
- attach_files_if_present!
72
+ attach_files_if_present! unless remove_files.include?("upload") || remove_files.include?("file")
62
73
 
63
74
  if @article_content.update(payload: filtered)
64
75
  respond_to do |format|
@@ -4,7 +4,7 @@ require "kramdown-parser-gfm"
4
4
  module Macron
5
5
  module Cms
6
6
  module FieldsHelper
7
- SUPPORTED_KINDS = %w[string text dropdown file color markdown json].freeze
7
+ SUPPORTED_KINDS = %w[string text dropdown file color markdown json latex].freeze
8
8
 
9
9
  def cms_render_markdown(text)
10
10
  return "".html_safe if text.blank?
@@ -0,0 +1,86 @@
1
+ require "open-uri"
2
+ require "net/http"
3
+
4
+ module Macron
5
+ module Cms
6
+ class RemoteAssetAttacher
7
+ MAX_FILE_SIZE = 25.megabytes
8
+ READ_TIMEOUT = 10
9
+ OPEN_TIMEOUT = 5
10
+
11
+ def self.attach(article_content:, url:, replace: true)
12
+ new(article_content: article_content, url: url, replace: replace).call
13
+ end
14
+
15
+ def initialize(article_content:, url:, replace: true)
16
+ @article_content = article_content
17
+ @url = url.to_s.strip
18
+ @replace = replace
19
+ end
20
+
21
+ def call
22
+ return false if @url.blank?
23
+
24
+ uri = URI.parse(@url)
25
+ return false unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
26
+
27
+ download_and_attach(uri)
28
+ rescue => e
29
+ Rails.logger.warn("[Macron::Cms::RemoteAssetAttacher] Failed to download remote asset from #{@url}: #{e.message}")
30
+ fallback_to_payload_url
31
+ false
32
+ end
33
+
34
+ private
35
+
36
+ def download_and_attach(uri)
37
+ options = {
38
+ read_timeout: READ_TIMEOUT,
39
+ open_timeout: OPEN_TIMEOUT,
40
+ "User-Agent" => "MacronCMS/1.0"
41
+ }
42
+
43
+ downloaded_io = URI.open(uri, options) # rubocop:disable Security/Open
44
+
45
+ if downloaded_io.size > MAX_FILE_SIZE
46
+ Rails.logger.warn("[Macron::Cms::RemoteAssetAttacher] File from #{@url} exceeds #{MAX_FILE_SIZE} bytes")
47
+ fallback_to_payload_url
48
+ return false
49
+ end
50
+
51
+ filename = derive_filename(uri, downloaded_io)
52
+ content_type = downloaded_io.content_type || "application/octet-stream"
53
+
54
+ if @replace && @article_content.files.attached?
55
+ @article_content.files.purge
56
+ end
57
+
58
+ downloaded_io.rewind if downloaded_io.respond_to?(:rewind)
59
+
60
+ @article_content.files.attach(
61
+ io: downloaded_io,
62
+ filename: filename,
63
+ content_type: content_type
64
+ )
65
+
66
+ true
67
+ end
68
+
69
+ def derive_filename(uri, io)
70
+ basename = File.basename(uri.path.to_s)
71
+ if basename.present? && basename.include?(".")
72
+ basename
73
+ else
74
+ ext = Rack::Mime::MIME_TYPES.invert[io.content_type] || ".bin"
75
+ "asset_#{SecureRandom.hex(6)}#{ext}"
76
+ end
77
+ end
78
+
79
+ def fallback_to_payload_url
80
+ current_payload = (@article_content.payload || {}).dup
81
+ current_payload["upload"] = @url
82
+ @article_content.payload = current_payload
83
+ end
84
+ end
85
+ end
86
+ end
@@ -8,7 +8,9 @@
8
8
 
9
9
  <%= stylesheet_link_tag "macron/cms/application", media: "all" %>
10
10
  <%= stylesheet_link_tag "macron/cms/codemirror", media: "all" %>
11
+ <%= stylesheet_link_tag "macron/cms/katex.min", media: "all" %>
11
12
  <%= turbo_include_tags %>
13
+ <%= javascript_include_tag "macron/cms/katex.min", defer: true %>
12
14
  <%= javascript_include_tag "macron/cms/codemirror", defer: true %>
13
15
  <%= javascript_include_tag "macron/cms/editor", defer: true %>
14
16
  <%= yield :head %>
@@ -120,7 +120,7 @@
120
120
  <% fname = f["name"] %>
121
121
  <% fkind = f["kind"] %>
122
122
  <% fval = ac.payload[fname] %>
123
- <div class="<%= fkind.in?(%w[text markdown]) ? 'sm:col-span-2' : '' %>">
123
+ <div class="<%= fkind.in?(%w[text markdown latex]) ? 'sm:col-span-2' : '' %>">
124
124
  <dt class="text-xs font-mono font-medium text-zinc-500 uppercase tracking-wide">
125
125
  <%= fname %>
126
126
  <span class="text-[10px] text-zinc-400 lowercase font-normal">(<%= fkind %>)</span>
@@ -131,6 +131,10 @@
131
131
  <div class="prose prose-zinc prose-sm max-w-none">
132
132
  <%= cms_render_markdown(fval) %>
133
133
  </div>
134
+ <% elsif fkind == 'latex' %>
135
+ <div class="py-2 overflow-x-auto text-center" data-cms-latex="<%= fval %>">
136
+ <span class="font-mono text-xs text-zinc-600"><%= fval %></span>
137
+ </div>
134
138
  <% elsif fkind == 'text' %>
135
139
  <div class="whitespace-pre-wrap"><%= fval %></div>
136
140
  <% elsif fkind == 'file' && ac.files.attached? %>
@@ -12,6 +12,10 @@
12
12
  <span class="truncate font-medium"><%= value %></span>
13
13
  <span class="text-[10px] text-zinc-400 font-sans ml-auto shrink-0">Current file</span>
14
14
  </div>
15
+ <label class="flex items-center gap-2 mt-1 cursor-pointer text-xs text-rose-600 hover:text-rose-700 font-sans">
16
+ <input type="checkbox" name="article_content[remove_files][]" value="<%= field_name %>" class="rounded border-zinc-300 text-rose-600 focus:ring-rose-500">
17
+ <span>Remove current file (<%= value %>)</span>
18
+ </label>
15
19
  <% end %>
16
20
 
17
21
  <input
@@ -0,0 +1,17 @@
1
+ <div class="space-y-1.5">
2
+ <div class="flex items-center justify-between">
3
+ <label for="<%= input_id %>" class="block text-xs font-mono font-semibold text-zinc-700">
4
+ <%= field_name %>
5
+ <span class="ml-1 text-[10px] font-normal text-indigo-600">(latex)</span>
6
+ </label>
7
+ <span class="text-[10px] text-zinc-400 font-mono">CodeMirror</span>
8
+ </div>
9
+ <textarea
10
+ name="<%= input_name %>"
11
+ id="<%= input_id %>"
12
+ rows="6"
13
+ data-cms-editor="latex"
14
+ class="block w-full font-mono rounded-lg border border-zinc-300 bg-white px-3 py-2 text-xs text-zinc-900 shadow-2xs placeholder:text-zinc-400 focus:border-zinc-900 focus:outline-none focus:ring-1 focus:ring-zinc-900"
15
+ placeholder="Enter LaTeX formula e.g. \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}"
16
+ ><%= value %></textarea>
17
+ </div>
data/config/routes.rb CHANGED
@@ -14,4 +14,11 @@ Macron::Cms::Engine.routes.draw do
14
14
  end
15
15
  end
16
16
  resources :content_types, only: %i[index show]
17
+
18
+ namespace :api do
19
+ namespace :v1 do
20
+ resources :content_types, only: %i[index]
21
+ resources :articles, only: %i[create show update]
22
+ end
23
+ end
17
24
  end
@@ -1,12 +1,13 @@
1
1
  module Macron
2
2
  module Cms
3
3
  class Configuration
4
- attr_accessor :user_class, :admin_flag, :content_types_path
4
+ attr_accessor :user_class, :admin_flag, :content_types_path, :api_key
5
5
 
6
6
  def initialize
7
7
  @user_class = "User"
8
8
  @admin_flag = :admin
9
9
  @content_types_path = nil
10
+ @api_key = ENV["MACRON_CMS_API_KEY"]
10
11
  end
11
12
 
12
13
  def devise_scope
@@ -1,5 +1,5 @@
1
1
  module Macron
2
2
  module Cms
3
- VERSION = "2.0.0"
3
+ VERSION = "3.0.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: macron-cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leonid Medovyy
@@ -135,10 +135,35 @@ files:
135
135
  - Rakefile
136
136
  - app/assets/javascripts/macron/cms/codemirror.js
137
137
  - app/assets/javascripts/macron/cms/editor.js
138
+ - app/assets/javascripts/macron/cms/katex.min.js
138
139
  - app/assets/stylesheets/macron/cms/application.css
139
140
  - app/assets/stylesheets/macron/cms/codemirror.css
141
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_AMS-Regular.woff2
142
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Bold.woff2
143
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Regular.woff2
144
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Bold.woff2
145
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Regular.woff2
146
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Bold.woff2
147
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-BoldItalic.woff2
148
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Italic.woff2
149
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Regular.woff2
150
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-BoldItalic.woff2
151
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-Italic.woff2
152
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Bold.woff2
153
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Italic.woff2
154
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Regular.woff2
155
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Script-Regular.woff2
156
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Size1-Regular.woff2
157
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Size2-Regular.woff2
158
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Size3-Regular.woff2
159
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Size4-Regular.woff2
160
+ - app/assets/stylesheets/macron/cms/fonts/KaTeX_Typewriter-Regular.woff2
161
+ - app/assets/stylesheets/macron/cms/katex.min.css
140
162
  - app/assets/tailwind/macron/cms/application.css
141
163
  - app/components/macron/cms/widget_component.rb
164
+ - app/controllers/macron/cms/api/v1/articles_controller.rb
165
+ - app/controllers/macron/cms/api/v1/base_controller.rb
166
+ - app/controllers/macron/cms/api/v1/content_types_controller.rb
142
167
  - app/controllers/macron/cms/application_controller.rb
143
168
  - app/controllers/macron/cms/article_contents_controller.rb
144
169
  - app/controllers/macron/cms/articles_controller.rb
@@ -154,6 +179,7 @@ files:
154
179
  - app/models/macron/cms/article.rb
155
180
  - app/models/macron/cms/article_content.rb
156
181
  - app/models/macron/cms/content.rb
182
+ - app/services/macron/cms/remote_asset_attacher.rb
157
183
  - app/views/layouts/macron/cms/application.html.erb
158
184
  - app/views/macron/cms/articles/_content_blocks_section.html.erb
159
185
  - app/views/macron/cms/articles/_form.html.erb
@@ -169,6 +195,7 @@ files:
169
195
  - app/views/macron/cms/fields/_dropdown.html.erb
170
196
  - app/views/macron/cms/fields/_file.html.erb
171
197
  - app/views/macron/cms/fields/_json.html.erb
198
+ - app/views/macron/cms/fields/_latex.html.erb
172
199
  - app/views/macron/cms/fields/_markdown.html.erb
173
200
  - app/views/macron/cms/fields/_string.html.erb
174
201
  - app/views/macron/cms/fields/_text.html.erb