From 3f56c251cd668605039c193fbfa431a4d89cd0ee Mon Sep 17 00:00:00 2001 From: j4nk Date: Mon, 10 Jul 2023 08:51:18 -0400 Subject: [PATCH] Possibly correct first implementation of automatic RSS feed generation --- feed.xml | 1 + md4tj.el | 71 ++++++++++++++++++++++++++++++++++++++++-------- rss.png | Bin 0 -> 7414 bytes test_blog.html | 6 ++-- test_blog.md4tj | 3 +- test_file.html | 2 +- 6 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 feed.xml create mode 100644 rss.png diff --git a/feed.xml b/feed.xml new file mode 100644 index 0000000..8aac30f --- /dev/null +++ b/feed.xml @@ -0,0 +1 @@ +RSS Testhttp://example.comAn example blog for testing RSS capability of md4tj.en-USTue, 10 Jun 2003 04:00:00 GMTeditor@example.comwebmaster@example.comhttps://www.rssboard.org/rss-specificationmd4tj-rss.elMon, 10 Jul 2023 08:50:16 GMTBlog Post 2j4nkhttps://example.com/blog/blogpost2.htmlhttps://example.com/blog/blogpost2.htmlThu, 02 Mar 2023 19:44:50 ESTBlog Post 1j4nkhttps://example.com/blog/blogpost1.htmlhttps://example.com/blog/blogpost1.htmlThu, 02 Mar 2023 19:44:14 EST \ No newline at end of file diff --git a/md4tj.el b/md4tj.el index 026c9c5..702fb6e 100644 --- a/md4tj.el +++ b/md4tj.el @@ -94,8 +94,8 @@ "`\\(.*\\)`" "\\1" line))))))))) -(defun md4tj-convert-line-to-html (line state inbuf) - "Process LINE with STATE and return html, INBUF provided." +(defun md4tj-convert-line-to-html (line state inbuf outfile) + "Process LINE with STATE and return html, INBUF provided with OUTFILE." (let ((cleanline (md4tj-util-clean-multiline line))) ;; If this is a signal to include another file (cond ((string-match "^@@INCLUDE" line) (md4tj-parse-to-string (nth 1 (split-string line)))) @@ -106,6 +106,7 @@ ((string-match "^@@DIV" line) (md4tj-begin-tag "div" (list (list "class" (nth 1 (split-string line)))))) ((string-match "^@@ENDDIV" line) (md4tj-end-tag "div")) ((string-match "^@@BLOGINSERT" line) (with-current-buffer inbuf (md4tj-blog-html))) + ((string-match "^@@RSSINSERT" line) (md4tj-rss-link-string (md4tj-rss-file-from-outfile outfile))) ((and (string-match "^$$.*$$" line) (eq (nth 1 state) 'normal)) ;; LaTeX formula (shell-command (concat "./pnglatex" " -d 300" @@ -234,6 +235,8 @@ (set-buffer inbuf) (insert-file-contents mdfile) (goto-char (point-min)) + ;; If we need RSS, go ahead and output the XML file here + (if (md4tj-rss-needs-rss) (md4tj-rss-to-file mdfile (md4tj-rss-file-from-outfile outfile))) (set-buffer outbuf) (insert (md4tj-begin)) (insert (with-current-buffer inbuf (md4tj-head))) @@ -242,7 +245,7 @@ (setq line (with-current-buffer inbuf (md4tj-util-getline))) (setq fullstate (md4tj-next-state line (nth 1 fullstate))) ;; Insert next line(s) into output file - (let ((linehtml (md4tj-convert-line-to-html line fullstate inbuf))) + (let ((linehtml (md4tj-convert-line-to-html line fullstate inbuf outfile))) (insert (concat linehtml (if (string-empty-p linehtml) "" "\n")))) ;; Advance input file by a line (with-current-buffer inbuf (forward-line))) @@ -264,7 +267,7 @@ (while (< (point) (point-max)) (setq line (md4tj-util-getline)) (setq fullstate (md4tj-next-state line (nth 1 fullstate))) - (setq acc (concat acc (md4tj-convert-line-to-html line fullstate nil) "\n")) + (setq acc (concat acc (md4tj-convert-line-to-html line fullstate nil nil) "\n")) (forward-line)) acc))) @@ -369,9 +372,9 @@ (goto-char (point-min)) (if (search-forward-regexp "^@@RSSENABLE" nil t) t nil))) -(defun md4tj-rss-begin-tag (tag &optional attrs) - "Return the RSS text for TAG with ATTRS." - (concat "<" tag (mapconcat (lambda (attr) (concat " " (nth 0 attr) "=" "\"" (nth 1 attr) "\"")) attrs "") ">")) +(defun md4tj-rss-begin-tag (tag &optional attrs self-close) + "Return the RSS text for TAG with ATTRS, SELF-CLOSE if necessary." + (concat "<" tag (mapconcat (lambda (attr) (concat " " (nth 0 attr) "=" "\"" (nth 1 attr) "\"")) attrs "") (if self-close "/" "") ">")) (defun md4tj-rss-end-tag (tag) "Return the RSS text for ending TAG." @@ -394,23 +397,67 @@ "Generate RSS syntax for RSS channel statement tokenized to TOKS." (concat (md4tj-rss-begin-tag (car toks)) (mapconcat #'identity (cdr toks) " ") (md4tj-rss-end-tag (car toks)))) -(defun md4tj-rss-channel () - "Return the RSS text for channel in current buffer." +(defun md4tj-rss-channel (rsslink) + "Return the RSS text for channel in current buffer with RSSLINK." (concat (md4tj-rss-begin-tag "channel") + ;;(md4tj-rss-begin-tag "atom:link" (list (list "href" rsslink) (list "rel" "self") (list "type" "application/rss+xml")) t) (mapconcat #'md4tj-rss-channel-statement-toks-to-rss (md4tj-rss-get-channel-statements) "") (md4tj-rss-begin-tag "docs") "https://www.rssboard.org/rss-specification" (md4tj-rss-end-tag "docs") (md4tj-rss-begin-tag "generator") "md4tj-rss.el" (md4tj-rss-end-tag "generator") (md4tj-rss-begin-tag "lastBuildDate") (format-time-string "%a, %d %b %Y %H:%M:%S GMT") (md4tj-rss-end-tag "lastBuildDate") (md4tj-rss-end-tag "channel"))) -(defun md4tj-rss () - "Return the RSS text for the current buffer." +;; Elt is triple of +;; Title +;; Time +;; Link +(defun md4tj-rss-item (elt) + "Return RSS for ELT." + (concat (md4tj-rss-begin-tag "item") + (md4tj-rss-begin-tag "title") (nth 0 elt) (md4tj-rss-end-tag "title") + ;; NOTE: change "j4nk" to author name + (md4tj-rss-begin-tag "author") "j4nk" (md4tj-rss-end-tag "author") + (md4tj-rss-begin-tag "link") (nth 2 elt) (md4tj-rss-end-tag "link") + (md4tj-rss-begin-tag "guid") (nth 2 elt) (md4tj-rss-end-tag "guid") + (md4tj-rss-begin-tag "pubDate") (format-time-string "%a, %d %b %Y %H:%M:%S %Z" (string-to-number (nth 1 elt))) (md4tj-rss-end-tag "pubDate") + (md4tj-rss-end-tag "item"))) + +(defun md4tj-rss-items () + "Return all RSS for all elements." + (mapconcat 'md4tj-rss-item (md4tj-blog-all-blogs-list) "")) + + + +(defun md4tj-rss (rsslink) + "Return the RSS text for the current buffer at RSSLINK." (save-excursion (when (md4tj-rss-needs-rss) (concat (md4tj-rss-begin) - (md4tj-rss-channel) + (md4tj-rss-channel rsslink) + (md4tj-rss-items) (md4tj-rss-end-tag "rss"))))) + +(defun md4tj-rss-to-file (infile outfile) + "Write INFILE as RSS to OUTFILE." + (with-temp-buffer + (insert-file-contents infile) + (let ((outbuf (generate-new-buffer " outbuf" t)) + (rss-str (md4tj-rss (concat (md4tj-blog-base-url) "/feed.xml")))) + (set-buffer outbuf) + (insert rss-str) + (write-region nil nil outfile nil)))) + +(defun md4tj-rss-link-string (rsslink) + "Return the html of RSS link to RSSLINK as string." + (concat (md4tj-begin-tag "div" (list (list "id" "rssicon"))) (md4tj-begin-tag "a" (list (list "href" rsslink))) (md4tj-begin-tag "img" (list (list "src" "rss.png") (list "alt" "RSS icon") (list "style" "width:20px;height:20px;"))) (md4tj-end-tag "img") (md4tj-end-tag "a") (md4tj-end-tag "div"))) + +(defun md4tj-rss-file-from-outfile (outfile) + "Return the filename that the RSS XML should be output to given OUTFILE." + (concat (file-name-directory outfile) "/feed.xml")) + + + ;; End md4tj rss stuff (provide 'md4tj) diff --git a/rss.png b/rss.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6c56174728f6c5e5db64ed193918c32285b5bc GIT binary patch literal 7414 zcmYLN2{csi`+x748H2&d9x|3J*|L>N8fysIk|o(?56N0wOSbGwiDb=^C0i28kg{hj zL^TT8_kI0M-}67`f6x2g=lwj-=lML(x#v0O-gBdLb*|DQ#5GIUlw;A9nJ6@gd)wFYBx?+l)8+ zj1TLv3s~~ynEMaT`mmA@6E3I7(b<1|R`NOqfDGr!(3^eMhh^56edZy{j4%5_z==6; zRhBMY84`IYcJ=l4)dFHgft5IrK03k1+dR?!RV^ zkIBsKu7aJfbw|6KbABfe=EjeYj^+YR&G~a4A0EuZ0!RD1ODXC{d)o^UQU~)t7amE> zKjfJYIuO98JFQ@73 z^pzhh|5*qXnSa2&*%-5&uD?Guu>YrbwcyUt*2-4fi_PZL_38&(t?7qrv#Ui;TOXh9 zjy7%7M=eI5UrJP2j8iyVpI@&DTFElo>8sf8$lIt5UCFt9rE~{pqg<3%^!#Ef>PX4raga4A$%n)gCO8Rtp`sdtPnS zh3%2LHs8muy}En2xwJd-e(O`_{@-tlk1uSs=WKr|U5J#~pBdi!`Dx=__*${+c6ZSp zsdH!O&30%0QaoYzTm8;J)$UmH;qv7E%*fWK=S%U5d%t@qJG_F(6C&rOuVVlLW&^kvF~}G zsSyCc$(tG~iiW=57c!$P4BA-QLZe0WeJb~Em%RC2_-)Ja%5tnFKMxhZ75)CHt%~LS z4nlr2EWLazjh` zVgo9_df9ZOJh?%cKH^rS+?}NEchVC%zpOAr6AgVU(iYcF9#EVwKW07lMeXh`Y;sVl z#2?)(bX&Sw^wsXT$Kt~Yxq~auKhKEtt`1&_WIw!ovzNENxp_1%ppO2yIl&y`vf|D^ ziT^dxbaWjlTZf_fTS$C?YupQ!lW`hnH?3?l@ohciVZ0g_D;wJAKjI@ zwL9V${5X<^OFSqa8Mb~UY{!w_A2_VP{RxN49cx7Y;r=OdpGz`Fb%&A>IKi!$OOaS$ zLfk(M>Os4Q?Ya`_%x}f`ybL6P1EcumZv8QRe|CG*@d=Vlh+<;U;r(NgXafI*qjCun zleJ>9#ZPK?#+>YqiMN@Sz7&0dl;Z^cq7%Un@!!Mps8upzP5Ul9IDRC$kNCxYF;uQ^ zD#ikK4+|FO351jw(Dh+^D8l)(R)JE_=y<79cE2+P<8`M422sdhDb{{E&5ZW~x2ToV z2aQqs7L>n@a03sVFGi6W`BYbx)T_Ig!tuKHO;oHDiPKjZ>ml{(-Y~X4G=cyC1dsp+ z{{H}TRk3ZB%HEv~v^Dcn?bovN*{lUh|&M7bU>O zZ{`p&ELIT0F7J2rq*4rC_T(R@}D$I7N1%YIVq;fRo z)@Q*dFw+@Y`>C72FokmHESpLT!lj$Sv7J(gh9p=SbW%Ga1+_BxcCz`DlpS<{?X{+S zRs)W_NV8{dqf$_qnk0E2OI=|aQehp(AaI1(y0&DkZOK?Lhmu1rA7v?OgWBX7K%8Y6 z3qKDzLI?9LDEjcUDi9dR-OOM}4xAg08AOqZE*~(vc(OplqVhb3Y_GYPeReQl0n`bs z|294wctT{l{q{r*&%ZjtO6G74h_N%uy{K*qKCk`lQ_>_`wPeX1vf9FFy7WO7_*06_ z|7-HGW&|LUDpeoh;3^QK9jgq;(zOY6kU9;~nzHOaZ|uc?T;mO8 z4rI0{u=)50_fP2!3xUk7C^&^7kgF|e|A)FA|3d;a|793ZBJpCt#&z{yf{Q2>B7xjU zw<*uR21JVgt9)S)XSr`!b98REs%h(MsNUa|2QFUjBDmRb@cr!A07v5=z8>?$!_%)9 zo9_^R=wOHpW_g7H-eS${Dgq;eco_EMxqP97cTU6I*S!}FA+5BzEIK%D+@|=;jUyDH zV+Wb3>@?3~%ay=H{_b3y;QT&qdf&-ymQ2h|E#zPVP2M>GczcQ=-I?L4g2>3HmCk;n1OX4MI z5Hr;Ttydj4m7y$l#7tP;H7d%zKRmG8I$_DW;0PV_SrHsocujtqb0wrwAZAQYclzgT zPwPq``<}AOYS*8{{lG#Sfy^7<=IX67dYxLYK!`jIvM;mc0#|h~k^`xmBbQJ( zX5U;ygD4{0dV2eA$blLr8@VJ?sDQS;h=+Mkx4f`^_$AJd2R_3uIHP}J4hD0Rp~)8w z(I*Od;EOUd*#s{Zq^?G=&@Oe2GI+z3;6~s=>PGX=&RQ}cb!p-kRION%x@F?{cQS?$ z#Pl&;;H_UbC1m)~C|PI_j=&Kv%q?rUG6aLU#7ogy3FP@V%*JklPQ-Zq4V>`nb0c(6 ziYPW!T{@c`A!Nc_)SYNYAQw!MGvPq?zr+!=KcD70dwqidROgkZJd>J7_%Yc+KP83H zDfg9ts>go57UISxjxgGpoub`ROoho_;I9z3IUy7m0XlzoBv=wdm=QwPG)s^ayi_!O zzgMR(UNjgq7ha8ikzM{pEhC!%x!t7>y=)_sbKj%3>iZvKBL|~ff%u=W8)gNP+`T?L zXLRq6VV&v6@2BNiV;qA4ciU2_Sg+>5rRzE@feiO`1d%)Arj^}tR3=DQ{l2_kG!IXep&dN6+xABn2N<4!* z!61Fkk?+KZ;k1itvJ8e6;Ik(SM_3z=9U&W{GQwmhCW-csMLgL@(55{C>9Yaidp9nM zRPc@Jv2|(PIg&7m0Hqcl>DG>o0w{Cvd zM~PEf&;%^$V~Aoeqfb1g|NMhtb7*uH2XpJSTok7XGnc+(`v`E}2{zJ`Kx9-T>9Qh8 z-+hBD`XpW676nes*kXvOt>+}UJ>P0k`;%f27@}XEtGT**S?*HWVgwk!H*e3t%u-Us z*y@ObFZrkpjh0;tiDrw%!B=PyEXZkD#nVgT}9qJhnK4MiJLXN=_k8%*a@NV3YVfS@CT` zS6IPs9$4s=|L##*%2l->DmE?fS-|{OP^`0N>oq(p0wYTI+Vk+jCa+tvCyNpYuJPJvB!`t3Tp!1UPuaLibM8Mydd{NFAU4??mb*E9TZ0*!TUs?S_EJsS&yAnR){ z{GQ}AKnCA$hW>aUc=|jubS^SOJcy~R1oC;~L(??5M-a(JjJ;Ig2^h%k{n9 z98Y^Zf3@vSJ2lRjH}2Qqg!#&?d7EC3vg@B1S%}v)O{2UhWR7Q9LOxv3;(`vmo?HuL zR&lwNK-A>vne5(^A8jN6RkeG^v z`Q$sWflYE+Xkj@Yj^wIj{8YFHhGK&n?#K0dwqc>4rzt{jRAJi(>l!}Yb9UxLG)mAK zp3{)Z))@=kS}DD*4EIzMwVp9SxtG7ckPiM+?aMDAfE=hk22Hb|G zPi!}6nViR1*E}AiupT=vYmMY;mY=<~kgcbJdYVS;c2G-ZYqNcDO5Fct#(R_W8-}_B zh*?ihFY(IL}^~2?u8%hLIAi5H$yiv2~`E?-4|;t76ro2!HwukTOCK zvJ@yJl<6T}d3jcyboZ*I0n#s!TO2){Lu+VKr)rls*+vXZwVx)C4uSS2R^t91p9#B(kS5 zZz71d&d4%z^!c_@_)t5rf@#CLVH|X_gQo&J5v>p5(aR;z9yGs@++zU^Ic}`L`kmh} zI}eHZ2e0v1ngtS06Q{6$g^8Fo1KnJ{h9FXP%10Zx2OpurRgqg%Kyvm`Vv5zCv^ZB0 z)2eGZz$M3}%_x{D{erpIJy9!^#NAPqrVeGVW8@iQ6MoQI)cGpxBIOc=Nd@0O)NUsa zZcNU;RzbZg#{kLRcft3-WoZ(!J%kZKSP*QcaR=TB2GV&#KJKvqSABAIVuI4vMmII& zIuag{1-(q7Jc5)to*%zZgHuB&33hzjBW3(wL-S%lQoKW|Y*u_F9HEHjWCD#2S55-Y zXZA=h#s>nxSN}>nSkpdWpE;!~27D6(n6X5DS^OYUiWCD#8eeB_|KT%aQvA^24H5IM z;W#-3uFM6{NgsWogNPZO%}*8QE;QzJ|9u=;2Jn|^-^Www(LB1nyBJr)3@D$A>bOo^ z!D#9*0{MoCicHnlj2~O9`W$TuVC4bMMLSZA#ALto5dm*slN%B|NN`Z)v)@@qz&=01 z-Jl_FuDAF+)@2sZ7!;J#1`n%<5SS4@NU-cQ8q5!;vG&2mW&x)G4*78OP@q4uI-x&c z98WxlSlU)y-3oxdzI zRy;%jxHs*NdTrh_bGnpAz@)=q0blUfdhH`OiW>g>rL6kk+c!${itC7~yqUmss*hePMNGUhU1SRPGJhUf1} zyV1y9tp>?l@-1AjNQ#=m>CEp>l}Psj%?>wEn`>yiN>4v~>D!%YTtdCMeO zz_6DQ|IoEQ^j8?P!q@}arEh+sje&eu)&4wvdVhX^vV>h=%k459`-3zIpx2Y^TRpwQ zSRn@6cRXw#Idp*>2A3v_cSDTYIGDgw2ecv2=bv57uuyjLeBfoqrJd)j2w`Kf;t49E zUZkL!gdp{HberqI&SpYogE0cvys`9Z(D~VgNJMtDH&u8|^$QY)FvH?79k3CiT!Gay zjYacIx=3FIOU)lX1k^a$Ht5^Im;6jk)fud3 z1WSQjoW*eqH>1_ni@(j}aKsc*d(22D8eaSi z`t}&dw^#8Y>R3@K4~P^d36`Ah#}jSQ=6Tr5zu$86zW{Y(F_5nB&~avQs?|z7uqRqk_kg3K6#KPgMsbe za(17G^-C6c(^0akz%oMv)GP(-&`zErbkZJudTkrFL$MO70c6a=37s`B^wrcR!?)K# z_7{GTexs_8;arLy^;aCd#)nA=?${hY#HvLfeh=yYj`&)l_rro2qLLUzL7k)f;eJh-@~t-NUL0C2MR1E;U=R=9)|7D& z(iEe*`J_TEs(@uH9`wE`qv3OEeZhcs12GK|C=iQ=fbU0!W=;K%b*BzJma721agY<} zJzU2{RQ`6#S_=Nk3C^bBbkXfG<=ZqWDpE>(G=PqS0_gFYM`I{;lMEP4tjtA$(0Mcw z*CHR57wt()FpTuB2E;{a6tO{N)j;Hjt9}Nc{9*{0!f`Ma*oXUi8^Vo?K9C$N6;t{Z{zj$Oz!;sDpyHMPWj%kDlw}cc>Ghh$vW-qPV%@(uyi9S@@uy z^HCbGw50LHbVbG>N*%X5lrMfp!9Onb0n%J9Zd7_*L#7fUvcoKqoLAGmzh(`x@fs9l(1X4rcd;4)vTn4!B<@l$>trx0CbSdEaK&DC&L{$v?Qr;Sh-JBKrni? z%gC3`g_YOFF6^v0iQXfg8DwKuDQ9v=BCm7X5XAKvBmlmH_2xuRC64>g$1o2_EN|=? z1Tfev**-pY5>;+ZDKh`s-Dxzu6}6ulb!rrDmp?7CV)}bhrIjB1jWV)a2~M1jgx{yF zMRH|JS*CCU9oF{(uXdh0xAGHW<)(Ji|L%QUvckZFN}KQBe&eXZtlZ2W66WsX_I}O> zbSM{}4jZLQ%>-WwQYD1unu|j2&jpsF(wN)KZKi>d^+484`?&%jCwq7*H1v)c)|6t# zO0gJ6$$xFz14Z;WY2n{->?GyXm2f3M60cE^DgojcV>ee0YsX}rHLCxKGF3%LgSho_ zhb7o}HAiu{h6{eu>~lEbCU_Tc7JBXHnar=ZT{8P^`H3sYvjv#sFpooN`jZ=9XU&!E z-Y74R*QEhp;>#~tv4x@lXmwonUnS1ZXN}YBxm~^+7ykR4Lhc=I=mtSmQ{yby$zB=h zuPraws}1gqlZ(G~J6}R6A=kOOwj4!89&dH>?@Sd0Og2=27b5V3NF+!E_$m~PeMXUy znsX$cY+3!SbQ(DAx+<>AWE*`+%pZ!8 zSwaJCeyu}&sfb;YQ0W^A&Vn^X`6_nMq}-gT`5^_hrx~c z*H!suk<4e>(;@K2_#l`?NZO)D$dSb|*r>#WpVkKP`hIm;ljfnOz0ine#|vhV+N3@$ z)NY#cdb;oADjGRTQDM|mEMLq*S1Pq;h~3N3GSp4Yvrh6wQoPbWqo(0Tz^9g5KUsjz z9UuS7qL}1ot68^*Tg#V!M~j|6dRi#&6zdy?$X9hvy)*UrrrcT0j+@ArnqPehHR5Ld z8*e5lk%VU@%qEX%Y>(< H$ui`BT--Gz literal 0 HcmV?d00001 diff --git a/test_blog.html b/test_blog.html index aaab687..4bcefbb 100644 --- a/test_blog.html +++ b/test_blog.html @@ -4,6 +4,7 @@ + Test blog @@ -18,11 +19,8 @@ -
-

Blogs

- -
+
RSS icon

Blog Post 2

2023 March 02 19:44
diff --git a/test_blog.md4tj b/test_blog.md4tj index 212dc4c..ba8a0c5 100644 --- a/test_blog.md4tj +++ b/test_blog.md4tj @@ -9,9 +9,8 @@ @@RSSCHANNELmanagingEditor editor@example.com @@RSSCHANNELwebMaster webmaster@example.com @@TITLE Test blog - # Blogs - +@@RSSINSERT @@BLOGBASEURL https://example.com/blog/ @@BLOGBASEDIR ./blog/ @@BLOGINSERT diff --git a/test_file.html b/test_file.html index d156cf5..28c7c7d 100644 --- a/test_file.html +++ b/test_file.html @@ -75,7 +75,7 @@ int main() {

This is a div that has monospace text.

-

Last updated: Sun Jul 9 19:26:42 2023

+

Last updated: Mon Jul 10 08:35:47 2023