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.
- checksums.yaml +4 -4
- data/README.md +376 -14
- data/app/assets/javascripts/macron/cms/codemirror.js +3 -0
- data/app/assets/javascripts/macron/cms/editor.js +34 -6
- data/app/assets/javascripts/macron/cms/katex.min.js +1 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Main-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Math-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Script-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- data/app/assets/stylesheets/macron/cms/katex.min.css +1 -0
- data/app/controllers/macron/cms/api/v1/articles_controller.rb +186 -0
- data/app/controllers/macron/cms/api/v1/base_controller.rb +35 -0
- data/app/controllers/macron/cms/api/v1/content_types_controller.rb +21 -0
- data/app/controllers/macron/cms/article_contents_controller.rb +14 -3
- data/app/helpers/macron/cms/fields_helper.rb +1 -1
- data/app/services/macron/cms/remote_asset_attacher.rb +86 -0
- data/app/views/layouts/macron/cms/application.html.erb +2 -0
- data/app/views/macron/cms/articles/_content_blocks_section.html.erb +5 -1
- data/app/views/macron/cms/fields/_file.html.erb +4 -0
- data/app/views/macron/cms/fields/_latex.html.erb +17 -0
- data/config/routes.rb +7 -0
- data/lib/macron/cms/configuration.rb +2 -1
- data/lib/macron/cms/version.rb +1 -1
- metadata +28 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
-
|
|
58
|
-
|
|
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
|
data/lib/macron/cms/version.rb
CHANGED
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:
|
|
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
|