From 93dc051533983162c0f60cd5898e5088b1468914 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 11:06:40 +0100 Subject: [PATCH 001/459] Hacker theme icons --- theme/hacker/icons/avatar_default.png | Bin 13285 -> 25409 bytes theme/hacker/icons/avatar_news.png | Bin 1953 -> 12152 bytes theme/hacker/icons/links.png | Bin 7495 -> 5464 bytes theme/hacker/icons/logorss.png | Bin 1522 -> 8875 bytes theme/hacker/icons/newpost.png | Bin 7067 -> 9193 bytes theme/hacker/icons/person.png | Bin 8943 -> 9840 bytes theme/hacker/icons/scope_reminder.png | Bin 1491 -> 5494 bytes theme/hacker/icons/scope_share.png | Bin 1625 -> 11197 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/avatar_default.png b/theme/hacker/icons/avatar_default.png index 9c50078f576684c859faf273da2b43c297818095..cb882cc831e08b7b56cdaf6f7229393da619b4c2 100644 GIT binary patch literal 25409 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 literal 13285 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 JPDHLkV1g4Zb3*_C diff --git a/theme/hacker/icons/links.png b/theme/hacker/icons/links.png index dc23017ba2f42739705c68bb44808d4687409b89..afde0b765a7a3667b44d719d873b40f07abe7cc4 100644 GIT binary patch delta 1593 zcmV-92FCfvI@l_Z7=Hl+0001xr{kRf00Q-TR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4R-&w^h0oxdx@PxmHnL{4`u!3rmyq!Jf-Zr2E-U8Z_HhZvINSbMiJx~^Rr2R) ze|k1VuPgir^AM7VnuX5^$hs*=MaNK+7zSbx`XYJT{Sre!zKT=)FstxNQrgc*lnYRDJC8aB+T3ZYS|o!3O9)X zoYE>Z*`!Dp2h0Y%0xREhq3yS9ca0p5J7FXf#Au~LSbx>uDBlXrxl*1e`W-9Gi&qk2 z8jIYjF$zFvKJrL+fFI@cT3iJLb%)upz#5NJ;!wJBYvqw_(1L-A_S|b^{SrWk;9D^n z5)g=)L@84=W+Orz9egT;oF(xA1f(V8WvtT|`P>+q{4mP{?1nOm{y;>p#sn|tu$wQvy}j#_fD z;-!>Y;j}`z;_He5wH|!Pkq$lbVTT{(s151UQqz{3H4m+{+PO=|Ini^s?!ELnXdsnF z8anc@;iHT?k!n+BnmY5e>9fpwQ=@QV{ZxLR8h<^g@g}tf+D#3gnmr_F9Vfb&ffy$O zaa#nC(7c#Mr<6D>bcCQ5&F&|;CvG97KjOv@a$%wS8|1=5 z_YJwv+`gdJ*RM^B5;mc=4OdX3VEc^%|Lpjz;g5HJ{l5LW4Zl0^E%X+83%!NjLT{nB z&=CJp2>(8m@E_OnqbJPmBG8lf2_S!4r79IsJBWy6s7@9{MI5yXMW_&Jg;pI*F8zWg z4M~cNqu^R_@ME#+;Nq;SgR3A2et@{SIVrkGiT_IqEn+-4?#H`(kGpq(zgA+Z*)|TS znq?&8F(I8<6@sq_q8FX$!?46meNGe;@El+F@bUF7%Co%B{W-dotjPeMKslq>}T*VPYZQ!g334{ zF*7(VVl`r9En+cYFkv}jFf?R2HIuClCnRQJIc8yDIb|(lW-&D_G&C|eEn+!jHZ5gl zV>vQ4Vm2~4F*cL<4nqw#H8C_bH!w9cHZeIlvq%pJ0wgnLGBPkXF)=MUIW;sbG%_|b zEn#C~G%aLfH!?S3G&Ny1H)OL!5~c|jRfexX00006P)t-s(^Xc8y?}hJ-e!{t965gn z5eXy;&NTl_0001^NklPq1vl)3;f~`0!0-}r-eB1#N*DwO( z0OhT1V*0(JUtjF+?;9Mo@c^j$CMZQV9Fn3g>y$80?|z{K delta 3495 zcmV;Y4OsHnD#tpI7=Hu<00013M{Ml?013)^R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?O0ou>#7d?*C@^q5Dx*v;knkG!5#i7f$cci$?o2d&y#e=4#8NcN-D`T{{O#8 z|KR1BE%^|WWD$dx&pz9NlVtX*tWP$6eXf^3e!};sak_r67=Lm&y8SiDG5;LrYk_?z zI19IhQagUA&p+hj4mQ0kblk|PMHVOhSSTM0IlF)0V}Y`_WhAF~-$EVx*w3A${ZIPg z;#&J!y7SX`8)m$2=lq|uCz^BY-4!ozsT`X+ zx3bPKvBKqUT_$)({0E%J_0D)F0(%?K>86>AcjL?!zMky4bK%Kj9UC{8=C&9*s1z{cyFr;R|Yy9E3i;B^?amo2>Ro7e3cCx6_yGiF-GVysjk{rDU2j|<>rJ97E|{UHSobL<&j%JJ4_5X z?y%t4j|Jc&+&jP+c0j-g2+zVMS{s?+#{_%|7E0E6TLA%wU^}d|-Z=p{$?CmvP2@G# z2D^umvwr|YaN;7MK}Z2BkHiN4M4!N+5?t^h1PL*ukV6qCUV?~3(d1X6i#~=ZF~$^g zEXgIGLXs3yqLfp~HajH8kR`{Qa?YjTrUfe(TwO4slxnN1zJ@9_)>LyX4fJWgg(fYw zL@l?{ZFe1cqDzlG_1w$g0V)kY!jKV18hMlns((#4{R~rPoN4A+F4UMdtUtWJj~bt- z*@cu&j2CLSG{$oY)^rjSGazO?1M#Q`fY4SkGl{*oq|hm5CR;>Bu&^>HIGq(SAm|6% zF!{pnf!q^r4fuq5B)ikwW(YxgT+RgIZU$X~xk)NW)N_Y8=836GkdF6&1B_ zAAjjrFZgevKVGPI4_#v4fv1lMB#ZAru(~ozY>d*O>-Xe+9lg$x^_$dL19++DWI?3b zYXw(l^$?QYX92N8XgSzgF}Fk;d>LbokphQSW@@!b!1XDEbai&{X!JOxOy}kZ6|2`M z12-2xqQfd4ENs(=UJH}i&MI>j6MLDXhkv1~nT9d46BojY0eTOZH4r6DH0-z*=Tf&g zUlFF)6wwPuaciU5Vpj-)XzPF+>HTK22ywCcGrUs+s9#%~GmArzblp3K9BwvdhfAlV zQc{l@ahL;_Ne;1txw+U{^5h&L!p9ys&Z3vEJxH9bYL3(mV3(cG4Kz4hJPE3Yf`6FQ z&_(cf2#?2KlXVDMwteeJUr!8UW<}pD|7;(?MSjs^`v81F&VIO|-rdluyYr{U8-M() zc5ePCeHt#Ny7=QbVOr@Cbc?860EYx_6*&*Ec$lWh02ny&2^ez%LkpSyKqfz#Btfs9 z%b*sJr+6Obz%<+IGDewI$)3hYFn=Z3EEP3ZVerWIhytrF=Z6l6GU5oBJ~*v6Hf~dz z<}&fY>hZ*CB`_KlWyTlmCZ+cqcBc?ZXuXu)nngk%(gSk0x-fCnSmSz&H8-DP4f*_v zwe)0_v0rhO-jX#7_gTBt{5ZS3m7*N1HMb+Z4*b%QAMU3g{r^4p59sFb4SzaqCFhn2 zI$L~qe;l_IRrz<^mQQgTkslB$4_5bQRs+9G7pv=?)kV|kiYwHpbMZz))DK*REDt`r z&}M5QBNr&T7*ZLq2sUrl8CsOzJJOpUX1rx-+b)LfOlxcnJMj+De07pd!~F63>1809>ief0P05w)sYVt=GIoRk&>xLhC0DBC~adHzJIsTE?HiwBFj8d z1=0*M@RcdD`etPcLEn#k1QZIWHXC~X$T6af$168); zFCj><6%NKdkeyJJAYP8}35Cl`CZOL=Aup+f-ly_!r4o1pj^^NEgpW!_<==aosK`}; zK9pN1ZVHNo<)yI0Ie)B{HZSUoMkwZ|preS*fKDCkLDS^iT{ezz(+6C)fO`oyI%(2F zQ2w4mzjIHhx7=f~N?f@IsYh0F)6~NV**-f7QYO>}3rRx<0S!Vj>S+-I=>UGo4fGC= z%&7(*nRArsl-dPOu&?+BnvM0Zeh^r&MAuIiit&6PiSbsTL#JwY&Z7$}Ad`N$o z7|q9poCXB166RhPJ<5<>6`h5M7DcNLp%o0Ms?Wd%Cst@9jx@M;Xd|pA8dOxdl6k}) z5)`KwzhwpurZ7DPy7@ZPaf8c!3tAPi7CIpz9_W}dLOjF#9^|*u>K5O(=*|)oeJY%O z8f~K}<0A^hj(;wH>NBjJvrXw0r6N3B!OF+!&BU)HfVKv_NPz7D5gMLqDs!u<-V{|d z4hNoAcgcWII*%dML%N-$i95)qAR7PoOrk%h;$|NGI`vNG0mHrmZ{bB3dk)ApdrpB~ z>^TJbVvj4;*NnB6@JL35U!ZHQl&jdh@|dQHD~qi(A%Bl7v5E%G5oV5&!H6xR)1Xg>a-6;14VccEtZW)dqSRHnC!0N86{k8XU0X(6Yev8|x zB*QgtXDm}(n#88|QG5DEP5!Ze)Tcb8AWHXw@}pn?eJThZO4^EAxqee9>353rcW$7c z`2m9Xg@1NDq=W7i5Gu(PtLW5b)atuxkO8%712itH8+MC(HTkN13l!2We@oxdA$dXh{m+pyKll z5e;__u;y%XNV}YM2J=+@VskMZ`d6UbzpZPZkALqPf=7iy?@E*#j8LtJ-W$b}mO37k z<@ZULK4;=~+;9K%SqZ&9EO~iW z0!tf^&b2VI(d6~$Hu3%G%`WTcXi6>;?VjXE8tsQzwkDJn$PW@t_n?QMoYS&?53^&xn#t888@G`g8UVz|Je2KO(6P2Kwr+d z*AVbgtUGqQVd@DUGR^R>+l1x*@~6cQHZmtPekuLy%jKcWapie=1+Vj8~V>wg{r zw%#RJmjAgwN3UA26d)iF&nkv#6K@btZ`uaqePUiIODgd>@t8>$Bz|PN?C~4plFJ;+ zVN?j~>u~IfQ;wj>|s_B%^dl7CAkR|N_=7Epr*$@PQ%!S8OZ;^c&v6iWi(7svS+ z0V2CVtKm4`$BxrF0fNuKmEQ5!n!wB_>5YySKLYx;fs5;oChq~4JHX(RA(@gZ1!;1{ zBJh4j-;@UiZh`2U*IRoZrw>4ux=P;w2Zz9DiL%#y-rdvP+rMYp{rv#@XbN(7DsC*? z000nMv$h1g0wg$LWnwZjGdL|{G-G2eG&V3|En+!1W-VqkG+{C|F=912WiXSK2OlIj zGdVG3F*Z3ZH!);nEi^G=H!Wg0GdL|UV>e zVJ$Q;Vq+~iWMnlhH8C?bFk>`gVPQBjlOqU24KXz{G%_(YIXE&gIXAO&2nYfsV>2^l zGh;F{En#LbVl6Z_FlH?{GGb#bH)A+CV`5@AHZx*0lb;GulOGCPBsF6&VK*@_V=Xjh zIXNvfGcz$QVPiKmEjTqZWH~uwHaTQ4IkTS%rU@UlA)Pn?000kAOjJbx0000000000 z0001py?`A1{$P_^6ghtd6cP##=rrvt0002wNkl^2tzAQf~=e*;{InE zKI!c2Zbq6-qwXND23&ew@A>(G%djDjypIUQu#87+0ay|FxB_elgw<^bgrxvCfvAv| z5LW3*1+8RL37A^8vsd^O!xM%8(1f~bJR!@(T?Gf;o+y!5#F7cKjKsdO8K_=(Tkp(=jajaE|2uy;AHf_$ VQJbE54FCWD07(Z$PDHLkV1oUHQ&a!| diff --git a/theme/hacker/icons/logorss.png b/theme/hacker/icons/logorss.png index a8eff40ddec082103de1484a7048c231599ee3e7..7ca5e7dbe7509feb756f7f3090d7c9d857538e59 100644 GIT binary patch literal 8875 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 delta 1492 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 O0000P2y4=;=KeZB^Ns;_~(yC>{x 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 delta 1620 zcmV-a2CMnKSJ@1Z7=Hl+0001xr{kRf00Q%RR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4Z6*g*i#xG3IA`PSPmgnI-G^sG zGN{xQfrNPoN!(XIGmv$X)ZJH{w$@$a9MFmIl?eoSdA+-j@mZi3k?&FXto~#4NqnxC zT|Q}`C@x z8O?Ai#9tDP?>=eb*eOJ?7mS$iirn<2{Si zq(~PBv&JKUSrUHaLOX8R@ftZIcfwL8IHL{w2QB>Fsa`o)y?!{~2BG?19;6sjd=#dXQ{3u6lD4&*^w%n|FE3J0!(y>kS+^u^ry$%WkJH<#t zM; z1W?etm_?_Qc#&JoVqts~%1BZdo6u%+fw@OJce^mg=i^mg=i^mg>W zaDRkQOhu@zX`lu>00004mX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ>8^J z4ptC#$WWauh>ALD6^c-y)C#RSm|Xe=O&XFE7e~Rh;NZt%)xpJCR|i)?5c~jfb8}L3 zkrMxx6k5c1aNLh~_a1le0HI!Hs@X9HsDGMeq~b9#lU)@9uMjYZK1?GhF;kyQCKK=+ zU-$6w^)AY@ywCkPx|N*C0G~)a%XGsc-XNaZv~yH6ykH@QG+f>{K$3LaarNK#aS&^S@WL!g`u3bvdndwLw|^4 z5lcuygp4XmD8oXOR*e)BDcX;D_y-(+l3X&mN?_zzKm{r!#}EDozq>W_6XR}DCV7P z5Jd-3P|~KbmW!;s*K%R)68Tywuu}LML@835R3;MIpFgljGkQY9XXb;5E|Zwv7&bG_ z3|E*|h03%sd}BfkIh+Zv2lgrO} zHXxs6a*@YMAiNW*m&qFy@_#aS*n`ZIK?TlonFO+-$z^7b3#t@tHK0hjXorNX;E<*2 z0G7}c3}MsRgs^njlK2iw64xQng{K0?;iM|W9WYMn3lGR-^4JL-iq;mYH?7UQuc?Oz zm|vQJHZE1Lks6MJ(}y@92kjEsl#_O;{hqWZRNv z*wkV7w*k$4#sT&xb|-ezXOkx)`s@2=T06K$w~y7gQe^y$zXJ~YhF@a^%KQWQI?noV SeW8;80000 Date: Sat, 17 Jul 2021 12:25:08 +0100 Subject: [PATCH 002/459] Hacker theme icons --- theme/hacker/icons/favicon.ico | Bin 1150 -> 1150 bytes theme/hacker/icons/prev.png | Bin 1440 -> 7165 bytes theme/hacker/icons/showhide.png | Bin 1439 -> 7166 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/favicon.ico b/theme/hacker/icons/favicon.ico index e0436576125749b090b80164ed8973d5f8f5e767..612635e5a0fe7c4f91eb8420c5936128861606bf 100644 GIT binary patch delta 55 zcmeyz@sDG}Oh(p<5LbKViG~uB^O$%ht1(+l{>9kM_-wKtqY6-jWAZyD1t`t4`5Ds; FMgSV|6Al0X delta 57 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 Pa4<((S4x>p@UDLWd2KD! delta 1352 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 JPDHLkV1ix8inRa$ From fee677be8dbd579870c613bfde2a4a7dd3b61a44 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 12:30:54 +0100 Subject: [PATCH 003/459] Links column header background --- theme/hacker/theme.json | 1 + 1 file changed, 1 insertion(+) diff --git a/theme/hacker/theme.json b/theme/hacker/theme.json index 14f6161c0..244b494ae 100644 --- a/theme/hacker/theme.json +++ b/theme/hacker/theme.json @@ -1,4 +1,5 @@ { + "column-left-header-background": "#035103", "font-size-header": "12px", "font-size-header-mobile": "20px", "font-size-button-mobile": "20px", From 96ed38b54d69519fc7514dda333979b3957006a7 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 12:48:17 +0100 Subject: [PATCH 004/459] Hacker theme icons --- theme/hacker/icons/pagedown.png | Bin 1505 -> 6354 bytes theme/hacker/icons/pageup.png | Bin 1498 -> 6414 bytes theme/hacker/icons/person.png | Bin 9840 -> 8218 bytes theme/hacker/icons/repeat.png | Bin 5639 -> 9288 bytes theme/hacker/icons/repeat_inactive.png | Bin 1458 -> 7597 bytes theme/hacker/icons/reply.png | Bin 1410 -> 6756 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/pagedown.png b/theme/hacker/icons/pagedown.png index 4eb89a48f16cadfb9f7ee63a44d1d7611cdf9ce1..8db755caea6ba7c653b93eaef97cac95e587430d 100644 GIT binary patch literal 6354 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 E-_zci8UO$Q delta 1382 zcmV-s1)2KNG2si47=Hl+0001xr{kRf00S3#R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Uq}T0Z9mnvf?XMeN7Gkv|Lx-3DSmit14$d@LUquVXnEw~t5Yj}!Qj_(=x6IXt!^^2|d>;_m(SQr1mU_gHb;+IQxw7b!cw zF#s7{fam4Jc7McYG+$!#7=LnwuN>>7!wc@`?4&{XZsAiJ||%c1?-J!khh$9=k*GLD?jp&lopuu;x_tt8m5Wi-RBnA>gz%~385 zIo4x~7h0o248)TXb*eN2jXi19;A7&!0L;t{)R)Y`qJMA$ev-5rO*Sdg#eu&eRRmI= zGaO%N+b!E(BZuWq*vbTDv~%V^TKLMQU$r?`EfIxNGjs#yRfce5ky|~sfe6hLRNVo7 zwAX|@U#h*DYe38h31+=#r_J( z#g|;^(kowf`BkpkkUlLnZMj+VR$A@crK65}?tj+3mtF@Aq|!)3!^p#ik22~+u1%R~ z>de!o&ob+i+M)Vg`wePzsPQJX_Susfq-OURcD_K#Vg_QI2*hm>Ktl6k7M)V!MQ$;R zg;5pCa8ehWPO(zv6eJB|o%Ce)jofc>3pxFg8-JBtSm^$c+~yXBe&F^4YJL4|w@vJ> z!ZH=pD5$1q~8j=(jN5Qq=;KyRs!Nplu2UkH5 z`~Y!tb5eAX691PJTEuv8+>dwn9(V5mpe3JSuIyt^Pc>Lp`5m| z%ypVWh+`2;NJ4~+DoQBBLX=jG6cZ`hk9qh99DkBrGPz1%lN54hX` z`k!>kkQ~WRQ^@Cm_cQvYEYN!kbgj9)HTQA)0Hmp_#0_w82#gdcd)?#R!S>$%J=5&( z2Zu*;uMnQL6#xJL22e~?MF0Q*h`oUJ1CpR3lhzkG2?84y7AMxcd=Zlv7$|>yNkl1Wk1sfql{;`J5sfjh9-zNmtfJJqJV+~l8@5{=zqG6C|TTwIcw9PLDo9x>^*4A1- o4MFUJ&;-5;Tv?BmKmV=^?oJY$!$MD}g8%>k07*qoM6N<$g4LIytN;K2 diff --git a/theme/hacker/icons/pageup.png b/theme/hacker/icons/pageup.png index 06140dfdc79bc634004bcd4d17aedd945bf24eea..9dbc17214538f03da3c1d71e4829040437ab42ff 100644 GIT binary patch literal 6414 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 L+)0&oJCpwbXFI^J delta 1375 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 b_y5?}n7A}y`k}sMx3&y)Oti~2&xigO24ZEn delta 5628 zcmVQNh~?NPPMu7*zYOl=g;bH+mm+Q(!b5?g^=*l z5C-zjO>h0Nf9G=s{`{%@2)`ZA+*h10<~&?8!69$UyN}25>Yz6#-)H8d_aA2;obT#& z-GA_9ynMvTmwz{`eC_=0;+KK_V@B@%My}R>TF&j{HS${XTwM(jkH#yb4s&~Fdw&hw z9{heS@=5#%=V^ZmpM2D#6Y_ENqw(psSjW)I*kFgPcG%_?VpdY(j^2HCI308O0c+X|`n2X)xmOWm#nURO(QGaJKW;Nu0`k3!-{^n!W>gpnU z27(upaF+(ZoJ;%kMaQo)eB| z30afIWdIj3wlEp%iNF!97aQ%7oJ-c?$4r1KGj~3?Mg**g8%)v#@1rxxWv65FPO|%1 zCoVd@41YEe2=UP;VpDu2SRn}clR_qks(1+^5+zACQlv&7LzEa}iVAWy`4p0*m{Q8A zq?&yWS#r!N=Uj3vu6Qs|N-U}5QcA7PR0gWpsytu0(p>W`G-9MDt zd+Bw!;Ri4=;z%QpGU~Kuf)q1MnQ^9>XPI@8Yk$kHuw=!RR$gV*N7SyUe){|(YW9kn z+mX`e(j#hIH7VUDtip+C%!rr^mWU^f0153hW=FOmIE|df>?n?+5XU4%V`GNZh!Mgt zt&?RRv3o}DM{zTfe-t#9CcU%2T%j2Fnf;-lbG&&YT zJ_ER{39B$7UZ&#W#D)C1CjQe?naRtXc7h8B?{ycb8_`*W#)MX}02b?*%MjmYb$>#- z2tV5_-i=ypQL>R%hsLEW8AvdqNb!aU)==4F5G(?Ay3Vw5K;ZiiY~0S+ney2kk2HDl z1$ZGalwvmE6GQH^4!{ZEDSeCrQg+v-mF?p82NyrBn$zseLtl!xC;IJ!;#fTdk!l!N z^Gm#U08H^HoG7Cai>fps%%g{3YJVKvj*=pzIZ0bZdsnHEnspKZD8g(^8pE%y%PclX zOh&6KMX(Nk=z|B<6}ELyZQCIoAC@St#|Y+&TjwD5!w|wnsZ&CBHiU#nK^3GicPFTjilX39Zmf`}@AE2s$t#-t6|gtW&a>t^Kuyu* zbMd@0or8aEjk7A~__EM|Yk%USAVZo=$~;ASrG*o+Y)UArf$V=pJ`B2yAv6T_i?R$_ zzIBgKE1nOdL*vXwG{S6?S%Ug#tuWNt3AlkHdUKEx_%c>f7;dW$Z3J`R zO^}GO0*=$W?Mu~Cl;Vxno4BNk4BfJDg(?}K7Y6Dbxpv-+=+OQ zA4T~H=aGwKc9{@lQB2lwv+mX4F*zXb3;jX|I=Hl{C7~JfW>J;)3@T{Y!CZPpE3wHgP6NS_-!_6a{z9PD6 z$lX%c(caiji}MFTXMcd+J*pMtGy#$7C^AwP-L~Mf7-;UiUJnVe(ZNMhXv9&#D_NV8UQRL&zx3J0*3ca@+~Sb zNf-~tyVzx}5F?mv)1W?9=rgf$ztsWh^6D=hdHSo)+h6wf7k?_(P@t4Wn22~cY0*;X zD>*7Wi>S6n0DFdBRC1S1pswA^*<_bxuf~M98MK{?8OX3}F;iQ73wW#%RHnJO4K!kn zUM|S5gy2TXw>lYyr6D6?Z6!on4j<`bN=YDDn&@{e))Uak5QVhe<`4*FQLW~6UpMbJ zKn>VcQCt-xD1VMdM1DkN3J)v*j!)}abr0)im6M_`^DnpF?!24N>9VdNcxRq) z=OaoippwZuIri8^322WAo`PCYv2(c&-f5b*0cN4Nm4Bh8Fy?xBH=CZGFAvXH*UMY< z>&x_BE6oCk=1GmfAJ}a`B;}B$qnx}`9}OY%$f@C2*#JtXK#=ZQgFjNI2R_&Kr{i9; z!K>J!?>Lma9efZs_j2;!1^D7CX2|>u(f(O%Hz7obhYR!^Q^eqMbGTGgQ9k8r~sYCuDA3KiDW z&SffnkU6Swt|*nPQuUNHqo|58hou$hJ*uF^|H?8UFIECXpnDU#wU^VaLeNx(!-Lw2 zQT)-B0X9SV!NfDIoZSz58v`PHx{{Gfq{hCR1AlEb6hRp6&YlbP+>0sGCIFWKHQ;<*3jrf#8I?Y#5RFFv>l2{Z-pQ>A~tAJ5NVcH zlEDZ9Alt^7Y2ipiOUFD@3(2Xh0A#N%Gf-shEu&(3DvkNfryzkQ$kar0R$6lbi5e?W z34d1AOfMW11o8PKpX|)gXtg*k!8D;739+ej3eppH z@S$9wGXsX_jXA07DRPG#6S2E+5tAcxrl3(ypzWTMlT>eF>9H%$(zV z(>~DGqOFJRq${gL9jM-*q@t@Takck_stw}3UW);Tj>l5)7?Y~V=5m3y?S?4z__~X(X{$o?}>F3@5j+=(;CEo)L>z@=WxRyxZ@MXKYtuA zE%|l5yC(uMia}9%hW2y?Oo=K3kdWyRFvVYVwFrem3q*bEx{Vh1 z;mVOaEyhPcKK&tHwQxSwEm_taF3QYy4&CWP-#{xScQ=kzai45Cb%*m_JCY^2AB4bs z9fRM8z4zC)@2jU{JoWyCH;(+#Eh)wv=-1h+CxZK??AWX*bBvf_$CR7&E*Rzdi zB+o@qW?I>--Bdz$SRuc9r;2ghAExPNY;oN<+l@rXund45B zxV2^e)14Kz$UsAW+;#Bu34cxCmUYJCy85?)%(o+HD@v(vQIRHswC9&mbc}NZj2NCe zZEOpwJRa-#c>Ue>2g~Mn3;6T=%?}r_B~{Z7#EgM&z9Z$8t!IJXMMGES2Jh++|8-5*{B?jm7|BdbrXFE2!Es^!nFotH+VbE zs70H5bt*@bzSeM4$0W+H1VOlB5>W|4uh1;ja^h8Xh`ju!lFi0RdPFx>Xu=r?j!Pix z&X#UQT>@E1e67X3)zikR62^29(V$$t>4=qvpqW|Ljs+_{C7jjbr#gCIrJ*15&_01> z)zkq*sy*o@6#afT(0?naPctJZPx^kuHOmB&Zq!BChxv){z(){CHv=vmY`zx4z0jBv z6$_e{b8`XQNa*jnv__jY=IE7Y3uXS&XFQG_&DjtQM1=e!grG()Ti=jMr5eqq!*56f+HIQ<%Bp*tzpgz- zF1o{hZqvv03hOxcXOsXo!ln|6aXG-84N)QrqcK*Ri4;-DUy=36#j-@Ui0H9Y6%ivZ zgOb=Pg34AEp?}|FlyQ+6%grCVGoQ#snw7m>q_yYIw!&m+Kc0QXu7EU7L0se#vSs0| z1N`Z&l#Rn{iB@FPrnQG&f?h^Lj_m=g(nr2-C8Qs3B_y=2`LGo-kM-;Age?cnKiv!Y zxD{gl>0Zdktq}9`$$vk0^Yh7nKX>!<$zQfTuuChQyMGF#soQ_l@9L(%@1Fq@e^Nl- z3ZP+JnYzC&E>!x*rgCUT?Yvy>fos%}Z0t|^bp{iIj#!KZtha`NjJAe3yX-n|5p&!k zri`ugD?}D1Xymj8D_x_dgALlS2Fm@hB@j)_y3MlNV*M`dy)AaHZL!>KaeUckp``}= z;{FcuqJN-NX7B{kQpI7Lx*gZ&j@+CK3qZ7{a+F@u znX0JS1|R{Sf&3_aNBl}p)*;XX?{UNc_;WdSwtsSe$MK{YbNrN#{&M_e$kItYDC4+E zL;nxG7uvvWA34Lw%tPh#?LeQlhENCI3gVM~!07|*BzyWXJbVvIo03$6BdJgW+MSB3 zI&zu+bYDj3O`CQw7w=#_eDih2fBwHj{C_p1dqU=a0eISTKb_FJ{Qv*~g=s@WP)S2W zAb)UjZ)Rz1WdHzpoPCi!NW(xJ#a~-XMJo<1qT-ODI$01Eanvdlp+cw?T6HkF^h0RU zkfgXc3a$kQKNhPFF3!3-xC(;c2Z)oSlcI~1_`jskBF2N`e!RQ)xO)fq>t&{z9blb$1v5(~vPmfM(>43&72IHsr? z<@>WPE1b7DtK}+d-jlyDoY$6@xK48jNi1LyA_T~&VgqGZh|{W(Vj@lZ2@n6E<9`>) zC6j9dj2!c*K!xP^!T;cQw`O5t+)WB6fZ&U5e+&bmU7%63?eAmTZkzytXW&X}`73o` z`jhlpON$)=z1zUWbxV`?fXf}A|4Ek&$&msy{e=SXen#Jv10uIT*Q(oFb04P|C#yzf6ILT>;ei33JMAe3JMA<6U^@ywzAu`tChQt z0)Q~Yz=|V)o4f>|005Y=9u|+EJJZSpXnrQ=E4=vM;Q^pSKg~^EXk`LYK0IHLz|mU< zuF5=svacwb$Q?wQJL7r3?1qFPdYX!^py}yV2yv4aFi#&L#Wn=8 zY|$5@Yx1ptgtgv2wX z$|++ze&^vOb(^)4DC2j*m%tVj5XR?S3~nu zLyBdVn9-vpF)Z1Q@uxJA5KjK%2GpIPx@6-PMfEHO7VZmQjMTP2bI@_&!^Oxbv3_u;>H7 zp+AO87n>)*qVd=>f8vTY>)hh86F$uq1N8!;Yx?uJ37~cQgG=i7>vFq~!dDL53~3T0 zaT0KU=j@VRt32|vryp#c3ZHKRm|1-_w(zgg(6{n83LM&mUQg@!c%&Er(1&PVj&E${ zqP5F>^xP*t_T==pow672`y${bLDB~af{t`(C!a?ZDf}ROR=xx9A2;2Lwc4+ZpWkKt zy=oYL)*>MwX~>V~24#jFYR3Ej8}RYm$K2$^$2JM9R2bvAZBS59P*6}%P*70tjDG=N Wp-bYe2Ri5g0000gVFfP9|eI3UqF0^2mh`U%zbaJup4u zb+>bGyh&K;%*b&&%{;le4;^pMRGfMGw*95rr;i2R*&<@QRyo#vQT&{9VD`6~=5T7o zyDFzk`*Q1L^BZClSkf<(FWFLieD|HJSbdc))3X~|N@1QQSFbxaJLlhyAO1jmgDJ+} zZKowSe0>D=Ek(_j-Q?N1jXd%w7rpYvrjxEK+}I&T(BRRhrqfiLHbh&POyHm9yS#QY z+&SCxm9F5g_i-h5_sNOw@%G5!?oM~&jceHf#vzdtA<>O0}q{i=qsw1Z=f%s_W-@`!Z?Qz9{;Ao+Odf#J8+ zJ!^*F;IKKhF<#bL5OVzJGv#t0h|=M8Gp^G!VPECyc6g*-$XqX*5#(v7Q227^uuZVi zL$dlv4LRB3k+hjr{~5Jqo*riVt`c&1NKJ_OQMvgJ4=trBLbPQ316^BYR;B9A1nc>m zTcgLkTIoR|%3gzm zmJ+D^)VyRkXBO2~L_T78nktl%6(OZvn8vwTsJNL?pN&!*Cm(@bX3uO$^Kj`{`HHw} zbkeWLxzClWb6al^_ZC*s@6wfx_2SpvdA!g=aWr0D{qRbwjA~?@oQ=}^3MD@69^&lT zoyoEnPcyYouW`6I)CWn~U^^IhgMKAruV|up8A#<}{B54bc#`~Y3AcIZcqzTq<2MYF z+sIdPr>LPl%(^ZPBA9sRGpz1(D`y*F!O_DZVM2AwLSNvzOZPM$xd!xAq{f}o+P*Vx zo#t$Y{V&IRBR{z|?H}^4?~w@dHI2HFx8k((r>G~KC^s9{fFgXChDQXecb<1 z!sd}#f3;noC;5>dV~kAKdVhZQNz#%(rOArrvlTfGKGSP%#GSH$StdUVSa?H^4o2TN zg1?=v7lD@_&QRX!KAu}$pT9{`mgQFl)hG3COC?}_z5435I%rZM)5XK$jGok3d|Su- zj@so!vY&_^Jk`S;H~0Ktom=!y?#&Hx-V)1@lD7&7DGAcgp+3!#+@c`Oxj84Z#OuMu z90_+Eb(P~?c+#}E7%217(Mkrc?!Lq$)Oqsh?p_+%e$})5`S@tZ&jp5Y+z?3DyedJ< zBpKQnlXA=a-k2~>7veDwo{BwWGH2=HyRXEuVYFwH?$&GDn@+Y@4O=IKkj8SR^D1G& zw>uW9C1;s0@LF3cs>Bsn360IO4{P_2^otMm+SfMaX_x#m8YPc;B;c9h<_(Wj{k%$MpNy|=6bu`xg_!A~#y-ogbb!{h{O?J8)Jv%DqQ zkBICGahGGTXL_@$pSP4`Scq(r3>;dEcwm)gd~P$SCVcht$?Gk@?RsTs7jZAz-<{~k zKb*Pt4Y5?(X@zYdzJ7aNJ8)|1^b7b{go;$;u*kotq-e=_#3NQP#n<-IXVRpDgZtJgT^766!Z`&%vAD}JJ1{H()Y0EbU@iotL zX-5aPimx>HtLHLb6nmJJun;t6S2b>+>T$4A=&nbSK(Bz@x=>n6_aUcqLs9+Iqv_2Dk8gc)BB9*SV`SllTX($(-U<3X zd!zwkq!Kp1dG6*+Zda<b&MX>uZPy06egcgTH5AmNI zJT!!}1RG@WX1b?kthUD_9NX>cZ(`^Rac~z32^b}+%${;&1j`}r94DEnLNw{~xhi>> z^Cru};MdleTNWU%oV_P6UvVtBY!wAOmnDD9M!Nq_!eFhuB4LD_2s z%}M!^2EN^z6PfPGUDo~YmMc`9h;GST;qGvV|MCWMYdZU`o$as#{Ccx}4(_T4$0u59 z{POw>Tg*b&%WTNX7kcp|DOR^vQq@-erb}DWfUu5m3*WzX!lR3>iR==o@-!3EKQ&2v0YJM;$5%c*blCU(P~D?$TP);i#@H zarNn~1vq8Kyl@b)+`V{jqfvnLfxfb_%9N53w==K33f<6MoC87AteQ=+!%n_0C#N^w zI9Ka_n0C@ZBH`MbX>oAIhs$oCnf&6$i`x(EC@y}zr*E87acXB(dZO0-4W^dQ2)AYw z-!wBcoE^^^%3~tNHaLHrf)}t&64-oV}5 zlxc6gitIIZpmWl8WeZiltg~`4IOz5fuBJUWn#%jIFHcvxd2}{Q{ z5s*3CPkt6XG`UY#g&g|mdc80j>9IzhTTv%g5nNe2%=T4Mj_AdFnlF6W;d`x7?y(jW zWTNyHD++-!0`iy4V{Uv z-8@or!J+<#BraEG%j-$yc7hZOSH9ft)ZCUH86?x3?iR59(WXkeN;+7yNFv%~2M3=) z+R2NYa9@b7NqK^KCYpY>GjpFWc7iI|lDxO`pGLJPl3CbgZ^luo}h zDcmjAw0hFqvbBgIv$DFUe*OMae&;n*9x1;2Rm>(kYu!heh+Rk5UAxEN>`I?!glDJU zvk4BE%-KEKGUAx+GMdS))PD4$&h_l{dC&dl%{*^pRyo^!KrCd4dK!{B$4=&_o0H9(C+K@X^A>QA2C~Yr`n(aL}&6 zA`nO@j%jJ>gtxT({q-7nsm|Je%-FWoOy!0*vEcklxvlXAxFUyDR#qyNQdUKfJqO*^ z65yX>G-dEd67vRku%{_ylY_&TF2-JjN<>Cg*BENo+9rIplZKIv3|_U@?K@f*66FL# z-`=AvuKQSTMOjhCw$dZf?xH=3@MR>ArXG!dDM@J{kX_@_5fpa+IS+ z4($+g1q!*pK>59v>s-J?I$Hkzfv|6m=+_DvTo39qDL?2d??iI36bVq?%U`0 zVq{jx*2R5Bd2kT2ro(x@w!Loaj(r~&2zgEG5+88NRPGl>fktTJWE#pQQT%8;D4E7!1{$lsu4__Dz%d`#W(1_s< zu9R>;iXm0q)I`cC6bldp(0C+hXn=np7aMAt%xM6`jQYb8tyIw%?jRQyHQaDUDkI4#z3OGq*RuIovT^*Q*ekTWJDORC?@p}Nl zc#)pVqayIYKo?jK06?ITNCOxW4MQ6umihy;_Vz!#1G(Q-1oT9NlGq3o9Ek`B_{oFI zvkLwr-k*AKU4dIC!iC0V1#u`et6*9nZ~fAsZ2utc(wIS9n!vP}w?CDR0Fqjaywt}A zZ}0TOM<64E8Ngoj5TKVLsgxf$b`ZyZ5ksXQX#TVSAQ3LWjQR=BW77ZFpr6(wnEAIv zfN($fe?tGR*J3ViAIxXRG6NgE)}LrC!=5n zdSrbV8G|;U>Z8b1v<_+s6+V#5BLz}u0xEzU&IEWUNEDJz)5E~1GzuA}i$T#~22>qA z7?O@8Bk33nO4oqCgu;=-1UiA_zcecW6&0Y;F+kGEIur_wZlG@n(>2s5!$=gAA&gAL zV04kXG&&inzepw62dudh-dG(CNB&`P@+a}=EKY#2I)ND&6#B=6D>Hyb!QA`ZjsN8#^C}*F5pBV;b`=tJC%a91{g^|u`vTk3>tzR$XJ{ZYzr1x z3?M8?&>H~rq8(TZ){;Xb@mL&J7R%pQUBC?$P%gF<)aZM&U0G34gCO?-HOrtJt0iffzDT*&C zkU;}Z+wZORyPf$bWo&>!AxVZLBn*YrN5XWGIyx`|8c=|`bh^F{ibTbr^eI2Hb6Ip= z2#G^8X8<(->@(0>i~9`KTC6_ppV1+{G@wQ{BT-l+QvKWZLX8lD1NHai83~RYdwc9s z0gMDk5*9D0JYo==?a!ofeir8ULit~COZ>kV<$rQt3j5}5$zq2AP3y~Z3<>mRxPmI8kZ{7-fLzsV)_#|@hn2)yWp0QcuMqmRpgySJnh!PRQ< z77mJUi0U~t4Fd*3JQoKd$ZV`1_*W!Ih-_zr1I2^a+6v%oTTdT!TYXneJ+_)?0(|2vfi|0k;vLy2k+ctM@2iDHD1_) l*&tdYGUeHU^`9?=i+qTBzInLmt2>Y)2yf+pyKLbT^Dno?^g93m delta 1370 zcmV-g1*Q6}JF*Lq7=Hl+0001xr{kRf00Q%RR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4Z6*g*i#xG3IA`PSPmgnI-G^sG zGN{xQfrNPoN!(XIGmv$X)ZJH{w$@!^3FuVfl?eoSdA+-j@mZi3k?&FXto~#4NqnxC zT|Q}`C@x z8O?Ai#9tDP?>=eb*eOJ?7mS$iirn<2{Si zq(~PBv&JKcz|4vr&5N6Ef$w(Vk5Bc3QUyWXGCN+d#^ZExD1FqHY|espivIkBC|JK7KqACeBtrrM zF_S1|ipFe2(9r>?B6F6+2MCZVcO=P41pAS(^NNjUjP_bq&b-Xnn*c&3i-Ap%3Ro#A z>c^6!hJUJxCRNR9>OqUvEIDP(Ia^+vTs5&|YT3-(id7d+uAbf8y?8BL1bd*CT&#F0 zrB)6Vfhv4etgo;fe8`awJ@R3PALXbG<1q~8j=(j zN5Qq=;KyRs!Nplu2UkH5`~Y!tb5eAX691PJTEuv8+>dwn9(V5mpe3JSuIyt^Pc>Lp`5m|%ypVWh+`2;NJ4~+DoQBBLX=jG6cZ`hk9qh99DkBrGPz1% zlN54hX``k!>kkQ~WRQ^@Cm_cQvYEYN!kbgj9)HTQA)0Hmp_#0_w8 z2#gdcd)?#R!S>$%J=5&(2Zu*;uMnQL6#xJL22e~?MF0Q*h`oUJ1CpR3le`=`e*zm8 z7c!!Hsk;CG0G3HaK~yNuwb8)|1R)Fs(1@@eEy3-?mbL>e%~CAEqcAdmGf|f;a~@9* zl<mF8#-D_J!R)?<=le@JK=I6#Z@{_0USvVaR|a1L%pf^JU@uF=dOsaj3nJO1Ib c@qX}+zZVFxhU^5XOaK4?07*qoM6N<$g8uuFSpWb4 diff --git a/theme/hacker/icons/reply.png b/theme/hacker/icons/reply.png index 1cc0ac4570db2fd7b9f2f3af666ca1b827dceb32..8946f6eb647e1425de0ff906bcde2844a2910977 100644 GIT binary patch literal 6756 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 From 4c7a8e56683fae30ae76e31af918dbc3b2ca8bd1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 12:56:00 +0100 Subject: [PATCH 005/459] Hacker theme icons --- theme/hacker/icons/mute.png | Bin 1446 -> 7320 bytes theme/hacker/icons/unmute.png | Bin 966 -> 6739 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/mute.png b/theme/hacker/icons/mute.png index cce158ecf5f7909c56c819dd293b99e27bdf59c3..ad9da3a655714ff6a84eba83ad477f25e0f647f4 100644 GIT binary patch literal 7320 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 Q7%Ifp%E7YKd{^{;11#~1_W%F@ delta 1358 zcmV-U1+n^=Ii?Gc7=Hl+0001xr{kRf00Q%RR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4Z6*g*i#xG3IA`PSPmgnI-G^sG zGN{xQfrNPoN!(XIGmv$X)ZJH{w$@!^3g}escZDD?uXpz`J`40B@;wTl)qjjWiO==2 z%O@@Kr4a^SPJfVoE&i7HDX=^;;@1gFx2rEip55mh=iAkkp=iD;Du!E~t^VwgJ;{D7 zqZv-c*mf&u9&2ns-eZdgTBAY?Dl<@`PL(E&-8X7TG4Wt9Vde%P%jRHIxPcf+T8$=~ z6zSq%)_4RFnE8BD^^`Rxq5bU_u{p15$u6laDVTE?$*7RUI&GNonoY+ zBM%!s%BT~yHf5%%Gf$g7%d8J-7uE0DFHob48h>w6Yn?r)!5YkN7qqq$UCcm?6M?ub z0w`!+%%W3DyvQwPu`oUgWhAMKP3W|U0bv@%I_bgggWOMX3+g|`jsJ;USm^!>aslYR z=k^7)zJBN0CU$;>OVcR$`bI%`4vxnf{uno2*YDf!_2J(-csqJKdOLbMdOLbMdOP}G zI1a+U4<-Bshrpj2=DYzolVb`Ze?=+|RuFZ_P@OD@iaKf)icq1{3avVrT>1q~8j=(j zN5Qq=;KyRs!Nplu2UkH5`~Y!tb5eAX691PJTEuv8+>dwn9(V5mpe3JSuIyt^Pc>Lp`5m|%ypVWh+`2;NJ4~+DoQBBLX=jG6cZ`hk9qh99DkBrGPz1% zlN54hX``k!>kkQ~WRQ^@Cm_cQvYEYN!kbgj9)HTQA)0Hmp_#0_w8 z2#gdcd)?#R!S>$%J=5&(2Zu*;uMnQL6#xJL22e~?MF0Q*h`oUJ1CpR3lb;(oe*zm8 z6*u4}Kehk>0E$UOK~yNuz0kc8!ypU=U~v>G8G%tW8W}C4FoJw(T==ZmMtaHNI-%h^ zex*nBpV56z>d1i-r~wJIfH_hi2Z}8{osU<0Y3|2{9pFxUVl4y9R-YGim&ZVBG$6a9 z0-K|(4%T@EXK+i5rkCR{{C?GgEDBNcR~LNi6up1akXvHNkaf-($^T}&0rzu{ja**L QPXGV_07*qoM6N<$g10Y%=Kufz diff --git a/theme/hacker/icons/unmute.png b/theme/hacker/icons/unmute.png index 90414d7eaf961db3e2f4b850c04f360363359dc9..ee5fddf02f17ab3c42a4bdc4936880f39da266ba 100644 GIT binary patch literal 6739 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 delta 904 zcmV;319$w>G{y&z7=Hl+0001xr{kRf00QHBR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?UzfE+#n2w_naa}uq1@UaWI&w+#tu#4`$qt=}gt6vdE%MHDDnm&?oRrIDY-? z;Rhbd!Xc`8$|-GlVvd;$I=-FH+0qtgJ)gLS@ctkV*8oE#Xn*B=O~=wNkn3%NtqHV0 z%7fKq$Csfs@L^DfXGUk~`7$JY%4xlayylg7x1IItmFKYT=g{+eGz6n5>>);c2+4-p zhZn&LBuhcfsPV=_lF-*B(busHSNAD+SMMoyTUl~>3`N+|Qb_HLXj54Ps6vI!^w^=fZV~Z8U8ffk)HBcr- zv^zy=RH)N2+e#HnjC`=6Wv(n!=3<<=PAG8{>TJN%f;ebYP!ovxS_@6TdHSp5@Z1QR zOfWWxek$RW@NddxsP~b&M4Ml+!n%0mG?p>QjT%}2LVtb5RA+o=33qw3-GLPl)EQ>S z2FtdeCU&J;Zpq{<(9Y5@qvK{24g0RpTT75QV)kwZmAV^Gzkrmk5_k`$Aslq`)q zs+d?bwSQ!0ZrO^9M^{g7?w-A5&VnmUbIKO7=bTG{(*n~4vkNpz*?E^e?z*SlcHi?} zD$=LMsx?)sUUMytn=~B%mYRj;Ew^&efmAx;p+`FG@FO3kBiFk0*tMr_-FxokqPD0$ zQllqwUsIz+Z4ir7&u-KpHv2F^<2up748%ARh=1E4fQ073Y;%f{2f4v)%Zx*z3~HnU zPNP8#1mi)hoi27aa$j-_8o$Mj|3oefbpL`}80Z$c&)i;6Yh`{>WC9weaKvD4su{Z5=#OvAH{0;H6W@m3hTewWhTewWhTew$OGEfuy*K>-7=8otjxkG3zvaLH z01yBMP)t-s0000e*C=T9Rkf3#88~d?3;_@{1@ Date: Sat, 17 Jul 2021 13:06:40 +0100 Subject: [PATCH 006/459] Hacker theme icons --- theme/hacker/icons/delete.png | Bin 1435 -> 6904 bytes theme/hacker/icons/scope_followers.png | Bin 1431 -> 7282 bytes theme/hacker/icons/scope_unlisted.png | Bin 1435 -> 7093 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/delete.png b/theme/hacker/icons/delete.png index 5ac4a3fb7c7119b1d03a4dc4711d672f775edd5e..01c4e876deda87edb95cf9fe613fde2cab0dbac7 100644 GIT binary patch literal 6904 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 FV1jhPefaG!xI7# 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 Ppi+npfoPd$?h*PwenVj* delta 1372 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 WmiEKU*k+(&h^wQ!!)g1Fxc>nR|LD2^ delta 1376 zcmV-m1)uu0H=7HP7=Hl+0001xr{kRf00QxPR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4Z6*g*i#xG3IOm;~o!z<*&xT}B zsVf2r^AM7_uYP7A>m;eWuQ+Y3yT&=7OH!^(Ajr$>-F=MD0=$Fza@SOERT%%b;8o^>I;!)_c_P;b~R-vny-r5V$~8> zI2B{tt)O|Vu>pCHEgoo%3Nfh6K#4k4nlyIbs3FC~gTaKE8$d0agHhoIVgxv7G})v` z7YDP(BZ$Dvk6dWSEjwN#N90ae$^>V$VgI0oznlD(7Jue!OCpN?&tJk+JiNjc1JZT2{`y%-EX%LM4lVO_2&%DJkm5 zlB0&Iihm|m&1&jFi`FbTWz9KTUYlGsv1Dr5%-o7q7f-I9-Q2x+EnEb9pq5;$cqye; z4i$kad{wNkupE5Ikq$lbVTT{(s14=QQqz{3HE*TW&RsgTiJrT4@1@s4VPK~iY3Rtq zhL1ApM6FGkY3j_=rq43#gW5&)yY>sz=%U7()PGuM4{ERmv)cu&?L-$d5aUE3Zi@g4 znisR^loBs;i&-p;k3tzq>S7Z*En+~J2C+_hu=^nQQ{003PjTaaA{Q3A|AJfqy6?Gt zL9MUfxweU&U*Xa;3ckKk5T1kMv4%g!jo0=2_IrK!w+`Np-j3dm-j3dm-j3dm{uhq$ z?|(xHe*t%{o>mJ^$^rlY0fcEoLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~mUMJf(f z5Ov5CmrBZ{g~zL0TQ;k?CJEmv9dp8SQOoVK#eb(%wnV}B7# zNJ4~+DoQBBLX=jG6cZ`hk9qh99DkBrGPz1%$%J=5&(2Zu*;uMnQL z6#xJL22e~?MF0Q*h`oUJlAt1!?ix9N0vi?@F^=7aOaK4?fk{L`R4C7-(6J4GAP@!6 zBNUX3z$mvGqcMsjSW=Lj;Kxe@6NA$5ih~cI=xMpFgG+}9b|Vr`SdoNT5ls8SJFt5o zY%T-Ic0>bkG`O0H2G9a(IO2UnOAQSzgKsD|T<5>SrjGhiAmkCXx>wAn^g@jz`1jQL i?^i8alvbx@F02Ehw}|?l(>1yP0000 Date: Sat, 17 Jul 2021 13:09:39 +0100 Subject: [PATCH 007/459] Hacker theme icons --- theme/hacker/icons/scope_share.png | Bin 11197 -> 13765 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/scope_share.png b/theme/hacker/icons/scope_share.png index a6530c948b884b859693dcafd2bd3e115aa79cc3..0707e7597ddc334036e27d3a2a804143ba417717 100644 GIT binary patch delta 9717 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�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 DbJv}I delta 7297 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 Date: Sat, 17 Jul 2021 13:38:01 +0100 Subject: [PATCH 008/459] Hacker theme icons --- theme/hacker/icons/add.png | Bin 1362 -> 6518 bytes theme/hacker/icons/calendar.png | Bin 6691 -> 7664 bytes theme/hacker/icons/calendar_notify.png | Bin 6021 -> 8456 bytes theme/hacker/icons/dm.png | Bin 1445 -> 6931 bytes theme/hacker/icons/edit.png | Bin 1436 -> 6709 bytes theme/hacker/icons/edit_notify.png | Bin 1441 -> 7124 bytes theme/hacker/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes theme/hacker/icons/publish.png | Bin 1515 -> 7987 bytes theme/hacker/icons/scope_blog.png | Bin 1516 -> 7987 bytes theme/hacker/icons/scope_dm.png | Bin 1436 -> 5875 bytes theme/hacker/icons/scope_event.png | Bin 6691 -> 7664 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/add.png b/theme/hacker/icons/add.png index 3b1e726c0551881199d6900a8f66e84701ff2f34..a8d7ed561a0c8ebbcc23987f7e4b27dd3590a022 100644 GIT binary patch literal 6518 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 diff --git a/theme/hacker/icons/calendar.png b/theme/hacker/icons/calendar.png index c7c0bdd1564c9d22ae639ac33c61380a471f97b2..9284d91628643a75ba0eabf04d72f472c1123ed1 100644 GIT binary patch delta 3122 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 diff --git a/theme/hacker/icons/calendar_notify.png b/theme/hacker/icons/calendar_notify.png index 8b94b0e086d8f7d6d5339671cef99c3fdb6e8166..c1ca09ea80e0641319b1dffc2793f3283186a1d8 100644 GIT binary patch delta 3938 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 delta 1483 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 PFbaqZ!2@4uzd7|^8#F>3 delta 1377 zcmV-n1)ln~G@J{N7=Hl+0001xr{kRf00Q!QR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4Z6*g*i#xG3IA`PSPmgnI-G^sG zGN{xQfrNPoN!(XIGmv$X)ZJH{w$|O_fKKJk1cJQ0-rdLeEYOR{_b7Z;|1tU`KG(}G zpR~-EMi_iKL4W$S_*>$q!1Bn5UneZxuD%d?cAs;cZ&y=>qWP+*7;bg8`m;m!B>S<9 zW;hjN+pVB^tg!)kk1ZZ(jS4ZS%s`1cRhl$*->4zQ#Dl?vnHxYYn}bo|24W;>HJWTv zq>F=D;}Jw)=0`5H^EMTk_=m1oaIZNUL1W1)TlH??U{m9sP#l|y6do3$xUS{k~0HKn_z@|tAtdtb> zW64oNRewd3s%ACyphatzoU-PeEw4?knpiTmY-Vo7s*5LA&u;EsycRBkJy1(7R=ku_ zD~F0e6}~FgS6B`{B;YUeH;+eFXZy7$uSpfIpgj5KuQ zVZ%omb)wd$%rtf8Y13z!^+D~T`d#}4YIITKO@C^wvj;U;gW2tZ)^?(c8HjNr5Vu7D z11q~8j=(jN5Qq=;KyRs!Nplu2UkH5`~Y!tb5eAX z691PJTEuv8+>dwn9(V5mpNm7K`{pGZ8*bi*RvAfDQ^bk6(4VOEqB;&b9rgDyz?$aUG}H_ioz z1)do;(y4jkFtJ!@W4Vo4(NKvei6e@tQNECIS>e3JSuIyt^Pc>Lp`5m|%ypVWh<{@d zOGrY5j4Dbf!$Oo+jT93p+K+ko2ONKrTr#;zVB}ap1u7)R5B>+gyEXF@<8D$Y26VpI z_Qx;~*aaFj+x|Yb?ZyelN54hX``k!>kkQ~WR zQ^@Cm_cQvYEYN!kbgj9)HTQA)05YVhtHcd(a0rYPD0|)G-NE+W{yo#|?+1rRa<34c zwiN&X00vM@R7C&)0EoSS_L86?lVKS-e*zm88#W1|BCY@c0D(zFK~yNuwUMzAgfI*Q z`5_>r1XBhPl!i-FDK0@FfyYO3`@zqAh@%a>H_~aE-hFcz)-;scaCXBr3^IHq+rR>> z{3*aG=Uj5Kl*Babu&rua*aN#`-`L3z4G8iodH<(L@){5X*WWb<(1B#}1_TYzBPjiq jZ?%+tQ2!2WFWvV6+vit~)IqR300000NkvXXu0mjf_;Zrj diff --git a/theme/hacker/icons/edit_notify.png b/theme/hacker/icons/edit_notify.png index e0a5e991ff70b500b552d6425116a0c46cbd5868..232b911394ea92632f6ed7f40949bf13ece7b6c4 100644 GIT binary patch literal 7124 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

}`9yFjuA!4OVlc02Uieasej_ zsfM2SL?Pq0gN#Hl*=W8*4*#mIVh+1{9Vks11;Lt{+S5{9G+ z<{l9P$EvGi;F=m}Diul8B9c-6!p>$;IXof@F!TU(09jJ-lGcArNp21;7ioB%FmH^X$1yrVkBZ{VdG4MEPHE3+%rp z<$p3?2%EPyW-$H1qV?q1@aTW*{x5*@3_ECKfX-(8UFZuT^Rg^>?tn4R_kqtd@bQTF z{(StJ7J|z8FMhtJ+kepmNd3#nKhpP?T)*V{M+*ES@UQIpCD%Vv;2(j1W!L|kT%teT zumL)F(Bpyc&zS4hR^Yq0i0uvsQ}F7#1ftZbH0@@z9c&13?5yn}2K^7gzqKGjBr7u$ zNaT`@<^nj=+=UH+sL2byLi`K*`@zno91DW!(w9qDiEV~Ugat-{T`UU|LkCQKduzVP zEu@&q&I}dLHH5*PuF)yzsx}>Alo_0*q#Pp45 zfTD_Iszj_sU#ZD4iDmN7HbnY{RK_SA%*VyYGbP1Izr7|c&c1V`MyRxNRm5oErSX>F a57zRteEpDTaw4F2h=r-O$z`K`VgCc|{a5Y) delta 1363 zcmV-Z1+4niH=zrV7=Hl+0001xr{kRf00Q=UR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U!Aa&fR?bJO*Zc~*e!Ot6 zLC32-_%*_QIl2aJhq6{?boz6Uu=8oBE0R#g*~!K2In;yCw%sB7$2}Wwe|nrl>p46d zl0l`eSV)+MkVMohR|In2b=grz$(To?=))d%<5wgQ1;t~H4aj?J@jz)*h@t)rl&DjsNn^K-8bVAw*w8XJmThw}F5F~sN~_Rh zlOkOluo{mb0y95ip}lU|>osz$+zC^e;EeFf4@>yF!GB*VVa~QBqUaB;5Erk!Mi`6S zsxb>dXkOfO3w)Oge|*Xxq$&vNmf7)vH6EvnL+K;8WOEkGQ?%z6qG0`U0Ew`+A{Y`7 zh#A>WnW8Zp5p;9_s)(E=@c{y)${j&+66}^>?7Y^-J4SmhD`#G2oJ|0slEuKLNCm8v z6!~Mxk$*!~MU$##HT9rHYnGg{=A13BL#~=wGPP`GZpEsLCs)sI?q0kWE`l>qOD81FT{@15p1XDLrPo1W;G`I7 z=*Yu{k22~+u1%R~>de!o&ob+S+C}xd@(a}HqJPGl)S71xYOn^g+Xb!TL>Ds<<3u2C zivSXu7qjS;5-)O#SuBiAp^PAPu?d|PF(6EXSSLN$eUSSpZbAL0xbZ)c3k%(UK`sE@ z_uRgq*4OV`+r-XYxHOG|-FM8Sl!98*g1Oin>pA{z8~&|>x1+bCx1+bCx1+bCx1;}s zBNhCuK0E&L41WPpr9JAVaI@v%OftgRzYb`Bu1oUkK7uPLK-UBXofPp7nG9*Xx(-iV~;QfrgDGT)90>L%6 zx8^=hAAmGQNs|a0IDg~{1`ZS)9_tcW0001iNkl zK_P+1M{@hY&wPlZ4ZJtfX`0@Ba~Re%l-qE2!!-;td?eez0<8Qgz$xckavYhz#zuJz@P!dKp~(AL>x$A1sc~i7^r~bfM)!sz#NcX7$(;} zF#V(MfW#@j@E|sfu$ci9BQ~C3>d|Rp^@G%*>jm-Yr5~H!#M+NbFR}Vb@grGk|Nm!T rm=DDLKztmC9|Q46ApQZwf2tW6{uwhc{AUK@W+(=-|AF{Gc@PEwCD?b| delta 66 scmeyzag0%bfq@YS1q7JD^ah5B0wRix47h;Cbq$czWLp-;$#yIb0OA7%+yDRo diff --git a/theme/hacker/icons/publish.png b/theme/hacker/icons/publish.png index 194180c27dd60cc6fba6aad1b4a947fd742b5597..f003503da8d3dd575cc1fc82e36aef5fc716cf3a 100644 GIT binary patch literal 7987 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 D=YOQ@ diff --git a/theme/hacker/icons/scope_blog.png b/theme/hacker/icons/scope_blog.png index cbeec5c3ca4b514f29a1620c561f7d5e1168091f..95658d810c594c9c28396fff4106b6c15d170c1c 100644 GIT binary patch literal 7987 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 MW^8R#W^f|nU%27hi2wiq delta 1398 zcmV-+1&R8zKI{vS7=Hl+0001xr{kRf00Sd>R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Uq}T>?RC`-&sYLfCNHfIb5rivk3zP0MaP3cmu272hNCF(@#whTjz=k*z8tN$V>up?J?|Kl zlO)oJKC)L$8Dm3m74huL(b+g}MREo77Ch#Ur3{~V1PXrlc6k}rO`+^(!L4pBJd=3! zJMbPT;Q~C($A3`qcQC)k<{6_OT)%RxlO{V2ZfdvgxBR$AZ|R}s$ipRnDoXeB1?xjj z@+J?r8GiA|o|gHmM;dPv{+;Ta-R2y7v5O@poH?CCJ&v+RSXfuI_O^tkxCLX~Eu)z) zz9DtG^9>bMG0DVWJSkGALX*aRHfnG&@?gc2xq{==7!rvFbwwtghcf&`~N6|;oN6|;oN6|;o|3VS| zeaP-_12VvgV8~!olNSvge;mbMQ>8^J4ptC#$WWauh>ALD6^c-y)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfb8}L3krMxx6k5c1aNLh~_a1le0HI!Hs@X9HsG4P@ z;xRFkT@?eb5HN^7Od}{UQ=dyF6Yv~g_we!cF3PjK&;2>Nm7K`{f1gM^%XGsc-XNaZ zv~yH6ykH@QG+f>{K$3L zaarNK#aS&^S@WL!g`u3bvdndwLx^J$OGrY5j4Dbf!$Oo+jT93p+K+ko2ONKrTr#;z zVB}ap1u7)R5B>+gf4epF6XR}DCHq)$ diff --git a/theme/hacker/icons/scope_dm.png b/theme/hacker/icons/scope_dm.png index 160f7c2c8f69e7c4f586514ec6d64d4358e82295..82cf9913c96cf7816f2a2a0fac863c87c538cfdb 100644 GIT binary patch literal 5875 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 J+v6L(`rk^Au`d7s delta 1353 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 LNkvXXu0mjf3XIR0 delta 2310 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 From 2f9d0293a52a589a81fbef9dfc3f690c6ee3f19f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 13:51:18 +0100 Subject: [PATCH 009/459] Phone style --- theme/hacker/icons/newpost.png | Bin 9193 -> 7481 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/hacker/icons/newpost.png b/theme/hacker/icons/newpost.png index 1ae6d1d6efbf22c91fcb292068dc45328c3ba7f0..f31f7ffdf2899319c4adfb90e182a76a6579666a 100644 GIT binary patch delta 3331 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 N002ovPDHLkV1kub8SwxB delta 5250 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 IM6N<$f?hoxkN^Mx From e1cc70bccd5c63276f704ad715ac59c0047afa89 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 18:54:35 +0100 Subject: [PATCH 010/459] Various icon tweaks --- theme/default/icons/dm.png | Bin 2420 -> 7053 bytes theme/default/icons/links.png | Bin 7057 -> 8551 bytes theme/default/icons/logorss.png | Bin 10510 -> 9754 bytes theme/default/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes theme/default/icons/publish.png | Bin 6016 -> 5682 bytes theme/default/icons/scope_blog.png | Bin 6016 -> 5681 bytes theme/default/icons/scope_followers.png | Bin 1416 -> 7161 bytes theme/default/icons/scope_share.png | Bin 1612 -> 9120 bytes theme/default/icons/scope_unlisted.png | Bin 1420 -> 7028 bytes theme/light/icons/publish.png | Bin 1512 -> 5883 bytes theme/light/icons/scope_blog.png | Bin 1509 -> 5883 bytes theme/night/icons/like_inactive.png | Bin 2612 -> 6613 bytes theme/night/icons/links.png | Bin 7057 -> 8551 bytes theme/night/icons/logorss.png | Bin 5861 -> 8523 bytes theme/night/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes theme/night/icons/publish.png | Bin 6016 -> 5682 bytes theme/night/icons/scope_blog.png | Bin 992 -> 5681 bytes theme/night/icons/scope_followers.png | Bin 1416 -> 7161 bytes theme/night/icons/scope_share.png | Bin 1612 -> 9120 bytes theme/night/icons/scope_unlisted.png | Bin 1420 -> 7028 bytes theme/zen/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes 21 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/default/icons/dm.png b/theme/default/icons/dm.png index 78d90ae765d5f80623d022212fc5f10e824594c0..ca459e5dcd6a1571ecce15fcfe1d9b2c551c553d 100644 GIT binary patch literal 7053 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 delta 38 pcmeyzag0%bfq@YS1q7JD^ah5B0wRozlO364CR?(&PPSul004KS28RFu diff --git a/theme/default/icons/publish.png b/theme/default/icons/publish.png index 0fe148eea2f959f79ce9f5ed82e990e2ac7cf280..13c55f0b811ad39774889d935c6978b3a99d092f 100644 GIT binary patch delta 1614 zcmV-U2C?~oFS0C<7=Hl+0001xr{kRf00Sy|R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U!4U>?RC`-&sYLU`Ysx<(d_)dW%~MY4K#4hKE*SXnD0Q`T#MQ16_btp{!rP*4dfOJI?DlfB-uBLMi~M;- zF;1GuBHGA@S;`nYoNMrBpN5Xcc`4F|LvO(~zm_s=;!{Wtv@HCvQszZc&>LD33s^|v zn6Tt^Lzd$Ypnrr3ur%+D%5gjMt8bn@`a$`XW9=|mVQ^Esb-%^OJ$efdD_brP`BPE4 z`xmU2ob*jD_cr`ukw16luNIlyTO99H$LK!BxfX|5V#1lzKGb!THNwI=y|o@osD?{1 zwpBB#ALVEWogR*cjH;MqVlW;QsZ*gr=jt0Zm>BtFLx0cQKz+&_EHXEl%;T(q1{=V4 zgaAuJtO&$>+d^yKytZrPi2F`h$pmFI*U|mZxoF&bB5^f+gGrQXGFt{Np7}S z1t2uGPeC*+7t`(NuVsQ25Y**n#{tVxA1bQSGqz-Mp4qRcalf(1`c?oJVQ+;oBp`4k z5~W0kF@GBo{OI6QU^z?VN}&;{a)*^1MH1j33$QnyFKFdI;4H*5}8dEn+JR!*c2&V}AMyAFB6^c+H)C#RS zm|Xe?O&XFE7e~Rh;NZ_<)xpJCR|i)?5c~mgb8}L3krKa43N2#1*Znzq)SShCfPhFm z%M8;d-XNadv<=St#1U4MRpN8vF_SJx{K$31<2TL)mj#{~G1IAe;s~)=Xk(>~S<%#p zr--Afrc=I<@mS@&#aSy?S@WL!h2fmOvdnc_!$@EeOOPN!K@}yGVIxMnPKt#ToyUFr zgRWm9mqM-*7&#VDfd<+2ga5&Q@7`MZ$q6qh90xjI9Oq*M2<-xmn&W&QJ5J*S2tET> zddpv_12dnb*IHWi2En;M3VPrWuH)S+8W0U^~A0%dFW-%~jFgGnXH#9jd zG+{6~En+q_WGyf@V>B~3IXE{lGBA@u2`3~lGB9E{WjHe}I5}oFEi`0iG%aFfF=Q+jiohB5&!ORL4`aw^hGGIy=58#Sy0M=}X3M}@+rvLx| M07*qoM6N<$f}xkrtN;K2 delta 2113 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 diff --git a/theme/default/icons/scope_blog.png b/theme/default/icons/scope_blog.png index 0fe148eea2f959f79ce9f5ed82e990e2ac7cf280..f733dd937e514869f658750ab3b61086232d420b 100644 GIT binary patch delta 1613 zcmV-T2D15pFR?6;7=Hl+0001xr{kRf00Sv{R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U!4U>?RC`-&sYLU`Ysx<(d_)dW%~MY4K#4hKE*SXnD0Q`T#MQ16_btp{u`qOBrKFa1HV7)6vm5FGczY=q>$TYlW5xAd^G z!FtI_-{f+e;TMnmX_>!zWOAE0-l>k!eT;K04za|9J*T~?Yb$Gng>^=2J(f@nmtt(I zW>i1Q(U3Yl91SO`Vv>o$cu=HHg$A9gZ`9yoQ z8at*S8diwu4)oVD!3qfK3bSK_<){x8Rq2^qGC9xFuc&dqvB>&XfDpm9!Wj|}I1-6c zqQjVt2!C;O@G0<|C328_yW6wXFOQNVHWkUlLoZK+xFmVaC6+$D4zXmmD)SGpHrhvjTa~hXzZp2vDu}9=IcZ!GZ5oQAa0WY z5}GHo!zo6d!6$6liYW?h3Nl?8^4na6WxD7E=+W9 z3`bN<}Inb`W*QP@OD@ia2T&iclfc3avVr zT>1x18j=(jN5Qq=;Ll>!!Nplu2UkH5`~h)ub5eAX62D6dEn>XmxQF+?_vP+8K&Y3Q zW_64Mnr@q^L|n{dSH;jPLg=G8glU;s#+)Q2;XA(W5#ak>jA!}R{W*HnoW*~DfJi*c z4AUmwAfDc|4bJ<-5muB{;&b9LlP*a7$aTfzH_io@1)do()2Vsl2(egbW2KE*(bR~i zh@+~eQ@)V#SmnIMSu0mr^Pc>L;hes*%ynABNMI35kRU=q6(y8mBSyPUiiH%N$9?>R zu3sXTLaq`RITlcX2HEw4|G|In-dg#|2`?!e2RdII=VJs2?E;OO<9r`GPU8d!J_A>J z%U`JjGoPf_T3Yl7=-mb`u3MV22VCv|15bu*%B~coCFJwK`x$*x7U;hPy4SqEHP3PS z0Hmo`$s6F{5Ev~`_PWozyW0EqZ%wm*Kb=r=xIs!DD*ylxTWM5SbXc<`2DSnuH#0af zW-vKrEio}MWi2#0VmB>eGcYkNVq|1tWH~uEWi&Tqlm7@GBsDiMIW=T3FfA}QI5;gd zG&eCVVPR!7EiyJ`VPs@5Ff?X0GLu3HCnPX3Fk&}lI5RCcIc7I4G-PHpEn;OcWGyse zFk?4mVKFl>G&7Tp2|^AsHZ?RdIWaRZI506XFtgnW2m&N#F*Pt@GdX1~VKz5nEi`61 zH!V0~F)%GOGdE;2F=jYnGdVJo2@Ft^i3?mLH8VJ6F=AzAEi^VXVJ$RcGiEI~WMeli zGBPnWF)}!2IAUQqvk45Q2^JHzdxQW000vM@R7GF~0AOi^b53OvlLr delta 2113 zcmV-H2)_5REPyYN7=H)@0001;w}I>c00dupR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?N?it+$aqFXBD#qOF{^i!}FZk!7P6+7`v(~>7+ZI{LC3s<{=A7=t@Aj{?FgT z{e_1~z4K}@q!11~KKaBMop_v2Sv4Jg)$8&56UJ}o?*70a34dBShlw}6gN{D!aoS4g z;=cr49m?qG<3rAyUf$Bn7Rd2c(6&J6)O@r(VG9!87H!k(wkV`%ZPML<}qkd1{ntP+x7O3KKndHnhx@Wq;8kut;2GV(w>gG}t6blZa@j zh!p{uZ(C^TO-o&+h`4VAP9_MW8CO?v&x5a&Zv>4obB5@Mx36FquP~+QMVU3Y00@ok zQy}V=i|Kas^D}`JAgIgD5ev*mIYktuM{LQ&d1k+&_`PG1wJiWHf^LN|BtYN@Buaq} zV>TlAF@FM|49i(O7jl(Il^d*NFA@L_vH-d<$7u1g@=3<-2_O_XX@Dk423X!J!pD4c zh@q^aUR8sdMopT7gqdL>SeV;zHZgB%!OWs1%Tc0_8bh=gV~RO(5{GSC6Y?>|lv2)6 znjtx3b_Pezg^Ltlw1i?MmQ->@_*7rDhH5p|RDW~hCaxioTWHo|OD%Wm2&L}3_Ry`z zo_Zb#wIRa~9bwprBaM8cwo!eH+ADHDMvXRVynqsi%5Ky^HoHX7+)i|22E^DC5Vwf{ z0-7ggM{$cH|OIo;l{7Xxe49xK+a9* zUVo7L7Ps%9*5+YQ7h)?5-E!(7V;+5(@hinw&#OPr(~q|9vlIRo^dA+JY8=I+1`{N3 zLxB4sHd-1j;O4TZN?y4K91NJDts%WmBDt&c)piQXM@Lql8gScOIx)rv5OGRZ3otq~ z@?(g!oW!i)S|j1HKy#swkVBhyzbW~bTYvX4#;&22g!BAe@3~j^^(O~@rgcAI$i=5; zO||iDFiJbK3;OQmgMV(D z?y`|qvy9X3f)z(JWZ;P8c~zt3=%3o;J{3fojLr?>8*tqh!{v$6mH0XKwcLqkwWLqi~Na&Km7Y-Iodc$|HaJxIe)6opSy zMJW{p3yL^os7@9{MI5yXMW_&Jg;pI*UXzdm8h>(hQgo3L?@J0TVtnv;ALqSuxbI#- zsF#^$b&La=Zkwq@T+C!w#jaO`Fn~S)oiekGIY~;wcYWO>z|XrF&+>ow=ju^&76Sqz z@hme;n|OnGdeb&I?-NH@QC5l1iN{PTkob}7ipOu93oZ*hGh(Jw^TZKivCzg!8?&OR z5r0n+M^#Oyd?Dkp%6W^kR<5#Uo&1O4oW8Qmb=t#7U=d4@AVNVEC6r+!MyF1Sg%sV# zef&eNUm}-6t`ZnI7Epl(+4Y0Z;CFAW{N#j}6pqtaUL5CR1nAlY8a2oHK6aeO2@re+ zuJo3_QU_)}Nw2lE=n>Gn4P0EeG-VIC+(H2cpA6ZQT`5Rg$mfCgGa6GC=)VO*YhK@) z_c(n3($uTu4RCM>j20++-RIxk?S1?AO|ySL@rZJB5D5D>000k*v+4x90wg$OVq#)5 zW-%=`HDY8fG-GBqEjcnUF)cG>V`F78FfcVSV`7uZ2OlJ3I59agIX7i3IAvoqEi_|d zW-VekFg7h>IW#jiFk>@eG&5wA3WyTaD{4cg==wz zYjTC^=fpCTUllkC(GG$z3`IAwYqbbC75@KE z9b$loscRR-1nv_sdN$fiQ)4!5`nqgf;(&C*-vh>X-U3n*yDmW2c4&7N9Fqu8ye?i6 z;Ak@eCL6#=4#Ew2f2p~@DtWa&XuB4}!{7t{sSnsL4KRcRq51`T?@xmd%GLTn3!r~c z2cWuysRkecV62LT001!n2mllb$IS=1fa}d<`#=JSwu3SkAjnf_Nj1Ch;=XAb#@?4) z84*BRg)BhI9GM0oG>1;gowPf&=S r=?fgx#2>7B>nQ;))8TpX3+fJV00$Ak4;}*000000NkvXXu0mjfmSnOH diff --git a/theme/default/icons/scope_followers.png b/theme/default/icons/scope_followers.png index 2e420954c566bae1bc43f891b2ad78ba78dbd37f..aada82f0ae1afa055435122f226c3ed478d9cfdc 100644 GIT binary patch literal 7161 zcmeHMc{tQv`yX4PD2k{&O`}pVW-(*TWQeg1#+ofljaeAXJ{TmsQY0lydsKQ7ty(B8 zLW@LXc|@{=gk-JI`<>BK`n}KZd4JD!{oem_T{FJtoX`Ea@AEnLeZJ>hljLA$sUW{x z9s~j@5UtFdfd89CA4w_TyHPfq1Omz32;br^a3Y6-IXpI<5kvzE!Z}04aMZR`SDT*`K_@=%#%G2ABzt%CithutP$U{Mgs&O}(pjOl8Cy zTFPyRS6Fpo#}#$0D+Tt)@6ty`LU&H>ToCozJ8vYheC@8<0^_ysM^gLWF1?hPtXHbE zCit%7Ap3NrjP|0~@r0uRRPKuq6UMTx_r>H8iLmUVgf+tr@h=*i6m6)H#HflGqE}hx zYa_`!74DTDEyuVkb{1TmI*;~u&rmWu4&bt1@6LI!SI*uIj#=Di4_*68kdWJ2fpiUt^U(x*nvMOWMlb;xt zJ(PUZxaAvDlg2l?(<4IuNKm`N|7zRV@7+}D`7Gzr*zp> z>$F^Jo}OP~sz9@gLM({S;!Y~rN2hiU=ibJ_A1$HB#Hg>aQ%ZO?==si)r91#nLlnB} zT^^7GDK*g7Q&y@q#d3pUn{$e4)!Ns$YZ3Ol_b?@75}Wn?8F%;+UAtcv4cnslg|re~ zO~07=Arhz7g4f4sr#_tbHOZ%)kgX9xS6t~~z*W?pytg`D4fUzJHz#!^A=%M)kH=9V zSx(v9>OdFuAc5Dl)XDaM(-ZZ*bgOD1j$&F}uIjeqjk2|llhb3BLFdTs$^|tLI@f3hN_#ehYv9!uh#Wq5i86ah7V_M^>6AE~sz4wi9dQ_iW{b=Ev!F z$y$LzR0ScvR`B47y4uyY=;3n<@0}{(T~8WrJK&Uel=&F4vB}iNm3P>&;@-)jijQ!u z&}ID4+i4SbJD&YkmF6^o$5<=VNY zPvUof&#em5VK&bDp)Qt9AhSJz<|ke9HE(S9)G1Yx)E-=bd2>rgH8?lKTacqtqfGW_ zT+n!5RTWZokm#k|(`VfvKP{7YT(fA0?4YK4su!x41{ocyQ;I0coisp}U8|);t891} z+vQ?OjN?DO)w6IZSw_=4ktlI>w~NV6No=_-^2!;6Ot4D&*^}-IrGrnFJfG)^+tiY_ z@P&1wY%q^2cM!8#{@g~Hw^;>Dql{}1dHn!0ri*64aZO_rzp?EoXr9zaffR=KG|mLa zag@6K(zfa9-U{uD7BHCE{;@nuDrWin6=jD`Hs-&$s2Gzzf7fpHLfrNE3xOovr+TX! zT}XM(4HDLGh2R|e#+#>4t}ni{#6z&XcCYb;u#awbGgTcIT|FHXIR~A`Z3_l$qB6Bg z)i)&8-{>O}%{JXp@jR#A`)1RV#mmFCCULBTR@R`}!c2xJ;l>7`FdQXZXZFv0U>Ie%pj-|mvnK+wIi>^Rh*XZ6N zd3D=*Js%mMWYs-2SoC4#)yb;Z_YKq6JMfwsQWZf>@=gZ{L#x{NCztT**U;<&l_f7d zR8eiY9WGsAo9?&$f|32To_qx473_A{akGSWsfnnwFDlMG+pi~$daqQ;`Pa9?P<3&4h{-R+e;h&aEQrF z^YLj!r=L&$@Yb+0t2XUPi~>$Vp)@IED-Ere=X7tHPAS_@d|YuL zEKQi7tON5re{aa|QL;^Tq+GFQX97<6!DQ%SM_>9^v+SX?TdF5tujVJ^0bfv)ZS~p5 zPN(teK;v9=G_t1a!uX0zN*TNAR#g6KJHXUg`lT$Bj>o= zs&@eeqhqaFn%>I_12Wk8+537^i#e?-FEIMfe$9nx`<8$c6vDD7{jq~D2M2tz3Oqf{ zpJ#nMeUr=#ztwNDkD#FN_|(82 zo|NZz{qFnhOGQ2hOh2BQ7z})O<*MqP=1&tx!kXFJDK~FW>yLATO5hru=bvd@N;#Vn?^1)#cF2TrfK%ocacu$ zsT|745OV2wLVl(m;VY3_XIx8=>Kod+guQxIMe% z{j|)kjq3w5@nbEoIaMkAt@0T~vPA7KRi#aZ)$=79^xsnmPdA)0V|bbyvp-&|w4sdE zxbZEbUL3>stMrYOjzzXu8|(E2w^*N;C>UyS{j7n^Y#ZG~^%*qZxu@#P*l&Sv-X3e8 zV1L?iqI!4hiW9H{y>EjD$2RuOm>)=QMwCt0dXbG8so{c>6Q`##XCglAioNz#qWh~v z^Q)MR?IW9lK_H0)27%x}BoO{Q!T|@jqmgNNtFp~%iLcLEn{R}KEapCP(0CZ@u4GwY zsnRu6gEmp|>lBE4_U5*4<$UnF{I0#@LVjF6c>YZE&0E+F#a2nL*CwbU+PJUE zB3^&tO=y7YpW0r96X0R}5ShnHPTYD~)0XU2^fapMnB4^18wR-Im;Y|Qf>o=oA_(qjB9s%*pe8JOy* zwzxAWu1ItTvE$tNzH}HB?}}w}8=kl{+}4UB8(f6JxW^h6A8@}1S+m>0p?uzxOG>%k zTIHsPtCtwfm^8xDY*shUQ^US>jG5Ow7Wei3P}|mo$@t8rcA#UheItm4@gXr5&$y+j z8hKnv=@T*9ce*du$Mo612!ukzrAJv(%Nn7iW+~m!=ol@6KFv-3g|fU%`*d>Y2HDZq zlCAkZuMhVaM1qj@`Sl7M&iv%18zP?dw0aLjez|eB`R=L`Yu>}XixBZ3(4xHz;7sjK zvc*x@OkFaS?Mu@YGC9DR8w4^m7IMgxK$-yTOY>u}@Q|UByAUvgiifzNNpKQ}K=Wr< zh4X06;dWan;eiw^6=G~8Zz#k81WcNM3>GqjSbUri4-xa?fM-!R3<4HI1c7*nJIMh| zVDo5TlrBma4mB4tLXZ$6d9WdmO2;{wS$v}aT6l=RK)}JlV4w6ehel@Y#~C=MN%o>ahzaYkQhUyz-U1h(`V` z5WwAc{-4l)>LrdvtSiopO$in`C7R(OqIhvsHibdOi61dY3>k&Q7(kJ9EES5<$D*LV zbSfO`OF_U5s7R_F8jhGlMP%^>WEO=cq5{Zu82}FziJ%(z`szbzzylOTF+fAHNDLiH zBcrHjUwt|qPWPQdVb5a#l|T-f8<1G! z4Pk!={x>FPe|9MA|HktJ`klpuCkSQp0_}PBz5z6f;O}|<3jCeP3E1oS0$v#LKTPU> z;0$Ne)e5j>^TOu%ccyV?TeBNd5JM~#7%bidaAeA?{Csi9EUKjSab*gy}7)kC7uP&gckf}-F+?)hTLz*dE(A}IzK1d5KL{LId0(*>bq9?irL z$N^xTfzlGMGkCo?`!@WH4)v!2Iid|m;NWn`?DB#QVWJK7&*>S8wj2@(H&*~d(UycG ziZahRn8OKT(0D%!^Jk*`FSt4O-;(k_na_pIS`*luFraAt1@@t=zjgl?z*z=c28G7r zv;QvixsX{|=Dc@+m}mQd_ZjedgnfTMeoG5c<@^`_zNOoL(E~vJ%gH~|_m^D1ldV(Ab9$S6I zRw41Z+#|*L(NvSir%KBvI5HgN?4!HW&2-Ln2f5^fu_vc{Zv<$!m=w##sy6FL>S=3$ zv(2kxd?YX*@}h%5Exva_!#g6#=Z zp6$-+_7k_G{ov+MW@JXE?IgD&;nPn0-X%UeIXo<9f5&Z!b~dl~-XKO(_=85=h2&T@ zpOFMvNRmA5sA1u05%gu({;PiOlJ1RwSMNo?qi(z0acO0@HI|i6GmaO4Pzho{gBJ*}yjLIazC~=I>{QgNYEaXtNpq0kgN6_+ z%yTN6n1465U}n*hV(s15(7csiFMG$?oRHH+#J;XE;oKh&P{ZGgPfb_9>{&=_J&%^>$sH#XugHe6epzM z^{X*6KBf5TdG+TXsPDAyyAwW+K8`+)K8`+)K8`+){&7e6=ON+e)Ng0Tk@)Qstmps$ z0e^#OLqkwWLqi~Na&Km7Y-Iodc$|HaJxIeq9K~N#MJW{rJBTP`s7@9{i#Tc(iclfc z3avVrT>1q~8j=(jN5Qq=;KyRs!Nplu2UkH5`~Y!rby9SZ691PJTEuv8+>dwn9(V5m zp;2L))inWVx^1SD2{D^n6?)W(glehxvqHp#<}RSz%wIeCOuCaAr^}rtaLCd znHq7NII3zo< ze!|B;Xpmh$_#gc4)+$U*dP(5~(EZ{#AEQ9eF3_wy&iAq7G*5uw zGjOH1{nZ9A^GSNWtwoQ3{%zpmx~(aDz~v4w_+-eY>`FnJLZJY>pV2qvfPq^ewC45J z+Q;bwkfE+pH^9LmFjl1Ob)R?lb|Lom@0nJAKW(FOrOMBevfFKM7P&^7sM#w0RrlT>6BT!m! zevLooPAxnS8eWlnNJ8j7u^MDEOqdCvoc91MtOZz|Hd%=^5xK@jEQ6s0LP}t?%nu}h z(OIF^ivH<`9 zPNbnOivCw~=ew7M{#%0g83O<~?1QZAXehKlkW3-DBlaKVJ2TE>4NJlnTUY>*Fd%ycb@IM^^2|=9`8Fl8~HzqTjdl*3VnMPUCyt>AddI;JorO zFt>TEbC%=iJx=WUuU&D|P1$?iP=Q|SoFV%y?%Er4%BWwr=ultpPOc73JzKxl@D3bm z&@`mBYKMs)Sf3cNs}gj5V=H@FCHAb>p+Jj~HaMho;aaiEbV_z&p>?sp)q92Q#~+m~ zyk86*5OmHGem~W*@bbQs2CG$fySc)M-_tG1y#Y+~b?Ww$;gk%j_d*#39tj(w42*j9dW|&|cp>C{hE(y25%_(~QZAuWtBaacqfW1o zyb;^dZbR-$4lT3hKsL>_rpZn)t4sDLv0n$y=eQ8fgzORst=T&|5&=> z?h;)EXid@Q@vH>7z5&7}r5kiP4v_AxjFpw8o{m}$sKXoB^K5&hl!CdNW&mu z_0yJy@=~dk^uX}AEsUr7`u_w0}m>t~fpF**0sQ-{?r_3B&sag1=jzpf*;keUqDeLwiwGoR+s z=5IjMH7sfGpC5dtJASwwqVv$eOVrXKtu5VzoqB0-D9>lcc_E{^*7c)MManOchk3}; zyysV51|H|nbh;{BI#p|6^UChLv+QDsjiAShyjRYm<>SL{Ww2WNKH6mR=W2JWeX}W2 zwxQUCT#0fgaVSdom3qC-y^Y-Y7nxU)1`1WsNE5gL2ZpmN6KfQ&WNsb{Wg?p&oz6Vo z7M=Ru^S<5&df2uxa?1tel}EnRqV{R8eZTZ$4J{`lij?ktVLk^c&hNIhr2T5y21<*8f0Umq z+0Wao65HWWHR?ghDQ*5NYP!&W)IF%uD&N-9B~gC5VoI`5az^g{$DY=OlgH-unU@MU zxIQU%?n%e;d)@B5i!6yzndoM0uXuEvksR#TUa>k-JHgT$>(R$i=yAG+f#q;9D@Eb; zH_YTM#4p{wsoQP)q^0f{vTMy}xzwd;<(B5BFbG*c_<;7khXU^DK^R|>`eK2+`|Q${ z5FL-$+uA-dk^PciTQn{_3~J+lenzh@FX_6ytoGIuiP+_U89A+HSF`nfGge~#s8;>@ zaOg*a3P!;$-FEIH%KXp0$VDSmS03NB){s)6!j`0058mZbdwR^JEW)n>pSrS+y}M%Z z0WN5}NBy{<-wf-E;!cjsMlI(D-CYmOu{PNH{>7oO6T$Z6`cQzsd6Fg>gFZ=S_7lP$VBImWNk7+zU z%3*~oEj2ev%eoWJSNhP3H*bbV`BA-KvgK2mZxc$Hq0kY^*LDK9XQpA_Jxd(GS9D(b z&BpLYiKJ<+$knJjK@oN0)i=G4Sl02Fz`V0JX_nJQS(lwvB+P@7QWV#3_7zMCxEg+u zZCaWwP*@2a{~G$~oZyn|D9QKH;G!E;v-_DY&zxNlwDQ&6!4aE#1j1vh>L{YRgxBTU z9$Obq(!O@Sh1Qe0vcWxi-5+l9OL*4b1e{=cS1H`=uKW0y317`@MN^k{p_e@_jwFTK z+vv{sxJ572^-^13mxcc1ABs`LK31!mX7FiBPX|RSKAW`?mcRR%?gOLxB zD~WRwZ45Ps4lWT=-{&a}^t9cz#b2tZ!3v~vgkb|(yAehw*4R&`mYZx-nBG$vlJ$cb zgetCTN*$fKF_d%nT+7)lEo&3PO|tKc46xcgf0=-bDb9o4LtJ>v=woyR5|0}R$4zFf08xYcp0EXDZ5x!00u{0bh&v@ZKQySHp< zVT>2A=DZX=Y^mm$cGSCGq?0o7Tp+5bFa27EtZ2|Qb8^7?ou11Lu4nQdYbzlpwlK4z zjWs5M9>=zgIpHPcX1UipB^z(uINPsT6`f07VfhObJHh&D3Fd!5i*S*>d4 zBI781uU`+G+Jm<+5ssTkS{`O679Z9i$}HUtd}LCCdzufODuXVf(${#4BOUd?bLYcy zN@8xP_9hdf%}TPS#h&HRYJPv3diFcKpbf|c-M-5eIYny*=)TZ7= z(DdUkXY`dR**BcyuTrS?Zcd&Jsj8I^g*~0mgr)4uNXzgqa>(`aG{&5G#0Wj&cyOw! zZTaTZHIbX7$x0niV4t~;?s#4q$?l*jRn`iKxL}M4v&y|PYfTX_9%x6Ml5H5nOBzlP zQEe;a>_oCQkzDsS{EI8{LWy4?SNJRK{vMAb8RjyQ)))5c zQS4NBhYk>GH1bUzAO9HIJS=gVVfJ{#{55d}^Lcpo{O1>^8LVo}j?cY9o*mnZ8pnB4 zgbs{W1OzM|kX+twU5N1O^wcki0}@Zxe3d)lvS4(jOT{m#E0*=knASYpD!#)rG~;-& zU#*`@UOwB0`>BD4#{8cy-wA5`_J}Bbo7v40eNi7pe0Ae%{nWsSd(Qz@xiOz-wVP6G zGlQa&=dPk<8SirJJD#L8UVn-geJinMLEORin9f~ln&rBD_B$8F4{FP~hPCF`NO_)- zBNwwf&9=!Ai+16rBcQ#tv zjF`WtyUFL^l3P8z5AX~&8mcTd^t~Q!#zdY;>(LVo(J6&F( z;Vr1A=HxG9!+b(1YHNMFnm za<4YKDz?FHx@Wc{)1RcK@oA7sh#GZpREuBXzURVO8frQ9_R|@wkY*m-+@-8Ze~~*5 zAV?gmbu2qIA$rg}KJs&c(|&iW&riF~hkTMa*gwGBcoJ>il9*jH1s)19%j!D&o}+I0 zm!~-wMe@FlsTBZ2#tNGJWQOh^YTy*VCE6WA?CF1%9 zTAqFIfcSLn;tJ1-i^+u`UCA?&H&339$=+I+=wSF<%bM)W68w!zL>;DJ6V+l205C-1 zb#yF{Iy%2yGU!)}%#b8C!v~r|(c=Y1ddJ26xP3Y;MB2{V@feh8^Bj+K_{Ewv#W*zb zC?_&5K6k=_yzEgiKQUQ)E8-S#@7J)pdbmu3VeGh6lz{x0&*+2T@lDDn5nz9}NfqKD zgK6z!8uW-eRn~3pz>&4y!+MVHSLJ1qG8>7vgPAtY2~OF6^$krFRU*j)fxJ&452|B_ zVhdq}h*fo;$W(0VMrJ@y9y_lF#c5^ZBDEJ6?xEjf5|pT-eL`Tw4v(>cDor9Mi%Q;J z0b|u{;3S{HF6+T2=}@%FZ7|p;YmhtHu0>oj#=@eVrRxq)u9I}TcE%Gv^{-k(;3VTC zLo7n@nN#5`!yhBQ{kk;f5Va9`jo%EA1x{R*=ZN$R*MIEOd`YCxhmk2eT=w3Z+x_8h z&7XULKtW7v#65yTAk*PJa{giA(mF7lE!%TmHs*=#gk~AmwQrD}(EeNf zoMiIe1b&&|$Fr}T7DF~`3x@BDH5yUc;<@A_0RXOeJpI;ZXKI4LkO*>UEXfTg7eFA> zZ-oGWszv}Ajq$?KfNnSsJW)-2sqwxz5RX+8w^cHQn38pHo_NC`3eGaf%nB3ag@I$m zHPqQu0}ylq0*;0T1`xc7R78N9_%1Jk{=72{76FWQWppVqVJ!v#D0u1)|_m}fmkRwq%!18c791MYip->PV0ip&H zY3KkDkt(r6@ts2#N5xR^WE!4C1nzL6-AKMPHF0tJI`9WMeHEh@K%e~KU}wBbPo-hO zNcupLz8^XO423{cKoBSh3J3pePhT}P{oR^K{h=aVPjCR543?LJfC+>@Sx{+uet-D; zOAD$M{qhY);ix2E3I?a=ha=J?es)Us_ND&p)0c|dnc9up8|x0H2es?@XBz{gsm1R$ zJ2HCU3FKXi9rVvgEarC{*_YzIi@{>RIBy(*9tf4rEdM7w4e$QP0sUz|J1hSZ2;JT9 z{C`6Krq^yPySgHDNf_T9r$}8j@tt@PSP}-0MeIJRD8LkyU8p1c#+ZC3iQp zvVsB@4Z|q!Qtg}rLdycFCJvQ@{4rzUji$MiCJOO7(L+^+t4}-## z6cykwWd(V8Wyts4?ef{+C{%iq?{LaP>+g_4cFU zw9y{m-JvS@-+}*y$4TB?Wyjx z98*)o&jP6Kv?K&_C-W?Q$z*Rlj`C+=eoK`90r!*rkEHxh=0C%}TkDX>f%KyFq?!8@ z|E>GK0DfmM!DDblD(T;a{xjseEI++>=rMnvqrcDSuSf9j@5di$u~Rw!kDnju_W$UC zPW_LQzoqX#a{VLM-%{Xjf&a;_f8_dG3j8hbKiTzvCKuZuH*6e{{*RtN{rPKJVdAS2JgW&#BJBm%CLkxQQr$W@oY^Rj{m4m|eeYO~ek?}p@3BRlUD9)D%UvmM zIgQL@P2*I&Fn`vxbnlDww!pB3Fp*QzHUbA3T8wfO^|pCOd^(~Zh7Gd!R4#C(9^AG| zV;U8)ICMWPRqJ|V)3FolFT&-8tA$^>==lU z!R8mTQ~9$cd#gC>?u%^(U*V^=O1d&FOL7~d?ui8)M`0I7J~%||$FP?AxwZSOou%#< zzJXl_Rh{s%^`hzo=g>7fxyO$AM3?JuCgT{3GYWN>Yi?PgE7J8)kzwDK_%hEOGvo@+ zW(UU~z*F?GcWFPcAdg%{J68dBd=#bqdDU;shc1qB@enw!n ziY?2x!m#PI3s?Ry~=my{Bw`02S*ZvO&=!q!vsKOgcIv?TLG?R zGB*HMamGalT4GKxsYBY!jN%OXra#K^Ho{V8mZ*~Dz0RjlXOkWdnoT%cX6EL-NbzSh z>2L_UkwB_Up0r4YePTErE{iHkwrMLN5zfrbOP?Gi#VuUnf6CPRY0}lEz?$oejjNy? z0g2EM+~7WDl-nskG;>K>xHtllG&r^|rYVcb(lLj>78Z|{P;SLi4~X`IPNAC8N6j#K zHSk6zP^angc-@t@&(8h~2Sf+T-j17*pRgKfvZ+q%xSy|&;S{L{y52Oee;W}M*0f~) z`RD{!Hl>4=ogp}+bBp<`lHFdr1)Tytq>rBkH(hG3rU|X-y;p0F+a5ACgZ-L3IceR} zAzZ#`UsC^iG)FwAw26s#T*xw{W}szNz~&@ex9FFLext)Jyso0;eAZqu)Ax6*kvpTfB|_9qobp1i=c zm`Jr(FzC@?m@rc3J7K50o_mOImx4o^Z?-%<4CjLEe*tlIr!S|3Wq-JQ!dgZqkt0$V zqHI3w26V|gU&m+iaq<2h6{E?d2$lv@zW|0g@s+{Qw9k4y35OXISjvqs{#}|!CRkk+ z$bgNgHreD9E!Cvc4kJQu6Fa2SbovdezRDUlF@C=Kqd6V}9@^TY1-$v_eBJ@rPIpJ@ Lndz2myPp3qS$P+F delta 1607 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 Z@=I@CE!~Ec7k*8HkZoOU%B=!-{tFz!#g6#=Z zp6$-+_7k_G{ov+MW@JXE?IgD&;nPn0-X%UeIXo<9f5&Z!b~dl~-XKO(_=85=h2&T@ zpOFMvNRmA5sA1u05%fj;S%1~fUDCY~@anzjchqf{yBw#Z+p@xoPTNlUUcaT%V@AYR zWPZ%ZAy`D@ zMi|KiXJGtX+^gaLmM2GJ%xz9|#7isW#dCiljY)3S7=HyIG`6O?^d)yT$Llr03JB_w zIbwnND5r~}bl+RDILo09L;ZfG$=Y@Ri4a?n3<(GvfkY|LVa!GZ9V7T;WX|HbfB>me zZpf0oNC2G3GdAWJt+A|pnsK}Ugh~(t8oWS&<-PiV_bp-zWv8NERfC#FO`3xQA2ftu zVV+aj#DBc01v875EJukxY7Eh0j49^CNpOs*OCec`DW#kdH2Vx+8RIhyg^Ltlw1i?M zmQ-@Z_Nl&V4b^I_spiH_8jb+hLbDcIYPnO#R_eZM58Zm~spo;cHe~prBMcjHq>(Rb zo9Z*QS91SNjW#u2pyoKcQ-j&;=7Q#NqLUejv41BJw@CmSnkTa(lJ`8xO=d@8aU#md zQYV~7lNbp4POO72c6V}rQ*UUdOF0K=UnxrZ^!5 zuV0Os@hQbu&#OQGKz*lm-<|Mr^l|iY^l|iY^l|iY^p88jKMx5%r+x!Xpmh$ z_#gc4f7U8YO?pY;1knBBI3J@x&o0ocJI?p9<1|lz;4^TgxBb-yF!M=zy{$!$fc|aZ z;<~LVd%)!mF!*H1rtC^VnnIxfyr0oG<$!@(AhhQ7*4oGE1CXJvQa8ZCAuv{?>~)`a z_jdO7@0nJAKW(FOrOMBHr>gjo?x`@%c0dmwBs1Iczo18_9Bnu!L`0%|zoeM3tP4K0IjC^uZ^zrv=D z`cWX{5w*Hk%%}81B#rp@)cNmMEn1XTr)4gz1ERNx`kvD@x&QzG07*qoM6N<$f_r3t ANB{r; diff --git a/theme/light/icons/publish.png b/theme/light/icons/publish.png index 5f6e45a14de8a7807b928dfd3c724d50106973aa..47b181df512eebae315a7219c6c1e9826b7f17c2 100644 GIT binary patch literal 5883 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 delta 1262 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 Pp~0vQ?)Ig2TXz00>yaUn delta 2539 zcmVnITXXBD#q@o2hP4$pID2ebUC2Bg@roy0fuHCAkhgrKRaeqj1Pe-HB) zKHid;KrAuEc;FLKNG_uz$NHrCYmYF0=O^q(`2X&{xgI#&@_%Uk`j_(_pK+f)UO3mV z<^H~r{lv+hXg@e7^t!S`r`;Nf`)=L0+Y*!d>ck{`Yie<;t*6QI@pb;%-ZBFJo7)?K zfl?zZA&wzI!uG#R6>-Q^I0XxxgLVB2#V48$bC z&(|U@#+T6Nc_mzO2GKCeyxaibqd6%5jZS+K!;lr=>m`=KpAdG;7bXqO9F!);Fh zh_E)n80*2n5v?a1?ZM8mwZKt8P=@8s2Uoy=C2|c$y5N0uhMhQF8}G24V;#BZ^fqD> zh#+_tgMXXkGsp^opdStb4Q28ah$s>xuu-BMeGHLej2tz{W%eAHI5M+v;w)Z*kVIh- zNs^OG9x;$oOq?X8l(RD#3uVleF+Sr?amANVsKmlWN-DWRJ~dRTv2vA~YHqIihA`1$ z<0dV&+^y+|6g_n6v2&N6dLGc)@FNTwaqy6lMt{Co+pIoZZ?I-JYi=^76AaLSx=b|eRuA`EyAS4m#G4`c z5pV80=8RJJUodAt-6L;LSZkW+>SSzrp<;S?cpsj3$;L_6q?Ui%{;wx0B@93&Q0wT~ z@qZWzy`f1vX8}HD4IZ6yJ)}=vy7RMb|xJbNU$cu@qv29z>IZB#v^fPc1Cz%zvp& z>~L&9rxMf!QE%#GP*CSqIn$oZhJIv(gad+DW>Ir-Ba4a7PIN;=VNfPY8F7j-Af))3 zpp)V1K%nz7*!F~&W2rY!Quh9gY7|CY8dbCQ4%H~Xj6zaPZIDJ;ioR}QJNt0t*+&-H zou@6&^#zkV{NVL$9Y)Sg4uAAD8H_K0EtZdconUX? zp6)8t=Ghjf`cA3hZ;Y6%l^X zWuiQ2?TO|d!^Y>RVJ_Xp)cxEo4kd8!74BM!$Va(T(u9TFS1fbbUU}5lp?_AxC5~95 zR=84M7WCS%&$*JSIEgHTuxvvT1PnC@5+qVyEOQAan_g}6+txfhn0~eWD|#x~@|Ht6 z-0KiyzSs$=F~a>`agmJjY0m&M-na9RD`UZf`VC z+|lyRgy#;_7xxaPn8Wr(*S5E2wu3&?z2LGNWdLt1xDbJXdN=H4M&M8rp= z2{oa}JB1p4AXM|!00sT2|7jUo;T;}%rlR7*P7DNwD)(I-;pC`MNq>RV#E&FrMt0*& z{F)NePW=Z`KhoUPofy zUYtQLsGt$U$(fJ*ErN1?U}| z<_$c*Y|St5y%Vwd6BR!bvH1eww?t}QBl@05&D*HHXwB0HEss-(M>%2Qfx2bEgu>hd zCT^$&knDhVFujY-CG9+;C-;X;vkTPEsYWw0jssdR0Y-CEE`QM~Jj)d3s+OWwODODf z;$~52Wt`k&*7S`w5#F+kNUBbA5?ne2)XdPO^e=tVz;EQd43u7FV!y0000CP)t-s01p5F00000003ZVgd@~zVUz3_IDg~@1r;0u>kF$)0001m zNklF01R{mcpKP2o=P~G zj^8J0L`Ptss3?)?4*A3xazs2t%oj1;0qN)dt_=i`Bd@T~2}b|`002ovPDHLkV1f{L B$W;IU diff --git a/theme/night/icons/links.png b/theme/night/icons/links.png index 4ef5fe6a4ea0fefb84f6d8792f0a83deb2cd6516..0bc37c7303a29c953aa1023427c87a25732d3bd3 100644 GIT binary patch delta 4776 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 m9AdvjXtqIL6LOW z0}PMOOn3RAoqzOW=ZEFuHzP~CM}DxI+(SG#dER4m4`b}d>Pk#-X*w5mEoIfvvBKrD zUQ0-ZTT!=_GLi>GZv&mawXGsG%Ea*gph%qx4cb@Ps9}nccQ%yFjb+(fj5D`M4YX#W z!3I`c955U33aot3h1R%v4cEvqa3}O+f*8$E2s8VR^na<)7&GLFHotv^dGQQFOk@GptrD;B`oam`r_nv#%X-6pSao0WV zw)>vVynjf|j&@VSt7ex3&ErHTGZ5oQAa0WY z0-7hY%_&9>6WwICWyY>h#w>Nh=`@LfVBCpy(9P}-xd(0`#((0*7r8LeeFwQP(S1Vh zJ+}|2_4&1Fn}kJZZo}!+O|bo%7sjvF*dnj`{JQ}?mh0_0Yan9G^=YI z(DbUAO2oxXc2(?oMF9O6L>N(-S;m|sCE+=~?&0I>U5saWpZjz4sX2=Qe?F0TmKkQ1 zc!PL)vubeOCl0ZqtP-CSkC}8q;zzD4F28XuI4tnYkeN=+6NiY!LI*1y%!;N)JVhK) zHJ$Q>jLRzLEzVlG##;B}FAV1Nm1VBe3?qR>EJ1<@1vQjVMintyby6&(Xg}`ZA94H= zxfF7hz{s(H3N*-$AN&t~e|Kx;CnwybU>xXvvF(phpl26o)@}Ry*tVM|fd3h|(%b$@ z1DN?Fz24R$N5H@~aB-w$} zW-%}}HDoX~G?V!eP?LEPTqHGRI51;4G&e0YI59LWG%z-1En+e^V=Xy2V=*^nVl*^4 zGBvaL5vB=$5BRGs00006P)t-sSp@)KX@pQaGj{+000DGTPE!Ct=GbNc000SaNLh0L z01FcU01FcV0GgZ_00007bV*G`2jmJ45-|^=F7cZH005s!L_t(2&$Z9H4Fe$z24Eu! zmyEy)Dbq0;m(3W35hy7v6V8v5E~$@(Ps09f0G>O4WjNM_!!aD}SQnbAuC73@hDx4f zZIC=WdUnwM*RGcS_pa`?Po4sTG%W>5hqlOy9@sb3=ruv3_wXt}hT3!{6vLs|4_UV^ zEg8BX_B_)058M}?<1^tD6pj?{SQ{kx{2@@dgMkH^eu2aA)cFGWC2RIH9S%hR0000< LMNUKnu0mjf&kN`7 diff --git a/theme/night/icons/newswire_favicon.ico b/theme/night/icons/newswire_favicon.ico index a368686dcbd23e359418e554f1059424ae6cd433..6e3efe2c90fe4071b11a3cc979bdc0ad2d8c659f 100644 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%b476k>7y%7#Lt- zL8~w&)?94vfQbd^Iq`1I0`&2D1t$EBB8{iOJjT($p!Avqt2`+@j4 k5I+Xuk3jqbi2qbGF#I!SVEE4r#LZ9)Wd8&4f$|^>029-gEC2ui delta 38 pcmeyzag0%bfq@YS1q7JD^ah5B0wRozlO364CR?(&PPSul004KS28RFu diff --git a/theme/night/icons/publish.png b/theme/night/icons/publish.png index 0fe148eea2f959f79ce9f5ed82e990e2ac7cf280..13c55f0b811ad39774889d935c6978b3a99d092f 100644 GIT binary patch delta 1614 zcmV-U2C?~oFS0C<7=Hl+0001xr{kRf00Sy|R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U!4U>?RC`-&sYLU`Ysx<(d_)dW%~MY4K#4hKE*SXnD0Q`T#MQ16_btp{!rP*4dfOJI?DlfB-uBLMi~M;- zF;1GuBHGA@S;`nYoNMrBpN5Xcc`4F|LvO(~zm_s=;!{Wtv@HCvQszZc&>LD33s^|v zn6Tt^Lzd$Ypnrr3ur%+D%5gjMt8bn@`a$`XW9=|mVQ^Esb-%^OJ$efdD_brP`BPE4 z`xmU2ob*jD_cr`ukw16luNIlyTO99H$LK!BxfX|5V#1lzKGb!THNwI=y|o@osD?{1 zwpBB#ALVEWogR*cjH;MqVlW;QsZ*gr=jt0Zm>BtFLx0cQKz+&_EHXEl%;T(q1{=V4 zgaAuJtO&$>+d^yKytZrPi2F`h$pmFI*U|mZxoF&bB5^f+gGrQXGFt{Np7}S z1t2uGPeC*+7t`(NuVsQ25Y**n#{tVxA1bQSGqz-Mp4qRcalf(1`c?oJVQ+;oBp`4k z5~W0kF@GBo{OI6QU^z?VN}&;{a)*^1MH1j33$QnyFKFdI;4H*5}8dEn+JR!*c2&V}AMyAFB6^c+H)C#RS zm|Xe?O&XFE7e~Rh;NZ_<)xpJCR|i)?5c~mgb8}L3krKa43N2#1*Znzq)SShCfPhFm z%M8;d-XNadv<=St#1U4MRpN8vF_SJx{K$31<2TL)mj#{~G1IAe;s~)=Xk(>~S<%#p zr--Afrc=I<@mS@&#aSy?S@WL!h2fmOvdnc_!$@EeOOPN!K@}yGVIxMnPKt#ToyUFr zgRWm9mqM-*7&#VDfd<+2ga5&Q@7`MZ$q6qh90xjI9Oq*M2<-xmn&W&QJ5J*S2tET> zddpv_12dnb*IHWi2En;M3VPrWuH)S+8W0U^~A0%dFW-%~jFgGnXH#9jd zG+{6~En+q_WGyf@V>B~3IXE{lGBA@u2`3~lGB9E{WjHe}I5}oFEi`0iG%aFfF=Q+jiohB5&!ORL4`aw^hGGIy=58#Sy0M=}X3M}@+rvLx| M07*qoM6N<$f}xkrtN;K2 delta 2113 zcmV-H2)_5SEPyYN7=H)@0001;w}I>c00dupR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?N?it+$aqFXBD#qOF{^i!}FZk!7P6+7`v(~>7+ZI{LC3s<{=A7=t@Aj{?FgT z{e_1~z4K}@q!11~KKaBMop_v2Sv4Jg)$8&56UJ}o?*70a34dBShlw}6gN{D!aoS4g z;=cr49m?qG<3rAyUf$Bn7Rd2c(6&J6)O@r(VG9!87H!k(wkV`%ZPML<}qkd1{ntP+x7O3KKndHnhx@Wq;8kut;2GV(w>gG}t6blZa@j zh!p{uZ(C^TO-o&+h`4VAP9_MW8CO?v&x5a&Zv>4obB5@Mx36FquP~+QMVU3Y00@ok zQy}V=i|Kas^D}`JAgIgD5ev*mIYktuM{LQ&d1k+&_`PG1wJiWHf^LN|BtYN@Buaq} zV>TlAF@FM|49i(O7jl(Il^d*NFA@L_vH-d<$7u1g@=3<-2_O_XX@Dk423X!J!pD4c zh@q^aUR8sdMopT7gqdL>SeV;zHZgB%!OWs1%Tc0_8bh=gV~RO(5{GSC6Y?>|lv2)6 znjtx3b_Pezg^Ltlw1i?MmQ->@_*7rDhH5p|RDW~hCaxioTWHo|OD%Wm2&L}3_Ry`z zo_Zb#wIRa~9bwprBaM8cwo!eH+ADHDMvXRVynqsi%5Ky^HoHX7+)i|22E^DC5Vwf{ z0-7ggM{$cH|OIo;l{7Xxe49xK+a9* zUVo7L7Ps%9*5+YQ7h)?5-E!(7V;+5(@hinw&#OPr(~q|9vlIRo^dA+JY8=I+1`{N3 zLxB4sHd-1j;O4TZN?y4K91NJDts%WmBDt&c)piQXM@Lql8gScOIx)rv5OGRZ3otq~ z@?(g!oW!i)S|j1HKy#swkVBhyzbW~bTYvX4#;&22g!BAe@3~j^^(O~@rgcAI$i=5; zO||iDFiJbK3;OQmgMV(D z?y`|qvy9X3f)z(JWZ;P8c~zt3=%3o;J{3fojLr?>8*tqh!{v$6mH0XKwcLqkwWLqi~Na&Km7Y-Iodc$|HaJxIe)6opSy zMJW{p3yL^os7@9{MI5yXMW_&Jg;pI*UXzgn8h>(hQgo3L?@J0TVtnv;ALqSuxbI#- zsF#^$b&La=Zkwq@T+C!w#jaO`Fn~S)oiekGIY~;wcYWO>z|XrF&+>ow=ju^&76Sqz z@hme;n|OnGdeb&I?-NH@QC5l1iN{PTkob}7ipOu93oZ*hGh(Jw^TZKivCzg!8?&OR z5r0n+M^#Oyd?Dkp%6W^kR<5#Uo&1O4oW8Qmb=t#7U=d4@AVNVEC6r+!MyF1Sg%sV# zef&eNUm}-6t`ZnI7Epl(+4Y0Z;CFAW{N#j}6pqtaUL5CR1nAlY8a2oHK6aeO2@re+ zuJo3_QU_)}Nw2lE=n>Gn4P0EeG-VIC+(H2cpA6ZQT`5Rg$mfCgGa6GC=)VO*YhK@) z_c(n3($uTu4RCM>j20++-RIxk?S1?AO|ySL@rZJB5D5D>000k*v+D%A0wg$OVq#)5 zW-%=`HDY8fG-GBqEjcnUF)cG>V`F78FfcVSV`7ua2OlJ3I59agIX7i3IAvoqEi_|d zW-VekFg7h>IW#jiFk>@eG&5wA4G1SBWin$nFfcYZEn+q~H7ztUGi5DeG-5R^H)b_7 zI5{|GVliShlUN8s4lpw`HaRjeGB!6cFgP`{s0auGBr`ENFg0d1I4v_WWn(QgI5lG} zVrFAEEoL=kWi~c7HZ@~4Gqa=%rU_VhW$V2F002l(OjJbx003ZVgkfoeVQGY6YJ*{F zgkoxhVrztBYlLKLgkx-kWNd_GY=mWOg=KAoWo?CKZG~rUglKPtX>WyTaD{4cg==wz zYjTC^=fpCTU==tD(GG$z3`IAwYqbbC75@KE z9b$loscRR-1nv_sdN$fiQ)4!5`nqgf;(&C*-vh>X-U3n*yDmW2c4&7N9Fqu8ye?i6 z;Ak@eCL6#=4#Ew2f2p~@DtWa&XuB4}!{7t{sSnsL4KRcRq51`T?@xmd%GLTn3!r~c z2cWuysRkecV62LT001!n2mllb$IS=1fa}d<`#=JSwu3SkAjnf_Nj1Ch;=XAb#@?4) z84*BRg)BhI9GM0oG>1;gowPf&=S r=?fgx#2>7B>nQ;))8TpX3+fJV00$Ak4;}*000000NkvXXu0mjfuYs}{ diff --git a/theme/night/icons/scope_blog.png b/theme/night/icons/scope_blog.png index e3cdb1b81c61c74c8309fc817f31f63ea321d815..f733dd937e514869f658750ab3b61086232d420b 100644 GIT binary patch literal 5681 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 J)y8DM^=}YUAua#_ delta 922 zcmV;L17-ZNEZ_%_7=H)@0001;w}I>c00D(*LqkwWLqi~Na&Km7Y-IodD3N`UJxIeq z9K~N#MJ*Kt3yL^os7@9{MFbbELJ=y2TA@`3lS{v#Nkfw2;wZQl9Q;_UI=DFN>fkB} zf*&AGj!ud$QsV!TLW@`rj{EWM-sA2aAXJM?v)aZ0O}EWNEPpDd(yL;}D+1_87kV)x zGs~Ehq&QlRuY36TdKckYtDu$CyB$Vrc=I<@>u1(#aSzsS^b{;g~5!z zvdndwLx^D!OMj3cLO~e?6k#Jmt4@lA1noy#_y=9TL@tF~1u$|fpac!F>j(dX-`!f- ziE%F}7zNs29Oq*g=-35n703BLcAVM?;C}|L^oGAw1*ShquQasq5zw;@TwFIaWe>RA z0S2B7*_2($PgBTdf%h}|rZmuZ3k24@-s<}}eE^cwRe$^jI5-4Ga+JN^;@zFiz5RQp z-ro<7UUIS!Agdh!0037|OjJbx003ZVgkfoeVQGY6YJ*{FgkoxgVrqn9YlLHKgk)=k zV{C+EY=mWOgk@}nWo?9IZG~oSg=TJqW^RRNZiHuUg=lYuX>WyTaD{4cg==wzYjTBb za)kiI*bYSi0004WQchC$3gikG2^|n;-Vuiq{SleLOcDnL*3yj^gC zDANmoR*5G8ybeH<0QwyOa5hfX&Vzd)F(7bv=Xrf!g3BzR6j*U{fOT0Vy>s;gO#qfo zUR4%`O!iro1KR0-fS?57o&;-OG`g!1NsEJwx-0S04wiM1=RQ)iNlhJ80gzrBKtTF7 z!q8g#wAaDd6oZUz0PwH}amxb-J@T*9qy(7#NQ-5WH2`>Kpj81PGm^`eSLg}i-vzwB z)dC<{2Q7eTI~aQbEe;53bVKys7=zN)C92DOjcijFphiD&2+)(6jF}DtLJv>}0U_Tf zm&RR@$Ck1KT0V5gy(t-5*D-)U!0okI06_k%?@CE)@o9koFPy!U%IwdxzZ1oi%jPX@ wupl^xZ{x@#>mNgGj_vOQAUZ#hzV%ns2gTn+a diff --git a/theme/night/icons/scope_followers.png b/theme/night/icons/scope_followers.png index 2e420954c566bae1bc43f891b2ad78ba78dbd37f..aada82f0ae1afa055435122f226c3ed478d9cfdc 100644 GIT binary patch literal 7161 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 Lndz2myPp3qS$P+F delta 1607 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 Z@=I@CE!~Ec7k*8HkZoOU%B=!-{tFz!#g6#=Z zp6$-+_7k_G{ov+MW@JXE?IgD&;nPn0-X%UeIXo<9f5&Z!b~dl~-XKO(_=85=h2&T@ zpOFMvNRmA5sA1u05%fj;S%1~fUDCY~@anzjchqf{yBw#Z+p@xoPTNlUUcaT%V@AYR zWPZ%ZAy`D@ zMi|KiXJGtX+^gaLmM2GJ%xz9|#7isW#dCiljY)3S7=HyIG`6O?^d)yT$Llr03JB_w zIbwnND5r~}bl+RDILo09L;ZfG$=Y@Ri4a?n3<(GvfkY|LVa!GZ9V7T;WX|HbfB>me zZpf0oNC2G3GdAWJt+A|pnsK}Ugh~(t8oWS&<-PiV_bp-zWv8NERfC#FO`3xQA2ftu zVV+aj#DBc01v875EJukxY7Eh0j49^CNpOs*OCec`DW#kdH2Vx+8RIhyg^Ltlw1i?M zmQ-@Z_Nl&V4b^I_spiH_8jb+hLbDcIYPnO#R_eZM58Zm~spo;cHe~prBMcjHq>(Rb zo9Z*QS91SNjW#u2pyoKcQ-j&;=7Q#NqLUejv41BJw@CmSnkTa(lJ`8xO=d@8aU#md zQYV~7lNbp4POO72c6V}rQ*UUdOF0K=UnxrZ^!5 zuV0Os@hQbu&#OQGKz*lm-<|Mr^l|iY^l|iY^l|iY^p88jKMx5%r+x!Xpmh$ z_#gc4f7U8YO?pY;1knBBI3J@x&o0ocJI?p9<1|lz;4^TgxBb-yF!M=zy{$!$fc|aZ z;<~LVd%)!mF!*H1rtC^VnnIxfyr0oG<$!@(AhhQ7*4oGE1CXJvQa8ZCAuv{?>~)`a z_jdO7@0nJAKW(FOrOMBHr>gjo?x`@%c0dmwBs1Iczo18_9Bnu!L`0%|zoeM3tP4K0IjC^uZ^zrv=D z`cWX{5w*Hk%%}81B#rp@)cNmMEn1XTr)4gz1ERNx`kvD@x&QzG07*qoM6N<$f_r3t ANB{r; diff --git a/theme/zen/icons/newswire_favicon.ico b/theme/zen/icons/newswire_favicon.ico index 09a2d99636b29c3ce3152f762f39e7847b572882..c1812c8bdbb775a9d6856a378e30ce6ee98e208a 100644 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%b40J5qW(36l$+ZBa7lud8U|@iS z1+BuASaY$t113gnJi*kX)5PirsYBNb;?qk%HoJ+nAD3QY^^@X9a@GC^hU9!8?g!%I kK>QepKLYU&ApTR$!0^wQf#E+h5H~|Hko^zD2g-vm0KE6NiU0rr delta 38 pcmeyzag0%bfq@YS1q7JD^ah5B0wRozlO364CR?(&PPSul004KS28RFu From d72122fccafffb7563406074c9925c9d2d88ba55 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 20:52:58 +0100 Subject: [PATCH 011/459] Icon tweaks --- theme/default/icons/scope_reminder.png | Bin 4132 -> 9399 bytes theme/light/icons/links.png | Bin 2257 -> 8103 bytes theme/light/icons/logorss.png | Bin 1458 -> 8095 bytes theme/light/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes theme/light/icons/scope_followers.png | Bin 1424 -> 6662 bytes theme/light/icons/scope_reminder.png | Bin 1484 -> 8807 bytes theme/light/icons/scope_share.png | Bin 1617 -> 8777 bytes theme/light/icons/scope_unlisted.png | Bin 1428 -> 6559 bytes theme/night/icons/scope_reminder.png | Bin 4132 -> 9399 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/default/icons/scope_reminder.png b/theme/default/icons/scope_reminder.png index 80937684043b3789bbc6feee0d8d176f355441dd..739ff6d1d16f07411dd5b7f85cb09d9f41d40eb8 100644 GIT binary patch literal 9399 zcmeHsc|6qJ+y4yNNwOqM#vmljSO$}Qr|hzYj9Hj4#>`+)k+Ni0wnTQamM#05k|Zg6 zwnT-jSxV#?>b~#p-}C)_pWpZO`aS>MUav8qbI$vEU)On`>pEx7%uQ2cJr+hDMgRc7 zVxX^WPI*_~f6>!Y{ug1rCMf-i{+2c*bA&GlPw>QG-O(VDA07=tld%{8fIM1y$M)6& z{IK?3U4&z>;_dEt+;Mb7P5NwYo3V{wAU2JCctz_ilCt^@V4_woDb>s$ov;1OG?ry=%}5_b8R!!EDAS*<#?5%c{0xj^DB3kUC} z_mdE7m(b4D_uh+>Wf{~(v(p84JOWH880#eX0IaVJ93mZj&wh7;qDs4@|H>tDay~ z8Ac^3>ZxK>HR5x(wFLNJw#ctU=Yijs)Uvsb4hLdEA9KD4N5bVD-epg^)Jn-?0(0*q z2GPRwwxJ-=j4>LJzoXYo^7RK>-_PQ!L7$TEvePEzED073xwsqfjnGuh>h^klOpV#) z0~~|axIjmPTiL2M;Gcwe*sGt$GF)b)7D1*Ls%JJSq@U+;@c^WuP`GROeR>2O?jCFRj)Hb(sl=ugmTSijf zILyx(=9Yx>tv2Y^sYiF{03)S64XUg_($S6CmtF~Pmv_UuVwO%ek~Lj z9)H#}m7n%qul5d;lE|xF-e=<1mUw5Hr#FGsQUm$?R+2Y_S)LSziR{)Qa-GfNvp3)B zd8e;KV^u9um_q_KN^mcyU3x3aTa}FNdPThy#|eBjSxTd$^9#O~orW5eDHeYsFtSb0 zJ{=hS9(jLlw_Jc%2iJ>H8LOnLPPkO*?UT69eEKox3(rd5x{xkv;*eYp|G)-DJxi2T zT=%0qL|}0oj{Y&mFA#f?mdAndRteKG5@(ZdEup&|O54sL#4Ta1b%8d_RM$EyDOkA|UcfSSZ*1m(h+9C!d_ZsB4inFDMGu{V{+4 z>K<^syCyWG$o!q!;JAI7R@Cw66)6>ePnR`#15XxVGyJyK6=GH?L!;{{+djXw zuug8fjLs6#)3~CMwRUusIqS2Cqmsokm3B8j>o<2mtX_rMFwAqg-D~C;yTsjFiz_{B z!0^eopk=MQ@C=@{Fk1%8%!a$7~%LXs(tt-LTZ>55pOTMDVk3(4D~F z_fYGh%~)%H-p?nth9*X;@TC{^@$@5D@AS;vgnfishwEH4;GKW&H0KkydGZ*Yx!&^4VAGK3zJhMqOSeCk4+@pd zN!1+2M4xyul;)-yT{2NCerh8e%d2xS82D-{T(;P;_yydb$%=J~`Fz;uVxr{q-R3o# zX-r}&qs~+PX3M)>$yKJRmdEdL9JhQ^f+U@ZQ5*xx^DOqgtUXuO6E1~4H2KPhJaeHc zSc>|1QtnVxrEn&9ZDcM(^)^**^bH2rT6<$Y3#u)0-d5dM_R_%8PV*2`7!PAJx?FE* zj&3hJpr39p;i1b|zodw{49l9aK-b&U=>}^!-qj+r5XgF=gD2Ds>#=h)HdTVE> z&pNFCc$tKaZN-g+f>ELPQ@{Y-OXLX4LR6oaEM#zrEN`+p+Wm6pjVSbRZspew9T^{* z`C95~tJ%6h)0zkU$xD0jI}Zvk?nb=%biGn(dXU~Ft$dJU<}=P^o&VBwM4f$kri9Ab zjo#ETviSD>(&|eYRkzp!r+^UKPr@ru$Lpv^V$H!9=U=`~jlWvUd=~cDuy<9v`hmNe z&7Dx68kbXJ*JgJ&&na2d-gMDK!~`F8=TepFlu~ofx|1A2<*Vw^@BAKcmdB4}N9Hbd zhE8c!lmy6x?y>Kq+1V*vj_DrrSPP%Keym-Tl!^SIwuq+{Jvd)7t(~sD+_-?gNBry^ zUil*Q$oAp&vW)hzQi%1#@(!g@9+d^gC2r9xC3JIdb1$!eG%h5!C+N9d0io##WZA(QZ;EnZcw(FQFo)IUQCzeHVXU@l5o@DdoF{!6Hg{?Gh=yr`nI0 zPJPkfaT#WCS-&f5Nt>ucz%lk2I8JqlD+?;c&wscX-T3OzGbSU_DoC|Zu+9o&sUY~^ zJ*Gqg3~RdwEvJWN90IXrEgWrdJ$fk*Ug+}i(L}x3(CjEXyH|-rjE-MRTbPLnV1PYX zG$^0RdY~-pRBhz&Yq}3l#jnLQ9wl054(VmO-Le8ZcotKYg=;Xk6n$P^=*#qPoZh*0 zkx$(V%3^pfK`IwYM)5A(6!fTbAeRjv1{KXYQCP8OTV)l*>UoiilqeGcahRKDet+8 zk5y~@q4IIpw7m5Jyzx0_bzc-(9Sc=(8SGX#FCDGp^11L~sLq?jlT%@=m4K2oop0 z@sn7F%GI86-^5?xYbess(IsBqh&^9L2kk3QdTw8Qa=B~ty%f#J zM4pp`ZUd)Kn&cQa|Fp8A3$k%&n`!l1zDfhQG1dK~$LsnIsoafR?=OYktj<-OgX3p6 z>o-F=px)Lh8^n=@B1dW8q17bQPk0{GeXFo{6}TG(lT{5D#HDHKMXumhQqFJmZPo+` zOc)6#n03t2OQnz>#Vh*|o$Hj!dwOnqc}*9Z-RlV5P8<#LUd>}4@j`_vYMtgtPU^3h zdp#d>f(P3(De$F0B{1Zq*$=t*Mi*vAlg_%lZPaq0wd<4O+vQmHt*xwAdC&kE-&xNK zG;6qqwd8=Cym1g&eEC6zPj%PQIfI8}+b7xIt)QuLq9VGe)OG#bLsqcarSp^Zmy-1b zVIwk}AC`|e8N@+fVzL6(eB4jwjA~vn$Gep(n^Gp^Tlm&;&XF_wUNy5qR&N-lZpLWm zieHW@eScV^S6Svvnsj?Vs@?5`2ZPT>?Yw+^A9s&TjK9*Jq8RDI*to8Yi(x#|G{VEnW*B-uD@! z+;$y=C9!k^|3$GvIx8>a095gqebUh`Z%-k)4B@Ec2W$E9^_m&-h>zX7(PQ5LwU)-$J-ABBaL&f4DlP|1p07{WD0O}I zWATiElbu;o{*_xtNnqcO((dmLP#@lGU*6ZF8iY3MicVN=cQVT%aT$Rc8*>LUT;t9t z6?g{a_ZA!_80#QRG&i{VV9Y+PnBtl{AE$hMJKs?J z)yg|3Zvyrqkz>4x&WJ#tq?G62sbhUl{k3q<^&Oq@?m@Zm#Vgr_W%k15{GHo2%NHI7 zUO&#yU^cyY%UCycZ*g|r?~LPI%dX0M@5*zJvM#3keE;0Lxi~Pn2m9i0x%asy?0W&- zMjI_0TPpO

HitPkKQ#_4c=kz-*;h!@XG(c{Ns!AE;waqJ!mtREJU(flA*{tK`wy zuv<;uGjZ6sV;5&DtNq@B>_T#-3X9jJAQBEABebR`HiM@%%f7NU?R?DH4jxY6(8}Kt zSdD&rVZ60S9smH|z-nok8fa<#afhPZoYJnutLQ&g=X-L|A}^aW-sgxHy?OfS@Ce0A zoDrsi{fwpX6#H!&Bc59v$Mo8&r=~ic2il(4TGLr;9N7VQ5sBuc_@jHt9CjW4__0{u ztrNb>fGjKL)B)&W*LshW@1f9iTTbc+^qB&J>rOV9B&etXLlytUt(DacspplW8Q+QP zMYfSZ)p&B!@qD934do^*Z`#()$0eZRj*lJ4lMZoT;};@2J=4BOF)7Qs=imC$fpCUS zwi_~41{boKu<2PE6(03N(!j5!;lRz3bf*B*k+H@1F z(m9w6Mq{g2S?jOfv}de~aGJ4EKhA?1V*Nqa(ojvf&F*Lpp(QmdzU-twGE6gnzVn-^ zPue!aR3L5HdVY&NHAdCo0j&BN+p8KDdwT_WG;8e`koF$)NP*Z&rj1TDjw4PlF&5qVcem#X*VFhZ<1C29wBKfPl$5aWsSsA?zA>W z=io?B4=Du7(-|#A_P|r_ya0f*8X1p3x}ixRXS53zrvhGXXaR$;C>5}^yb;6*uZ4ES z>iZMW7XHSTNPjn^5(=!Q%BW0+QwTiJBm{`;;f^E1$tvIjUO44>zgrp%I)ISeRKPYy zrXVd(0vaSIB_{=u)FESiWWcJ7AY}py12@;!{Y61(seoNcBs^SN+Sk`t%2!s(li(r^ zRZ>!thR8_E$VgHUl0-io2|<>`5ry_CesXA|iAVw#Pr`cQK>M5sXHRdE3K&co2mKc5nk%WGCig))W{_fM8h~Doy zh}#{7k){N7;Q4nOJp&`tKW+A9bisPy4=nc4zavq|KXG_(g8Kmmg_K6Sqdh2r5Gl;i zzu`$(%wG%ixB2Xk{I@_T?*8Qe8~P8u4q`db6|U`x^xk)BpsfPlj~9;eL}F3!gGX6} zGX{xKKuN+N@-mWga?Xm9&M2guq>PLV8ZCp8SAZ!f{zhehBa#p}Bzm8ULN0}+@L(`d zB^cBhA_+sFoh9X*${?K4vd$$Q+EIkZ@fJ zBZ87_SPz5?S{jdYIT+Yq7C2=x6k!qjwLxJ%=%>sDu0=p2NS*{sPfvFh@P4eIeaeH9 z0xAD07Wg?&(Mmw_g>fiExoVXevwpJMe#FvT*hE#r?nW{004!MT0={^(45N z5KNpep^>D2&-1Uqe=?a<_BtYo;AikZOzQu@DgR7YeTuCo!S6T!7HF@Zt)Cl`JN7^- z5a?hNfFqDUr zk$^zq5D56^@`9A5_czo(rl-8Wke-}V`e@lWJ>}Q^ZHy-bfMHBum%pZyJf5H7` z|0^l~C-dK7KdrSq@qUz|btRej;{L7se*yf-a1M(^p^*{}K3~?D|Kp|44!V2>efW z{eP2-@vj>;8b|q{=S#UiPnGo0QSRQf&S&+s4{qUrZ(0q3lqUwfzAX^|U}E2Y0RgFL z+>}l_l7W#9-7Fm&I|uzzPYmS?7Mc+QZ4FEE=whmyyQL0CXJBQ+vtlRaQ{uEn+LsS; zu;&E#X;YiAA7#5N2%UKZT1^6TKh7Dy_O*7lZ{_IQm_u3ASErad;<$6n*rHjr&BWtC zuQOLUk<{FgrCH;vT4AX1C7USS-kS9EfDdVVXzPUe;_W?Q4;3_~!{7UFZI)->bTr+; zbZ%@?dmeugk(MghxnUT@-g@4kJK$QLUDq2W`jMnEJ}xl97OAyS7pJTTJn;}T=e$RI z(p4nz*}DB{rpY3XS>Op^{+Y5GHk+J8cNRg=1Z`$l!J11@$aQe?X?o5>lA~JJ@>&T3TtNH@8*1kqtJO!$%7MVA zq=xA5@WuPFzLkdyvUQzaTy~Pj*O?r%09jq)RO5v+<Gd_i*){O@#T7@RLmA zOdW}D#;@-cdmPGG{E}wDwW1J^Bfd&~14+~Py1+nc$gpgy>pN8aJE^XveywFV`+TE{ zXH$@sIa7yXCYS4$*GpgEuz=|9lw6_@;C=P2YD7YB7nM2gGn}d81@i^fKEnz2xQ{TW ztD!AwmADP>vWiZBYC!3XxKk2kxq%mgZi;*FRSFIpUTBNfxBK2z|9S~Ha@b8oElNB4 zc97wevr&tY#5W^)#};bo;S7(~v#mx=g_j@Lwg#qEG@kGhgMrn!V4Sse{5XqRYv`1-?l zL2Rh#g1a044Xb=HR><+M&%K&-GoR-_|4|L-+<0C;I*Dt~f9+t`)q7=)k6P~bJNf!N zt10x2;cVXNMZZ{J%z}}Z{qn=oPQCAB6b7)#iZMHpdB`RVs>eTc_l^P+J@l9>?Y$wx zrL)ssLJ(#2Buy#G;`O~Qb%&rw0csDmCH$&P0Aj@}p~oM~oZ(-4lmCcH9t6X3?upWL zShUv4%|3WGqhvw3MsF_P9Zuf>dZuCp!@Q*xfAsh z4F;vCS8$coHo+?evrj|kjZ0DKEcqKK@(9)ClElkr+(o!+Ebhb`xaln_&gZCp(x4g= zvFoZ-&wKCBNTx~QLGf^QwePTAxJ6Y3=!k)RMMBRgu_JASoQx&!N$(Pg>xou2yt;aO&`-YErb`_ka zUgXVcyr>@tbfPLd7DXe>NE37}!iNheh6gY{rGZBPI1~ZcuoN~yI1^9M;>Lg<9cWNs rlquy>{YlMu;WwUn`Z}k~o98Z8s;sYRGn8=c9}5OL#@c0?PQm{L8(ysv delta 4073 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 Date: Sat, 17 Jul 2021 21:25:11 +0100 Subject: [PATCH 012/459] Debian theme icon tweaks --- theme/debian/icons/dm.png | Bin 2420 -> 7053 bytes theme/debian/icons/links.png | Bin 6027 -> 8551 bytes theme/debian/icons/logorss.png | Bin 5876 -> 9235 bytes theme/debian/icons/newswire_favicon.ico | Bin 198 -> 1150 bytes theme/debian/icons/publish.png | Bin 6016 -> 5682 bytes theme/debian/icons/scope_blog.png | Bin 992 -> 5681 bytes theme/debian/icons/scope_followers.png | Bin 1416 -> 7161 bytes theme/debian/icons/scope_reminder.png | Bin 4132 -> 9399 bytes theme/debian/icons/scope_share.png | Bin 1612 -> 9120 bytes theme/debian/icons/scope_unlisted.png | Bin 1420 -> 7028 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/debian/icons/dm.png b/theme/debian/icons/dm.png index c0493f82e3b53b208e3b19c0ca713bc43be321f3..ca459e5dcd6a1571ecce15fcfe1d9b2c551c553d 100644 GIT binary patch literal 7053 zcmeHMdpML^+aJlX6sb^R5IHnvjKRz>h;cRu<&Yh7crY0=V`eavLP!o9ok*gh_Eu3j zv~!3QA+~ag6bZ>LhbY=zI6K-)~>n_x_jbdS;%r*6;q^_xi2-Ue9{WQ6~p0 z3Gq$h5C}xV#v1Poew*^H6@uVrm&j=X1R{nEcXj7D6GNd)7Q>fH1E8ERCIAJvR9^^$ z+cV}K$a;fbGxyMCRBB~3v}W*a@jm+X`sczL8VS;VeU)cTD)d6?E&$>yE24yD4Ho9> zIg57_kK<*wQk!)YxUup@aM_U?-atf~)ZW~Yw51-Um zB5Y=-KH;{W>k@i%edh4dwFkmKJQcWU!tz2`|GCv6!}_YMZei@bOxw+=6HKha-ZHn{ z<5IWF7R|)#Bc8vw?}r$SnEznhIMZJtG)V^NGY@jc>*t~-q=#ZBM_&)i?+;bQn{IkY z_-T)aiv88dtS++DM2txdx*+uao-G%Ci<&xEk~`3=eq020d_}M(ih4C5S6Jq-qQVL8 z#OC;-{o)Blj05Pm(xZg-Z7t7gGG7UYKXI>!IA2QN+ob2b{h639u13~F9XUW_(aLcN*H=m%J%(4cJ1TE}LYADMc2G4)+{W2ngZdy} zJ;15;SDQ}i<6{xN@wr18E|0U=8tx@`ST*fJ(zf|kEQwwFuIBIiy%ZJWxQgIO%g+xe zJ2`F`I)YEZ1&@`b?+`qXx|=FxwFx_=E`447=T%a9^}BQ zAgR(CoV?#Mqyrk{EQa}6*2@2$+jhapoMQi*R23g1&sNL^7Q$+hT5xpoYE_a#=?OV$ zi4I(zqS!&pva`6DKWLZh)8$m>Mn$p}pQVjr49YGQ9&L~c{xkaY`|I1Ts;jI}uSa)nQObE-N2b-*KaUN1;VHEujrStUC5|k(diT|&+(SA6>D`5Bi7y) z$aSxKwO2JJE}?mSI=4(%U=R2F;p%HLR}!7CQx_uJ@Qo$51oDL=*Sfnsv8P_&QR}&< zesZ@L`4Da{)#I7~4vz0Vu<~8cN0ltL?33JPnNd}u^rc&s%^T~ARkkl!CYB}$Mxx>J z{@Rb#m}W5*2xMFH&F)kqZ2x(kb;=#g7_D}-3+yePGqppAV*HuLT%y{~dj1|(Gcl=g z;{EnfmggD+p%t>s*=zB<2t&8VKR`5i4`$qchwgVVK&zRfS&Q*Pd5hdINoBvYHD zlY3PR)6fO!MOydTvOfwOiHgFH)jd00PD41zYcZbxE=>5)@<%5YM76wzlV3RL{yU zs7bBFl-xXNoA7MqlcNOV+#$iKBX;91867GrjW&9li(UjYuX6KzraYm0GP6U;gSFT zQ>22xBOiY+0jCMbxh)XSgZP)a>f_cY1lNRca?F(*v;M7UOu9Y?QJTNA<-6}|8X*Pe^T+!bKO4H z60J{-o?kHxP;1{MDHRg&W+JT9A-BO|kK6p^Un`E(zEBcKtFWtWOMgE2MD@~3^wh^J zTFpQgCu3bYbILks^z49r+{ds7J-_0LMJ-)Z3VNS7mzW3PE~iYZaFIow^qF*^>f~x$ z*l>@fI9|~67e=@U=QqEf@2Xs_P3N97i|>-7c%^`oj)u_ZG( z=+t}kxwsRLF3flx51iTO`N7xiDQUj7u<^zEi=Drj))ju-EPp1#T-w2Z{iBRF)0YJW zay5Bxb}=K}o+3=PzRE{G+Kkr;FH)hE1$j>o&lI8)O<$&oO*Exkds6JUvNEq<^4+{w z*M$YBPxq;fBQfbe-@~;SI!&bDI?nF7!$yVrCFUqL9(zbp+PdoXMNF@-f55uVg&AYP&$?{6LKHZZl`gO zT66J1=j#gZKc?LpEhbG5#rv8NRdzPn&6TA??BmNL`{ZH`wR?L4seKyqTwKX<(KcCQ z=ib{x<>y3h>_$CV%%bE_`_eC4B~|=lvhkLBR)^%l`|o>iMU0WBJ1h71Ow?VUA8LL& z6c%{0mz-=cUa2-UQ}&0$>~r)xt%gd<;)KdOV&84_J3HL;6KA*Z`b`2X~0NLLdS$RC9AD8*}q7rzh~tlogSN zv%Y648~6N@t%as?h-7es)7kCu3Fv(r5}cGe#0#()UY|q=n~trQv8pNU?|(?@tf}*G z6LH%iIVTXrW;=7z)>Yey!$_5bw~owI&Ac2pH2pRGv~O^dU?%-cKHjUD;#T)a%WNbU z6GJ`iaX)LT>I=iFajE{ zi+zz*Rn#2pdC%HM$}SaZwqt|p38IQ+g+oH@`m^;BbB7DPmD9}%*U4fwIzO7$x9}$Kd_mouye?1A_o5@c!rFy< zGK)mK#vy`^akK}8>+@8H-rbp%i*VUEQY}>8RavF?Fz6E`FW;?kbAP|2EX?Uc`NN8e zp4kr!Lh&J!vrepAJ8TuSAdt0*RPZ$GPO!(48G+hF3WEe_a|4;+X%_-9GT}0b{d^I!<|_qFNbBrQnp^^auz7(;V=pT8Fa$mv9GHa(Dn4Ls2#{7B}KzL4rVlLxgez zX>>N0i&N(FV!?Y}H(VLYhj0RL%I*Xws5yfLKvCK#Z3N7MOASFP8;e7YSQKBZGv4wu z1=zwV`*S!<-kBatu=0b_^JIYcgu&Q{@3EOFogHkn0b za;OYCl*dUVF@iZbWo2+2`k5RYC0lU8&d&~bZ9YAlLxJ0X4HP&Z5CBIa5NH?z2}5Gw z%k9BY0^zGQo&8xwP)|6Q$b{=?BjAC7-&wFZ79rpK{iOxl6@00HI|FP+FpCUWgaCAo z%5tYnS}=RL&tNvd>*B{vqxizXp!lAb+gRBUoW9!dWb~s3GWixf^l~JH{1wLxX3_W< z3KsqfgR9V<<3+E=doD($~?4k%%Z?m<|y`Lg*ThDFBJMjLL@2<`C&*fJX(A zYg0iUw6B3K3W*@W3;;b{7>cM*gn^eY4296qH9#Bq>X7sh%P1UKRIn0=wB=Fps3;&6 z1))#W*GHjXdME$^L+SY%z|aUX35EppkU9uoeZUumr)*E92XntcU8#Y93x~)PO-CQ8uZPmrK_CqfC<7Gw8)!GcVuMM}g{vInGyZq{H787@4)}Y|k+18LR+D zmLq8&K<4~C&tHMRGC6~L9h<`nv-uB``X4x>rF69hZ5gbvW&T}&przK*hD4+CrGi5F zn*f$bUXq_p3;`(oB>;6?8Y24>>3#sX+kP&!Fa6ZNNMocv5ram6dmsWu!N5=ylmU!H z0f;a(iDE!T>7j{w`b)X;m7UG-<%AMhzz#n!2f%d(ON+nGP<4LxX?!0`s6PPah!#Qz zi$EwZEicpv&f8GGOwWk7y*8N`qml*7+;J-WAjK2$gIb=zeWzQWj=A}OHc?Lcn;a{JR zpVNX@Ise7K&*}DG^Z-)-aPp7z{UO&6x&Dy?{|NjeyMD;^j}-Vv;2+ub|0b9Cw>NBn z4j%ME!T0AN(Y1=;yZ34*dshqoTR88-hZ>{O5kLg*1USwPE)dh(M@BmFT`pQ zqC!4Of|Qd_p;uP@mMSQ!9|k{Pfa}?OZb5zM9{!3l!w4@C2g`W%HcWfffTB~RfpKe% z==R(#S#IuD*q9Zxy!VbGBHSH!BYidmn(i#wlWJVJ>t@s3B;vA$sY!5@fLG57zcWY|Iu&A|vH~R=ViI>Mjb z{K*m4>h2=C?CC4$#U9sC!;$kdqyPx58#mbwejGRa*Q@wJr~*NM*={x%Sg&@LXhJWs z6*tZia)R-s5Ct9g0&o%b7AQjz2wa(>P^L?nl?Z+`092tl$HWZ?kO&Vb$tlLnLBUGC zH=a@2vMik0r@>7CLSl{zY)Y&EDvZz>)A~naHvgDjKJIPgznp8Ba zs%g<`(UOT}Q!^`nRtuL14wRCM7AvLH%AsPTinS`_D`pzE*iw_0n>K5u)sFD#sY}mY zyYx09#)*KqIsyo2cFZoLl-QAT%&x+JND5&nDUMATK)= z6T27WehIfA_Di_&cgO{Y?w>#|0J@L3eS+HXI@dR1Ckx%DS48&V{q1WS@;O6U^W2}~RRnbBSu(4Lj+FC4qHCw56sVf(RWI=^P*ri^Eg)Zyz$4&i= znm0k8v&htcRYub_rOeTDjC1P9EhF~1RbkH#Dh)Ybw_c!;bMb?5a)a_-n5&g-Lqerp zv)9(7GIE)#G#fG7LJbCqylC0ii4B zt4E&-rb>c8Z~5Mi;W;65M43GH*apFgsUKJd8qM#23=ZE2M5|xwoXd>M@1gzFfbJpr zk5MwHr%tJ55gH4doutW*h`msrppv)-w{GY}GRVBeF-nJ@y4C=~-D_XhS=+oZiD&6* z#vrPk!LsOb*D|pFnw2*Q1=p)F!peaOzlh9;ATLxfHdw%j!vc5Wtsy)9=)_hSDQ!C$ zKUNlhu{N>ywdYVn1_a%jwIYIVThk2dkG|^2jDnG>`;0^Ci1_!={sjcX4`TXH5e%;( ze2rjUcNWji&m3M;^VrhJViZ7Raan>IBNy75(ok6t!QGYjv_5(4D14X0w&12oedZC| zD+rkKjCCdPJvS5^37E2xfb(Br;gNN)fCWH*BlyK9fN0f0g3>6|vI)v+1SnSF{g0XN zi61G*yjdPL7-G>;gtXw0>BZ~f?L5uQC3r8!R$ zhhvj&o8HSF%x>w2`9~D_GPM@8$JGmeR?nf|(GUN=K`*ZaLA9cX)Fv^l2iM+e^1HrpR( zuN)BzozI?ff#&LnqN9Vj5DhV3t?mi6*^q6NPkX@XA9xVEh zMon^Q$bvn~*m^@4uLBXn1lqI)=Mq<&Ndm|!9e-o^SVm5*MFHG==#>^RbjWbm+O)|K zy?^lXRMN}<8Vr^kROd!e>SKYsRAYf>sZMvPHm#C}XsU>#+A3adYyZXM{B(pz!k3Ol z=T(v_)r5`D>}Lq0`P@+)z6$w&_hF0)^b?wL!HeF*t5<&pUHJQr@qY{bdly<>I>P?| z4w#s*!M`il0004nX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ%glE4lN?$kf92K z1yK=4twIqhgj%6h2a`*`ph-iL;^HW{799LotU9+0Yt2!bCVPL580iY`*(|B^zB zSPzc-@$TN^?j0c1%S^Mn#sN*Y%~T>TX0oed@D%~{BMcFvGP8_1NlK#Q__~LWuXiz? z)%o0?qgTyY4DgA>v&=AU;tk^IP21qSPaI)IStULv9y95J#E)E8JbvR`a9QA)5i^~d zCyo${g*H~&m=#Tpc#1fGs%kpr3mK19&Rd+da+Njj$zK@C=_|`zr#XZK7O@10I#N(Y z31!%b(W;YTAw~P~4*o&cFOf?jR|$+93#dSY?E1m~;CHuHesaP~3dMoIi{pF@1HoOO zQFEN{W5;Qn0RCs-N^kiqbztU`^jb@c9szyZz{Pb-Q}%$%9bn*5$&gLimHae?d>(i| zqi@Or;ai|*&FihXkJASrOkH7FYyoj0001$Nklr~F01DrFR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U-3|<2VwA-*JjL0`}!NSR>{JbNu-dASu~pmwRIBr6XDji!=p-K>kZ+O3nZL zd#Qi%a@l0%L&~wlsCoI~i>;hweO`5c8twPze);Pw{{L`YJ%3+VE;)|w{F~!7{=ROW z3C^K%7TzYx{raMP{Gwb>u=&$O_8gq7NY2MKQMo2^_IzQk3F_IFjwPq}Ewp)#%i-{YJZ=#nEl+cAJ>g+a~MWi!(>LN zpycCEyuY1jEup+IIzO>uT~kWt}K>$Kq!Axi4y;TC8V5W;j6~VH~ zAm9umh=HJ=#)f4c>|V%yz)gAof8l0t4(5aw_`mmHWaXNRtYCW1XMeBFwC+i>`&zfFxwL9Xus+4PR*9+M2R*g2 zLhYj~i%Ve*_=!c41-*47MVjvk$vGk=Z9 zrf@w>C}3wXVtFh);HhEvkj4lO+GVdH>@6GAvK$Al4UGs57b zQ*8_&MT3GEF}v%PvO2VZ`T7R&gM_TRK>)^z<(mMg6MC|V;8xIwvkCBT~i_fx-y3mCLnR2VTCC`3@(VG)t#5B5WkbC5Y z&1}$L_xMb0yW`=;DC}YM#40QNW>IbgYG+Eh6Ni!W=5UWh13!|oooI5qkB41jqcNP& zE3XK>yo&|J!M_>Bf3}+`H;jpX^HJ>{Y4$)@SaSgqe1}}!#Sit` z&f;+vySB58i$`Uf%R=*#>dEmUb4~e9&ZP&7Y>}|DND#g$9EmuEgFCWOTQ})lH|*?^ zO+K=@WJYuFXfnK0ayTfd5M_jt{x$a=eNVl|pQ(kw`QmRhA-gu5?0?0?MpV>WOx(vf z5k}T&UL#9j0({u`~sk77Shj&)+IwHERkDhTZo0i*U3 zk>C=l?_Bf9x$Pw)XMY9T-H|hEq*{}h-}s~#KI4TC5f^xfVd$_S^xlWHs`ktKINZCC z-x)ZxgWVVVkR9}so9~UNFLsjK%95zbqbDjwQjZ7}N~P`9%!hS>@|iS{Y3*!M_EMS- z>qxI#&38OR%1<zeqr}z-ER$|L-{DY#2NlSopO{XP z&q?haH@Pi1#eXto-6iKEYRS!|-g6$ps4o~Zn0FeQ89OeaG-D=)2rT-rM;P8NGe7)R z>vnUu-8@R!QNGz0B$T|lD=MkjzLVsPX&MToT~!|9ZJkLCx%v`U+s010 zg{LX8z*(p^oG)Y2%DF<&t~{ZO6HnjUp{|4ul7MFy!G8=fkIhH2OCG8Wk=*jSey=pt zFWUH0X@!}M)kV>3-@pg6f#-{Ti`o3%auP8S*)2j`1Caj@eO!e#w zV8!k?Q$EE`YHAw3Qy!eMQCM7pI$}tV7~Bzql)1}=PPH}kxSmqIu>0$%DRw`;nU#d?DQ%dpoY>6I^zWq96{7#nYgD%MmJh}|7qcxTz4|Kk zbz?;l^t)~=;|^nJ@l#rV|>@*l#=-#tKX8Ls$KWK zZm&<<>*q-K=-sTXhavO5qDqG&WO~(O?R1qEud7#T%O<|+)03a%r=(9A>M2f7hEgX* z?SJM8b0IftC&nNl;X!XCyi^4VA5lpqC!!+s6r+@PWL)E@+M~x=juln;=~0%Yb=anJ znNM_~RkN$Yb<1NXMfD*oe#(jUrH&tymb%nYhN~qFli|B0?j568eSX&U!%>A@YrEnBZcBM{Ho@R{0@1SbN(vlGQ#yaSa^CuC;B2^?w~= zE>zbQXQTyET{Z9^YzrvbJm_{Ltix zA+%I9fl#j~_=I|_mafT3=TV>LZ$o^VzhBr8?Ovl*eXFTv+!C&y-)1NmmuHx_|1wup{>e%c@Pw0004nX+uL$Nkc;*aB^>EX>4Tx z0C=2zkv&MmKp2MKrb;qptwIqhgj%6h2a`*GgeDD1ii@M*T5#}Z zvFhOBtgC~oAPD||xVbqgx=4xNC509--f`T+d*AzV_Z=YA%S^L6#sN*Y&3|+MV zWR>`wc+8{=5WM$AlljyOUr7TZ{9V^%UX;z{DDs_B$3WIa|nZ*kVj zRo1*Ge_=SUFE4SO))10dz<(knh)__)2FkDzqg^M(LYmGKKK?=1FOf?j*9I6l=23wL z+4Y0}!SCK$h4_S*6ixu0FOKsu41{)pM$K`)j~%CR0tBCdE4}5f)PdLpEhs3epk^1>pURz9|Rv-vZsMUf-JMIDG&z)FP|Y4RCM>j1(z* z&F9@+?S1>VrrEzAyGnAziw`k`000k*v$F)d0)I9(VKg~pGGi?`Wi&V~G+|{qEn+h= zVl6o}V`DaCH8C?`HDV$PARr)kZE#IZI!SJGbYX5|Wl2OmB6w|ZE@^3GI%G37F*G%0 zG%aQ}WHT)^F*GwRI5sdeEn+Y?HZ(D1HaRk4G$INhARu^ca7|4*PjYEzX>MU`L~mnt zZIkT>4dfvx*4r0VFYEH)J?5WMeI6G+|~fG&W*3En;RhI4w10G&3jlBpCE`Ju3XV+6Wm0-~a*_XU<eJT4Y*~5 zFXwqkxjyH=k7DXrW7+Ow1qGnDC%Y23dz3QNLzeCK-*qd1-(%`{$+F#9gSrDO+bsw7 zgxnKn)baQpII2>fRI-26uUF|f8z=%M0G9((fy$6Y=mc@4@1>5YZ#oyK0!n~Mz->T9 zNDzKZh@$d;#`M$Ke8OVQ#FZh=e+FW)@1+h7c8M+9odT={?g|((9^^B?;{d+r`~__8 zCx2F@h?=7NGKB z-*YyF?4}!lU78_Nea~r$dI`?fC~D9-ub2)r0Xu=OfG>bA`>H-!HEEcRqB2evxJsjFYlPi>PRPYMa4QPl(9F3$;;GUVKjm=(5Og^n^gZWrgbyi-n|d>8 z=NJ%wRLf7PUb-p~!R}s-EEy*NGT{GXF`Asfv#3j1fi+1sdufEa{)9$c947!>K`Rk4 z0EaX(T_Y7_7b#^)m{AGmX~aEo0vfU@DYa2xH zmgQa5ALL^U@S$b9w-15ilP%kQE2Lfb2Gb191ilNg{~Pc=@Ga2nd(OTb9d*F-Av!*P zRyx{%Qne_51MRB0>IJ%m5T}qbDf=3s3xS!yt-u{2J0J61~e0Mi&fv}JT6B(W|BevM&lBJhIhCCH?m)4+pjB}ao-!+Hp90Q|Jm z1X+V@gDkqWFqq@C&Nfnfn~QJx_?gb?>; z(y8Mb9diI%9j^<9!12AndV$!O>1(Z{SD1sf_~?_XMYikA9N=o@#Br4hn_>n*bQ(|w zTufgrU#hyv9(828fNW6K5zw6RoHh&wgTY`h7z_r3!C){L42DSj3kxBA2$gLFQ2+n{ M07*qoM6N<$f=`kUjQ{`u delta 1712 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 Ef;xcXdjJ3c delta 1633 zcmV-n2A=toNc1g`7=Hl+0001xr{kRf00Q-TR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4x+#n2v-&sYM5Zf4=fn?H|xpS*4^}bY^pa5~mG59mgg#PR2 z2tRO9wiKe8r<~G;E9RKFpyS(dl~vOgSG}%yzQXf^JzOssCVxRI$D^ln{LN06z|j=+ z^iIg@`C@;3kxvioPeP|7nKNQ^+9{MzA>rc%odV@p)|Rr{{Sx+mwC%PMKkhIqv88>9 zJU>T6^qRsb%w0&fRr@gkS$B{E6QO2F7)WTR#Zim!ITHlrRh+iFS%qgRSC2liKS7an z*aHlY!Aw{C(tl3+I{0CQ_|CJ`%kzWX^m6eKhK(sVBBwv^Swz#So% z^;tqO+={-fl2JS$dn@R)t!_I?rA!PggCaF5G^nq(QpFS_cQ&-lm1Wsnj5F6sjU>)O zgH5tD@M5XJE3oo47g~Pva#zXWxDiG&L2Q!dFsttI$=Cfo0ndiCyW;t(ix%Knn&c+GAUgwNn5gf^WrSumd7( zi9{*UX3R!}I6C+g2sumS9SBI38)nH-uv?6sr*C{?w8paXVaAyS5GqLwXtETria6Mi zKNcN1RDV=7s%lbG*Q_N;ib+#SmgeDCOe~sOGBdYq#l@qmCpY)t*-PduI2<*nY}s?p zrNC){a>3UH14`L>mp$&fr`>kn^Ij^_r^c!^RjVFquBCC4hI68&X3bk}rBg>L_1Lwi zZrywCWgyjtj5u_pVZ%or<)TL6!1|$lof_S!@qZ#U2iipqpPJnyXdWjznSmHb0&$xJ zkkCAtZB8+AnCK?6Ei-n7GG?h0PNPW-1Y;-GK^MC}xU!%HP}Y)q!uJx6oVYE%X+83%!N@ zOAsL&_ydt4@Ebq*L7n(1BqWnz4I&4k6$Lv;5zLc{4J!h2bd$FYCNa^22tw$`u*6J# zP85^y9AEeF@%1jov%Js!IeL|x$pD`~Jj-;$BHkdL-n4Yi`@~_B4h|LuY@}23#9@;v z4kv#hfkiAqga8>;lu(9+7_Ay9CQ`H?_wYv?zep~bTqQ7aET94vlH&*egWuhn`N;`4 zDHI1fUu^qh1PJZ|jhbzLAKP~01PD9>S6a(osRJ{gq}N(n^a$wN1}?5!n!E>G?f`>N zx@1U>6rkzP=YjV#`lc)}a0_&=xxF>_ar%?c4jBwuLvo#}PU)Kf01+#*^$xoNe_>`d zH#jjkGc7b>WiTx?WMnrjVqs%qEn#6cGGjJkG-hICHzEokARu^ca7|4*Np5p=VQyn( zNkly&cx`YlX=!9SW;iuwG%zqVEoNk6I4v}0IWR3aV`VliVP!NpW@TnHV`MflA_^cN zAb4$XO-(vaa%pF2ZeeUhZ)0_BlP?kvBse!?HZ(P2I4x!{I5{meW;ix2VKy@~EigA_ zI5RjnGBadhVUuDKLMSjcGdVLcF)}$fG&VIfA_^cNAV*0}P&!s+a&u{KZapG0E-^4J zF*vi#67B&cVmM`DGcYq^EiyD@VJ$Q=VlXXYW@I@nWHDl7Gcsm4Vq#`Clin0ilU@{D zBx5*ZGGt~lWi4VgH8m|XHZx@{Vq|7yEiz>}W-%}}HDoX~G?U&GP?KI1Tq87NV`4dG zIW;Y2VP-ZhG-5V1EjchYFfBP{G%;pkIbmivWRu+#MFB9gBNbr@FoFp0ssI2022e~? zMF0Q*1EiFiC@CfY0004WQchC5hqlOy9@sb3 z=ruv3_wXt}hT3!{6hgzH*biB^E-e|lAoe`c`48L|p5rs&6cmmW?^qio`1~PIxPyTO fnSO!8@YMMN`6X-iG#w5_00000NkvXXu0mjfN0+rP diff --git a/theme/debian/icons/newswire_favicon.ico b/theme/debian/icons/newswire_favicon.ico index a368686dcbd23e359418e554f1059424ae6cd433..b2b79a9d47f0d8b9034defc48b0152209e05b924 100644 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%b476k>Gyw5`axDPqh2c>%7#Lt- zL8~w&)?94vfQbd^Iq`1I0`&2D1t$EBB8{iOJjT($p!Avqt2`+@j4 k5I+Xuk3jqbi2qbGF#I!SVEE4r#LZ9)Wd8&4f$|^>07<}@dH?_b delta 38 pcmeyzag0%bfq@YS1q7JD^ah5B0wRozlO364CR?(&PPSul004KS28RFu diff --git a/theme/debian/icons/publish.png b/theme/debian/icons/publish.png index 0fe148eea2f959f79ce9f5ed82e990e2ac7cf280..13c55f0b811ad39774889d935c6978b3a99d092f 100644 GIT binary patch delta 1614 zcmV-U2C?~oFS0C<7=Hl+0001xr{kRf00Sy|R9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U!4U>?RC`-&sYLU`Ysx<(d_)dW%~MY4K#4hKE*SXnD0Q`T#MQ16_btp{!rP*4dfOJI?DlfB-uBLMi~M;- zF;1GuBHGA@S;`nYoNMrBpN5Xcc`4F|LvO(~zm_s=;!{Wtv@HCvQszZc&>LD33s^|v zn6Tt^Lzd$Ypnrr3ur%+D%5gjMt8bn@`a$`XW9=|mVQ^Esb-%^OJ$efdD_brP`BPE4 z`xmU2ob*jD_cr`ukw16luNIlyTO99H$LK!BxfX|5V#1lzKGb!THNwI=y|o@osD?{1 zwpBB#ALVEWogR*cjH;MqVlW;QsZ*gr=jt0Zm>BtFLx0cQKz+&_EHXEl%;T(q1{=V4 zgaAuJtO&$>+d^yKytZrPi2F`h$pmFI*U|mZxoF&bB5^f+gGrQXGFt{Np7}S z1t2uGPeC*+7t`(NuVsQ25Y**n#{tVxA1bQSGqz-Mp4qRcalf(1`c?oJVQ+;oBp`4k z5~W0kF@GBo{OI6QU^z?VN}&;{a)*^1MH1j33$QnyFKFdI;4H*5}8dEn+JR!*c2&V}AMyAFB6^c+H)C#RS zm|Xe?O&XFE7e~Rh;NZ_<)xpJCR|i)?5c~mgb8}L3krKa43N2#1*Znzq)SShCfPhFm z%M8;d-XNadv<=St#1U4MRpN8vF_SJx{K$31<2TL)mj#{~G1IAe;s~)=Xk(>~S<%#p zr--Afrc=I<@mS@&#aSy?S@WL!h2fmOvdnc_!$@EeOOPN!K@}yGVIxMnPKt#ToyUFr zgRWm9mqM-*7&#VDfd<+2ga5&Q@7`MZ$q6qh90xjI9Oq*M2<-xmn&W&QJ5J*S2tET> zddpv_12dnb*IHWi2En;M3VPrWuH)S+8W0U^~A0%dFW-%~jFgGnXH#9jd zG+{6~En+q_WGyf@V>B~3IXE{lGBA@u2`3~lGB9E{WjHe}I5}oFEi`0iG%aFfF=Q+jiohB5&!ORL4`aw^hGGIy=58#Sy0M=}X3M}@+rvLx| M07*qoM6N<$f}xkrtN;K2 delta 2113 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 diff --git a/theme/debian/icons/scope_blog.png b/theme/debian/icons/scope_blog.png index e3cdb1b81c61c74c8309fc817f31f63ea321d815..f733dd937e514869f658750ab3b61086232d420b 100644 GIT binary patch literal 5681 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 J)y8DM^=}YUAua#_ delta 922 zcmV;L17-ZNEZ_%_7=H)@0001;w}I>c00D(*LqkwWLqi~Na&Km7Y-IodD3N`UJxIeq z9K~N#MJ*Kt3yL^os7@9{MFbbELJ=y2TA@`3lS{v#Nkfw2;wZQl9Q;_UI=DFN>fkB} zf*&AGj!ud$QsV!TLW@`rj{EWM-sA2aAXJM?v)aZ0O}EWNEPpDd(yL;}D+1_87kV)x zGs~Ehq&QlRuY36TdKckYtDu$CyB$Vrc=I<@>u1(#aSzsS^b{;g~5!z zvdndwLx^D!OMj3cLO~e?6k#Jmt4@lA1noy#_y=9TL@tF~1u$|fpac!F>j(dX-`!f- ziE%F}7zNs29Oq*g=-35n703BLcAVM?;C}|L^oGAw1*ShquQasq5zw;@TwFIaWe>RA z0S2B7*_2($PgBTdf%h}|rZmuZ3k24@-s<}}eE^cwRe$^jI5-4Ga+JN^;@zFiz5RQp z-ro<7UUIS!Agdh!0037|OjJbx003ZVgkfoeVQGY6YJ*{FgkoxgVrqn9YlLHKgk)=k zV{C+EY=mWOgk@}nWo?9IZG~oSg=TJqW^RRNZiHuUg=lYuX>WyTaD{4cg==wzYjTBb za)kiI*bYSi0004WQchC$3gikG2^|n;-Vuiq{SleLOcDnL*3yj^gC zDANmoR*5G8ybeH<0QwyOa5hfX&Vzd)F(7bv=Xrf!g3BzR6j*U{fOT0Vy>s;gO#qfo zUR4%`O!iro1KR0-fS?57o&;-OG`g!1NsEJwx-0S04wiM1=RQ)iNlhJ80gzrBKtTF7 z!q8g#wAaDd6oZUz0PwH}amxb-J@T*9qy(7#NQ-5WH2`>Kpj81PGm^`eSLg}i-vzwB z)dC<{2Q7eTI~aQbEe;53bVKys7=zN)C92DOjcijFphiD&2+)(6jF}DtLJv>}0U_Tf zm&RR@$Ck1KT0V5gy(t-5*D-)U!0okI06_k%?@CE)@o9koFPy!U%IwdxzZ1oi%jPX@ wupl^xZ{x@#>mNgGj_vOQAUZ#hzV%ns2gTn+a diff --git a/theme/debian/icons/scope_followers.png b/theme/debian/icons/scope_followers.png index 2e420954c566bae1bc43f891b2ad78ba78dbd37f..aada82f0ae1afa055435122f226c3ed478d9cfdc 100644 GIT binary patch literal 7161 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 delta 4073 zcmVL2`b>2k{Xm_iQW;-AYdo5GuP?SFaw)|Xqq*FSfL`1|R;x_=KCZdtB&{~61fU%2nT zK5%=6uFv;XIwK~Xh0efjK`T!-yy=fda^LLx`dFg!*l$8k@zK<3_O;ziPG3JZuBGpb z$lvPmhKqQqduCZXE<`?c%Q*CwC*HB|k zHP_OBPjk(;&|*t1x6)0w9h&H&$DVrbWoR{EihtpTA7R9iMjmB?wP~lDeuf!mnt7Ic z)J{}iUVjlaeWGSoq-0*aM~%BG#@iBF=)_CRh?sGjh)0P4fHo4dD{1G9L{4IMWkXVU zgOOfhqq>%e5kfb#@l)Sp_Z7Kc#ZCG2tGJmzMNUcR{u_}~gzhzNZ=%+v&b1Y>orU7l z%YTx6ql^TOQkj|jD26}M-(0Jm9*gX(HnuWVZ!_nCb7rbKwP1VggPl!$53k~_$x~Yb z2R)Qgi%+^t0#!gr?{<=T(YT}r{wpJX_+Z!F@ZdSP*f^sde&(3wFl|7P>^_G%ODnMX1 zwgtYuiP2IhW^%>Ok=v9dj9Cj!Uuwa9wwLKlY2dzreCkOat$P-p{favDv*7`tqwPbC zLz_g$vMfDRtG-PioqZWPBi#?rwno(eYJ}E`uW5Zi$BE~wd@5ZkPI>TI>uG3>xPK75 ztYWx70yJB*R4B1+niL8#!RBVX;@NJ*Ws59R0R7ct9z7Pk7IeuzG7d&2-WtWVTuIsG z@QYo}Zu9cdaGq^cF~}1dmYGTJ_#wkXDp9pQ%$zXIl?JBeJ8X`Qh2!a||Xi%cJs{60Ey z5679o1bFi7m2Jvy*IuDs>%oBYL8n~o=p!TjBA#g>cg#xg9TUDg^3jjez*ATEBocP; zN9*X5HdL$&;~U^YF#J9sA1Qtuo8$(N?>&(Jvq5Z`zpwQfvjIOB79?fLZGW}bk^Ne2*^X1$6aX3T&* z7~=q=Hy48^lS?LEFf)jc7-p!b&9gNa3b&J=+QZI%WVW8G%$v2fX)W`tz`|nFlxb7< z1gSv|ev*PImN=EeT<(+`n|}@axBPAMnA(xFt+HUdmP0Xp#8UfJY9qHswkETu>2fC_ znk5%o8&s;jO6?xux~40c4_B=sUl)R!#5v$*uq_P8D&Ez(R!Jq*?ryC3NQ&waFwa9W z(x+LAqogK$h%(DzyhXdZ!Tn<@=#2NNwN(m8xe}pzAPS*ePf;4`T7M|3rpsbbg>_NN zddZT*utge_+$WE`cZ9n8wltDCLh#;=K6MxP<AsGbGE#A~V zGsR0s?)0IK-qBm{PzjHhTs>X_4}Ez_@L=(h;30Br;bCCZ)qmWqip@}wA#RFEg2@d+ zf@nbv?=*)<4qFf8b|BhZflz|E9SMp;wWD^LPINPRy>?N^aBr9_ET|VG9#PV=dE!g; zOqfr6DcGYz`Dt}d5V}Qjn@Al2xII)|9JtzEn^w8<#dtT7w5|v)h&1wq?ojB*kU z!6i+)f`6DV<6caAG7R=Djrdb|)HMhu`ga>q+S`&-X^QI?`S!cF?eTW(3OMt5wzvDO z-ZPDT8RCbP+&_yPN8)U+DzUxZZI8$HxooH9XtW>Xeyjnd*Tn!bx;E@0`RUCM!dj+4 zzS+y??(Ezq3^K58L%h(cT72Dl^FkN{{51M2Ie#FqH>G^9l&elMyKd$3Ttd~Zp;{aP zsEMpB$zH4xnfe=$wpBdT7cs-gW(#4DApOAD1#Lkem>pG6+UyJqIt&t>O{xmli6_i9 zdi7na>k=aIx&N82hLW~bnnc4B92`)Ijgg4N-H~)>9t-XpWwaW8qbhBew)7RqsOSx( zo_|a6(#_BsehD(sW1_|+20}DlN`5H_M2JS3BoU*VJXr3LDL0spV(NFrWRGHsTQS8u z9MpF_PyzYhf@4p)ceqUsjj}HGXPB!a=1+_J(&4(>?UxWBEa1Xn$xrdQf=Q))u6R5B zd`|Ka)k!E{Jwn>%tfd+Md;45e1W;CC_kUa#a$T`*W~!cb`xd6YbxZ5wb}GzE>$Q0v9H3kJvrSO zDXGgYNYd93l;zUccC6q9%AY;AC00#hwWx%FSr#d@mCiP*vC}oRn*DPEZzgb5&wrV> z-`JUiZ8O0dkT0beVgSV!a^R8ht(?y|jXQ^&NB*UaYK=g_EKEwJ6(vKf6_#Au{ltNu z9r7fzS(0k>R8H0BdYU2lE~Pv>L%kybT+_`XS3)lXXxw3Gy6s&qmE^QLJMgO<$)c-Bt~I4d%9>InnKnNJ z9MO#$7uIsQnW&^C!;7Jq{sp5CSUDhiK^IC@xj0HiQ(A5rSafwC%H7=w?Pn>ww({f0vQK+W$C z9KN47w7Cibm9~E7Wau^oYM{pTF@!3o?Ug{WAyiSjxAB!C?bVH$geXAi12KIXu}>!h zy2W^XJgob8gpr=kRg;rb)#b{HhSd8QM*_2tDGAK!y6x_|r*z%+Ie)iqskM%RjIUFHx8EOq{$}YRU(`Ke{)Xw_ zT-7fpjz4|vp+0`^QJ=r}$PWp+YW}ywDD~-F@#Vzv8b2aK`TsQX->&NVpD(mjI`r^w zPIO@nQM2OhlVuGc3R|@*6$c9zamY}Uj}0n+a&%I3krMxx6k5c1aNLh~_a1le0DryA zRI_6oP&La)CE`LRyD9`<(S-;?=)s`GOnpuilkgm0_we!cF2=LG&;2?2l$^-`pFljz zbi*RvAfDc|bk6(4VOEqB;&b9LgDyz?$aUG}H_ioz{X8>lq*L?6VPc`s#&R38qM;Ih zPZ38IRik`=#$|=`7H73wWzBo?7lv}$$}-n!4kLj@EJ1_-8C8@}hJ_fd8Yw1Hv>*5I z4>^93Tr#;zVB}ap1u7)R5B>+gyEXHZ6K+x{4s^cQ_Qwbi+yxpn+x|Yb?ZycZcm}St zmcLR5WlN54hZa0U}SjWJrz_py|)&f%h}|rYtaU3-qqJy*2l7 z`T(S1~!ypVq5lf#W;*=c4OEjnK3=RoMX;f8Btt`xo zKVxu84_dCbfT#F|SpYvk3DC|67cjp8O`x10Ljd(Xpr0#gn)(v78F;RCfK7b~yt|=O z2Z@OwbXi?m)I}TPmjEUKfaLfti#b4OjXymH3}zyRG^T^ivH<`9 zPNbnOivCw~=ew7M{#%0g83O<~?1QZAXehKlkW3-DBlaKVJ2TE>4NJlnTUY>*Fd%ycb@IM^^2|=9`8Fl8~HzqTjdl*3VnMPUCyt>AddI;JorO zFt>TEbC%=iJx=WUuU&D|P1$?iP=Q|SoFV%y?%Er4%BWwr=ultpPOc73JzKxl@D3bm z&@`mBYKMs)Sf3cNs}gj5V=H@FCHAb>p+Jj~HaMho;aaiEbV_z&p>?sp)q92Q#~+m~ zyk86*5OmHGem~W*@bbQs2CG$fySc)M-_tG1y#Y+~b?Ww$;gk%j_d*#39tj(w42*j9dW|&|cp>C{hE(y25%_(~QZAuWtBaacqfW1o zyb;^dZbR-$4lT3hKsL>_rpZn)t4sDLv0n$y=eQ8fgzORst=T&|5&=> z?h;)EXid@Q@vH>7z5&7}r5kiP4v_AxjFpw8o{m}$sKXoB^K5&hl!CdNW&mu z_0yJy@=~dk^uX}AEsUr7`u_w0}m>t~fpF**0sQ-{?r_3B&sag1=jzpf*;keUqDeLwiwGoR+s z=5IjMH7sfGpC5dtJASwwqVv$eOVrXKtu5VzoqB0-D9>lcc_E{^*7c)MManOchk3}; zyysV51|H|nbh;{BI#p|6^UChLv+QDsjiAShyjRYm<>SL{Ww2WNKH6mR=W2JWeX}W2 zwxQUCT#0fgaVSdom3qC-y^Y-Y7nxU)1`1WsNE5gL2ZpmN6KfQ&WNsb{Wg?p&oz6Vo z7M=Ru^S<5&df2uxa?1tel}EnRqV{R8eZTZ$4J{`lij?ktVLk^c&hNIhr2T5y21<*8f0Umq z+0Wao65HWWHR?ghDQ*5NYP!&W)IF%uD&N-9B~gC5VoI`5az^g{$DY=OlgH-unU@MU zxIQU%?n%e;d)@B5i!6yzndoM0uXuEvksR#TUa>k-JHgT$>(R$i=yAG+f#q;9D@Eb; zH_YTM#4p{wsoQP)q^0f{vTMy}xzwd;<(B5BFbG*c_<;7khXU^DK^R|>`eK2+`|Q${ z5FL-$+uA-dk^PciTQn{_3~J+lenzh@FX_6ytoGIuiP+_U89A+HSF`nfGge~#s8;>@ zaOg*a3P!;$-FEIH%KXp0$VDSmS03NB){s)6!j`0058mZbdwR^JEW)n>pSrS+y}M%Z z0WN5}NBy{<-wf-E;!cjsMlI(D-CYmOu{PNH{>7oO6T$Z6`cQzsd6Fg>gFZ=S_7lP$VBImWNk7+zU z%3*~oEj2ev%eoWJSNhP3H*bbV`BA-KvgK2mZxc$Hq0kY^*LDK9XQpA_Jxd(GS9D(b z&BpLYiKJ<+$knJjK@oN0)i=G4Sl02Fz`V0JX_nJQS(lwvB+P@7QWV#3_7zMCxEg+u zZCaWwP*@2a{~G$~oZyn|D9QKH;G!E;v-_DY&zxNlwDQ&6!4aE#1j1vh>L{YRgxBTU z9$Obq(!O@Sh1Qe0vcWxi-5+l9OL*4b1e{=cS1H`=uKW0y317`@MN^k{p_e@_jwFTK z+vv{sxJ572^-^13mxcc1ABs`LK31!mX7FiBPX|RSKAW`?mcRR%?gOLxB zD~WRwZ45Ps4lWT=-{&a}^t9cz#b2tZ!3v~vgkb|(yAehw*4R&`mYZx-nBG$vlJ$cb zgetCTN*$fKF_d%nT+7)lEo&3PO|tKc46xcgf0=-bDb9o4LtJ>v=woyR5|0}R$4zFf08xYcp0EXDZ5x!00u{0bh&v@ZKQySHp< zVT>2A=DZX=Y^mm$cGSCGq?0o7Tp+5bFa27EtZ2|Qb8^7?ou11Lu4nQdYbzlpwlK4z zjWs5M9>=zgIpHPcX1UipB^z(uINPsT6`f07VfhObJHh&D3Fd!5i*S*>d4 zBI781uU`+G+Jm<+5ssTkS{`O679Z9i$}HUtd}LCCdzufODuXVf(${#4BOUd?bLYcy zN@8xP_9hdf%}TPS#h&HRYJPv3diFcKpbf|c-M-5eIYny*=)TZ7= z(DdUkXY`dR**BcyuTrS?Zcd&Jsj8I^g*~0mgr)4uNXzgqa>(`aG{&5G#0Wj&cyOw! zZTaTZHIbX7$x0niV4t~;?s#4q$?l*jRn`iKxL}M4v&y|PYfTX_9%x6Ml5H5nOBzlP zQEe;a>_oCQkzDsS{EI8{LWy4?SNJRK{vMAb8RjyQ)))5c zQS4NBhYk>GH1bUzAO9HIJS=gVVfJ{#{55d}^Lcpo{O1>^8LVo}j?cY9o*mnZ8pnB4 zgbs{W1OzM|kX+twU5N1O^wcki0}@Zxe3d)lvS4(jOT{m#E0*=knASYpD!#)rG~;-& zU#*`@UOwB0`>BD4#{8cy-wA5`_J}Bbo7v40eNi7pe0Ae%{nWsSd(Qz@xiOz-wVP6G zGlQa&=dPk<8SirJJD#L8UVn-geJinMLEORin9f~ln&rBD_B$8F4{FP~hPCF`NO_)- zBNwwf&9=!Ai+16rBcQ#tv zjF`WtyUFL^l3P8z5AX~&8mcTd^t~Q!#zdY;>(LVo(J6&F( z;Vr1A=HxG9!+b(1YHNMFnm za<4YKDz?FHx@Wc{)1RcK@oA7sh#GZpREuBXzURVO8frQ9_R|@wkY*m-+@-8Ze~~*5 zAV?gmbu2qIA$rg}KJs&c(|&iW&riF~hkTMa*gwGBcoJ>il9*jH1s)19%j!D&o}+I0 zm!~-wMe@FlsTBZ2#tNGJWQOh^YTy*VCE6WA?CF1%9 zTAqFIfcSLn;tJ1-i^+u`UCA?&H&339$=+I+=wSF<%bM)W68w!zL>;DJ6V+l205C-1 zb#yF{Iy%2yGU!)}%#b8C!v~r|(c=Y1ddJ26xP3Y;MB2{V@feh8^Bj+K_{Ewv#W*zb zC?_&5K6k=_yzEgiKQUQ)E8-S#@7J)pdbmu3VeGh6lz{x0&*+2T@lDDn5nz9}NfqKD zgK6z!8uW-eRn~3pz>&4y!+MVHSLJ1qG8>7vgPAtY2~OF6^$krFRU*j)fxJ&452|B_ zVhdq}h*fo;$W(0VMrJ@y9y_lF#c5^ZBDEJ6?xEjf5|pT-eL`Tw4v(>cDor9Mi%Q;J z0b|u{;3S{HF6+T2=}@%FZ7|p;YmhtHu0>oj#=@eVrRxq)u9I}TcE%Gv^{-k(;3VTC zLo7n@nN#5`!yhBQ{kk;f5Va9`jo%EA1x{R*=ZN$R*MIEOd`YCxhmk2eT=w3Z+x_8h z&7XULKtW7v#65yTAk*PJa{giA(mF7lE!%TmHs*=#gk~AmwQrD}(EeNf zoMiIe1b&&|$Fr}T7DF~`3x@BDH5yUc;<@A_0RXOeJpI;ZXKI4LkO*>UEXfTg7eFA> zZ-oGWszv}Ajq$?KfNnSsJW)-2sqwxz5RX+8w^cHQn38pHo_NC`3eGaf%nB3ag@I$m zHPqQu0}ylq0*;0T1`xc7R78N9_%1Jk{=72{76FWQWppVqVJ!v#D0u1)|_m}fmkRwq%!18c791MYip->PV0ip&H zY3KkDkt(r6@ts2#N5xR^WE!4C1nzL6-AKMPHF0tJI`9WMeHEh@K%e~KU}wBbPo-hO zNcupLz8^XO423{cKoBSh3J3pePhT}P{oR^K{h=aVPjCR543?LJfC+>@Sx{+uet-D; zOAD$M{qhY);ix2E3I?a=ha=J?es)Us_ND&p)0c|dnc9up8|x0H2es?@XBz{gsm1R$ zJ2HCU3FKXi9rVvgEarC{*_YzIi@{>RIBy(*9tf4rEdM7w4e$QP0sUz|J1hSZ2;JT9 z{C`6Krq^yPySgHDNf_T9r$}8j@tt@PSP}-0MeIJRD8LkyU8p1c#+ZC3iQp zvVsB@4Z|q!Qtg}rLdycFCJvQ@{4rzUji$MiCJOO7(L+^+t4}-## z6cykwWd(V8Wyts4?ef{+C{%iq?{LaP>+g_4cFU zw9y{m-JvS@-+}*y$4TB?Wyjx z98*)o&jP6Kv?K&_C-W?Q$z*Rlj`C+=eoK`90r!*rkEHxh=0C%}TkDX>f%KyFq?!8@ z|E>GK0DfmM!DDblD(T;a{xjseEI++>=rMnvqrcDSuSf9j@5di$u~Rw!kDnju_W$UC zPW_LQzoqX#a{VLM-%{Xjf&a;_f8_dG3j8hbKiTzvCKuZuH*6e{{*RtN{rPKJVdAS2JgW&#BJBm%CLkxQQr$W@oY^Rj{m4m|eeYO~ek?}p@3BRlUD9)D%UvmM zIgQL@P2*I&Fn`vxbnlDww!pB3Fp*QzHUbA3T8wfO^|pCOd^(~Zh7Gd!R4#C(9^AG| zV;U8)ICMWPRqJ|V)3FolFT&-8tA$^>==lU z!R8mTQ~9$cd#gC>?u%^(U*V^=O1d&FOL7~d?ui8)M`0I7J~%||$FP?AxwZSOou%#< zzJXl_Rh{s%^`hzo=g>7fxyO$AM3?JuCgT{3GYWN>Yi?PgE7J8)kzwDK_%hEOGvo@+ zW(UU~z*F?GcWFPcAdg%{J68dBd=#bqdDU;shc1qB@enw!n ziY?2x!m#PI3s?Ry~=my{Bw`02S*ZvO&=!q!vsKOgcIv?TLG?R zGB*HMamGalT4GKxsYBY!jN%OXra#K^Ho{V8mZ*~Dz0RjlXOkWdnoT%cX6EL-NbzSh z>2L_UkwB_Up0r4YePTErE{iHkwrMLN5zfrbOP?Gi#VuUnf6CPRY0}lEz?$oejjNy? z0g2EM+~7WDl-nskG;>K>xHtllG&r^|rYVcb(lLj>78Z|{P;SLi4~X`IPNAC8N6j#K zHSk6zP^angc-@t@&(8h~2Sf+T-j17*pRgKfvZ+q%xSy|&;S{L{y52Oee;W}M*0f~) z`RD{!Hl>4=ogp}+bBp<`lHFdr1)Tytq>rBkH(hG3rU|X-y;p0F+a5ACgZ-L3IceR} zAzZ#`UsC^iG)FwAw26s#T*xw{W}szNz~&@ex9FFLext)Jyso0;eAZqu)Ax6*kvpTfB|_9qobp1i=c zm`Jr(FzC@?m@rc3J7K50o_mOImx4o^Z?-%<4CjLEe*tlIr!S|3Wq-JQ!dgZqkt0$V zqHI3w26V|gU&m+iaq<2h6{E?d2$lv@zW|0g@s+{Qw9k4y35OXISjvqs{#}|!CRkk+ z$bgNgHreD9E!Cvc4kJQu6Fa2SbovdezRDUlF@C=Kqd6V}9@^TY1-$v_eBJ@rPIpJ@ Lndz2myPp3qS$P+F delta 1607 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 Z@=I@CE!~Ec7k*8HkZoOU%B=!-{tFz!#g6#=Z zp6$-+_7k_G{ov+MW@JXE?IgD&;nPn0-X%UeIXo<9f5&Z!b~dl~-XKO(_=85=h2&T@ zpOFMvNRmA5sA1u05%fj;S%1~fUDCY~@anzjchqf{yBw#Z+p@xoPTNlUUcaT%V@AYR zWPZ%ZAy`D@ zMi|KiXJGtX+^gaLmM2GJ%xz9|#7isW#dCiljY)3S7=HyIG`6O?^d)yT$Llr03JB_w zIbwnND5r~}bl+RDILo09L;ZfG$=Y@Ri4a?n3<(GvfkY|LVa!GZ9V7T;WX|HbfB>me zZpf0oNC2G3GdAWJt+A|pnsK}Ugh~(t8oWS&<-PiV_bp-zWv8NERfC#FO`3xQA2ftu zVV+aj#DBc01v875EJukxY7Eh0j49^CNpOs*OCec`DW#kdH2Vx+8RIhyg^Ltlw1i?M zmQ-@Z_Nl&V4b^I_spiH_8jb+hLbDcIYPnO#R_eZM58Zm~spo;cHe~prBMcjHq>(Rb zo9Z*QS91SNjW#u2pyoKcQ-j&;=7Q#NqLUejv41BJw@CmSnkTa(lJ`8xO=d@8aU#md zQYV~7lNbp4POO72c6V}rQ*UUdOF0K=UnxrZ^!5 zuV0Os@hQbu&#OQGKz*lm-<|Mr^l|iY^l|iY^l|iY^p88jKMx5%r+x!Xpmh$ z_#gc4f7U8YO?pY;1knBBI3J@x&o0ocJI?p9<1|lz;4^TgxBb-yF!M=zy{$!$fc|aZ z;<~LVd%)!mF!*H1rtC^VnnIxfyr0oG<$!@(AhhQ7*4oGE1CXJvQa8ZCAuv{?>~)`a z_jdO7@0nJAKW(FOrOMBHr>gjo?x`@%c0dmwBs1Iczo18_9Bnu!L`0%|zoeM3tP4K0IjC^uZ^zrv=D z`cWX{5w*Hk%%}81B#rp@)cNmMEn1XTr)4gz1ERNx`kvD@x&QzG07*qoM6N<$f_r3t ANB{r; From 6a4b7eae9b5ca955907298bd58751be348281dc4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 17 Jul 2021 23:07:02 +0100 Subject: [PATCH 013/459] Starlight theme icon tweaks --- theme/starlight/icons/links.png | Bin 7516 -> 8905 bytes theme/starlight/icons/logorss.png | Bin 8002 -> 8896 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/starlight/icons/links.png b/theme/starlight/icons/links.png index 577d596733aad3b6f4695cebf9543b96a99417cb..84e330db22ce8949d601a43ac7e3a80c8976917d 100644 GIT binary patch delta 5089 zcmV<76CUi`I>|+l7=H)`00020X>r~F01Q}qR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?U-A# z{Zq_8_zON36Jjo@rgZTaYN)PpQm*sY_Pf2pe&2uL{)*3^u78`?7miDwqdy-@y6=Ck z+t&i;&^U{43zczy(LaAt?;TwEvXDCmuNLX~yceqXLf)M(?7cucZe6LC{O&?u=Qtlv z%K2~fEtJn@fBT$?-i++yom<}ei+AVUF~IA8YkcD*^KO0C8@=CG(tfY^Vg%%;I9+d6 znXjn4&g?I)e}4hRr*qwK;d3z4>-gh!8^2t?7c@J>Z#(<(+WFJ<;V=2>&huYn79+Bb zcP%7Bnr^2aSN0h9qnx)nTpyt)MR}F8k`IFCaI7*K^gqY=5zP-@3OOwy^QE-03WfS*3!} zxBtfLmkWs~<*mKSD^}R$l!Ta}$mu(G0f?1{M{x)Ed42s;Ts;WJ9p-|A?doTV9_`I7 z<&ob&dv1&zPp;WK?*#}Edq<419t5sreYDA5*|}^jaV+4|Sg1H3Tn7RhqnlXig7*n< ziqpOMO@FrgUKicDjAj8uB#koARNufVAZ+j_hXRLM^fAO3Q_Qi%8e8%yq?l65NyV>a zpF@s0<(x~dxfNeRi6xa>iYc|U>Z(T^V@);JQfqCEO&cpWe%-jCweGs_p~s$j?xok> z2J{(W#F0iG#f&=Ibkk3CVy2mAnRT{h7gSnd#ebDnUS-wQ1geRbAW@QJDN?1qP_yR1 z`t9}iQL_(fZX=~P<`-)GH0Ngtt8x+*GZ1sZf_PR0Ahc7=u3|%QMxj&8uBw_A9acsK zXIe!J1jDi}ihW`Cf!rtDObGuGZtg+OD0Ke~PLoI#%|28Wt%H6GPTdR~uPYbF!J` zrXKf~JroXd#Rzk&jA&Pu5DnVw$u{XxoNrl30_jsqmSH`VZmzFFt`pGT1}q=!5kA7ykmYtLy-p)g~6OfA*kDUfB+*0z2X@u8yQ zq;9lm33XYIxnEL}AAaw96&!0_ruU1AlgMrnH+o8D&ffbZ;7ei{$c9T2TnVj*#(xcd z5tOo-7>Zkl&LKf5`&4OF%%m_2L04be?EmMM0Rle4dtjhEaSLgXinMaG2~OUSi_ z9*BX`#x+{#-!ZGx{amI?iMl?@yg48(DLobe24MtK)cK4ejyz`zd_sxoWn3M-Cf`e4 z7JN`hJcEO&ZBp*vSNr240*$T#G=F@F`D~l_w#^eJ^F;kU=s5rvrKk1;%R;8PYLU|( zXE;6;)cmuT=GO@&o|u-%{?K=)&A>)wZXdEX-${-UL|qdWDw=(x%c>QyaP0#VfqCQc zY$p1dDG^5oO)C!d3?@-nR=fe^;aHR{SuwAf&x%P`9J=JQ;+%`|fwMk~>wi18qItmY zk}NYLHG^0e(jeVG?1vr`Ym^=OL+V=YHI|D46@>bTRFr+4f1|8Ld%Qm|cjzM4%K^oC z>03e$8wRPNpTH$ArC^>TdCm}{r~(qrJAbRH#NjV~WAJ;dl)uL_s`~OLjvOb=rHXQlElr7}oWe55LEsS49!q;rYhho21Ei?S4-qnoPKxz?38)GW)1 zJ%qfDZXaQ>aC3~klXM{77F>SocHbI~EYYE;+n!t$tr2vQMuj`7h<}q{%JrA=b?5Oh zz8)!PYwRS>w0Km278W2@!{Kt)gr;wYglMW~T3?gVC#t*t-$9*E zP_>F=1`_53CW1ntTvOxQTkO}#+Qe8-dZ>(GrDOXvwhi1zd1_iAb`*?0Qql;d6OB6% zD9v(y&T#|*T*ZkL|su!)MgJW`C< zP$a^a+%$hDIe!Vo)(|x8m+51Ekw45^xJG&p$hnmA0U zX_8mD+%@WZ3$v4(@k^R!e`09##W1L8x{m8v7uc@u2X_qcX=I+v@ zs_o5Fo7$Cc(chGvr>a3&`Xe{p$|(W4W4xrlP_eRS+kZMR97v3GVy86;j!><%NvIJ< zM&t`fx^f)+7za0dXbA~;$c(c9Xg7dKF*lobh*=3IClK?;xG&kXlZmRfgE0}Ggv4Fz%x5TcOx4t{^IlFlzfQ>pzvQdN3`Ku43(9bX zzM|A>;N4kSRpI&#!?&K?>3^t@{Z9Tv1xARS{P*y*p5e(-;FVZdo+x;bNAkWbB?PNr|oUVoasaasV^K&_oVj}&l=j@j%+ug>ee)x-J1`nGD~ zDp~djr!-OikO;2&*e%006}{E8GVn9Sn?NQl7m~YeeuG%HCB>BaK9b}R+X~(T8 zGSAcx?I-{rWM%)cCrL6TRSoBGu1?$Pz<(UES$o(wKTLTV*6TI7#ZQx)d`xbX75n>|tY}_Ko)!CUj<^Ai_kgl7=cU!} zDXxV%J}iFmT~7>cYToW5p+k#W@PEH)UVhcP%**UfUFG2En%c_ssO>Ej4fBNzG$C8H!hxIR+?0 zBjl+!RH#urvqH8aYLeOA2@8#=4-iQ)LKJTQhj>6{iwo$F%lP0g5a6^$qp35c4h;e9x*xjQ4CZ2dGr_mLH zg1+4y!xDs&PJ3m3At$wW*suC#n3|jg0v5&^U#{Dok zIz^i)6AWTxO13%Ooda}xSatnyG{5@dPmlj!=zne@sAgupF7t0F7zQ!z$SnB2leq{S ze?T0?UsI(bEg}{XamY}eEQpFYY88r5A=C=3I+$GgAv9@7Qd}Gb*Mfr|i&X~~XI&j! z1wrrw#Ldk~(M3x9Us7lhI zW|lE4Nh$b_uX_aedKc$e{^$N25jAfye;^+lWR>`wc+{i| z5WM$JrmjyOsz7TZ{9V^%UX;z{C|s_B$3WIa|nZ*kVjRo1*Ge_=SU zFE4SO<`9xtz#=4wP*BAN%CHfqRVT$ln)VYu{z2C-kxL=h1{gW!QGo{8^@IPxfA4Os z!o;|j6ixu0FOKsu41{)pM$K`)j~%CR0tBCdE4}5f)PdLpEhs3epq`1>pURz9|Rv-2&aKUT@8PoIU^<>MC^u92^28Mao|Dd3RTPZ~vZY z_V)t?X>xm8ey`vF01u04R9JLavr-DS0wgpuHDhIBGB7PPHa1}`G-Nk7EjVL1VJ%@} zV>mcwW@9#CWn+^p4Id;nIA&&MH)CNfV>LH4Ei^M_Vl6pjIWsLWW;8N4IW}Q9Ibt)D zZw)6TW-&BkVmUKrEip4RH!UH#j*sF*PwTFg7q^le!H;4l*`1H90jm zFfubRH#Io33JwSYBsMuSW-wzmVl6i}VKpr@Vl^=>Vl^^lEo5Y5F=R0~Ib=07IkTn` zrU@3BD~W6X000JJOGiWi000000Qp0^f0KV49)INr5f%mte*9e_000EbNkl8`HAEp%v6eeQqiIlsi6%aR5&nXIgEoZd#)WJ|6ItLL zrNbjJ0Z~y@eBtA7Dj_^#V^GkT*hoc`3ZXzt!B*OF(OJ}+`@eciSvX(vZ|<2n=bkf< zbAQf&!{Kl^91e%W;cz${4u|9ag(y}*qvw|zm=0V9OajV)3}Aq6;4E+q=&)0a{o18g zzooSoczy*is$Yj_l{(ix>;VWo|8`(LFc-L~2>TsC6R@!)!`A9GsyD*sMBoEpW}c`O z_z2h_wff<_1|abKxxh=nE$3l3wgAhdR)5<S!I(00drL8JN|;qG33e zYXm%#)v6~i0Nucd1oOLqg;J}xlNie+SoLx++lR?{fW-#3^#^@Wi~=?Xo?l;d0Cv{a z3*ar_u3^5M?*X$m@3+rV>R1(cer-_!=r%nofcq~{hI%aU{DHe}8SqC+9j^tRKYw#j zcR=9zmB7B3d*UBpJMcYl+@?HZW#{bIYjs=cj@i5DQZO|@ zIWPgp0#z|V_%TCCaXLa=nkE39 zVJnfa#W)fnjB2F9>|%^5kAJgjFk>UcJ!t}PAS^EjopKBIME0D03y5P!$T$Hkw_iS+f#$+BL+*5fj&*UwkUc5tfVy#D+JK8f zl=)Jt*JJYXBi5m??N}wX+B;N@L*V&3qGLTGwVIIPM$Wa{*OxGIfq&;eyL(ApXsIj& zAKLz)5F>z31J9R3;P|A#^WTeU*WVaSGc*JEF2?@vz{kM1K(o~9K%S1}z$-C2KDRm! z1DDuE`5S1n%~dbZEf8lArpy@1fh&L+z#QQ2n4aZ0aGliZRO%O@!1LDuPo-Fpqrl5j zt5pDvwRIznG5dh2Tz@zs%jme`q8|7)g|YF#tG1UQ+mJg8JZe|+Lhxo>50QzfuRHr+ zXZS0n;U@;@Xa*M8tvsB3A)vSyA?rX3a3^puh4HdH9q$4U(EngL9IQu(d0DNxiw=Nw z;1>`#1M3q0fxQKEd}14tug;_6ZNS4)tA(;5*ORQkN}qP7*>(qp`M{M0hBOIyFss$c z2pzM5CH8n-J_L^M2i6K$Z(HjW73LuM#(Q3zqjNTJqjln>O@(_>26QS=0bB))wW+Yg z7E4{WKj@3dI?Yn6!#Erchr{7;I2;a#!{Kl^9EtcBNDO#GrFK>t00000NkvXXu0mjf D)*G1D delta 3214 zcmV;93~}?xMcg`&7=Ho-0002j2boO(00@kFR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?O0K+BP$O5XBD%A0%Huz!GNXN!CU?uQyubG=Uytzj%YS&dA0!MBu5|uc%QgQo z9`6OtOUE5QEmX(#KwlrUYll?d7P>YfwhW|32v95(4sFoQ@}} z!aFMWUHmZq27d~bb39<+HJIhTemfrd`{0Ki;-{Ty+&jOFuXX6VJCBdqtif!Z-t}A% z(sDWVaAn_NA{NQCe^&4od>Hq#-wp2?z}^9L<}t9N;=aX%=v&Nj#vU!MeI0SY#GFDli*>moS=cTjJig?SJh?tk`%)%uErBMW;gWuix?h zxR9AsKE|Wov4UO6NeD9>Ise8i021xNBi;diyx(7rD*-{?VXknnA9^mK94~G;k7xr; zn8>)EX%S^!3m`<;n_&!CpW{&?aV+tWlq5#Pu>zltg=(Ul6c9w_G+|}RL36e%u#T}ZO96cMS? zq*toBaxGPA4Yk$YQX_~Xx6-85)>`k_v}5ItUw2ICU6Yb#Wi2XNwP_!q&&Y#E88T`Z zZSISMAS${URVq(>r^&7PqPFR0<|2k^%fm+%~ z`9!%o6doPtB=m>n%4D>%VQc5r5N#DHL4F|oxP zyD#Kka0@yABizyhx!}D6W&6gEPhh91;|kbao3YEQL$QTyd< z_KuhuSTmJ1k&hipg3KBk z=kVm}mQnO8V(75NFX=HUi0emXPzGfTYZ zD0QFFIb|!9dO!mrxxAo{I zt66AGw$+*y_r1UvO|vOF|FsP5jOol$0Z86)IO+mCV)L*yBSODNTTvz7Mb{8A>3`am zhrLaN9VPpWdviRJCK=F5El|q}8AW~L((ohbb6b@VnPwi7fh9UDge&mGyO9!ki9xIh z`k6Z9e#NMgidqE;&`S_ws#|+U#BF^9}O=yc+#}P@^ETuj#257*si{P&JNxkfLoU?O7eozz2dRO3d zlK9vn#ji;T)OKEaR_0SkU|H!4QVarGM`b-$)?F<+U#L$+V1K-}FyX0}K~30Y6~5T5 zBrbQilJG-G%B3WTBj3T;mx2=9qJ-sdQJN1?8oOL-L|fDA zDV&JKRvtNd;XT3=ExLZuV!Y5|p0o)4(I@=6LrqtyD5`PVA?^a=n*uM-Ogh_vZ@M!@ zk-a;>N8LpS_KIGn9I0?w-b?;9Qy zmaeiz>5GN1>@G$0Qi}R|l%lvxWdL1X_zzQ^=pfT>B2g(7f`ihD4q>8N>#o*U(R*Ti z_(b`4#`DE^{lNIJDIXco{awJj_aBgSXlQKc*l?ajvVQ|lREOhYm#2f%>m(Q2-Vftj zKF`i0Jo(vQ+@vozUBI@R@M;WKw~53oadmj={~JMV4DBZQj~ve*pYZM{(d3Q_zk8bp zI)ApoyHI;{I+_nu%Cjp=dkjZ+$^W%)3U7_mPsyNLH)9(G2_9GT1`a0&bV_2uyIzmt z7iocy(|;DE#eNg07RR^@G=HD~p($H(%D&6oxGGclJ*Txl3;kK>|6n2WCNKO4sLb=3 zgI(I10004mX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmP!xqvQ>9WW4rUPPkfAzR5EXIM zDionYs1;guFnQ@8G-*jvTpR`0f`dPcRR11wrr!#L3Y~(M3wUFDbN$@xkSN zocGS*zWV^7Sz)@>GY;s6ZKjh6DVtlBLazuTLM0-qFsn8v%PIJ-uX_aeei!Fi{`dY| z{aW5)KtLj%Wi`_#-XNadv}>IAiA7dYG~#pOF_Ri3e&o99@f+u&%L307%}jcpSR|H; zU4N{0F)Num@f2}P(+$cOvL0)kw>aySI&1gIUl_?7E6ZG`iXw?cEJ20@6?K$RfsHur z1}PTObe{0>54(PuTq?QBVB}ap6*?5x5B>+gXKNKEC%mLc0_c5loR1M8vK<^p0}MUY6jO1fAeB%k0Pkn?O*vrj z76`9-b8DaD^a04wtWr0?!67hOr0jK{clUMA?cY1?`TYPg7jli9qn{=K01^AM84kMw zBw;r)WjHr5I4x#nH#sddG&4CZIX5(6En;P5IXGiBGd442GL!faA0%RBGBjdiVKpr{ zGiEg{G+{Y3En;FfHZ3<|Vqr66F=S>oVKI|C5ho;LG-fw4HaR&hGG;P3Ei^SXV=ZE1 zH8d?VVlg!^Vlg=}HaRkrh7m#zFfuebI50LdGdVLjH!-u;5eNb#W-&H6Vlyx?EoM1p zI4v|bIAbkgH!))^Wn^V$IWb~pG+{SllL8b_lY$dmBseiQVPiHoFfC$ZWn(QgW@cqA zIWjOXEigAQWn(xwWHvB3Gm`=oP?LfaTqH9&VmV?lVl^#eH(_NhG+{I|En;C}H!Wi_ zFfcVSGGS&hVl)SHDqQmFlA*eGC4A0Ei`2^Vl6mfIW;X|IWl5oI5s$C zH)S)k0u-hRGnXGy4FCWD2~bQ_MF0Q*0002JW^1P+@NxhE00DGTPE!Ct=GbNc0004E zOGiWihy@);lfE4uf8+`b3Kaw5OlDO8005myL_t(I%k7e}4Fe$zMNPp7W}X{?0#h&o z^PER;ah@{9K%zwHlj42>pC@GOF92@+RFUZA4uDWqvY*Xe#c)?G^MW}BK=loVlPcsA z2Z?D*jsY5%XxBAExF$HkL4oKX7#Ichgfv0t573?@~07*qoM6N<$g4kxW AWdHyG diff --git a/theme/starlight/icons/logorss.png b/theme/starlight/icons/logorss.png index 90ab9a7e8289e5718816687a0b638b784e6f170a..7d7666eb03650f47f55b679ebf0d0ba60ffe212a 100644 GIT binary patch delta 5004 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 Date: Sat, 17 Jul 2021 23:13:14 +0100 Subject: [PATCH 014/459] Starlight theme icon tweaks --- theme/starlight/icons/dm.png | Bin 1457 -> 9022 bytes theme/starlight/icons/publish.png | Bin 5718 -> 5888 bytes theme/starlight/icons/scope_blog.png | Bin 5718 -> 5888 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/starlight/icons/dm.png b/theme/starlight/icons/dm.png index dcbba2b09ca3e09b8345fb893fbfe39c65c86764..3d4e7399e7069ed458ca21b25fa4b13f1e66b8b7 100644 GIT binary patch literal 9022 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 delta 1396 zcmV-)1&jK=MzIT!BYy#fX+uL$Nkc;*aB^>EX>4Tx04R}tkv&MmKpe$iTeVs$4t6Nw zkfAzR5ET(8twIqhgj%6h2a`)bgeDD1ii@M*T5#}VvFhOBtgC~oAP9bdI5|2gx=4xt zOA0MwJUH&hyL*qjcYwcMW~$jS3aFZ8q>~9Dn_CfrujoPuJ%5-+RAQz+D~c(2j<0+8 z_<9%TS>EUV9KA~3WPnc~o?*IS5w8d5g1JuCnGm`3u8&ZF!07GzXEy0u~`c zfQ%|OP=vVNRA);4}N!R7RJZiq;LZ0e6j71 zAt1O5G-|f}eQeu}6Cm&mTxl(Tr4CGel3r_Ru_K^w8@RY`Y4RR$xdTL>bjgq$DL~U- zC;;zg^i4US{}u?Xy1g~`aryvcsH@ZsaBv6=7b$zq;}YH7?Y;ebrrF;Qbz*X;@hJI< zlP@1If8+)V3I#W5p)za$00XT_L_t(|+U=U(i&a$=$3JJ9G7KAJw63It;EN9>`s6?b zwfqCd9)jwz+JqZ3P>}+kMqlhF;%a2SD(Fj*K@X9;hae1!em&$IP)@L(M#n2FOYlJ5_AWACfz?F(=^a_LT$DX=*b ze;F&@*bPG_zaF?)h(LgEfUQ-U;3{yrqe*H<#1(Js^T4_xY4C8QqrD(b=e@CWl?fnD zm%Oph0-plAJ1(sOJ|A)xiwg~&Zv86Z-vfGKhMaYyOP$6kOi-JJ)X3xB^0+!Ck9MpcBe zDX=+^&%<#DSn$Ta*Cy|c&Bi$eRwLslz?;1tjT&V;deVyi$7e|+VQ z&AqX$aTH*=kA;c$?@Qj;9V6!f-q^Q$5Zn|we=MATr9*_a5Z`-a-vYci{c%)G@OU3# zo^^DFM}za{z^jGp)4)3+8s^05ALHNwS!@Irr>}(`Ayx)A0hhe7OHC`4gEqmJ!#=^a zE+VwNv3Hu(F!dwAb>Pp!u?##Jf5>)5!fNOUHv)fk5#oMt?2RUSKoL)u+3Xajc@W_d zZ|pkY@5pWIy|I^KUu-ln0!nRh6=s5kF1P&yoE)+r{5z-!_`n-`t&6#>5vO~L?s-ki@@uVs?jFDov@T0f5@K%_K(bZ zK#$Ph3w%QN^}-C;CQg4VoZnif2TXTy{n--4XMiU<$j4G;pEzCY@`#%ialPJGQxi!2 zO#EBGhk#u@`v2ndz6LIe(}mcANrz{g10EY9`5AG#qi+FvyzR$<-}`Kp-xsF`BbA|7 z)o6StICBr;ZBY-2(}V32R@aMQ-Cf}z_Xaay5jZ7Ie{BM}ss*?pPWLfl7=!><#c8Vv z`CNqu+~)4LK;MSr7jfF^C)X1a5)u*;5)u*`hW`L9`)a}uOV=d;0000<(921A6=DFk?QBp~TD+V4PY zY2htDA;*~Zylf+Mt-lmq1F9J5^Fgg!e!R+$t&sg)(e^;-VLA5w!d8DIygk~kx9w3b zZZBKwZCg%T3XUTthtjbaXT>OOZYTdJC@kwUpr#pF$GRvhc%8 znLm<>-q4a*meihjom ze(}s`xG~Ai7OMb+#*Qh7h81GE1O2s3umXa*!tB^!jmJa9q4dlxnVje8SJbp`EV8~8 zAVjdOaE1f~VkS|_6ph)45Jv}}3eQ;*9~2sqDtCCvNhAXfP62G=9iz3Dl^E2z#7aM@amIO10jE+ zoh*ooIBFG&P$AR`tvZ-o`XMxFNK#xJ1=oUuAB$B77iV1^Tm?b!1H{eENzp}0{9jUN z5#zyeKi=JY+`R*YdYNff#~7gLwwX>Q#B6Rw480eGcYkNVq|1tWH~uEWi&Tq zlg0-hBsDWMHDNb6GA&^_G-E9^H8(LWVlp={EjctbHDog|WMyPzHG&7S_2tp1rHZ?UlI506YI50FcGqa=! z2m&N#F*Pt@GdX1~VKz5nEi`61H!V0~F)%GOGdE;2F=jYnGdVJo&+jiohB5&!ORL4`aw^hGGIy=58#Sy0M=}X3M}@+ QrvLx|07*qoM6N<$f znik&j6LO4c&&xJKkM$2lj{#MT^!cFHEk9o6$5zPxu4sE8bXkslzp#H532%?K>ur0K zi`&cAdfS%M7Wwm+#5ijvOXw3vvs^HC1lJJHJ{=v6%TlCQKySfg{#eTJiBBPk=vnyk zQm$W}a`Y?`u#m*A$$FqG@DV8C0z56-P>pvmzsBYnqaR$qvaOvaD-CXHx9+$6xJPg4 zVRg&pl0OyY`~HIUAt!%*lgn*}Up(@sW&Y}srQ1Y&r#eRWG0wF(q?$7JocE?4TUjG4 zJZ7}kYZ=Y(Q;co3g66TthScdHHdNGsNg)QyL5Vt58g#CEqXrifpKQ1@HnCe@wikRO3&Pq$$3`4qNROek@c+r zA%bm%GbA7oGpdI&MPoK1#L>a0!gH3y2ZctY${k*E63KvrDu8XgW3<+?@+HRU2@on7 zwdnv<0V^d%`dC=`W+44MwtBEC3%Vy?Qth#t|_3Y;E#cSar z0mrl!)MN2dO06(jp}FGiiWRjUe8`awJ@R3PALXbG>C;lvmYX$irPa<|LPsH!nF26> zXE!y7%^oUf9w$1Pffy$Oahn8?&^(z%r<8b-J5V(N-yKwiGQ89Yr_&?`g6SmIK{vZ6 zx$kld$^Q{IekT_uy8nV)nCRZfear1T)cX7wv_))HVOT+pg7VYKUim2aVC1;QI0S$B z9)9fqw6tAnc`2!4P#IXWr2NQwVT3N2zh zIPS;0dyl(!fKV$j)odFFRLwGyv8b5Ntco442w(uc=tq~tOnokqh{JPy-NVP%y9m$n zKKJM7R?W>f{|kZWvGxGKlmT~?$*pre@?hb!6?xFV%r}hK*ugn zuiEzav2E8+0RJ;^r8WKK8Zh%odbO#AkAR+S;NrTe$$P-%4lwwnONQh~ewsoq2fUxr zH)VjnTOhFJ_SV?P=>w3WuHrYq!67i3r|fl)cXzh-_V1ZSe?KyHa*ccV`F78FfcVSV`7uY z2OlIhG&DA0Vl!hcWMN}sEi`2?IW1vfFkvk*GG#F{FgG-2Vlp(73kWA9Win$nFfcYZ zEn+q~H7ztUGi5DeG-5R^H)b_7I5{|GVliShlU4{q4lpw`H!v|dH8eCbH8nP~rU(cE zBr`ENFg0d1I4v_WWn(QgI5lG}VrFAEEoL=kWi~c7HZ@~4Gn3K^tR!VOGB`G6Ib$s_ zW-&4?G-5Y3EnzV5kV>PpK45kSdAa}pm00006P)t-sWCZ}dW^3A` zVnLHG7B~vz3KtL;4^L&j#FKCqD1UoNL_t(2&tqVq0bu|C{{w{j|NjS0#)k?JqX9?| zWHA0e0A@7*|Nj8YXooSHkr)RkFsGd)b6^a(;&yBX5^qie17icKE(~)RnE(F=s{sQP zM*Rb11}J#zpF_jh9>$PI5%~a93<7_l491^O2J;7~3^Pm#;{jYz4FH66iV+Sm2JG?x O0000<(921A6=DFk?QBp~TD+V4PY zY2htDA;*~ZyzEWrT7N0J22?T9=Yv|e{CJfgTOs?qqHTfDW9QiS3tNAY@V021-nK=# zxV;>$x4m=PB7gpp7-!9732owNmJ7xX=NkOkr=g>9S&H=G&|7fLucZu|_!N?emW3Zy z%KVX3^oEwi!dxh_L;?2T%?o%z)_PaplD{K~O*n5;0ksolEY;^Q8@ zg@@HGmxuhRDBt}H)=Pg*`X-lq8-B6KpF8tci!9w+#CNJ=bRXkfi$kg@N?6A zVc{{owO-4phM!_=s}@v`H8zA!53wPm4onI$7!OL+snVcx^^F=#OnkDTXKtWAWeygF z8(5R1Spy9=fbZf6gN9fUi21gK*1lzJ*T@m~ov@M#%4o*bRl|SR$-k-JC>mqt3{mvk zSFnp`M8k|pZnjtjAT+j5K{PBE)9vW5Wr7tD)a7Q!0c$)SDh{P*Y{}$2vtLouzOl&q zRsa`aZ-p@=AP_T&Ql@CkMg%`P_*7WVlK7y|h*Y`5N=_mfaF7Mq8}AscwXFOQ_ zdoR77bRv|_bn2N;JN+zY9SF4{BMlvS*zi$Cy{T=guhf&h0tQv)G?r6MgN77=mCP@OD@ia2T&iclfc3avVr zT>2q2X-HCB90k{cgCC1k2N!2u9b5%L@B_rn%}LQkO8j3^Xc6PVaX;SOd)&PPgnF52 zR>v5i>9(0pCd6!RMGUQl81o(Ov=UM*e{u~iCZ!sVs63;My z!?cOliKjMggY!Odl$B(a_?&puqze*1a$WKGjdRgufoDd|OnQzuN-P%JSZQNcGBx5! z;+U%GlrLmGRyl8R*2-1ZyeEHQIIk}+ah>K6l32hZB#2N@#Rkf-5vNrr#X_3)6F&Yy z*DsMvA=d^NIp$G;2HEw4|H1EWt-{2AxR(@80G%(6^DzvBc7aCCalVfor*Q%VpMfjA z<*(F%=}*#YEiHBg^lSqc*DX!i11@)ffhR*YWmgK)6bc34{fxdT2lU+n-K$=2&3&9c z02%5kbpsq60wYDrUh{c(S9@>&o@w^?0|jYvds}|5-~a#-D``|%bXZMHI%99M@dU8~ zBsViSF=jA1Wi2r=F=Z_@Ibt_0VKXo>En;M3VPrWuH)S+8W0S@QA0%UCVPP{gGB7PR zH8?aaG&Ey3En+e?I4xx~Ib~yEWiT{2Ibo9t2qz>kGB9E{WjHe}I5}oFEi`0iG%aFf zF=QqIWjakIWV)N2nYfsW-&D|VKX^pEnzk{ zVl6agIX5jhVlgl+G&473Gcjg3VKX^0lh6vRBsDWQWieu9W-T-}G+`|?V>4zgIAmit zEiy7OH8CxH4lS&mz3grh876~O%wS2deo)su)Q%OWYR4C75V4yl+{|^K|2rwFe zL znik&j6LO4c&&xJKkM$2lj{#MT^!cFHEk9o6$5zPxu4sE8bXkslzp#H532%?K>ur0K zi`&cAdfS%M7Wwm+#5ijvOXw3vvs^HC1lJJHJ{=v6%TlCQKySfg{#eTJiBBPk=vnyk zQm$W}a`Y?`u#m*A$$FqG@DV8C0z56-P>pvmzsBYnqaR$qvaOvaD-CXHx9+$6xJPg4 zVRg&pl0OyY`~HIUAt!%*lgn*}Up(@sW&Y}srQ1Y&r#eRWG0wF(q?$7JocE?4TUjG4 zJZ7}kYZ=Y(Q;co3g66TthScdHHdNGsNg)QyL5Vt58g#CEqXrifpKQ1@HnCe@wikRO3&Pq$$3`4qNROek@c+r zA%bm%GbA7oGpdI&MPoK1#L>a0!gH3y2ZctY${k*E63KvrDu8XgW3<+?@+HRU2@on7 zwdnv<0V^d%`dDr9fE=nSnp8EbscX@iCC>`W+44MwtBEC3%Vy?Qth#t|_3Y;E#cSar z0mrl!)MN2dO06(jp}FGiiWRjUe8`awJ@R3PALXbG>C;lvmYX$irPa<|LPsI@+^u^r zy`FR;mCkhPnNK_YEN2}^ldl3Be`hx}h|L}W_k~>f} z0pA@|g)+R<38&K}27>7%)!f zRbf~`je_#i%3k>>_+aF?#yA9j`5u1vg^!|-qK~4FqK~4FqK~5gfg&0B4Es>R-?s$7 z95XTcS(8-*A%8_J6$OimIAo|!7DPoHwF*V3P-=x%9ZW9$f+h_~ii@M*T5#}VvFhOB ztgC~oAP9bdI5|2gx=4xtOA0MwJUH&hyL*qjcYshUG1Y7v2UN{6lCh|m&a8?ZuLxiO zz34}m#7uoIk%+@{eBHyx*SiSM@;>+H=vJ~O1AHR!EPvAti+F>0dehQ5?-PeuK~jj% ziN_4OAn_yDWtZPL=N%S!X2?h-=ZQnaV!nmt7G?!QC7vP8MLfU18^A=~dRAG&K z@)riP+R8H5X$~WXMJyo!5i%+$q67;OS~XHkBxyhD;U9AR33AEgDuR(?0cEI=96$IU z{O;DwO@B_fNx>-4{$kr7BS6P4P_Nqd_pxo)PXPZjaHTc<vGGWMX1sGiEU@HZ@{oEi_|h zHZ3_aFflDNWMgAxF)%PSF=Jwr$Oj)JHZ(LgVPZ35Eo5P1Vl6afFgYz@VK8AWF*0Q_ zGcY$aW@0illM4tZBxN#VHZU+YH!Wf|IW;XbGBaf@Vl-kkEjMO0G&ngpW@0g7HIr5d zLJlx9G&e9YIW;siGBq_ev!)0L0wgmrIWRS5H8?FZGG${eG&nV5En;S4H!Ws0Wo0%t zH8wS4H8Yda3alh$H!?UjWjSLlFlI3_Ei_^`H7#K=G&U_}Ha222W@a)lWn(q7atx*k z1R!_6*pnOzyEgy; From f5cbc7f8eef305ac57120efeeb25fa25792af5a6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 18 Jul 2021 10:55:49 +0100 Subject: [PATCH 015/459] Support language within contentMap --- daemon.py | 31 +++++++++++++++-------- desktop_client.py | 9 ++++--- epicyon.py | 22 +++++++++------- followingCalendar.py | 1 + inbox.py | 18 +++++++------ newsdaemon.py | 8 +++--- posts.py | 60 ++++++++++++++++++++++++-------------------- tests.py | 34 +++++++++++++++---------- 8 files changed, 110 insertions(+), 73 deletions(-) diff --git a/daemon.py b/daemon.py index 4f247e267..74469d140 100644 --- a/daemon.py +++ b/daemon.py @@ -421,7 +421,8 @@ class PubServer(BaseHTTPRequestHandler): schedulePost, eventDate, eventTime, - location, False) + location, False, + self.server.systemLanguage) if messageJson: # name field contains the answer messageJson['object']['name'] = answer @@ -13085,7 +13086,8 @@ class PubServer(BaseHTTPRequestHandler): fields['replyTo'], fields['replyTo'], fields['subject'], fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location'], False) + fields['location'], False, + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13157,7 +13159,8 @@ class PubServer(BaseHTTPRequestHandler): fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13284,7 +13287,8 @@ class PubServer(BaseHTTPRequestHandler): fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13325,7 +13329,8 @@ class PubServer(BaseHTTPRequestHandler): fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13370,7 +13375,8 @@ class PubServer(BaseHTTPRequestHandler): True, fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13417,7 +13423,8 @@ class PubServer(BaseHTTPRequestHandler): True, fields['schedulePost'], fields['eventDate'], fields['eventTime'], - fields['location']) + fields['location'], + self.server.systemLanguage) if messageJson: if fields['schedulePost']: return 1 @@ -13449,7 +13456,8 @@ class PubServer(BaseHTTPRequestHandler): filename, attachmentMediaType, fields['imageDescription'], city, - self.server.debug, fields['subject']) + self.server.debug, fields['subject'], + self.server.systemLanguage) if messageJson: if self._postToOutbox(messageJson, __version__, nickname): return 1 @@ -13472,6 +13480,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.baseDir, nickname, self.server.domain) + intDuration = int(fields['duration']) messageJson = \ createQuestionPost(self.server.baseDir, nickname, @@ -13485,7 +13494,8 @@ class PubServer(BaseHTTPRequestHandler): fields['imageDescription'], city, fields['subject'], - int(fields['duration'])) + intDuration, + self.server.systemLanguage) if messageJson: if self.server.debug: print('DEBUG: new Question') @@ -15013,7 +15023,8 @@ def runDaemon(userAgentsBlocked: [], httpd.allowLocalNetworkAccess, httpd.peertubeInstances, verifyAllSignatures, - httpd.themeName), daemon=True) + httpd.themeName, + httpd.systemLanguage), daemon=True) print('Creating scheduled post thread') httpd.thrPostSchedule = \ diff --git a/desktop_client.py b/desktop_client.py index 0e2b0bd85..44dd245c7 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -468,7 +468,8 @@ def _desktopReplyToPost(session, postId: str, commentsEnabled, attach, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - debug, postId, postId, subject) == 0: + systemLanguage, debug, postId, postId, + subject) == 0: sayStr = 'Reply sent' else: sayStr = 'Reply failed' @@ -529,7 +530,8 @@ def _desktopNewPost(session, commentsEnabled, attach, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - debug, None, None, subject) == 0: + systemLanguage, debug, None, None, + subject) == 0: sayStr = 'Post sent' else: sayStr = 'Post failed' @@ -1217,7 +1219,8 @@ def _desktopNewDMbase(session, toHandle: str, commentsEnabled, attach, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - debug, None, None, subject) == 0: + systemLanguage, debug, None, None, + subject) == 0: sayStr = 'Direct message sent' else: sayStr = 'Direct message failed' diff --git a/epicyon.py b/epicyon.py index 0fc7f86d6..cc21d48fd 100644 --- a/epicyon.py +++ b/epicyon.py @@ -892,6 +892,8 @@ if not args.language: languageCode = getConfigParam(baseDir, 'language') if languageCode: args.language = languageCode + else: + args.language = 'en' # maximum number of new registrations if not args.maxRegistrations: @@ -1124,7 +1126,8 @@ if args.message: args.commentsEnabled, attach, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - args.debug, replyTo, replyTo, subject) + args.language, args.debug, + replyTo, replyTo, subject) for i in range(10): # TODO detect send success/fail time.sleep(1) @@ -2245,6 +2248,7 @@ if args.unfilterStr: sys.exit() if args.testdata: + args.language = 'en' city = 'London, England' nickname = 'testuser567' password = 'boringpassword' @@ -2334,7 +2338,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Zoiks!!!", testFollowersOnly, @@ -2346,7 +2350,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Hey scoob we need like a hundred more #milkshakes", testFollowersOnly, @@ -2358,7 +2362,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "Getting kinda spooky around here", testFollowersOnly, @@ -2370,7 +2374,7 @@ if args.testdata: 'someone', testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "And they would have gotten away with it too" + "if it wasn't for those pesky hackers", @@ -2383,7 +2387,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "man these centralized sites are like the worst!", testFollowersOnly, @@ -2395,7 +2399,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "another mystery solved #test", testFollowersOnly, @@ -2407,7 +2411,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) createPublicPost(baseDir, nickname, domain, port, httpPrefix, "let's go bowling", testFollowersOnly, @@ -2419,7 +2423,7 @@ if args.testdata: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, args.language) domainFull = domain + ':' + str(port) clearFollows(baseDir, nickname, domain) followPerson(baseDir, nickname, domain, 'maxboardroom', domainFull, diff --git a/followingCalendar.py b/followingCalendar.py index 9f038899e..e5453eb2b 100644 --- a/followingCalendar.py +++ b/followingCalendar.py @@ -13,6 +13,7 @@ import os def _dirAcct(baseDir: str, nickname: str, domain: str) -> str: return baseDir + '/accounts/' + nickname + '@' + domain + def _portDomainRemove(domain: str) -> str: """If the domain has a port appended then remove it eg. mydomain.com:80 becomes mydomain.com diff --git a/inbox.py b/inbox.py index 1a129dae3..62cd13a7f 100644 --- a/inbox.py +++ b/inbox.py @@ -2107,7 +2107,7 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, translate: {}, debug: bool, - lastBounceMessage: []) -> bool: + lastBounceMessage: [], systemLanguage: str) -> bool: """Sends a bounce message back to the sending handle if a DM has been rejected """ @@ -2159,7 +2159,8 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str, imageDescription, city, inReplyTo, inReplyToAtomUri, subject, debug, schedulePost, - eventDate, eventTime, location) + eventDate, eventTime, location, + systemLanguage) if not postJsonObject: print('WARN: unable to create bounce message to ' + sendingHandle) return False @@ -2183,7 +2184,7 @@ def _isValidDM(baseDir: str, nickname: str, domain: str, port: int, personCache: {}, translate: {}, debug: bool, lastBounceMessage: [], - handle: str) -> bool: + handle: str, systemLanguage: str) -> bool: """Is the given message a valid DM? """ if nickname == 'inbox': @@ -2258,7 +2259,8 @@ def _isValidDM(baseDir: str, nickname: str, domain: str, port: int, cachedWebfingers, personCache, translate, debug, - lastBounceMessage) + lastBounceMessage, + systemLanguage) return False # dm index will be updated @@ -2284,7 +2286,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, allowLocalNetworkAccess: bool, peertubeInstances: [], lastBounceMessage: [], - themeName: str) -> bool: + themeName: str, systemLanguage: str) -> bool: """ Anything which needs to be done after initial checks have passed """ actor = keyId @@ -2488,7 +2490,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, personCache, translate, debug, lastBounceMessage, - handle): + handle, systemLanguage): return False # get the actor being replied to @@ -2850,7 +2852,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, maxFollowers: int, allowLocalNetworkAccess: bool, peertubeInstances: [], verifyAllSignatures: bool, - themeName: str) -> None: + themeName: str, systemLanguage: str) -> None: """Processes received items and moves them to the appropriate directories """ @@ -3255,7 +3257,7 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, allowLocalNetworkAccess, peertubeInstances, lastBounceMessage, - themeName) + themeName, systemLanguage) if debug: pprint(queueJson['post']) print('Queue: Queue post accepted') diff --git a/newsdaemon.py b/newsdaemon.py index 21b01c67a..647eecd16 100644 --- a/newsdaemon.py +++ b/newsdaemon.py @@ -516,7 +516,8 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, federationList: [], sendThreads: [], postLog: [], maxMirroredArticles: int, - allowLocalNetworkAccess: bool) -> None: + allowLocalNetworkAccess: bool, + systemLanguage: str) -> None: """Converts rss items in a newswire into posts """ if not newswire: @@ -596,7 +597,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, followersOnly, saveToFile, attachImageFilename, mediaType, imageDescription, city, - rssTitle) + rssTitle, systemLanguage) if not blog: continue @@ -773,7 +774,8 @@ def runNewswireDaemon(baseDir: str, httpd, httpd.sendThreads, httpd.postLog, httpd.maxMirroredArticles, - httpd.allowLocalNetworkAccess) + httpd.allowLocalNetworkAccess, + httpd.systemLanguage) print('Newswire feed converted to ActivityPub') if httpd.maxNewsPosts > 0: diff --git a/posts.py b/posts.py index 757751e31..5d8b645fb 100644 --- a/posts.py +++ b/posts.py @@ -838,7 +838,7 @@ def _createPostS2S(baseDir: str, nickname: str, domain: str, port: int, tags: [], attachImageFilename: str, mediaType: str, imageDescription: str, city: str, postObjectType: str, summary: str, - inReplyToAtomUri: str) -> {}: + inReplyToAtomUri: str, systemLanguage: str) -> {}: """Creates a new server-to-server post """ actorUrl = httpPrefix + '://' + domain + '/users/' + nickname @@ -875,7 +875,7 @@ def _createPostS2S(baseDir: str, nickname: str, domain: str, port: int, 'mediaType': 'text/html', 'content': content, 'contentMap': { - 'en': content + systemLanguage: content }, 'attachment': [], 'tag': tags, @@ -906,7 +906,7 @@ def _createPostC2S(baseDir: str, nickname: str, domain: str, port: int, tags: [], attachImageFilename: str, mediaType: str, imageDescription: str, city: str, postObjectType: str, summary: str, - inReplyToAtomUri: str) -> {}: + inReplyToAtomUri: str, systemLanguage: str) -> {}: """Creates a new client-to-server post """ idStr = \ @@ -933,7 +933,7 @@ def _createPostC2S(baseDir: str, nickname: str, domain: str, port: int, 'mediaType': 'text/html', 'content': content, 'contentMap': { - 'en': content + systemLanguage: content }, 'attachment': [], 'tag': tags, @@ -1075,7 +1075,8 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, maximumAttendeeCapacity: int, repliesModerationOption: str, anonymousParticipationEnabled: bool, - eventStatus: str, ticketUrl: str) -> {}: + eventStatus: str, ticketUrl: str, + systemLanguage: str) -> {}: """Creates a message """ content = removeInvalidChars(content) @@ -1206,7 +1207,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, tags, attachImageFilename, mediaType, imageDescription, city, postObjectType, summary, - inReplyToAtomUri) + inReplyToAtomUri, systemLanguage) else: newPost = \ _createPostC2S(baseDir, nickname, domain, port, @@ -1217,7 +1218,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, tags, attachImageFilename, mediaType, imageDescription, city, postObjectType, summary, - inReplyToAtomUri) + inReplyToAtomUri, systemLanguage) _createPostMentions(ccUrl, newPost, toRecipients, tags) @@ -1424,7 +1425,8 @@ def createPublicPost(baseDir: str, schedulePost: bool, eventDate: str, eventTime: str, location: str, - isArticle: bool) -> {}: + isArticle: bool, + systemLanguage: str) -> {}: """Public post """ domainFull = getFullDomain(domain, port) @@ -1454,7 +1456,7 @@ def createPublicPost(baseDir: str, maximumAttendeeCapacity, repliesModerationOption, anonymousParticipationEnabled, - eventStatus, ticketUrl) + eventStatus, ticketUrl, systemLanguage) def _appendCitationsToBlogPost(baseDir: str, @@ -1496,7 +1498,7 @@ def createBlogPost(baseDir: str, inReplyTo: str, inReplyToAtomUri: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, - location: str) -> {}: + location: str, systemLanguage: str) -> {}: blogJson = \ createPublicPost(baseDir, nickname, domain, port, httpPrefix, @@ -1506,7 +1508,8 @@ def createBlogPost(baseDir: str, imageDescription, city, inReplyTo, inReplyToAtomUri, subject, schedulePost, - eventDate, eventTime, location, True) + eventDate, eventTime, location, + True, systemLanguage) blogJson['object']['url'] = \ blogJson['object']['url'].replace('/@', '/users/') _appendCitationsToBlogPost(baseDir, nickname, domain, blogJson) @@ -1519,7 +1522,7 @@ def createNewsPost(baseDir: str, content: str, followersOnly: bool, saveToFile: bool, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, - subject: str) -> {}: + subject: str, systemLanguage: str) -> {}: clientToServer = False inReplyTo = None inReplyToAtomUri = None @@ -1536,7 +1539,8 @@ def createNewsPost(baseDir: str, imageDescription, city, inReplyTo, inReplyToAtomUri, subject, schedulePost, - eventDate, eventTime, location, True) + eventDate, eventTime, location, + True, systemLanguage) blog['object']['type'] = 'Article' return blog @@ -1548,7 +1552,8 @@ def createQuestionPost(baseDir: str, clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, - subject: str, durationDays: int) -> {}: + subject: str, durationDays: int, + systemLanguage: str) -> {}: """Question post with multiple choice options """ domainFull = getFullDomain(domain, port) @@ -1564,7 +1569,7 @@ def createQuestionPost(baseDir: str, False, False, None, None, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) messageJson['object']['type'] = 'Question' messageJson['object']['oneOf'] = [] messageJson['object']['votersCount'] = 0 @@ -1595,7 +1600,7 @@ def createUnlistedPost(baseDir: str, inReplyTo: str, inReplyToAtomUri: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, - location: str) -> {}: + location: str, systemLanguage: str) -> {}: """Unlisted post. This has the #Public and followers links inverted. """ domainFull = getFullDomain(domain, port) @@ -1611,7 +1616,7 @@ def createUnlistedPost(baseDir: str, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) def createFollowersOnlyPost(baseDir: str, @@ -1626,7 +1631,7 @@ def createFollowersOnlyPost(baseDir: str, inReplyToAtomUri: str, subject: str, schedulePost: bool, eventDate: str, eventTime: str, - location: str) -> {}: + location: str, systemLanguage: str) -> {}: """Followers only post """ domainFull = getFullDomain(domain, port) @@ -1642,7 +1647,7 @@ def createFollowersOnlyPost(baseDir: str, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) def getMentionedPeople(baseDir: str, httpPrefix: str, @@ -1694,7 +1699,7 @@ def createDirectMessagePost(baseDir: str, subject: str, debug: bool, schedulePost: bool, eventDate: str, eventTime: str, - location: str) -> {}: + location: str, systemLanguage: str) -> {}: """Direct Message post """ content = resolvePetnames(baseDir, nickname, domain, content) @@ -1717,7 +1722,7 @@ def createDirectMessagePost(baseDir: str, inReplyTo, inReplyToAtomUri, subject, schedulePost, eventDate, eventTime, location, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) # mentioned recipients go into To rather than Cc messageJson['to'] = messageJson['object']['cc'] messageJson['object']['to'] = messageJson['to'] @@ -1735,7 +1740,7 @@ def createReportPost(baseDir: str, clientToServer: bool, commentsEnabled: bool, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, - debug: bool, subject: str = None) -> {}: + debug: bool, subject: str, systemLanguage: str) -> {}: """Send a report to moderators """ domainFull = getFullDomain(domain, port) @@ -1807,7 +1812,7 @@ def createReportPost(baseDir: str, True, False, None, None, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) if not postJsonObject: continue @@ -1891,7 +1896,7 @@ def sendPost(projectVersion: str, imageDescription: str, city: str, federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, - isArticle: bool, + isArticle: bool, systemLanguage: str, debug: bool = False, inReplyTo: str = None, inReplyToAtomUri: str = None, subject: str = None) -> int: """Post to another inbox @@ -1952,7 +1957,7 @@ def sendPost(projectVersion: str, inReplyToAtomUri, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) # get the senders private key privateKeyPem = _getPersonKey(nickname, domain, baseDir, 'private') @@ -2011,7 +2016,8 @@ def sendPostViaServer(projectVersion: str, attachImageFilename: str, mediaType: str, imageDescription: str, city: str, cachedWebfingers: {}, personCache: {}, - isArticle: bool, debug: bool = False, + isArticle: bool, systemLanguage: str, + debug: bool = False, inReplyTo: str = None, inReplyToAtomUri: str = None, subject: str = None) -> int: @@ -2093,7 +2099,7 @@ def sendPostViaServer(projectVersion: str, inReplyToAtomUri, subject, False, None, None, None, None, None, None, None, None, - None, None, None, None, None) + None, None, None, None, None, systemLanguage) authHeader = createBasicAuthHeader(fromNickname, password) diff --git a/tests.py b/tests.py index 54f16e1e7..58bfa3d7b 100644 --- a/tests.py +++ b/tests.py @@ -452,6 +452,7 @@ def createServerAlice(path: str, domain: str, port: int, shutil.rmtree(path) os.mkdir(path) os.chdir(path) + systemLanguage = 'en' nickname = 'alice' httpPrefix = 'http' proxyType = None @@ -501,7 +502,7 @@ def createServerAlice(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) createPublicPost(path, nickname, domain, port, httpPrefix, "Curiouser and curiouser!", testFollowersOnly, @@ -514,7 +515,7 @@ def createServerAlice(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) createPublicPost(path, nickname, domain, port, httpPrefix, "In the gardens of memory, in the palace " + "of dreams, that is where you and I shall meet", @@ -528,7 +529,7 @@ def createServerAlice(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) global testServerAliceRunning testServerAliceRunning = True maxMentions = 10 @@ -579,6 +580,7 @@ def createServerBob(path: str, domain: str, port: int, shutil.rmtree(path) os.mkdir(path) os.chdir(path) + systemLanguage = 'en' nickname = 'bob' httpPrefix = 'http' proxyType = None @@ -626,7 +628,7 @@ def createServerBob(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) createPublicPost(path, nickname, domain, port, httpPrefix, "One of the things I've realised is that " + "I am very simple", @@ -640,7 +642,7 @@ def createServerBob(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) createPublicPost(path, nickname, domain, port, httpPrefix, "Quantum physics is a bit of a passion of mine", testFollowersOnly, @@ -653,7 +655,7 @@ def createServerBob(path: str, domain: str, port: int, testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) global testServerBobRunning testServerBobRunning = True maxMentions = 10 @@ -761,6 +763,7 @@ def testPostMessageBetweenServers(): testServerAliceRunning = False testServerBobRunning = False + systemLanguage = 'en' httpPrefix = 'http' proxyType = None @@ -861,7 +864,7 @@ def testPostMessageBetweenServers(): attachedImageFilename, mediaType, attachedImageDescription, city, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers, - alicePersonCache, isArticle, inReplyTo, + alicePersonCache, isArticle, systemLanguage, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -1052,6 +1055,7 @@ def testFollowBetweenServers(): testServerAliceRunning = False testServerBobRunning = False + systemLanguage = 'en' httpPrefix = 'http' proxyType = None federationList = [] @@ -1185,7 +1189,7 @@ def testFollowBetweenServers(): clientToServer, True, None, None, None, city, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers, - alicePersonCache, isArticle, inReplyTo, + alicePersonCache, isArticle, systemLanguage, inReplyTo, inReplyToAtomUri, subject) print('sendResult: ' + str(sendResult)) @@ -1456,6 +1460,7 @@ def _testFollows(): def _testCreatePerson(): print('testCreatePerson') + systemLanguage = 'en' currDir = os.getcwd() nickname = 'test382' domain = 'badgerdomain.com' @@ -1500,7 +1505,7 @@ def _testCreatePerson(): testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) os.chdir(currDir) shutil.rmtree(baseDir) @@ -1554,6 +1559,7 @@ def testClientToServer(): testServerAliceRunning = False testServerBobRunning = False + systemLanguage = 'en' httpPrefix = 'http' proxyType = None federationList = [] @@ -1650,7 +1656,7 @@ def testClientToServer(): attachedImageFilename, mediaType, attachedImageDescription, city, cachedWebfingers, personCache, isArticle, - True, None, None, None) + systemLanguage, True, None, None, None) print('sendResult: ' + str(sendResult)) for i in range(30): @@ -2869,6 +2875,7 @@ def _testGetMentionedPeople() -> None: def _testReplyToPublicPost() -> None: baseDir = os.getcwd() + systemLanguage = 'en' nickname = 'test7492362' domain = 'other.site' port = 443 @@ -2899,7 +2906,7 @@ def _testReplyToPublicPost() -> None: testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) # print(str(reply)) assert reply['object']['content'] == \ '

' + \ @@ -3391,6 +3398,7 @@ def _testFunctions(): def _testLinksWithinPost() -> None: baseDir = os.getcwd() + systemLanguage = 'en' nickname = 'test27636' domain = 'rando.site' port = 443 @@ -3423,7 +3431,7 @@ def _testLinksWithinPost() -> None: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) assert postJsonObject['object']['content'] == \ '

This is a test post with links.

' + \ @@ -3457,7 +3465,7 @@ def _testLinksWithinPost() -> None: testInReplyTo, testInReplyToAtomUri, testSubject, testSchedulePost, testEventDate, testEventTime, testLocation, - testIsArticle) + testIsArticle, systemLanguage) assert postJsonObject['object']['content'] == content From 9daac007f743c64adba3d7d05a773af299c89aac Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 18 Jul 2021 11:47:55 +0100 Subject: [PATCH 016/459] contentMap language for pinned posts --- daemon.py | 10 ++++++---- posts.py | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/daemon.py b/daemon.py index 74469d140..a30c25be2 100644 --- a/daemon.py +++ b/daemon.py @@ -9569,13 +9569,13 @@ class PubServer(BaseHTTPRequestHandler): path: str, httpPrefix: str, nickname: str, domain: str, - domainFull: str): + domainFull: str, systemLanguage: str): """Returns the featured posts collections in actor/collections/featured """ featuredCollection = \ jsonPinPost(baseDir, httpPrefix, - nickname, domain, domainFull) + nickname, domain, domainFull, systemLanguage) msg = json.dumps(featuredCollection, ensure_ascii=False).encode('utf-8') msglen = len(msg) @@ -10795,7 +10795,8 @@ class PubServer(BaseHTTPRequestHandler): getPinnedPostAsJson(self.server.baseDir, self.server.httpPrefix, nickname, self.server.domain, - self.server.domainFull) + self.server.domainFull, + self.server.systemLanguage) messageJson = {} if pinnedPostJson: postId = pinnedPostJson['id'] @@ -10827,7 +10828,8 @@ class PubServer(BaseHTTPRequestHandler): self.path, self.server.httpPrefix, nickname, self.server.domain, - self.server.domainFull) + self.server.domainFull, + self.server.systemLanguage) return if not htmlGET and \ diff --git a/posts.py b/posts.py index 5d8b645fb..8f1f886fe 100644 --- a/posts.py +++ b/posts.py @@ -1342,7 +1342,7 @@ def undoPinnedPost(baseDir: str, nickname: str, domain: str) -> None: def getPinnedPostAsJson(baseDir: str, httpPrefix: str, nickname: str, domain: str, - domainFull: str) -> {}: + domainFull: str, systemLanguage: str) -> {}: """Returns the pinned profile post as json """ accountDir = acctDir(baseDir, nickname, domain) @@ -1363,7 +1363,7 @@ def getPinnedPostAsJson(baseDir: str, httpPrefix: str, ], 'content': pinnedContent, 'contentMap': { - 'en': pinnedContent + systemLanguage: pinnedContent }, 'id': actor + '/pinned', 'inReplyTo': None, @@ -1382,13 +1382,13 @@ def getPinnedPostAsJson(baseDir: str, httpPrefix: str, def jsonPinPost(baseDir: str, httpPrefix: str, nickname: str, domain: str, - domainFull: str) -> {}: + domainFull: str, systemLanguage: str) -> {}: """Returns a pinned post as json """ pinnedPostJson = \ getPinnedPostAsJson(baseDir, httpPrefix, nickname, domain, - domainFull) + domainFull, systemLanguage) itemsList = [] if pinnedPostJson: itemsList = [pinnedPostJson] From e3e1716f1ff3c173c7b5f6c8f3cd4d568314f886 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 18 Jul 2021 12:48:29 +0100 Subject: [PATCH 017/459] Support for contentMap when creating html posts --- daemon.py | 72 ++++++++++++++++++++++++++++++------------- inbox.py | 6 ++-- utils.py | 17 ++++++++++ webapp_confirm.py | 4 +-- webapp_frontscreen.py | 7 +++-- webapp_moderation.py | 4 +-- webapp_post.py | 36 ++++++++++++---------- webapp_profile.py | 13 +++++--- webapp_search.py | 9 +++--- webapp_timeline.py | 44 +++++++++++++------------- 10 files changed, 132 insertions(+), 80 deletions(-) diff --git a/daemon.py b/daemon.py index a30c25be2..df4165008 100644 --- a/daemon.py +++ b/daemon.py @@ -2867,7 +2867,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) if hashtagStr: msg = hashtagStr.encode('utf-8') msglen = len(msg) @@ -2921,7 +2922,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName, 'outbox') + self.server.themeName, 'outbox', + self.server.systemLanguage) if historyStr: msg = historyStr.encode('utf-8') msglen = len(msg) @@ -2955,7 +2957,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName, 'bookmarks') + self.server.themeName, 'bookmarks', + self.server.systemLanguage) if bookmarksStr: msg = bookmarksStr.encode('utf-8') msglen = len(msg) @@ -3049,7 +3052,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, allowLocalNetworkAccess, self.server.themeName, - accessKeys) + accessKeys, + self.server.systemLanguage) if profileStr: msg = profileStr.encode('utf-8') msglen = len(msg) @@ -6173,7 +6177,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) if hashtagStr: msg = hashtagStr.encode('utf-8') msglen = len(msg) @@ -7113,7 +7118,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) if deleteStr: deleteStrLen = len(deleteStr) self._set_headers('text/html', deleteStrLen, @@ -7321,7 +7327,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7409,7 +7416,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7507,7 +7515,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, self.server.debug, - accessKeys, city, rolesList, + accessKeys, city, + self.server.systemLanguage, + rolesList, None, None) msg = msg.encode('utf-8') msglen = len(msg) @@ -7605,7 +7615,9 @@ class PubServer(BaseHTTPRequestHandler): allowLocalNetworkAccess, self.server.textModeBanner, self.server.debug, - accessKeys, city, skills, + accessKeys, city, + self.server.systemLanguage, + skills, None, None) msg = msg.encode('utf-8') msglen = len(msg) @@ -7738,7 +7750,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.showPublishedDateOnly, self.server.peertubeInstances, self.server.allowLocalNetworkAccess, - self.server.themeName) + self.server.themeName, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7947,7 +7960,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) if GETstartTime: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', @@ -8083,7 +8097,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8212,7 +8227,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8342,7 +8358,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8472,7 +8489,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8611,7 +8629,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8746,7 +8765,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8842,7 +8862,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8955,7 +8976,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9081,7 +9103,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9196,7 +9219,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances, self.server.allowLocalNetworkAccess, self.server.textModeBanner, - accessKeys) + accessKeys, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9312,6 +9336,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.textModeBanner, self.server.debug, accessKeys, city, + self.server.systemLanguage, shares, pageNumber, sharesPerPage) msg = msg.encode('utf-8') @@ -9425,6 +9450,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.textModeBanner, self.server.debug, accessKeys, city, + self.server.systemLanguage, following, pageNumber, followsPerPage).encode('utf-8') @@ -9538,6 +9564,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.textModeBanner, self.server.debug, accessKeys, city, + self.server.systemLanguage, followers, pageNumber, followsPerPage).encode('utf-8') @@ -9674,6 +9701,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.textModeBanner, self.server.debug, accessKeys, city, + self.server.systemLanguage, None, None).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, diff --git a/inbox.py b/inbox.py index 62cd13a7f..f45f6f72a 100644 --- a/inbox.py +++ b/inbox.py @@ -164,7 +164,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> None: + themeName: str, systemLanguage: str) -> None: """Converts the json post into html and stores it in a cache This enables the post to be quickly displayed later """ @@ -182,7 +182,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int, httpPrefix, __version__, boxname, None, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, not isDM(postJsonObject), True, True, False, True) @@ -2593,7 +2593,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName) + themeName, systemLanguage) if debug: timeDiff = \ str(int((time.time() - htmlCacheStartTime) * diff --git a/utils.py b/utils.py index 997cf68ce..70be68a75 100644 --- a/utils.py +++ b/utils.py @@ -28,6 +28,23 @@ invalidCharacters = ( ) +def getContentFromPost(postJsonObject: {}, systemLanguage: str) -> str: + """Returns the content from the post in the given language + """ + thisPostJson = postJsonObject + if hasObjectDict(postJsonObject): + thisPostJson = postJsonObject['object'] + if not thisPostJson.get('content'): + return '' + content = thisPostJson['content'] + if thisPostJson.get('contentMap'): + if isinstance(thisPostJson['contentMap'], dict): + if thisPostJson['contentMap'].get(systemLanguage): + if isinstance(thisPostJson['contentMap'][systemLanguage], str): + return thisPostJson['contentMap'][systemLanguage] + return content + + def acctDir(baseDir: str, nickname: str, domain: str) -> str: return baseDir + '/accounts/' + nickname + '@' + domain diff --git a/webapp_confirm.py b/webapp_confirm.py index 7c820ea39..00b570392 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -34,7 +34,7 @@ def htmlConfirmDelete(cssCache: {}, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Shows a screen asking to confirm the deletion of a post """ if '/statuses/' not in messageId: @@ -75,7 +75,7 @@ def htmlConfirmDelete(cssCache: {}, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, False, False, False, False) deletePostStr += '

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 delta 4073 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 diff --git a/theme/light/icons/logorss.png b/theme/light/icons/logorss.png index 62c9a2fe08995c94cef7a5f48c67bd3d3dcabfd1..e651b93ded8eca438f7ee5853446e4a6c1fa1295 100644 GIT binary patch literal 8095 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 UkEvBDH48P9Z0kZQu@2brFGFejNdN!< delta 1402 zcmV-=1%>*bKe7vu7=Hl+0001xr{kRf00QoMR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4x+#n2v-&sYM5Zf4=fn?H|xpSpk^}bY^pa5~;82mZRg#PR2 z2tRO9HV#qEQ%-5a6?4p7(DCiK%BpFLt6o<;U*Y+|9PJwc4YfIVfehYg)+IHKCA9t9Q*wVg4 zo}Z&3dQD*z<}M`Ls{L4i%$tG~bO<#`=GpVrPpIP>N=HCm#c8`+Rd}Xy_2?7(6BJ2@ zJ;3l7%yhLc?SG`NgCACi?>tMrJU`ftFBcC%p7$8d!x;V8U5N=HP3NX=TUk8}+!1nF z(-Ml|R`hL^jN$>=TS2F7b=y%YWny3%6sb|6L4CEADwY_zGnU586`-cg#W-`7Er3v* zg$A2sX`seZfmdMVYb~_==H;%E!*L^wWP%uB28UUFM}PTLXpEWiM4Ml+!n$}SDwZ+H z%^ITsgvKq8bOrcUp0CMOKu}kh9UCm$en{*}XKu|rk_B2YP|+Tr1z9@<5F+?iEQSOG zwnUHN9gg83*6bLy>!6F>A94@eLX7{!jc??_ME4!!!bJB8 zxsTjlP;2worfw1zq4^okpl*WCulMeSz0BXVueafEC%lE;LT{nB&|By&^cMPWgz(oP zhY8=%^`X&DRz+=-FbN!gP!xqvQ>9WWg6$yUkfAzR5EXIMDionYs1;guFnQ@8G%+M8 zE{=k0!NH%!s)LKOt`4q(Aov5~=H{g6A|>9J6k5c1;qgAsyXWxUeSpxYGR^8512o+> zGpVGQ%dd!`R|F76Kf;K}%ra&rDGlHHx~Fccy9Cej@B6cQ)q=%;fPhFm!wl0VUMHT~ zv<=St#4%QuRpN8vQIjr6{K$31<2TMFmj#{~GqagF;ux`5>R_dVS=rQxCyC>#rc=I< z^H}A)#aXM?SnHnrg^_~3yu@`{Lr7r(i;y5fK@AmDVIx7iPKt#LohN<#gRWm9mqM-z z7&+##0S&V22mgbA-?O!f6XRY|BnfoCIL^l~5ZVQrb;tQWcAVx35PSx%^tOMa0Zf0A zUT${ujJ0}MPFvMIY#kd{y^0`F(^O?e=C3xrp_xwX!5`T%5UR_Pny z;1C!oQTCe8yL&q4_HR#Xem?@pa(b>O_~8Hm00vM@R7C(I006tYyEpE&NdN!<0d!JM zQvg8b*k%9#00Cl4M??UK1szC}X&N7Y0TU7sFor0kvj6}9n@L1LR4C82&oK_gAP5Fv zj02-b@EULPX!d9x#UnU65U2%d(&nvo@k@js1o-Zh;gB1LV>s9$H=3zRH=tKTB~RLI zkUTqjcJO|v+W%YCeeaW}f*{SEf@DKmWJ3?^8(OqXu;|rR706JV&4kiy=3!Vp581XZ zEp2o~?0KZ?AGj{O#%IE*C>$w1Aq%7ke-NHvU`3`kaBO^aPSu2Jg#V4f6#xJL07*qo IM6N<$f_}=HrT_o{ diff --git a/theme/light/icons/newswire_favicon.ico b/theme/light/icons/newswire_favicon.ico index a368686dcbd23e359418e554f1059424ae6cd433..e9a67c9c83afcbbde26a40164151e3071207ad8a 100644 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%b4D8;$dj=5yC)WayUKk!VgMk4S z7PJaeV$H?o4wx9R@dQ(kP7|viqz+v#h)*y5*z6|Oeq4Hq)lZ5a$yNIw7?SgWxF3j* k1My=Z{s_cBfcQ@}1H(UK28RF4K->()K=wZnA1DvP0JjUtjsO4v delta 41 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 b^Z7MSSY@$3`qB96db_u^bU>Au`yTo)n!!3^ delta 1589 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 Q;sxRCu*SZ~Hek#D0SG=3g8%>k delta 1369 zcmV-f1*ZC+Gn5OE7=Hl+0001xr{kRf00QcIR9JLUVRs;Ka&Km7Y-J#Hd2nSQWq4_3 z004N}?Ur4#+$ao%-&sYLfFy*(axgzt*}*J72W95`w~ZP?OgtE&F>?c`Wpgnu+%~ba3QaaC z(m{=-@dzR?^CK48>z2JJ}otExmoj8TJ7AWXz)PIW`|B75#=>7|G0qB0_ z_64=Re&^aIcJ9KZX%y_fF@__LysqE0%Kx@~J9;~MJ9;~MJ9;~MJNo}S!e56HegGg@ zoPXyhz-!m6rn<>6z@3Dk%GRw#C2LjNMQkskRU=q4Sy9> zVIx7iPKt#LohN<#gRWm9mqM-z7&+##0S&V22mgcLv$cv7<6crE33R_W&c`qi+69_* z$N4^XoaPA-d3_KaKDZ5gTmQXAL?`QN) zc_4ZVgjc<}wa#(+0Ay)a=^NnS5GoicQTCe8yL&q4_HR#Xem?@pa(b>O_~8Hm00vM@ zR7C&)0K2=pH}17blfxJ}e*qH`7%3{6hI;@20D(zFK~yNurO>erfFKYB&?6L-jKC`+)k+Ni0wnTQamM#05k|Zg6 zwnT-jSxV#?>b~#p-}C)_pWpZO`aS>MUav8qbI$vEU)On`>pEx7%uQ2cJr+hDMgRc7 zVxX^WPI*_~f6>!Y{ug1rCMf-i{+2c*bA&GlPw>QG-O(VDA07=tld%{8fIM1y$M)6& z{IK?3U4&z>;_dEt+;Mb7P5NwYo3V{wAU2JCctz_ilCt^@V4_woDb>s$ov;1OG?ry=%}5_b8R!!EDAS*<#?5%c{0xj^DB3kUC} z_mdE7m(b4D_uh+>Wf{~(v(p84JOWH880#eX0IaVJ93mZj&wh7;qDs4@|H>tDay~ z8Ac^3>ZxK>HR5x(wFLNJw#ctU=Yijs)Uvsb4hLdEA9KD4N5bVD-epg^)Jn-?0(0*q z2GPRwwxJ-=j4>LJzoXYo^7RK>-_PQ!L7$TEvePEzED073xwsqfjnGuh>h^klOpV#) z0~~|axIjmPTiL2M;Gcwe*sGt$GF)b)7D1*Ls%JJSq@U+;@c^WuP`GROeR>2O?jCFRj)Hb(sl=ugmTSijf zILyx(=9Yx>tv2Y^sYiF{03)S64XUg_($S6CmtF~Pmv_UuVwO%ek~Lj z9)H#}m7n%qul5d;lE|xF-e=<1mUw5Hr#FGsQUm$?R+2Y_S)LSziR{)Qa-GfNvp3)B zd8e;KV^u9um_q_KN^mcyU3x3aTa}FNdPThy#|eBjSxTd$^9#O~orW5eDHeYsFtSb0 zJ{=hS9(jLlw_Jc%2iJ>H8LOnLPPkO*?UT69eEKox3(rd5x{xkv;*eYp|G)-DJxi2T zT=%0qL|}0oj{Y&mFA#f?mdAndRteKG5@(ZdEup&|O54sL#4Ta1b%8d_RM$EyDOkA|UcfSSZ*1m(h+9C!d_ZsB4inFDMGu{V{+4 z>K<^syCyWG$o!q!;JAI7R@Cw66)6>ePnR`#15XxVGyJyK6=GH?L!;{{+djXw zuug8fjLs6#)3~CMwRUusIqS2Cqmsokm3B8j>o<2mtX_rMFwAqg-D~C;yTsjFiz_{B z!0^eopk=MQ@C=@{Fk1%8%!a$7~%LXs(tt-LTZ>55pOTMDVk3(4D~F z_fYGh%~)%H-p?nth9*X;@TC{^@$@5D@AS;vgnfishwEH4;GKW&H0KkydGZ*Yx!&^4VAGK3zJhMqOSeCk4+@pd zN!1+2M4xyul;)-yT{2NCerh8e%d2xS82D-{T(;P;_yydb$%=J~`Fz;uVxr{q-R3o# zX-r}&qs~+PX3M)>$yKJRmdEdL9JhQ^f+U@ZQ5*xx^DOqgtUXuO6E1~4H2KPhJaeHc zSc>|1QtnVxrEn&9ZDcM(^)^**^bH2rT6<$Y3#u)0-d5dM_R_%8PV*2`7!PAJx?FE* zj&3hJpr39p;i1b|zodw{49l9aK-b&U=>}^!-qj+r5XgF=gD2Ds>#=h)HdTVE> z&pNFCc$tKaZN-g+f>ELPQ@{Y-OXLX4LR6oaEM#zrEN`+p+Wm6pjVSbRZspew9T^{* z`C95~tJ%6h)0zkU$xD0jI}Zvk?nb=%biGn(dXU~Ft$dJU<}=P^o&VBwM4f$kri9Ab zjo#ETviSD>(&|eYRkzp!r+^UKPr@ru$Lpv^V$H!9=U=`~jlWvUd=~cDuy<9v`hmNe z&7Dx68kbXJ*JgJ&&na2d-gMDK!~`F8=TepFlu~ofx|1A2<*Vw^@BAKcmdB4}N9Hbd zhE8c!lmy6x?y>Kq+1V*vj_DrrSPP%Keym-Tl!^SIwuq+{Jvd)7t(~sD+_-?gNBry^ zUil*Q$oAp&vW)hzQi%1#@(!g@9+d^gC2r9xC3JIdb1$!eG%h5!C+N9d0io##WZA(QZ;EnZcw(FQFo)IUQCzeHVXU@l5o@DdoF{!6Hg{?Gh=yr`nI0 zPJPkfaT#WCS-&f5Nt>ucz%lk2I8JqlD+?;c&wscX-T3OzGbSU_DoC|Zu+9o&sUY~^ zJ*Gqg3~RdwEvJWN90IXrEgWrdJ$fk*Ug+}i(L}x3(CjEXyH|-rjE-MRTbPLnV1PYX zG$^0RdY~-pRBhz&Yq}3l#jnLQ9wl054(VmO-Le8ZcotKYg=;Xk6n$P^=*#qPoZh*0 zkx$(V%3^pfK`IwYM)5A(6!fTbAeRjv1{KXYQCP8OTV)l*>UoiilqeGcahRKDet+8 zk5y~@q4IIpw7m5Jyzx0_bzc-(9Sc=(8SGX#FCDGp^11L~sLq?jlT%@=m4K2oop0 z@sn7F%GI86-^5?xYbess(IsBqh&^9L2kk3QdTw8Qa=B~ty%f#J zM4pp`ZUd)Kn&cQa|Fp8A3$k%&n`!l1zDfhQG1dK~$LsnIsoafR?=OYktj<-OgX3p6 z>o-F=px)Lh8^n=@B1dW8q17bQPk0{GeXFo{6}TG(lT{5D#HDHKMXumhQqFJmZPo+` zOc)6#n03t2OQnz>#Vh*|o$Hj!dwOnqc}*9Z-RlV5P8<#LUd>}4@j`_vYMtgtPU^3h zdp#d>f(P3(De$F0B{1Zq*$=t*Mi*vAlg_%lZPaq0wd<4O+vQmHt*xwAdC&kE-&xNK zG;6qqwd8=Cym1g&eEC6zPj%PQIfI8}+b7xIt)QuLq9VGe)OG#bLsqcarSp^Zmy-1b zVIwk}AC`|e8N@+fVzL6(eB4jwjA~vn$Gep(n^Gp^Tlm&;&XF_wUNy5qR&N-lZpLWm zieHW@eScV^S6Svvnsj?Vs@?5`2ZPT>?Yw+^A9s&TjK9*Jq8RDI*to8Yi(x#|G{VEnW*B-uD@! z+;$y=C9!k^|3$GvIx8>a095gqebUh`Z%-k)4B@Ec2W$E9^_m&-h>zX7(PQ5LwU)-$J-ABBaL&f4DlP|1p07{WD0O}I zWATiElbu;o{*_xtNnqcO((dmLP#@lGU*6ZF8iY3MicVN=cQVT%aT$Rc8*>LUT;t9t z6?g{a_ZA!_80#QRG&i{VV9Y+PnBtl{AE$hMJKs?J z)yg|3Zvyrqkz>4x&WJ#tq?G62sbhUl{k3q<^&Oq@?m@Zm#Vgr_W%k15{GHo2%NHI7 zUO&#yU^cyY%UCycZ*g|r?~LPI%dX0M@5*zJvM#3keE;0Lxi~Pn2m9i0x%asy?0W&- zMjI_0TPpO

\n' sharedItemsForm += '\n' return sharedItemsForm @@ -249,47 +258,25 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, contactNickname, name, actor) if not resultsExist and currPage > 1: - # show the previous button + # show the previous page button sharedItemsForm += \ - _htmlSearchResultSharePrev(actor, domainFull, + _htmlSearchResultSharePage(actor, domainFull, callingDomain, pageNumber, searchStrLower, - translate) + translate, True) resultsExist = True ctr += 1 if ctr >= resultsPerPage: currPage += 1 if currPage > pageNumber: - postActor = \ - getAltPath(actor, domainFull, - callingDomain) - # next page link, needs to be a POST + # show the next page button sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - ' \n' - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - '
\n' + \ - ' \n' - sharedItemsForm += \ - ' ' + translate['Page down'] + \
-                                '\n' - sharedItemsForm += '
\n' - sharedItemsForm += '
\n' + _htmlSearchResultSharePage(actor, domainFull, + callingDomain, + pageNumber, + searchStrLower, + translate, False) break ctr = 0 break From 84f3b4f2046b6e2fbaed3c47645e90da38d4022e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 11:42:49 +0100 Subject: [PATCH 151/459] Function for shared items search result --- webapp_search.py | 91 +++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index 08e29cd64..9907085fa 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -205,6 +205,52 @@ def _htmlSearchResultSharePage(actor: str, domainFull: str, return sharedItemsForm +def _htmlSharesResult(sharesJson: {}, pageNumber: int, resultsPerPage: int, + searchStrLowerList: [], currPage: int, ctr: int, + callingDomain: str, httpPrefix: str, domainFull: str, + contactNickname: str, actor: str, + resultsExist: bool, searchStrLower: str, + translate: {}) -> (bool, int, int, str): + """ + """ + sharedItemsForm = '' + if currPage > pageNumber: + return resultsExist, currPage, ctr, sharedItemsForm + + for name, sharedItem in sharesJson.items(): + if _matchSharedItem(searchStrLowerList, sharedItem): + if currPage == pageNumber: + # show individual search result + sharedItemsForm += \ + _htmlSearchResultShare(sharedItem, translate, + httpPrefix, domainFull, + contactNickname, + name, actor) + if not resultsExist and currPage > 1: + # show the previous page button + sharedItemsForm += \ + _htmlSearchResultSharePage(actor, domainFull, + callingDomain, + pageNumber, + searchStrLower, + translate, True) + resultsExist = True + ctr += 1 + if ctr >= resultsPerPage: + currPage += 1 + if currPage > pageNumber: + # show the next page button + sharedItemsForm += \ + _htmlSearchResultSharePage(actor, domainFull, + callingDomain, + pageNumber, + searchStrLower, + translate, False) + return resultsExist, currPage, ctr, sharedItemsForm + ctr = 0 + return resultsExist, currPage, ctr, sharedItemsForm + + def htmlSearchSharedItems(cssCache: {}, translate: {}, baseDir: str, searchStr: str, pageNumber: int, @@ -248,37 +294,20 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, if not sharesJson: continue - for name, sharedItem in sharesJson.items(): - if _matchSharedItem(searchStrLowerList, sharedItem): - if currPage == pageNumber: - # show individual search result - sharedItemsForm += \ - _htmlSearchResultShare(sharedItem, translate, - httpPrefix, domainFull, - contactNickname, - name, actor) - if not resultsExist and currPage > 1: - # show the previous page button - sharedItemsForm += \ - _htmlSearchResultSharePage(actor, domainFull, - callingDomain, - pageNumber, - searchStrLower, - translate, True) - resultsExist = True - ctr += 1 - if ctr >= resultsPerPage: - currPage += 1 - if currPage > pageNumber: - # show the next page button - sharedItemsForm += \ - _htmlSearchResultSharePage(actor, domainFull, - callingDomain, - pageNumber, - searchStrLower, - translate, False) - break - ctr = 0 + (resultsExist, currPage, ctr, + resultStr) = _htmlSharesResult(sharesJson, pageNumber, + resultsPerPage, + searchStrLowerList, + currPage, ctr, + callingDomain, httpPrefix, + domainFull, + contactNickname, + actor, resultsExist, + searchStrLower, translate) + sharedItemsForm += resultStr + + if currPage > pageNumber: + break break if not resultsExist: sharedItemsForm += \ From 4369eac4755671f0defc28b17300944366483640 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 11:59:06 +0100 Subject: [PATCH 152/459] Federated shared items catalogs are searchable --- daemon.py | 5 ++++- webapp_search.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index 2630295b1..7bfa71dde 100644 --- a/daemon.py +++ b/daemon.py @@ -3137,6 +3137,8 @@ class PubServer(BaseHTTPRequestHandler): return else: # shared items search + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains sharedItemsStr = \ htmlSearchSharedItems(self.server.cssCache, self.server.translate, @@ -3145,7 +3147,8 @@ class PubServer(BaseHTTPRequestHandler): maxPostsInFeed, httpPrefix, domainFull, - actorStr, callingDomain) + actorStr, callingDomain, + sharedItemsFederatedDomains) if sharedItemsStr: msg = sharedItemsStr.encode('utf-8') msglen = len(msg) diff --git a/webapp_search.py b/webapp_search.py index 9907085fa..f662c8954 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -257,7 +257,8 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, resultsPerPage: int, httpPrefix: str, domainFull: str, actor: str, - callingDomain: str) -> str: + callingDomain: str, + sharedItemsFederatedDomains: []) -> str: """Search results for shared items """ currPage = 1 @@ -309,6 +310,38 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, if currPage > pageNumber: break break + + # search federated shared items + catalogsDir = baseDir + '/cache/catalogs' + if currPage <= pageNumber and os.path.isdir(catalogsDir): + for subdir, dirs, files in os.walk(catalogsDir): + for f in files: + if not f.endswith('.json'): + continue + federatedDomain = f.split('.')[0] + if federatedDomain not in sharedItemsFederatedDomains: + continue + sharesFilename = catalogsDir + '/' + f + sharesJson = loadJson(sharesFilename) + if not sharesJson: + continue + + (resultsExist, currPage, ctr, + resultStr) = _htmlSharesResult(sharesJson, pageNumber, + resultsPerPage, + searchStrLowerList, + currPage, ctr, + callingDomain, httpPrefix, + domainFull, + contactNickname, + actor, resultsExist, + searchStrLower, translate) + sharedItemsForm += resultStr + + if currPage > pageNumber: + break + break + if not resultsExist: sharedItemsForm += \ '
' + translate['No results'] + '
\n' From ec74a69c6a53f6ff6e499860105a31444aadd928 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 12:01:50 +0100 Subject: [PATCH 153/459] DFC formatted catalogs must be converted to shares format to be searchable --- webapp_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_search.py b/webapp_search.py index f662c8954..67a81421d 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -316,7 +316,7 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, if currPage <= pageNumber and os.path.isdir(catalogsDir): for subdir, dirs, files in os.walk(catalogsDir): for f in files: - if not f.endswith('.json'): + if not f.endswith('.shares.json'): continue federatedDomain = f.split('.')[0] if federatedDomain not in sharedItemsFederatedDomains: From 80339ecf20526193fddbcc924c389affcbf8c9ab Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 12:29:07 +0100 Subject: [PATCH 154/459] More descriptive variable name --- shares.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/shares.py b/shares.py index 0c8b82fbb..36d8d41bf 100644 --- a/shares.py +++ b/shares.py @@ -1011,19 +1011,15 @@ def mergeSharedItemTokens(baseDir: str, domain: str, """ removals = [] changed = False - print('Test 46237') for tokenDomain, tok in tokensJson.items(): - print('tokenDomain: ' + tokenDomain) if domain: if tokenDomain.startswith(domain): continue if tokenDomain not in newSharedItemsFederatedDomains: removals.append(tokenDomain) - print('remove ' + tokenDomain) # remove domains no longer in the federation list for tokenDomain in removals: del tokensJson[tokenDomain] - print('removing ' + tokenDomain) changed = True # add new domains from the federation list for tokenDomain in newSharedItemsFederatedDomains: @@ -1131,26 +1127,26 @@ def _updateFederatedSharesCache(session, sharedItemsFederatedDomains: [], asHeader = { 'Accept': 'application/ld+json' } - for otherDomain in sharedItemsFederatedDomains: - # NOTE: otherDomain does not have a port extension, + for federatedDomain in sharedItemsFederatedDomains: + # NOTE: federatedDomain does not have a port extension, # so may not work in some situations - if otherDomain.startswith(domain): + if federatedDomain.startswith(domain): # only download from instances other than this one continue - if not tokensJson.get(otherDomain): + if not tokensJson.get(federatedDomain): # token has been obtained for the other domain continue - url = httpPrefix + '://' + otherDomain + '/catalog' - asHeader['Authorization'] = tokensJson[otherDomain] + url = httpPrefix + '://' + federatedDomain + '/catalog' + asHeader['Authorization'] = tokensJson[federatedDomain] catalogJson = getJson(session, url, asHeader, None, debug, __version__, httpPrefix, None) if not catalogJson: print('WARN: failed to download shared items catalog for ' + - otherDomain) + federatedDomain) continue - catalogFilename = catalogsDir + '/' + otherDomain + '.json' + catalogFilename = catalogsDir + '/' + federatedDomain + '.json' if saveJson(catalogJson, catalogFilename): - print('Downloaded shared items catalog for ' + otherDomain) + print('Downloaded shared items catalog for ' + federatedDomain) else: time.sleep(2) From 0db65ae069f76ae5d9e57af753301b7c803b3861 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 13:55:44 +0100 Subject: [PATCH 155/459] Convert dfc format to internal shares format --- daemon.py | 3 +- shares.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/daemon.py b/daemon.py index 7bfa71dde..2bc46a425 100644 --- a/daemon.py +++ b/daemon.py @@ -15340,7 +15340,8 @@ def runDaemon(sharedItemsFederatedDomains: [], threadWithTrace(target=runFederatedSharesDaemon, args=(baseDir, httpd, httpPrefix, domain, - proxyType, debug), daemon=True) + proxyType, debug, + httpd.systemLanguage), daemon=True) # flags used when restarting the inbox queue httpd.restartInboxQueueInProgress = False diff --git a/shares.py b/shares.py index 36d8d41bf..bde0203e1 100644 --- a/shares.py +++ b/shares.py @@ -128,7 +128,7 @@ def removeSharedItem(baseDir: str, nickname: str, domain: str, '" does not exist in ' + sharesFilename) -def _addShareDurationSec(duration: str, published: str) -> int: +def _addShareDurationSec(duration: str, published: int) -> int: """Returns the duration for the shared item in seconds """ if ' ' not in duration: @@ -151,16 +151,17 @@ def _addShareDurationSec(duration: str, published: str) -> int: def _getshareDfcId(baseDir: str, systemLanguage: str, itemType: str, itemCategory: str, - translate: {}) -> str: + translate: {}, dfcIds: {} = None) -> str: """Attempts to obtain a DFC Id for the shared item, based upon productTypes ontology. See https://github.com/datafoodconsortium/ontology """ if translate['food'] not in itemCategory.lower(): return '' - dfcIds = _loadDfcIds(baseDir, systemLanguage) if not dfcIds: - return '' + dfcIds = _loadDfcIds(baseDir, systemLanguage) + if not dfcIds: + return '' itemTypeLower = itemType.lower() matchName = '' matchId = '' @@ -185,6 +186,19 @@ def _getshareDfcId(baseDir: str, systemLanguage: str, return matchId +def _getshareTypeFromDfcId(dfcUri: str, dfcIds: {}) -> str: + """Attempts to obtain a share item type from its DFC Id, + based upon productTypes ontology. + See https://github.com/datafoodconsortium/ontology + """ + for name, uri in dfcIds.items(): + if uri.endswith('#' + dfcUri): + return name + elif uri == dfcUri: + return name + return None + + def _indicateNewShareAvailable(baseDir: str, httpPrefix: str, domainFull: str) -> None: """Indicate to each account that a new share is available @@ -1111,7 +1125,8 @@ def authorizeSharedItems(sharedItemsFederatedDomains: [], def _updateFederatedSharesCache(session, sharedItemsFederatedDomains: [], baseDir: str, domain: str, httpPrefix: str, - tokensJson: {}, debug: bool) -> None: + tokensJson: {}, debug: bool, + systemLanguage: str) -> None: """Updates the cache of federated shares for the instance. This enables shared items to be available even when other instances might not be online @@ -1147,6 +1162,13 @@ def _updateFederatedSharesCache(session, sharedItemsFederatedDomains: [], catalogFilename = catalogsDir + '/' + federatedDomain + '.json' if saveJson(catalogJson, catalogFilename): print('Downloaded shared items catalog for ' + federatedDomain) + sharesJson = _dfcToSharesFormat(catalogJson, + baseDir, systemLanguage) + if sharesJson: + sharesFilename = \ + catalogsDir + '/' + federatedDomain + '.shares.json' + saveJson(sharesJson, sharesFilename) + print('Converted shares catalog for ' + federatedDomain) else: time.sleep(2) @@ -1171,7 +1193,8 @@ def runFederatedSharesWatchdog(projectVersion: str, httpd) -> None: def runFederatedSharesDaemon(baseDir: str, httpd, httpPrefix: str, - domain: str, proxyType: str, debug: bool) -> None: + domain: str, proxyType: str, debug: bool, + systemLanguage: str) -> None: """Runs the daemon used to update federated shared items """ secondsPerHour = 60 * 60 @@ -1208,5 +1231,70 @@ def runFederatedSharesDaemon(baseDir: str, httpd, httpPrefix: str, session = createSession(proxyType) _updateFederatedSharesCache(session, sharedItemsFederatedDomains, baseDir, domain, httpPrefix, tokensJson, - debug) + debug, systemLanguage) time.sleep(secondsPerHour * 6) + + +def _dfcToSharesFormat(catalogJson: {}, + baseDir: str, systemLanguage: str) -> {}: + """Converts DFC format into the internal formal used to store shared items. + This simplifies subsequent search and display + """ + if not catalogJson.get('DFC:supplies'): + return {} + sharesJson = {} + dfcIds = _loadDfcIds(baseDir, systemLanguage) + currTime = int(time.time()) + for item in catalogJson['DFC:supplies']: + if not item.get('@id') or \ + not item.get('@type') or \ + not item.get('DFC:hasType') or \ + not item.get('DFC:startDate') or \ + not item.get('DFC:expiryDate') or \ + not item.get('DFC:quantity') or \ + not item.get('DFC:price') or \ + not item.get('DFC:Image') or \ + not item.get('DFC:description'): + continue + + if ' ' not in item['DFC:price']: + continue + if ':' not in item['DFC:description']: + continue + if ':' not in item['DFC:hasType']: + continue + + try: + expiryTime = \ + datetime.datetime.strptime(item['DFC:expiryDate'], + '%Y-%m-%dT%H:%M:%SZ') + except BaseException: + continue + durationSec = \ + int((expiryTime - datetime.datetime(1970, 1, 1)).total_seconds()) + if durationSec < currTime: + # has expired + continue + + hasType = item['DFC:hasType'].split(':')[1] + itemType = _getshareTypeFromDfcId(hasType, dfcIds) + if not itemType: + continue + dfcId = dfcIds[itemType] + itemID = item['@id'] + description = item['DFC:description'].split(':', 1)[1].strip() + sharesJson[itemID] = { + "displayName": item['DFC:description'].split(':')[0], + "summary": description, + "imageUrl": item['DFC:Image'], + "itemQty": item['DFC:quantity'], + "dfcId": dfcId, + "itemType": itemType, + "category": "food", + "location": "", + "published": item['DFC:startDate'], + "expire": durationSec, + "price": item['DFC:price'].split(' ')[0], + "currency": item['DFC:price'].split(' ')[1] + } + return sharesJson From 974b97dc878611bf0fed9bb680796dfdc75e3b77 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 15:06:14 +0100 Subject: [PATCH 156/459] Include federated shared items within shares timeline --- daemon.py | 58 ++++++++++++++++++++++++++------- webapp_column_left.py | 21 +++++++----- webapp_frontscreen.py | 4 ++- webapp_moderation.py | 6 ++-- webapp_profile.py | 2 ++ webapp_search.py | 2 ++ webapp_timeline.py | 75 ++++++++++++++++++++++++++++--------------- webapp_utils.py | 30 +++++++++++++++-- 8 files changed, 148 insertions(+), 50 deletions(-) diff --git a/daemon.py b/daemon.py index 2bc46a425..e21191fe4 100644 --- a/daemon.py +++ b/daemon.py @@ -7665,6 +7665,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, rolesList, None, None) msg = msg.encode('utf-8') @@ -7740,6 +7741,8 @@ class PubServer(BaseHTTPRequestHandler): city = getSpoofedCity(self.server.city, baseDir, nickname, domain) + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlProfile(self.server.rssIconAtTop, self.server.cssCache, @@ -7765,6 +7768,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + sharedItemsFederatedDomains, skills, None, None) msg = msg.encode('utf-8') @@ -8076,6 +8080,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = htmlInbox(self.server.cssCache, defaultTimeline, recentPostsCache, @@ -8109,7 +8115,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) if GETstartTime: self._benchmarkGETtimings(GETstartTime, GETtimings, 'show status done', @@ -8213,6 +8220,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlInboxDMs(self.server.cssCache, self.server.defaultTimeline, @@ -8246,7 +8255,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8343,6 +8353,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlInboxReplies(self.server.cssCache, self.server.defaultTimeline, @@ -8376,7 +8388,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8507,7 +8520,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8638,7 +8652,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8778,7 +8793,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -8880,6 +8896,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlInboxFeatures(self.server.cssCache, self.server.defaultTimeline, @@ -8914,7 +8932,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9011,7 +9030,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9091,6 +9111,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlBookmarks(self.server.cssCache, self.server.defaultTimeline, @@ -9125,7 +9147,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9252,7 +9275,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9335,6 +9359,8 @@ class PubServer(BaseHTTPRequestHandler): accessKeys = \ self.server.keyShortcuts[nickname] + sharedItemsFederatedDomains = \ + self.server.sharedItemsFederatedDomains msg = \ htmlModeration(self.server.cssCache, self.server.defaultTimeline, @@ -9368,7 +9394,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.allowLocalNetworkAccess, self.server.textModeBanner, accessKeys, - self.server.systemLanguage) + self.server.systemLanguage, + sharedItemsFederatedDomains) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -9485,6 +9512,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, shares, pageNumber, sharesPerPage) msg = msg.encode('utf-8') @@ -9599,6 +9627,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, following, pageNumber, followsPerPage).encode('utf-8') @@ -9713,6 +9742,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, followers, pageNumber, followsPerPage).encode('utf-8') @@ -9850,6 +9880,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.debug, accessKeys, city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, None, None).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -11901,6 +11932,8 @@ class PubServer(BaseHTTPRequestHandler): '/users/' + nickname + '/' + self.server.defaultTimeline iconsAsButtons = self.server.iconsAsButtons defaultTimeline = self.server.defaultTimeline + sharedItemsDomains = \ + self.server.sharedItemsFederatedDomains msg = htmlLinksMobile(self.server.cssCache, self.server.baseDir, nickname, self.server.domainFull, @@ -11912,7 +11945,8 @@ class PubServer(BaseHTTPRequestHandler): iconsAsButtons, defaultTimeline, self.server.themeName, - accessKeys).encode('utf-8') + accessKeys, + sharedItemsDomains).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, callingDomain) self._write(msg) diff --git a/webapp_column_left.py b/webapp_column_left.py index 034153184..00fb2f129 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -32,15 +32,16 @@ def _getLeftColumnShares(baseDir: str, httpPrefix: str, domainFull: str, nickname: str, maxSharesInLeftColumn: int, - translate: {}) -> []: + translate: {}, + sharedItemsFederatedDomains: []) -> []: """get any shares and turn them into the left column links format """ pageNumber = 1 actor = httpPrefix + '://' + domainFull + '/users/' + nickname sharesJson, lastPage = \ - sharesTimelineJson(actor, pageNumber, - maxSharesInLeftColumn, - baseDir, maxSharesInLeftColumn) + sharesTimelineJson(actor, pageNumber, maxSharesInLeftColumn, + baseDir, maxSharesInLeftColumn, + sharedItemsFederatedDomains) if not sharesJson: return [] @@ -69,7 +70,8 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, showBackButton: bool, timelinePath: str, rssIconAtTop: bool, showHeaderImage: bool, frontPage: bool, theme: str, - accessKeys: {}) -> str: + accessKeys: {}, + sharedItemsFederatedDomains: []) -> str: """Returns html content for the left column """ htmlStr = '' @@ -159,7 +161,8 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, sharesList = \ _getLeftColumnShares(baseDir, httpPrefix, domainFull, nickname, - maxSharesInLeftColumn, translate) + maxSharesInLeftColumn, translate, + sharedItemsFederatedDomains) if linksList and sharesList: linksList = sharesList + linksList @@ -272,7 +275,8 @@ def htmlLinksMobile(cssCache: {}, baseDir: str, rssIconAtTop: bool, iconsAsButtons: bool, defaultTimeline: str, - theme: str, accessKeys: {}) -> str: + theme: str, accessKeys: {}, + sharedItemsFederatedDomains: []) -> str: """Show the left column links within mobile view """ htmlStr = '' @@ -314,7 +318,8 @@ def htmlLinksMobile(cssCache: {}, baseDir: str, editor, False, timelinePath, rssIconAtTop, False, False, - theme, accessKeys) + theme, accessKeys, + sharedItemsFederatedDomains) else: if editor: htmlStr += '


\n
\n ' diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index d95719c07..255ef9917 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -99,6 +99,7 @@ def htmlFrontScreen(rssIconAtTop: bool, allowLocalNetworkAccess: bool, accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: [], extraJson: {} = None, pageNumber: int = None, maxItemsPerPage: int = None) -> str: @@ -143,7 +144,8 @@ def htmlFrontScreen(rssIconAtTop: bool, getLeftColumnContent(baseDir, 'news', domainFull, httpPrefix, translate, False, False, None, rssIconAtTop, True, - True, theme, accessKeys) + True, theme, accessKeys, + sharedItemsFederatedDomains) profileHeaderStr += \ ' \n' + \ ' \n' diff --git a/webapp_moderation.py b/webapp_moderation.py index 482210b39..395043e51 100644 --- a/webapp_moderation.py +++ b/webapp_moderation.py @@ -47,7 +47,8 @@ def htmlModeration(cssCache: {}, defaultTimeline: str, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the moderation feed as html This is what you see when selecting the "mod" timeline """ @@ -63,7 +64,8 @@ def htmlModeration(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, moderationActionStr, theme, peertubeInstances, allowLocalNetworkAccess, - textModeBanner, accessKeys, systemLanguage) + textModeBanner, accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlAccountInfo(cssCache: {}, translate: {}, diff --git a/webapp_profile.py b/webapp_profile.py index d6548cb57..bfabdbb65 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -455,6 +455,7 @@ def htmlProfile(rssIconAtTop: bool, textModeBanner: str, debug: bool, accessKeys: {}, city: str, systemLanguage: str, + sharedItemsFederatedDomains: [], extraJson: {} = None, pageNumber: int = None, maxItemsPerPage: int = None) -> str: """Show the profile page as html @@ -476,6 +477,7 @@ def htmlProfile(rssIconAtTop: bool, newswire, theme, extraJson, allowLocalNetworkAccess, accessKeys, systemLanguage, + sharedItemsFederatedDomains, pageNumber, maxItemsPerPage) domain, port = getDomainFromActor(profileJson['id']) diff --git a/webapp_search.py b/webapp_search.py index 67a81421d..0001f8e2d 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -316,6 +316,8 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, if currPage <= pageNumber and os.path.isdir(catalogsDir): for subdir, dirs, files in os.walk(catalogsDir): for f in files: + if '#' in f: + continue if not f.endswith('.shares.json'): continue federatedDomain = f.split('.')[0] diff --git a/webapp_timeline.py b/webapp_timeline.py index 8175ff10f..918f02fff 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -359,7 +359,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the timeline as html """ enableTimingLog = False @@ -618,7 +619,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, getLeftColumnContent(baseDir, nickname, domainFull, httpPrefix, translate, editor, False, None, rssIconAtTop, - True, False, theme, accessKeys) + True, False, theme, accessKeys, + sharedItemsFederatedDomains) tlStr += ' ' + \ leftColumnStr + ' \n' @@ -655,7 +657,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, return (tlStr + _htmlSharesTimeline(translate, pageNumber, itemsPerPage, baseDir, actor, nickname, domain, port, - maxSharesPerAccount, httpPrefix) + + maxSharesPerAccount, httpPrefix, + sharedItemsFederatedDomains) + htmlFooter()) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7') @@ -860,12 +863,14 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, def _htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int, baseDir: str, actor: str, nickname: str, domain: str, port: int, - maxSharesPerAccount: int, httpPrefix: str) -> str: + maxSharesPerAccount: int, httpPrefix: str, + sharedItemsFederatedDomains: []) -> str: """Show shared items timeline as html """ sharesJson, lastPage = \ sharesTimelineJson(actor, pageNumber, itemsPerPage, - baseDir, maxSharesPerAccount) + baseDir, maxSharesPerAccount, + sharedItemsFederatedDomains) domainFull = getFullDomain(domain, port) actor = httpPrefix + '://' + domainFull + '/users/' + nickname timelineStr = '' @@ -931,7 +936,8 @@ def htmlShares(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the shares timeline as html """ manuallyApproveFollowers = \ @@ -953,7 +959,8 @@ def htmlShares(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInbox(cssCache: {}, defaultTimeline: str, @@ -976,7 +983,8 @@ def htmlInbox(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the inbox as html """ manuallyApproveFollowers = \ @@ -998,7 +1006,8 @@ def htmlInbox(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlBookmarks(cssCache: {}, defaultTimeline: str, @@ -1021,7 +1030,8 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the bookmarks as html """ manuallyApproveFollowers = \ @@ -1043,7 +1053,8 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxDMs(cssCache: {}, defaultTimeline: str, @@ -1066,7 +1077,8 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the DM timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1083,7 +1095,8 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxReplies(cssCache: {}, defaultTimeline: str, @@ -1106,7 +1119,8 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the replies timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1124,7 +1138,8 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxMedia(cssCache: {}, defaultTimeline: str, @@ -1147,7 +1162,8 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the media timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1165,7 +1181,8 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, @@ -1188,7 +1205,8 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the blogs timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1206,7 +1224,8 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, @@ -1230,7 +1249,8 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the features timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1248,7 +1268,8 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlInboxNews(cssCache: {}, defaultTimeline: str, @@ -1271,7 +1292,8 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the news timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1289,7 +1311,8 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) def htmlOutbox(cssCache: {}, defaultTimeline: str, @@ -1312,7 +1335,8 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}, systemLanguage: str) -> str: + accessKeys: {}, systemLanguage: str, + sharedItemsFederatedDomains: []) -> str: """Show the Outbox as html """ manuallyApproveFollowers = \ @@ -1331,4 +1355,5 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys, systemLanguage) + accessKeys, systemLanguage, + sharedItemsFederatedDomains) diff --git a/webapp_utils.py b/webapp_utils.py index 265f2e1b8..2818bb98f 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -10,6 +10,7 @@ __module_group__ = "Web Interface" import os from collections import OrderedDict from session import getJson +from utils import isAccountDir from utils import removeHtml from utils import getImageExtensions from utils import getProtocolPrefixes @@ -331,7 +332,8 @@ def scheduledPostsExist(baseDir: str, nickname: str, domain: str) -> bool: def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, - baseDir: str, maxSharesPerAccount: int) -> ({}, bool): + baseDir: str, maxSharesPerAccount: int, + sharedItemsFederatedDomains: []) -> ({}, bool): """Get a page on the shared items timeline as json maxSharesPerAccount helps to avoid one person dominating the timeline by sharing a large number of things @@ -339,7 +341,7 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, allSharesJson = {} for subdir, dirs, files in os.walk(baseDir + '/accounts'): for handle in dirs: - if '@' not in handle: + if not isAccountDir(handle): continue accountDir = baseDir + '/accounts/' + handle sharesFilename = accountDir + '/shares.json' @@ -360,6 +362,30 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, if ctr >= maxSharesPerAccount: break break + catalogsDir = baseDir + '/cache/catalogs' + if os.path.isdir(catalogsDir): + for subdir, dirs, files in os.walk(catalogsDir): + for f in files: + if '#' in f: + continue + if not f.endswith('.shares.json'): + continue + federatedDomain = f.split('.')[0] + if federatedDomain not in sharedItemsFederatedDomains: + continue + sharesFilename = catalogsDir + '/' + f + sharesJson = loadJson(sharesFilename) + if not sharesJson: + continue + ctr = 0 + for itemID, item in sharesJson.items(): + # assign owner to the item + item['actor'] = itemID.split('/shareditems/')[0] + allSharesJson[str(item['published'])] = item + ctr += 1 + if ctr >= maxSharesPerAccount: + break + break # sort the shared items in descending order of publication date sharesJson = OrderedDict(sorted(allSharesJson.items(), reverse=True)) lastPage = False From 712f7d9b14e0c9b5a3e4fe6bb21f858e5e963c75 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 15:26:48 +0100 Subject: [PATCH 157/459] Only include federated shares if the domains list is not empty --- webapp_column_left.py | 2 ++ webapp_utils.py | 49 ++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/webapp_column_left.py b/webapp_column_left.py index 00fb2f129..e96bab04e 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -38,6 +38,8 @@ def _getLeftColumnShares(baseDir: str, """ pageNumber = 1 actor = httpPrefix + '://' + domainFull + '/users/' + nickname + # NOTE: this could potentially be slow if the number of federated + # shared items is large sharesJson, lastPage = \ sharesTimelineJson(actor, pageNumber, maxSharesInLeftColumn, baseDir, maxSharesInLeftColumn, diff --git a/webapp_utils.py b/webapp_utils.py index 2818bb98f..00221fe4f 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -362,30 +362,31 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, if ctr >= maxSharesPerAccount: break break - catalogsDir = baseDir + '/cache/catalogs' - if os.path.isdir(catalogsDir): - for subdir, dirs, files in os.walk(catalogsDir): - for f in files: - if '#' in f: - continue - if not f.endswith('.shares.json'): - continue - federatedDomain = f.split('.')[0] - if federatedDomain not in sharedItemsFederatedDomains: - continue - sharesFilename = catalogsDir + '/' + f - sharesJson = loadJson(sharesFilename) - if not sharesJson: - continue - ctr = 0 - for itemID, item in sharesJson.items(): - # assign owner to the item - item['actor'] = itemID.split('/shareditems/')[0] - allSharesJson[str(item['published'])] = item - ctr += 1 - if ctr >= maxSharesPerAccount: - break - break + if sharedItemsFederatedDomains: + catalogsDir = baseDir + '/cache/catalogs' + if os.path.isdir(catalogsDir): + for subdir, dirs, files in os.walk(catalogsDir): + for f in files: + if '#' in f: + continue + if not f.endswith('.shares.json'): + continue + federatedDomain = f.split('.')[0] + if federatedDomain not in sharedItemsFederatedDomains: + continue + sharesFilename = catalogsDir + '/' + f + sharesJson = loadJson(sharesFilename) + if not sharesJson: + continue + ctr = 0 + for itemID, item in sharesJson.items(): + # assign owner to the item + item['actor'] = itemID.split('/shareditems/')[0] + allSharesJson[str(item['published'])] = item + ctr += 1 + if ctr >= maxSharesPerAccount: + break + break # sort the shared items in descending order of publication date sharesJson = OrderedDict(sorted(allSharesJson.items(), reverse=True)) lastPage = False From f07742dbffc6667c5a3f5b214565fb5585b8ea6a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 15:36:31 +0100 Subject: [PATCH 158/459] Update architecture diagrams --- architecture/epicyon_groups_Timeline_Core.png | Bin 148072 -> 149603 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/architecture/epicyon_groups_Timeline_Core.png b/architecture/epicyon_groups_Timeline_Core.png index 82296522bb5a67e75bd62ded88985c87a8905e78..eb27ff3019c39c9958428b77d8fcbef1ec216f24 100644 GIT binary patch delta 116514 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%0A4UJ9VS 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

N~LAyV@)S!Szg=v||kc_RN@ea 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 delta 114971 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

y8lrfypaptvhlsWu(AS7gp zN{381sS#;uX_j6r+Ny#N9uya*6c^``L$K}#$V9B2wz`T6s+tq~{-fU)}i{rlJlcDL|F59kr-)2X+Q2HYng zzP7biHW?lpi}kbIZss)z6+d8j7qF!5Yo>nvDn|kl5!|{zkkyjVzfFC0?GbK@cz%z& zR>7d*lq&2_NCmP(19JE901iu9lq&-6RwadsO7{w7Y!xLW?GE5-v|$Q?3jL8rC>r26 z_#fg3G>G$k!Ej)RXt;BWd$`JM|lPEVE{X<6|WZjJ{w`p%_%4#&$*9@F=G;S zZH;jCVp38P#9kjfc;KgT5l&^(Br=%{^8MYN$^Y!*hzMc;pV~S)DVjOGFG`+hXh2+7 z7Qhw!ns%)PSzL+ft9&}q2POq8cH`lO1}~5W(0FM}3q1pa7woL0fzZjkQ&(4qNBto5 z-KDF2kX=C!F$mAUK+}x%yrxQxk*C>CZs=Z@Q&58h7!)jgAU7}q*KB;x^$~%RuAbf_ z+_v#b7Z_W#duyY<(rI<=?STMbAm)G#eF2jzrS=K<=fGFUpO_is$`AELt1bQ==>6T> zZS(If0(~@rmLASs3WzohI3PO5+PoQq)C|f~0g4WPH^+Umw~Ar3*ptGDFfwvO%Ykn# zgi7(O!DJalXEG_VrBqUOCdmyP`{8O4h+55_H z^gHUCLK?4MkvEc+=z0tfXS^r%4h#&8U1JqaY;e`{1KtgRkCv`3>CApY5)vtGZ3+-R zNpmiGXLLs+r9{zj=`sBzNAgrMxZ-RIf^8h6Jg+$mJFrTMH{jTid07FUf)-v%nBzSN zq7UehY5hxrFB9LedOsUR%{D>7fLk93W(igm1>MF6vgtf}>;G;+ZRTw)eR<6k(|%i~9(2 zB=9kw!AuoCUM+&JJgYzoAo&BDqY2**v}`#grAL|lsF~pC$crMA-@ij6A|yd@OxW>* zst1c?4DuQ1T2?Bi5oDDhi@^+;U(|O|N7NrBP+)+WdW(Sp``R@oM#e9lj)k9|z=aBL zH4>wF4N&0TgZ;G{AXe0CS@n4#dd>>u64;UfVm$>aEgY1YjSUr$-4njN4K%j0dhr+g85GLR-&;4rZArO*+rGr0~y#L#% z`d5IU=nLHaokn7nF|~`|>Wt_?5y6xA1WO;lsii+sh=cbAmhg`+57?eWVC`eCH6a8k zNR0WNJMD5=+J%jEbr`@ggoTA0+Z6BIxdXr1+}&*)9i@V94gA+OPAX#WqF+z4EYU+o zn@135I0T<~xWb7YB4(L2HJ>180;_%FBxvYTp3$5!5Qn5?xR*rIKwF6!33a z7J&2ews!L#>^yw2u=egByS)gyoIh@xU7%m zQbHG4P@x>2sQvP9R?sF8z@SnxGN8TtfE@y!en&?K-!!Oj@DaSBxY64HyvP!M zF}%QP2i5(pK$bNKGH}CS2LUofTffy+?i$y7=d9P7@x*DPeogI}AgUt*!G*E!!eZda zi~UDD8VKpu$s$D5fF43@PD)A&Rtpi?!u^m}p2fviiT>a&^mk$9??bQ(q`O3PcPL}E z;QROQL0I82F@w?sUvqOwCRYHOYc2xO#dv0H0zL>tI~ZcxpUNRx9h|#!=)qy_k7Z4Dh`O;aBIBpC9l#o)I zK7R9W`4P+@G+w&YkN5&DLuWI%l34io|8qScI!HsC1HI1VoUUDZtlm}i+h7q12axDH zL&5r7;@+w18J5K8IPZt+gmFp6?J%zhAF%O^ z9TOANL?aEdX%Oy&K@P0h^5x4fP*D!ZLKi4DALY|iZ6|9WnWvw~wNIS{F?T)N>ZZ(O zPkFG1HmY{6g9B0-Mpvo++{We^*tlrTLKUDcCdB0-7|bXtTB9z!f9;CbP=5}HH$W{Q zzzNc4D%>=HoG^jG_qH~TSYBQIn4FwE6q1SB!1D+UEdGBE`q%0YzgJ&K?X?8!09`=< zMcNxCA~&1`zSED4j1{8?8L zeR*eQ-adx~E%5ggE%1S%r&7V5*HHBv{v6FblLnt_Fv<~#acDp5j7Qfc@)a01LF9x=V2qjeram(Hwb=P^Q@9{6FC7RqU|GKQ=oMhtBW8RfLqmZ5&_D&mMyBnL zogi)gG+W%xMXKfF`GVMaE64}vQX2S4kJ8n)x`Z|!KYsiZ1vFr_Lc|AITK#K41_yTz z{udb3-H`eZp*A_F@sRxG+gsgCe9az*K|c+pW2HTFGfy+tE)W{mT_S_0{}{s zlYxN&8tVZh@K2*08!mhYgvd=WcU5(D+hF?YKrRGw1LzwINJvTVnj*jOqB(T>@>VHv zad1$32RP8`P@!b@{10VqJ}#I=Z)#5eti(5=p;>Wp40NA^j7;sx6Dd_yLR5-+^2D$7 zj>Mz5w`yNW^5)TY)8+-~)gG8;z@1-|#G{sRTU#UBJt$9*^w8+?>M$|H-eC+kH6cy# z9g2rxU-9R!Uss@5!6<~z2S&)#;#3G<;{fu4@J(aJ5Q$c0DEqDfy;fBxf*;M z7Z+Ow6D-=ZddM00WM(dq>MfdESlk1Q4GoTGm#aWb4{Ohl#h@mK(Ru`$ zbK|bbWdsrx5Z%+?)tLd6;2E@OVQ9Z7(nePiq1*o#C`MB(s5#?o3M#irH#US}A?JVN z{)UupJl{qgeFy!hnC^cKsal;so>q=MsKKQ+5h;Z2hNCg$~vA{ggAouZV&S z4g@41qnv3>C}3P0C7ZnDX0#ypaJ`fkT7ut z{ogph3Csf-gAmcs$jJ7;LBo5mH>bL2!(p8B&zhvS1B9ws*L;X>P6Igv%@JKiHE`d0 zgs#t8Qbmgkh*!bbBe{o-c~f$62V)_R#BFcYKO`h%$i1ZcTkrTux~<0LoMhupK5mLq^3-mWu zZrDtJAirX;eQNb*(e>c3d#QCh*_tc(dAC0qa$u^0!Gg-C$;otZf?mIV?KhoCA5c+Q z87O{+Zf1k~?X*ntpZkrDtU4n!V+~+EH5Exk12aIEAFF;i)eB1dHTBe=Dn`Ym zJdx77GVn{+)cWPkKbMt4=>wUFbXo-C7WQQ($C95&?8SZAKRz4_VR8|0;c-})*h$<; zM5Q65_~H=z!MbTG9XY}f~FyRFa9O9K#?aNki< zKzz3;;AW=*42lOyi1TShao<1K?^OZ~27PM@B95738 zdv<{{X&rl}-recGxqzT#)L%;C(iJ?TN>R$0m9V5FC7=$Gv9T>(cI^CS-T5s zpbjraOF2udN2B4wT1H0`z-9Xidf@EZ+G(tcCkU(Q85!UCM9q(t;k7ofsa{ksp`&37 z?1vTRCa`1{@Yf;3B@0r6qSllHU)_4LCJFX}AHi|B4cZ1J2n(Hw0!UG>lRDq!#35*bb*hh0 z3K)OS;<{3@Xi_xSQ~8VUe)ipjP!aD%5E-9C`4>Zv6a-7*zSWQS;89!rKz3l|RHoW3iOF#Cyf27~yal|D>U<#R-|n%U(f%Bdj^?sf(9yAG zFE&F^5Is9<2K8k2=q=daeQ-xv;4J{8ybudvp{IX40<-1}G#;y4x8TA^PGN^E_nYdI zCyndL#uS=8Mif3UDP$Rm!dlHz!T1&Z;}7ndH1v{mFpxESfS;OTUgPI4li$GYe0FaQ z40!a$qE7)t>Uf0Dno=oAu%z$H2;`$#p|gU%tN^A$8dx}wL9QYtBMaB&$bdJ2;NatD zLi9lm=3A0$|E14Rg8fi}&tcdX!ww`4=Eq-xepNg$o)!~qYf_*s>Iz26$K5r(Vh!PU zF8%U{aLc3eEqE!HK=A#1*YsCUg%h&!V)rSi(w({&X&}u8QC?QikxvDZyKjRMh69Qq zG-p_HXYw(&gp57`y+$Kg+}h#K zlhEn7HgaDOITKD2Lq4IOfSgPJGxQJQ-pRpvco&1JHlj`OZ+W17nd1w3xZ!eg5A485 z0eDRBW1Izdk%W>`9%gR_pdpwAFN!6&Wcp`8;Yo#K(+e1f_1?XRE7z%I<xUQ-V#E9#C>+SxDe@ z8xfY!if2KHMYr)e4XR=xH@67D>Ne=5&{m2aTkg+KRKMW`nTQRTG2~wEBg+nj%m7e^ z7~O}H;kcg{z5SlaXDt|7Ss9A@BOuK%fv*Z8!rb0gS&)X{BbiAv%t1In=+Syw?G|M- zR$k{E?&{{I_VD3_%1CS~I8c%BY89~S9HA))f_MG2!qFc-3fsp@MQzU~v+y7V4v&ta zS6H^Tk!_Rnu9>Yxp!%tBEsy2paS|Q_57!EKcV=nb+UM)uU=J$o(bg`2$G7m)4f`I2ZtqS zuDPcJVGJ@X@Hb%_ffdj}NzL+j<^SX9OTe+*+WjR{NC;b|Bq39hAtXa6q>wpeNJ%P$ z6!m1DLS-mP=8~DBC^N~F6rxf}GAksN%Kx|aKHvYl&bhvQ_9@=?dDgnuegCGV{OV7= zBR7VqZooHVq|3rbyNUx$t@duk;))o-eNzB5<plVj77xjQ<-+`y;qJCMtQ8Xn2Vqq8cya>n5Q_V|gaQblVY7xt8fF`Pg5)6m!&-I9 zhV)sccM~X7+c8+!L=PQ0WEkEf(TcT?%2U!?41JaRND#IvoV<8Ww9PmhA6%pd=qVS7ZK=wD$(zc%>-OtLW4X9oNC-+}(C z8&zoxrz<(tFdP*KQ>GjuS0Z3+dj$Uy1E0f)t_!Gw{8yn(dEVTPOO)?X2$aI` zt!y06Ji*{|44Fb)>(=Sx?-Fms4Yf0}cdrpCX2p830qVDzi`g)IP6u4N1bmEy{sz|M z|FOz{aV7^*Q-OrOvAK7|sM_o{5Qn(GKZe)*0(DBhnD^566Bc{9nCQqceFL@E2z*8A z&)HL(WMpD|XWr_AHcUsecJE{2Ua_nz8>!u<4^9s9l@IpDKZG(~5cU`HYthY;a{z*m zPm_~|#H0BLG|NHN9xu`#!oH@9zWFx3;tQs3S!v_>>onbm}sKGG4M(Eq3op$!=VIlbh+MngdFR-dU|AZ7zf;e zF<;NC@}~XygqG)d>ZeQQhB zKE#31M8^v+eG=Qe7Mo)ew2V(^Fd71yaRh zT%mn+)w}po7xO0sP6D1`jE0Zn&+^_BpiVFF#*YEhG3Vh+5kYh0{dGv|b*VEWDU_y^ zsVP=}bm~0GFBCgLZvkWnIY`+gK@StsW-LUUe~>mta_>a{wi{lhB({aEWaPjfJ4}BW za~NspRa8|?QNi0+33FR3czN_FFH3t!rZg+#Fc&*-_f!1;&g4~)t2u{joqSUM1{Nc@AdRF|u0BZLMlm;due>deiEUJIUU)YLJE`5T1*AP+&vo$|&e^OhGKI3e{$2VoE0k0u3PFPTkXyC#X?#4td`mk%WGJg=$(#+RcTZPXaVgjr;r0||2l z_h2O1nvj;ZA7%$x`^S5rLd=Hq-7x&HC%WM=9I*+=S_2p54LUU{CME|H3~*sdv_>3p zEgOa6EcdyxM>qErSZ^ciQ0j=;w>wY*S2itbte1rle4N5O{jGgV?Q;~nF{}`b=xwWM z#D6_nf(QK8&D7L^`p?jfit_tk+6<31=STJ{gbS6n1t1?PGWYUgDXRI+tQ zZ9r7cZOpJQ(LEglM`MxrRuaf9*+RgUPJlc@%bROiY_C9}wB3+%)DB&&ienR@5Pe5> zz&9d9GRP^bTi3G&X)jn7@EkpZoVmM`B!Ge}8B#LF7k)83 zFvJ^JUk&k+Hxz7ShQYb63SrP7K#+(CFbo8oLaswEQz(^$(i80a<(&~Zm{Fb&pa75x zKK%=_pHlCLg=hGe$M7reL+$?>DFhhEFoJE55mLWF`hdBwUrW3r_KN}mfhUr5omucB zzeJC67t9wL+p&S8X*oEVpWo}+X^7%x)K_(>u?is-8zFop{#SfEf)K#OIVf6HDU~JL z#HKCdH=a}b4~i|@@sCiFtmJTnx;=?46zeny79L3CHtg6Tpc8_DUMt6RgQ{v(!vbC> z$NKeg=-4^X!4a4JN>DoN9K2zHpo#(iMUq4Y)Ws+GZw#WQ14bl*96PhO2~G~ z)s15H_U_xq2B|C_;hHc<;Y0%n%^l>E`+cBwyLI;SIyu&r0P+!*a8T~!uZ-MWCQLDa z=VaLb`3D2mL<~xjZS^^BJcnISAae zev`!8D@9TO0pEe)(Qs@)I2HU3K$#nGY)}5+E>Kw`w4)BIyt~wSH#-Mne4^2oWB~}Y z6IWLkq+lYkx3?df{|T`K^zL#gojjL*dt#H9LgWvz2?yQ*LNyR|Qb$@XA7FD~*nxyc z6BA>gK{Q3(@NBx7WP9ov=iAWr>4X6K7{qWBz4)G5&lyPvbT$9QFLd~!#u7~^N0TaL zQCm|w<#<`q8egQ0C5YvSVEI7TNt0V`k1n4$f{>+rq~k?i-Xn^HI9Z&;C&o)A=tUe}-nN-;_^*33 zib7`FeYC4kES7jn;0N&_Jdg#bG9Dftp@*+;n0AJynOTo>YbpRZ2aMQ$U#b_M*HV;{ z^-*W%U`+A3W|Gjqtbr&>3lE+ubl#TI`P{7>e;rP{$L5=8Rrr#CIwK{dabRE_P(vsx zA^+X-;2AW5=u$M2DPOnM7J!DrpqIYad7DKCCu3%2h^CA2YE9+wSrghlQj zf5nC+y7=Y&sr20ZeP}W)ifkj{l?{#n@GbrrW)3*>bg{@h(TaObo_hCq9eM@gIK{*- z&E+#VJ5;}todcuCv2o)(UG#;ZYK7xl^B#Ai(XXe5XTPFA^*SHke;ZbJco@;PU=4tD zCW<+nZ$t!S@lad@GB<76YvAwdon~!V0Qf}2#DwZ)Y3sL;C7_rzDaQ`<>|6s&vLyUrGSMY@Z=QO zi1Nv-$R{hj+1}c!g$WQuAV@`^)cum<*tsx!L*xm~(JMnA3oS}(1wID4tot@6Qop1% z{vunGF9z$`{mx~ml#d)aQn`SJTliYLmJS3OEtn#uxjg?k%O}eLlE*_S>m5zXE-7J$ z9uLcOH+~_GIQ&k;*g$!O-wb?oAnGT@Ob7Kh3hlS6?)ztXW2{hqkh(^q426~t{pW_MU z&JVC!bY6b_5WXa|Htfsbqn4)X*kgHE@ExR-S^}{ZZoYxr{ZrkGXE3L9BT1q&`#2V_SHzyVJNg% zs{JCUjV}YyQj(WlVN4`M0bo6$K}WgbbZ^=*qmJ-hK6#FV(7tvhnIvR*UzX60Xk+> zXuXidsF`D0YVNH=2PzPy$5r75L;mBcv-4sg;huv^;f;B6PD3c%2g(RW{57mNi@G0o z%(-dMUy?{E9DH3n=2`K|v5p!koUL-`fdGsN>i#@{e42o8mf|r% zNyU%mTnmi{hMf;%V?VAl3G+mE=b1}k7+DRA1v#K_x|AZT2Fh6;48<*|msb)JLaigSAXNpY@zG{QR5!O;0T^=&^`0Wr<>Xlz> z_78Nn+BiV^`gkxQKA&6|sd+Q=zBaD%^k^nFi+i9xAOTDhj`dV-VKg2PB8Zr=_A$)~ zivU&Im1A1(s?gWE&~Afz&<|A!2parwv~wkB%3Go|`S{~@2IJAkOy6Q<5vKVoDd!YeN`h0%<=niI66heDe+-Qc>5t? zCDac|C%7)}!VEKKEGV`5moMkHMvI|~MC+BlKW!(BSsD4bes20>7Qiltg= zR&G&bUmb<=%}=3eL*rWa=u1sBw!LAvxj1l7EI#e5ZcWL=vWg@ita$8L!*SNhULIy7 z2LLSt(gB3!M1pqE2KNIBvopzpgHMHbd#v<57=pe8jQRkos0=5+w$xDvC#l_ty zT~bas2B5pGb5#&Q(kM>M)s$B(_)`*#2%*?nrciCXMJW0{;KOMlnxgzd7oxGif40C6 zvlT%vxG4qrTBO{)C7})H2JBxG3q>gVmPZ!gG|Vl~n{@Q3n2(Y!tke z_NZkD#r$x7$jDmtCg7xD=Ku^OBWMuw6QLnEEQ|{miw4}EpaeL;lWN{rx8+a`Yo#q) zm?Pw57LKpp0ZZEo!zGM4rNOs=5xg0bx-uDzWda*7r6rlDEy1NN&9e*tfU!J? zUKXJAR!EP8rfUOFG^jdOZ1m0-j=h_~?Be`>0=^~+?vApL;aJV!LPIdKegHs6s?QY! zvW1{tV9SsQwwM=u|5tkcBIT3;|#TC&gxf2|0S>D7!XAu?V~XBT0S~Gg>_Dx-xvc~!W{o92UDNya4f#e zH(cKAOGAJyWSEBF7rdG*87%9v!PZPYXx=L(2Hz*f0(K7IYiu~xLG<=bdd3N;(1O`z zX1rpl1K0pUI$Frp;AKlpx%b~-CiKX|nH9@S`lGO->D{M4M9BBzdBpy!0ToJ zI7Z)3U03(7&@JfY3xbk}9A=PjPPd`px&PCb1s4yC+Vg{V&B_`d$LMXu=zXL&@)f|1 zAqXm+bc=v6IRm==0X8>Z+5y|t5&D4ZK+^!jGle-?Ep3L^FrxXR*Ps;C4P+8^+wpjZ zipo0tlhE;EB{c999Pw+#;_tStxyBtSNCO$qMR;+cIQ@VeV8y6nc|1)drdzQS2353nM3M>M zB@AT()HO2>R8~HP+-_DCBg(*lmP2u(awAQN&!mMMYcRljv*VAjzC-}#?K_lbF(CBE(v!AX8 zi?{GhtRMn}hJgihsq#C&M#=Y+KKkZOYl`NBvm~$YrNjcDXUAF ziYvF4T)T~Q(g3^hG(Zo#g{kX_?$_;<&7Czt(RktR%CqM_lnWR!Z<8R!E!WdeXxto+H zmODl<=z+-L21_l_xPq@m#u5&4yf->-#sNGpu&?Gr6HMGcXvqf;v*`WRN3p^gW<8r# zKruDEf4?5bHO_7HJomk?3s`i3!$>!lsFP#;*P;TUT0%Bn2GGajv z4N*hPf7%O^p~-+5oD*xX5gHKUE+UX5B=zpvx4K5NaJZ4QX7ImJ1Wm-lXdtS;rQ2{l zv+ocpe*5*)lb`{2Jq+KVxmST0{I04<{SCm+oQur74a5O7KM_%*^99|xLWd3~wFQlr zw>Ne8kRd(@h7fW&+;;q(Lgy_K61mUq3(FSW?IHv~nbpgL;$xjL03ahvrYvZ#m7`;#* zrrtL@Z6!rpTU(kEbSqZ@6T}-d?tMuZdWLZ2233dWjkWiL=Q^wwD+q9mca!gu);jxzNS9q9%)^OZSk)f< zUz_>$O!>;Rt`;2C(t^yW3Y*AWff0qAV{c-ObUcuxW4NW7^dpL64g*!6vFPX<<#23} z_a9pc1tbs$=$QvLH0|eSLNa&j#Pl=)p)oJzS0o}Jw*|ywX~0h}qqk__hY6QThYB@Q5JfK!o^Y_=J-p_&UAc;VbVzX-LfJ4|wOs}>z0hKRK zfNukW^A$H=IpQv>seldeDU$;WqwWrBQe&SE-Yj+qdU_wXRzkj^^pm`GvSl!BN-cCy z300hGZMic;bB`bIz_TTfinS3FjPWGgnM}8Kz#F^8S7yLavN?e5s=s4Z8d!Y?GX+3XmGu8*0l18*K}cy z6oRy)RAUQfD$jfo!tzCP0Y4E7e(K$)@z<_}!+&$JWf+(i0>`~7)O$anfrWQ3NIQe< z<_IPbN;qg^eqH#Dw|5;uJ7^0a5G<5gpF)e*ibsh5H^9{u9QB}1u$urTL!}$@eX-Nz zjQg7kYBg|eS8Vh|W+y(*|1egXuaGJQNATrTuVCDLAPWEvGzjEcOmy)POu(<86s#lm z$fin$m@AOcZx~hh*}g;~N8fRjKs~I}sNFu;WPrAHayi*5k^C+J2;P^zYB1-)QxenK|UZ z+#tuj`rHEM6s#jaL=BKwN$q7o)nh^TSw{jbpS>kB91_n+;lvojjq>fjNj|P+7tR@C zN8C{=MP(}?)szlqu$7H+W6(H(0XEwxmGX;S?P43_HE2$6aB~d~4(7`|Q=k{(Hpf2# zjA>tetFTje?zIP^)VxucfKnlBl^L)8_TFSuN1w0Y)Dg1yTAVq}rxk9?{w_;h?G^wn z{GzeHfTCxC4pQ8wcj3neDjZF4KRo(^yDc=xECv)RVCQ)wuX2H=u7x$zM=*xBnYECNQx)T7uYdl86F1_XN;cY$K=OU&zCmRYq7M1kO-bjIA%o;OxMIWb`uf@8O~|_;S(&It z9b&@Rnq-c`B%nwyljGkXJt*^w^K(Nc)9Zpbnz+MNnQjF z${MhHs%jQ?#Jd40rnY1vYE&Z<6XL~;8c?x>?(xH2L>h7+_W0T0BQ%9IK}=vnA*7z> zu34Ln1qUI%h;1CuWs&`KJAT_h{UU|pTwW27x21HYgvP@FNL+~vlfD_MkSvg!Sf)QP z*eJSf+gkKV=s^KkOesR!?udXfKZKD^9%eslL{i??jv~`93@j2L|Mpd;NA9hI zB#%C>r4dVjPqawf+IlN%Fvz-ct&h1|0H8CH{SO>~@5Cvkl&u$j`+-hS%455WSrCil z-m81!K0WB*Hz1Kwhc3gIKuLED(l}1WYBiG(==JBnaj3<2fF%R<6I}05(w4E9=%|!J z2vI`1KsI)kmQ z&xHxGh0(JK?IWMg;VFays!@39&fxYizZaVOswHa@c?#-;pwi`>Q#+N7RL<2pbmcake-A;tiDPzXVxsMtKXN*rwzZX6 z-E^@NBH!85Qww_o21FVfWciFVkt=(8{CDgxD?`8O&e<3h&blrLD!7U#Z$?L{(T#Y! zzzo;rX>8JK;5 zB$GY`wPh9nw9<`5Q#@+yCu|$YqOCG|7XXmZiD+Xp7Kl_H-;7G!Zf*bPUOH~NR2s;n z6l3Sesf|{93=bKIs7v?%ssW|p3ZX~$3rFbxiPtI|3=p|Z$(?=^YACiBK;YhV9{wb9 ze<>-oSZUIxC)!QaNR`UW#KeR%05uh7K>7Pxqz__61|eV#@?86)v(Tt)*h%da0z2UT zfb1G62?D)~#cO4C6Fi;}=l7f~!!dL3NOJu&PjC}59msGV&?Op({N7AFw}Rc37Wpzq z?^^a%1Mch(KM$=(-}`eofBZ&M&yYzt>JgeU6yzu1Qusfq8r#?~1I+;{N(O4`aGC0Q zGrRRpyK#D>Nuxt=7}4wmDoGl7jivP?E)_>v8|z?6lw>^8w+Vo{(|bdm5QKBmj4)m~ z_4@uQs15!=$V4AeuEbs!|+IHc9$N4t+}g1qAs-D+_qG0|AUU7nr^Lp&|V zhbAg=t>U{D%u0y-3dT^U)aK8f)s470hkwU?FBm#3t4_l0tj6Ek(sHNaRbc^3tmAbr z-*Wr=`)95dWAFd6W5yphCuz!|(w?&hMSJKu8dRQ|Y#CE;xQ}_&3gBbL&yZJiuBC<|C zq?UIA2n3JPf4CF}MFlkt#qSE#X{=ZHNe<+%_yk=rq3(As|2~bchRDRS@6w3GCqTq3 z=6EN3M2Nhy&CSVq#t>?bEMqAMGaB&S0eGASuh%&G{qo6!Xn5=q;IXn{%^C-#D~oBY$iS{HevatO7sncfJM z3Dz$ZVFg*ad`a?GUZJ1?cZc#R^4@!L1xm%_QHX5GlcgbXeIxJ0IU_?-7EFwU1U+WP z#-8%50sL|~bn)Tl>B)U_IN3j?y2l!FB-52$c zMn%-zL{b8`(C9}@ZXl+5Q|i8oLO~G;1d<#=&`#ri_q_g=; zbE}8AJq{aqkLcAF&Q}3D-#gi{E_6a)C%NMgh7CMR%hxUzs6xQ&Dk)pGooQ=8B_kA& zp}0g=Lc-xhg``c0wqx}{2eZR2BZv#lW?(A@r7LnJx375QMUElBC_c07!sC#&<7PTU zO|W5l1{7h?OqJW%%{+1ilEGbbC^#sH?}zjaHsHZz47h6s2*Bace)Ph)r7ZuqfEe)7 zAJo?mIHWI3{S9py21c4;qjY;&4{U3H{dbVH1s)Rdt@s`Mx)0Xu?uAtf4C6&B>52&U zr~lOh8lnyXT1uJ(w){?qWK0G{M;nGZ zWf)87XliOEHhqIO*OgM>BhWH7G-g}9tkMzTthn9#dIp+Qyxu+h-nShiVIx2t*LGW5+(wUbg+f5SpUJ?-A~4&kT-VuPWqV9+gj z%;iL_f9!;_QVO{ZAQ0F9DyI8$=hOdE1dJCE^Q|q+?r|trK!BSaS~G;cc}41DhQ?Ld z;7s|8tH1Ld95M6Fn)vWx1(GPR{0zfSDH3?6f~u$}pX>UZOF>7H@p2r_f#Vka(NR%( zK8YVd`EB50#zi*k^Q&YfB^yv1@kfYSg6QP_6rA<B#GzH}bNm9F*TTapq9%40fohgXG2$gzG|a?Xl~PX42Q-#k+CkS% zYzmV{Q`yh9F3fSFdcKvD^uSnD`3q;P5LW>h8&{UNbsx92!#KVIcsn#Xm@(@eR`Z-;_lAP_ht%D?)E=1)I`*%Y&0PJ={YuR+SL3a z!^6Xh8x)QJT!GjpR74fKo6K#LCxx;r9@4^|scu zhkecKbpvoHqC%;xBYNw1=O$&)nq!K%x5fv_OG{ZowLu#@1Ag=2 zb2fqlfB{et##t!v=_t%iSGMPg`~D97k1-{Iy<2`5-Cbn8KLY*e*;c=9G1%D0ba+?I5HW z2-@;kZ%-)_^w!PS_)<6uZ1UXqaOPp~Cl?f(i?A>(Q{+ zPM#rrR%pGT$}|fW_&*|;2A2t4jPO8WBK$uMjyZ2@sr5kjrc7V0+I}3`SUNDY{vSIZ zLm~!#3(}sWvu;wyp-@&wo%?~h3i`VcDlSc27}LvQT?^?*qOy~0-HnshpeqV4<9|cmzIXjMMg#NQh(O2r9zT2@89RbFZXfVn210a=4_u0US>Yk zAt7|QkdWXON2VuD<4VB;%?64y*J6rCRuMW>ypp0ECh@H+x@FFg9%f>oQTk>mfb| zR8iD~%KXoOK8>BSvPF6=L>2{UU?rP!aQ)Y&yZR(( zw$yiqJ0n2}sMcEEQxe+YSLgdS$vPF4udKn+$9@v=iFIJJ&}QbfL`)K&vfeTCgB)-m z$lTrzac1@UVhU>W&tC+4Su1~k(NI2BG%iH6ByRiB!xFP029Ih)a>5}2x`&I)7^ryV zCKw@6t=s>QDR|`bXH)#y!S3+{gqEnYkSSrq#t@ey zKC2k6p(Fm&NF5Pki36S$3+8?wvA_}MBSM(=LUe<{UzhmbP|zfCI|-auz@f9vB#GC< zJgTq;0^oE=e#1bXRz7@q8Fy?T1}Ss3plS8nA5OflI)NS7ckM4XVI< z^qrtVWuo??{EUg#1+AP#R+mNEXPq}&2Rl=D*r~U^9E?XtM(q4{d8bzsSP^4e;R}B* z3O^UEXV1N(`_E06zj8%t9sFE}M*dia!0tm{9P)8?Ly0L$!L1K%TQ%6Go=n|nugMwY zn*5Ec-WFFp?zjA7)LnVWfc-e%ahY-{(;kc0Hqm)W6*gQlc?&re6UD#G%e_r{Ol!Ex zElsNW`IannrW0PTwH6hnI4j#n7hCj2o_fXm&!}^Z{(1Z?`mO)Ww$n#GcYN>I@e&PD zL?%;wYY4zLBA~OLFxt28333d4h!E8WRu&-fxBzk+;(-Q@5eKH65yGk6 zkW^=3OPr{9r-5!MS&5m#A}Dex)BJ>=mXZ?FB~KukD5~vhCWI zxH6Y^ZIcAsVKa94kQfj(vJp7*u@^JAR)hwJcr$uac23TZesnCPBOpFq0lt+z#fR2o zHtU-I0(YM0_=`>8-_{`|<_WeTK^uT1yp4Dics*q+E}Czg4tY~@gDTv!Z|^gcv^&M( zI{G2SwOb%>HBm^Hh@$7ggop689zMTI32NXplA&5~1$vt$$8Y}Qc1NuqgN+F&JxRj`8WBdS< zj-0KPZii%lrA4v*iN$S$xbkx2G7j<(3=6nxm1nEx)`uKtR(dKAdu*E#>$rPFbdzXc z#Z-G<${l_#WqSK}_DoEyn%#AoOw3H$SL#G?hCh3{B=6EDEGnw=>aVN)hDaRJ4!~*q z@uN{!7_F2374K5huem{|XyWp+irQkEa!L5e=<&AknbYx{WuqVUx;2A1+xCkaA#vvO z>T~j$nOW@og3*s!&)hrTqW)NY=<|m#5iMQg9l6`v5+s7rux4q8GUuAz<&?B2h``gU zbblsXY*lDMS{v&3EF*q>IPe`cnS!^!s4U--7jnM5yu4h@M0NqEMgYpkx!ohBA)??e zJrU=EM@_sg7#)Z(8wzyxb%K%kCYGKXCm23Df4p+zs?q3+Lt&r9S6ra^!&Jq=qroD< z`IS*v`;R5<5R2xD%V%Zl)TZw-p8Yy6N@K0NVSbd3eN7$n5D(3^i&WG7FYz>+b%U-; zYd@6H+{0c+*KorjbYWi~1H=A9`{@#yv`f>S)~yw9j^f@0NDE!Cai-2HASoH3A-^sx zbeiryNqSB6SbC@3~{IAKywtGq69*N;jl3VV$&o`u2-X4=ukBuJEJTL*ua0gR$-ejWUfbeYV7^ zxwGp&pFYyxp~RGVv8`7st9hHu^M{_!3MXE3xwIvCzP@)J!VX?Ia_O`*q0S=Ue)x-HhIUV&O``6K zhqOagq8~0v55Mm*>)+$&@0aeGrRVxOlODI8h9R=zo$}8Mi!+p6f~+0i)~&}}WYhH6 zXprH@g0?sW4K-4@S76O{{E)@FCwe?s5XdG*TbqeTzuoH-6H4J0@+PoQVMXLed1N-> z+6oKu5~~14Q`i2I4Y<=Y3c`lZ8XpwHLRVdDzg8oHa%B%hSBAm8_!TBK0cUYZhsHNs za5_XvhW5R?_>cx#Pu1(0)~8`J>_|~wgQuhmCpY4!E&|HUL^v(YMZDT!h-4t+c_w!b zVrb6g_^Q+*aDMq<1Sys2;r4lEDKLk@8?QNTP@3%=k$c72|{0)QC1 zkdVEDc;6gRg<)mhDy;9ZCl^qXyFjcjoI%I|deb3j6}RK!v)EhPqnyQKH~ez*exiOg zoUZaF>%o%MpGA(8$F8Dbzy7_Lscy^k=Wlv^#x4Bg=DS3Cw(j0>@!|bRvGgWMioH0x z#P)vOjesxeQWjObdFs}y74v&_0P>oYyP3nd`qI()Zaz@G7L;%jFN+PfUO0K{>>%t< zKx=n=OTpLLhy(F)~ljg>>*364}ydp z5cIdEH_)<}vTp`)O!SX8krEvqE7*`6fAsRzgsYx$oBQb!RupJiSgkBvYy+gFoSS<8 z+ij8Rv|I12SN5y<0Z$*V^QL;B|zweV%>4@OdgYA^+*pMComc8p6k&CmX* zc}_hoy`;jLlS`YCNppJDHr~C$OcNuCzAN1P>2s$!(kqPgX)=2aAIfS*n$_J4QPC@W zw0B0VC)aMJ<3b}@zeopodhAcoZx2|*NunWni0Gf@)Q?`_x8YS|RkmPYpcq1faOBKF zN&7;LFEj}s6?*E6WF|*w8UZ}!ky`B%e3VaayXf_@@e9`&G9T?Zux_oDBfLWHvB zZ^kAwc8!ZxYn;>~nAn$t91+%LJO1UQvA;@9muqh0+*zpI@1GnL$kbi#(S9M|kY-XaxO?El0r$2`S z_VL0g6d*rcclwmR6TkImYQ;$g-c`9vj|Crdu9#um&N6hI3aWb{wsu8HMDdpQ0mG|{ zkZ=pG1~9m<-e0cGri6RkgnQrOM@Mat-05Qd>}x78`D3`w_7;Lo@GEeckT|4|1b~Ok z=JmP{3&^j=S7`HSo*!CA%CLK1L2~==Rilac({oVuWJp8-NmT}bw9Xow$RGhWiPzhFQ0ymDnNpsXdfb|IvqhfRu` zaBYqfQCC;T9ca|4@PKQh;Wz|g5l)6yQFTQL>L$EsM=FkbUgB4Nc5;v>npjO_Aj)hF zpZJCm$`kN}0EQI)5XJQD+%L7JW*NV4?!81)%G$Joz4UBHJpb8;%1Sbqa)k9RdL2(T zFe$&;Dwe!oV_u=<(z-^}^Y^b_y+}b}xaUtT&fFLW;K~bOhYIoxI=v^z9lcXCi?4^D zxWZO*f581WCO@3vw!pgI6xe2Y>8WpAdaBT?%u%him$sCr$uajlWvQEzUamj z;}+X@6aJeFM5JEjcEI>e)HI_IM^C>vCsqsM))Pr3gwRHOsf0eo+1SwV17OfL$R4uK z?Q0MIDZxaG_mQ&Y+GDiuqqupgA6iFN<<#+`rEmp!&VFph4b+l{CVp{7C(0&q+`gL~ zi7&~^>~rY>cawGQi4dtzVm+SU&aofRd`y3X@4Cd&({(OGtvB|UfiAW!anK6%e(*~; zdHy}_wW*Q&qp&ebde-Kc{Sh-c|NcGwtOom% zW|PVW4QH(>`mO7mG@qMwAqRHy=8KG+0d$Uk5%&JrXEKRl^E5Bc7?()SP5A8McS18l z`K22B9UUBuC5VUs&IZrwtl2%dW{K(qVc>z8^*3Y|KJKf^CQfS4PgX(}2O!9=I`_%Af2%ol3uborAM(h6EGq+~QH>dab0TojdUf?T>)+ zN!z4&BgA`T3p0UpITFLC=qjH)HZsx-AO8^u2(KAfsCmt9si+4FJ4(0I_|l zQVQ_z29l~Fs_%}I9`nz;!ZQhFoAT>NpoZKF9?BQjDh*%aKDtg#ek}(_J^C1VIXStk z4pNMWY(Ch4;qDi&h{!P(r%kqM-Pg_eFK{+JNbj+4yY5ji?=8s2uOyunyOu5xduJH4 z2uss|BaXVm#rD-!D3|VCs_b-u_(Ar!y10-jPYprHI)+>9-izM5n;AK_62>fNGp#Gp z#!F?Vf^Rdssf~_Lwq84QHW?8^9zWzML4meS zOnva}+cL$sE;G&vQkJpA^8_{6o>Qm3gp2L}6Wrq^bynZ(hGdgmZnUQCn7!#~iszt> zfMpObgQO9c!7aD*X4yI%-_KS50~p4A(fXfrS8*>JT=4+jV!h9txsxg3{_^(fLuW@4 zr6;AVldSVC5cB4@^}XYb&Lgt8+YSSIPI20oYWcMZ6;H>uu^$||*?Ll@z3wb~yA~^b z;uB`wGYhFqvj23DQv=d{dg`xqBq@FX^QNGbz!%%BZ>slxOa9|xC_%!sq_;o;a2kJd z>TS+8?lZcAr_~GDozrDIyc1_7rSt<;YvuXOCi0^<8VQN{j`xM4`R?u&UN0oHPyZ?u zv9@={lrIS~;6KOU-ASdHIjDd1vLb}upU{;5fFhRi1b6J-bD!fp?aVp_o%lTuBu{d^ ziJWjtTqk@Xyy}2f7sLC@{`r0uT6Vqf>A8N>Gr#x~A3iRqjdVc#)G|0tq|Nhsc3CFN zyZk_>Fm5Nx!Ugj^+`QYDzKsvhl_2yG53@r&?VPEgt$I$bR6>^bma~iPj_kp`f?`yr zXxU`D-A^psk#Xy_Yu65-XS&q;XP~g=+v4IVp8Hf^3a)(yT;qv5%ZYYN)p2CJ6*FBd z0yA+EJ7a8(RmD`IXTo(BBkm6M_>FLPpN7=;+rqc(aM4*Kt)Y|u@F{y$712zse17OE z3anbi@fHwkBjeOhn4jCRb&fpB>uYHVhx(99_X@)LJ|RJ0UhJQ)RJ9hotF2@YxTD>l zi{4Tr@in{({=FNR(GTO)q#$3Utu6arUq3>yBK@oVwbb$wYfO=xZ}wVTJ_$AR%l6%N zo>KICCY&y=t$GzQU?>^!Uh_lyarw*C|HK3_{?n&N#HHpzd`Nq^2qS?K9KFguHp8n; zcvY{60xj`e@CCOzpp*85k z-$(8>5tL_p7zOMS5Z-kN7~95J4j+ArY@8vyoVn~}Obe)90__@iXR;H|A2tx&CjBSN}&a-i%#M>k@t9o!(XF^^^L`j0}Ncr2+BV*QRm;Wcx=*=)B%0=SVQOCv0xS zp!_Ch9?UBgU=r1TsyFYU9pRCmRiCT5_dJnvwbMSEN21K%R>26r{z7ibKS3BepCjc8 z2uiQ*1IH=6Ou0S7Kur~rslB#kha=yaY&Nz<|ZXK@& z$Upk)G1R#Q*!mSxk?S9O@kJagw19ItE$#gGb?E90bWJ3pL?(OgHUfb2!g4t4b=;xU z$|}cz`#WMZXWnl|LV(Eb7(U%oYt}GqX0D~-wWMJcu(sp5sIj6zs(qba9C+}NUzhM? z-H~-sL4_55Ywf`ZG!J2cXs^6HXaRWab`0Ob|9u+KMR85pZvo#SkiZ|uC!BHHa1Y-X zi2>cu_2iwS-^DsvetsUS9SGqpBthBN(*D`BCemrW$@HBLgX3R>=qp*B_#;Nr-|j6G zm$E2)ii7f{yONsnis{22sD{wWqLBTERNg>5<{s0G#``0lpLLP8iqFUKW1-hqi|8%d zde~-abhv5@}!V7fqh#pnWGFUfg7ZO59a z3{bPPyy&WEWTpfzc8gS*KoH$$d_;L3-sB*oy!;O*jAK@YT03d}W->?>jhYd0iSMYG ze(gnS`t1|vE~B{|?T_1g>JH2kKasC=I3J5oJs@L{g-a*UKtl{g{oxACgqxBF#zDmuRQs0+vIRL65h^v7y(|Y@l~M=$RI&22Ew?* zEzbnknHl)Z3r8G_e^jy9Xm$XbhlReEM%to)_mR)!6NGE2eX==@Ia?ul zudMZ^asc{Cn@xiLmUZYGcRhRlCf%{iW#vOk$iYZABPA1DWcL7{FgAS`8TBUI_9wag zU94Rm0sIVS#K5I4!vu+Xgk5Y^Ree4FGlLtV(R5^B;$wB-&>a^{-En2-L(K3Z^jbpz zev?PeKU469pgJBfT;`((YKd2wXX%;08?--u%39DF8kH;`|1tKn zOaHXFyYWr%`6X(hbAHCik_gxyEyV>1e@(QsKMOSkqQ7hYKAALo%iVTCzI%OMU}K*D z$+3;I!aPBy{EZ z7B-aBy`**GmQc26ahO)EI6eOC8+aX4cDkZNwUqsV>afzry1F{ywcy>Ia|{x{#fG)- zIrB;$h=kIy-cRowQ_48QtIy!IBN#3S@Y_tfaj zF!nn-*$V&k8#`T(4OT6jFS<*ol^!0>c(B|p(PUq_hm=#P6)zuO8#Dkdj&>>-|BQU6 z-zL-6z0$p6LSJ|BN?JSZSN@9^$~u@w+Gb&}fc;;NRMlsJQ{DjxOx);(4&dsxz_Qn)0XKi8lnOry z^h)2mMPmytU{X00zHu}Z_<;$RE(ZL4rw4I5I=Q-zZL~f`jUet;t^c+ifBA0qa zIeBY>-tg~=t+ZxQk_krg{@*9=rbK6V5L1RPznc6ZT#@1g2i*Jd<=CqDtXS>edH#}W z`;{&loS=N}v|J2aY3nO%2)%&yUDHiQXXx=1R<2wbycgcsc$yzTFDC`L@8FpEfg!?+ z#M!a;2{%6cVBed{%d{X@B~_(-FKkDiQ(%>oCxe7Hx5Pi@mQG7Nvo0;yROg^?7yG?4f=0xIYZ+-&^Fj{rUZS z?(o>2+7}NB!^6VLP{wUbotk%rM;)(6z2B^V^*}%ux#~bDEJ3pQYE(lfeVs!_vk$j} zlzygu5BC4j(}-q-z} zpF7s=oS)jpB^ubku4Q-Z*Y-UjReT2$bRjc+TeNNK{li!>0ZY>l1`zG4g#2^Nl1I_@ zVtPrNJSlC$*}LnM27{;JVnWaUlYzV}211;>B*Q+9?bKzN=qqf8k;AQM5mPmCzlKO( zQhBnvzl?HW;a5IZu>jzA_1Xa z9i5#rSW9#%x#DnaS^YdHPn_FkXil#RY}EY|q|1JWtrP%o8-Kd=4zzRm#`$ zA=QNSe&dmQgnA=}bS%^SGu{eN{?Lk{VYfQ9GwVj#E$Iatc8Nac;<VMQg%^;^J6pHDytbRGh~c zwf9|;HZHbTM{5ov75BdGT0q3#&MlbVFBvGQbJM+iK)aif|Drz4lPHnL0RfO9dAwgF zI56Gikj^8!e5pn#ZO;v%>-N47qKJDghG8vlAejfaGh;1CLSiUfh_$HkgH3w2Q+XTc z3GKZp-`!#ti>y|7e<`wgB;W#N(b(VvW$6(axh}<(3j(ix?r~fx$A5WuTP~*!{c+1e zU02uKccrv6nB_Ji_yRfK7=1M%uBf4Y{gMhZ0VXlh`39+ zkNxpIwRh)*J7%OmC9<5%7D|4_?v@`d8bt0M7;?CH=Av zeAhIJaTxQEzDv}wG{pQ05aRO%jem$;J4BCmdqka-Y+PAQ6YWPzr~mKK#=0|%AKUL9 zYUrdoJCD^b%nb5FbJc)pUUBpKAR6gIY>$6?)tDD3`2NdGN)pW_Q2t55)!{^uP3B3q zRJG@wFOAesXO-&vjC<%UF3^VUC~^v`k~~>b^@5Qm`kKr$v#FqeS?*4bA_f>Ad5q{Npx$?3Fz#^B5Ib$;jR^vNJ+bBCC>3(tWIq zQix>ly=ReC5mNTbrjSi!hUattp6B_u*DL9q`~H4Em7 z(U}UIoT7jFA(@quS!^0jS%uTj6&_6x_Q-+fuLU+=lIM<+KcYqTdl%#`1WG*u?95~J zHcntP#@FC`)>`r}R*n3C$6OM4oA_xPDQO2Uk)%0R-r+ZktLxuk_#^q|z3y!=#tX2R zztJjJuH!b~HzW9}X8T6a>J4n*hh;gyf&{;@4Ae#sI-#YOo;VjLT4r9HB<4Uj(J<~% zV1IFZ&GDun(uC9U8B}vPpy~?v2D8TBtLw!tO^23J9=~cF#}i>@(BUc^&cAiL+nsZ; z(d>Jj_~6zJljRiGgHA2|omjIEN~`y~uMY%(ZuUBZ+A%1@5`Onv+lBnsWAPe6ixGw) z4?;TXVfOfRhodKT@^});%RUM^5fas`weML7=QCLW#DDLMn6atfp^0||KDg*_rcqT@ z#=)0R4x2^Ye&@;`BZsS#G2`d{D%Q8@7%HW|QvAWQ1(|oYfITP?GGwn0KP|jg^^day zhWZtdzM$`63{I=e{yoLrbhyR4^yh=hUO^(oLnc?AK zz6CYtU{fl1T<+stQhxtul&I%=^o`4sZvhY0FKhYN2HdwXJYg^kwFdrlH2SHdVxa)+ zBUl0Ota99xwqDWD)SPkaR#5*jypfzz{DhD^qZzc|bHY*L=-*~gRhsa-+bd_w&Q^~5 zr05^Oy8+DSAu&Awf3!nAI!Iy=D+Tw>lX z&EH>bXz;}zpfPHiFdu}O=y&&f=vEAkv)hNmK}N0V4V19q;fc!SAM)6loI?>O9z2E* zp+@5j=CBh-Gw8yQrJK!U0^L(kI)Zq11Z4Hec@e^)t?z1-~-nw3oe_!nT zmEmSB(bqeduLZ|g8yUsS@1r%s!Qr8k_4NPEJk25-4SnGQ0yPUKAi(#bZ|k|zV`@1l zN!LO-ktnZnsu#Jn;}07Kn&txWLZ;A<`X)ws`u-;n8k&@Gh^CBmOsKvXl)FOyavX0i z;c{xhAp;}{CPlCLWJ4>AZq{<71(_&P9Y!Jt8H2sZaWhJ#PLhhwUS28ie)eyBh{G%t z-TfXN9qq#-y2Qp7VpeniteU|u03fX&jw3(5yabQ8evI1el?kT|b>*N&e7PsG?k*=a z)~)1&`X^Qm*VAb<5@sCdzQ!i}(gDB{-Ud96L#kFLp#FlR>ifS^nLyNU#c(d_6V|_M zF{SfW>W#sPYZ*MQklR)l8whQEA2geW>K%`)<{hPozn-%lDzJJEgQmJ)2m-)~W$~ii zwUx`B?!SaYmiA>RKQT+%vJPdcQv*kK{EpoJ^@l7_ySBlkbcrqE5N#-A-wSc|Jn z`z0tR)?0v(z@MvyGxU!CMaN_IbZYE^7-AB+QX`xPL+}IhngKCC%MPef(GeGLVY?hG zPAoe>>a{pO{{@y4^4rOx5o~BU2ya-6Y2C2mtCv{;@O&6M8r^|>#7VkwJQzW!cnN!X z(EXs%QT5hK3wcKZqnPDK$Kd#?Lq2@if?~V}`>rwcFEvpFuyKGjt*I zZ`ox#<6>UdqB+1WInto+g`c(; zZlBb4r~N12Oq!utI_$_Hq@D#jnFcEzxRm06&Z_G|gaX0B!|mN0D_?gjy$%Tj)OG2+ zL+WWzHf8OT|A2*p=zDtuDj)3$Q#tQo0>WlkKU!9bc`~Btphu4`ISm%HLO;3)`?nA& zSboBKX%DP4c4j0pog?5NHp|qd{QBMIE|KT0IecCpO?1-mC$fLP?Y=}|q0KUHi{(UW z<3_02-?&>a9o9Kz3Ye=I5{@TnN-yp7psa$2f1(1IDL(myY?gkCfWAzTsl3PeaI5gGl zdUAgmfCfMknz8)MAH}1A0OEGMlfjs=ARj4_)$?Oz&mhdutKz-wrqo#|Hg!p-pMo4e z;fKHyQ{Iw&+Zh6p6o4e3OJPw((?glLVSV3Fu*qlv*lsy*Fo3 zc^XR60TBd1as+(Kf875)St(<9F}qo!ZT@YAdiuC5*GSG-LUl4)g{nph=I7;|LFWHU2ksaV8sJ)-_u z%-EvJ-oU89hz#ocJ|pL+m;NhCg6ZJmmrb(yl(?oO#3rKG`)kFcckv% zNiWk;5(EYW;@>8`ebgVWz(!*9uMe!=>|bFGVzwSQL*`BMLB>$@elsR;-~z zu!mJC1i}Qtm;QNp*mRv&N>y1I|CaX*5s-?rij+2l>-PR6{`+M%7RV2nW5&p0I`Ju}IAmXdM67r5V=|DD zrn(Q==R5ADW%UP&mW2#s7KzB#DgC<7xe3Fhx^<^`?9RTzbCJSi4%KGat`~w$&PuTN z&MANebQx6MIv(>RnKw|8r#ZAzIH0%xgc+%e&&VONyRLqyyLxBQ(cdGPal_4r;S@Q+ zUyMN6&r0=(XE>i%XYFRY(`?7yKta}fjv0(%pXl{u(`zTqrn&=0ZV_rZmDD}jB#v~v z)(Z8H$8(g+q@Q_}^7}UGaFKkx#4y=++t6MZC;w7CU$KWyF|;EH_Nj|(u{Y7!-%+II z!E?YD-bO!TFQm$D0GddY)Q+MsnvQl@@L}!QW)*}mMw&*pQwN4(0*vCRNMHx=4=tB;Ru1$P zs1gdkYw_?K$WUlTC^paK;GE+}P+M2zd5Pj;*Y8UE{7S(zqwAOazPLv) z6c`noGr~1EY-IQU5C0qJ$1;Enu?HS$WV!E=^F&>6m$28f?0lk{lOLFl?2jyGWSL_K zz9oK>e7Yw2>=fJVpJ^yNP((C{2OLYhc1tG88f^xRoYPx46e*@(eMf#X1;e-McD-9fvzb4W>&#u}^!Pbth0i>D zY-dPJGfH~nXv;BVAZynW6!{ryMXp(Jy#JnRQb0kzs5eTNT380=_yH`#4{@I<;zdem zKGLfGibXU4Li63>4&cjXha;Lk{Y(Gx_Pc<+5zz)$+3ijITw)cvWr>o&edEfV5)qCI zgw5lhUsSUUITqA_H67&0btS7rw~eTwi3?5w0{RRLe@E(lDmy;``s;Frv-;<{k)h5GOq1{up&}xcM<20@x%otKfefRA*~8Bxz*cs)dO94a zVe~M2rJQiFhkvVlpQL*Zv9pHE=QJ_l zdNCA?Hn-i&*x*SB)<@N6E$059_RWET}tf6OcM_b>a?b%kZlp;-je|B zo#{vJ1YkuswVp3?zJErJvwcS7w(!lPR^c~jup(7^GhRxw)TM&H`BRd$3H8NCLF0dT za=kvL>~4RoNU+%VG(1}_QEolvxj-52#>K}+1{Eg&e|ohS){KIRI zIAlM6opn`MGPALW@U618?s7N8r`(2}Wm#iddb)fr`J$5RGSW64kYJ~WA;oM^;j#p_ z$MJe-o1e!wc01WVW^AT-AJ;Uc+%IZY)NN7j(0_g$?%OG8tv3Q>N;HrDE*U@Iilsm| zqZ+diuHmNXZC397!;qJf5v!__{cuHgC5DtJ=79Xa0ZGIDFW{#d zPR!Q0A%>WpUmv&^lln)xQ#RcCORppHY!+8oYVMB)ynp|G+_~!lyu9k){+>yTCI0H_ zXu*2c%5K=s&;uOX74#!feWGI#K%IQ@vhst4}%_XpmhEW<7@S`X>%Reu3+`!#| zKMmc0*q{t-!7i#Xw)OG6lkHfw)QC%(>?i4T)SVlPx3!@l#er`iVLng}e2ftonBM7r zcQ^5wC3}h=$$V3f07*(*Tu*@eZk*&2*|Z(zIbN0R0e}o7aAcSZ_}fI@+^dK8Fjg3E z#_4mo@BPQR!%bvq)Xnsm)A#~Z8+BK^m2qtYGty81JAd!9;785-b>K3DBdqF%z+-iF zdNj#bE4l?X!6Qd3xSNf#%j=M!kYA%`b`zXx7~}@jCt%5Zi^jizKnv!UL`ql_CnGDH z!Q(j-j``}w%3P}N`AyR;2Zr8ew{zp99gLLM7CugFy}ClbK$8r z2~9UnlB)WZ7)#56_8mcd7db)UgP*r<*)f_tv^{^Sh2O#Dh(wiOe2T-}=w;yn7Z<|C z`c)J>S5Q2vXVOK=ph8ET^YmzgQgKOiOEvaF=t(~>^$H)!;%t$~E4iQHNwcOsUp?wx z5U8Y{@-UZzllmRsyXflY>w$~*dW|yCU9}NMt`rJajvoJA=s$Ngq8mmzNhZW_cPjVt z$dBJ!P=}{Df%D|26W6l*b0l-!&qSJymV);p7hLWIX2(3|&(mRGc^>J^CiPc+)h2jF zx-uLdWq>~bh*{4)$#da^X>VQ3Glp`Y^+iHcxibbE!#;}4b~_)R{|gA>O4M?RRl zdJiLg5#%$&!s+(+$}7?bl;a&5n9mD+=d2lE0`c4^fMIl3IokC#Vzn+E6ce#*}A zVbeQx#Pbwh4|es00Plr=9Z$TcyoIOB2}v^*#z5cHnStOE?XJm-kwnjRJo|r&No+p1 zd?l%|tj)pNE{SiLa+fgy{qmhcs|=X)d4=lZ=_Pc%*4d#Aq`2DGXIV@hHoMy|i;7(+ zawuFsr#ZH2ionxJ^IYn^%TS7JhmgWG+qw(6yJBu;-;1C4j?t$6Gt@r1AT4daWhqe` z)%Y2}(JG?JA}%hZecEH6f4y1#VSJ@H(pS8fyqQG*w3<5p-dRO5%AYAILJ)MSm~WWv zT0b0%+Bo1MRe?doCMcw4pgM)x{|+=p5o;63hfMG(dM_8h`}0c+%SYC}w3C%p^rp8k z>qA9&SlCBU6ookS=cI$u@b^Ie>&JQJZ;1H#U42FV7*0uzT0OE({CazKNok?zSGVM$ zQs?KVtE(r!hF*!N=$uqyE@t_iq`joa$ir0JD~Qddv*a6(ilrY1Wg725__CY*@@aXb z_~zBCS7-BcQ@*}q78WjLow`=M2-h~G)62m3Z5~%yQ({$1Wxwn*u-Rzzv`tpHx$$v( z%HEGH*%IF2#TbGTE6+)@%cq5`aOYZT?E*0EQd5>uU1q_E(CMScI}81M`0etSg{&Hj z%4dC1K(o+!Ze#aH!@tkjOw~wAHzHk2Y}M~hqK0=TX?YZ>qJhPf9j^uX#NNKXP6eV0 z|24~j%6iZo^ujZTno?l5UU>a;_=^`tCz&30vKP#iAeO~{>B27}%!z-Ujw>}T-eK2L z+>f*^xj-|=|TKm@MXWP7DAB~*ui#7*B4We%>M~yLM436<(cpD4@ zx+Xf5Ki{&<;aG;9-(ON}1{8^67rpmBC#-q4y?Ro&>D%S>DmwL=8~^XAdiRc7e*c{Q zQCs}jL?yQ$5@2%8ON}fnY@;82qGDDRcLpOG233(fwOq~twr%t1rvjePwS#Ak688MV zw)oxgs8n_jTo7B^iSH&2OaB4F3J(Lcq|e{t9oQnh@C5v~x>nm)TidVsE!ZO;Qx0UB z2=PVA%@2}l)d6s#`kFMcR7WOzX9saDMA8h?bpOSw?|ci9-rp@rMxT+J!P1;>f9xDj zyGe#~U=vh9O(&D}Wo&FL`wNxX8xc@RqnAIZNUz=)31p*KZ~;3=7EexIiA_~Y?|%=; z&P#sR!ZCK$Zr5a8GOUycfQG^#W@qT;QFWfv66g1>Pbp8l?ny5&JVhQJ{9Wm`Q@$uP zA(|%R4;2q79y9CErf`L`1uL7CU2x^3F9~fpDcblMJN+>*&$Qb|E(?dNT1>i;S9t!W5zp)sddiM8CTYsjXASNt??Ei{R4Tzcv%}fS$)oM@uP&usXB%W5!ThO^lGVQRi z&UTJ?gTj5DVe>QSX9=TuX1-89h!6mgAq}1QZ`{HaJ!HjCPgK1(<<8IpUcYy zRKU77e3y;+O87>M8p1c?tr-*?uBz zueyyxu%t93KcT~{B>Mi+$Q*8SaVsUW?UA@Qn(*p;6Q{7Wa=gi=@qp&E@k=s%( zteKiTzV(nX=Iqjp(#iCsO@E*^;fBn>(~Ya+l_m4BhWe)|TBj$2W&`(^dA%?e1*?;9 zIa5dOON3@Py}Av_licPt_rsv`K<+^MO+z@GLF3^fP6=ST zl{{XFvn-Kkb?KArO5~BGBw|*GHW=56?<)#5z1uzf7dcjWZh>z1kG|>mdXn43xG~Jt zd8HR;|;L#t^mHPq=j_Q9cF>ZbG9H*vzWRtF#m6J6~ z?bZH+9;Nw)fIEY(3-AGyn(lSO2E8C_97h(?gm=~8{A%qQhP%Jv^mz*1P-Poc^`fkIx)Q47QYKK zStGu14PFL?9*&S5Wyqu32sTrRXpzW ziEwA%L*Je4yQ4}v{Xg_iZx?AC3LQmSpIur@_ri#UJWg|(A1vQ7#9IRPJzV zkq7ThA``eJ%~i5@uY-f(Muzw!TN5U!cY-fpl=_Dp3cUPw!6x@Qfj}=41OJ;;Vo~4m ziHWG_=nOcnW=FFz(4!Jd|A9{swKSriWD?V}5F~`;*5WaF3?KP)8jI!!mq5pjM{NzO zuZq)|71@)@_V5%L^C&$;@{+T!xFA1AshjQdD39qdENPx!RniV(SMWTBzmmN7VkceM zYiO8Sh)YWOp7LHeq|Q7?c7UpD*{3I!UP^BLwEhL(YuUm3x09Z!$RVu+yH5)4{r%yZ zSBM}f`1)6Lrl@OM_>=Ep{`IHuV;plIt0l;rmQj@bbND#cFy(20+vT-tl!bz&xVCGc zeZW`r!~Vx8Su%gW^~JznMvcj4S6>pqb}GTEV)f(j@agaj$rUXgQ9YwP+;t)I#RNtv z<113e^kW!p|0TJP6a~*WXE1d$DW{3lrHBwk20}<4u+I`fl4QK7z?-8EY6^5udYW0K z4URSu$P)+2(dT@b8=|k}d(G38iBNVk%<9pwiD!UPq&ugD270u1GLUeIkzXp@5mu0_ zMC z@J98_VP@0k*PrY2D=Q{H83-W2WohKyD9=!FgeCp!a+TiLff7p=6z%~#Lr@o3#syov zyJx8}RxM`-$l&RJB6?5T824L$%t=#9-Ml)H;8AUy5SE+rn0%JqU}7^{oU`4ELt3;p z+fDbbg6}7!Tc_#P(Y||4;TKKyF7B@ppnm-HxUHb>z%?dmlu7-|u;P#(llqiz@qyA} z68?QY2^gko64>s7$~Dq9dDleJwcZExYqnq@-^lBMY*$b)2H&gv;c(|BPq4=AXYx##7p*2b|vcAr_l@;`5di9{_3d?H*GoPjM6 z58DthED2jO%>&9@!+)j};zpMmnU$rN+&>-GyHn6((*S9VQ)QdRBY_ zJqCQjH#Pw{H4Vb4w^y7~`aki)x@f*Tx%8p%(N~9$XU@ou^5D6upM4tk^hU*_Fd+j} z#tRZO@YX8{d5w$(LW@m6XYY-m#yzmj{G3a~0Pcd@ataRl%g|BjN~cKi!#{SGC7&HLh_altg~7Cjwr z0EGjkUS4|o?;*w%_<@ArKjdzks(R4RG&Er{C>O1CY)1KZT*rQNz(I1iuDz>Q5gux02kEJ{Zn(1cVATfh-Y-*n>CHF)#eDbt`!D?4Mr3%P_NaUSi@Wu%jhwFSglZ-TgQ;^)5p- zjIK_V8{Fb>FNKEod~FNYn!0kJeDmTx*2V{sl!bW6m=4P=-&OI64EL1_9Kjx zQ73DLVfNMcCj;v%J`e;#LvtoA#raO8!hPJI;($r#ZO_s4CQJ_ypWpYhykeoj$rZ+lbE;zngT`_zn{#3JmY{FO#obtL@;+1ZW;Wz5}j(+Jf|p zJlagGUZiN3oJc!Cm%uP?^j#{?-EZw8y2ZJe+kMD|k99-j_dOji?SKBUxN`r93j2bQ z<~K_yXk?`2io6b+C_0%akVh?xN{qL-Fr5+<#FVUA`R37RsXZtAg@ilW`9F&=@hdKH z3qZAvVqTNkhw6Q{^4{~sSah}!O=~?dmBFt5{eRyQdB(Va3((LeCigT%4SrZ$@4!}G@rIh^sW#NonW0*r>?z%v!%qv)19VU1-b zcTwNXy%L1o!3FR^ZW2@%}w~H{#|KLOhR@r_8=P$cmDyYena$MsOph_^wGoi;m{g@tRd+ zb$;@cfZ1mo*eXz5W)(nt*MI+NhfZr|eV*Fot;qH_(M{YR|9~sLO^eYIZa~B4T{2+k zlOY;V`^NoZ`(+Te6nhGC$nRJJfy$6NHg5ZSE_kW_QB-Wxjwb4 z1n(Jb!ZNBGVn|fy&Eanc$j+KfN!Z~D!5)7tkK{SNmbtPkz044D$D?g(UTMEL%T_Dax9^I3_sw`C89NCfJzBUlI4Wa7cxnhvd^> zMf+~&I!^Y+OZXH1hGif(Vx}L=KhqM|j4g{|JN0?M1UTI`wzg`JAn0gz%fTUkw(F{( zNElm75dD*dKW2Azbc!)Sf3Z>iq0HjWs-ST*>a<(4BqBnHXxm(XgqJh$6!K&{Mn1hI zOf9!AEv;sad|&=HM>{!Vz~h!B@KCp*V7yq}4WfiMr&PJLhH(arPZXZHvc@OAg*Yf$ zJZzMPDu86LuvkUGP2h0VIp=lZG=x>dEU}}dB?w-i?Cs-yqnkIi;l~YthWR2t|8>N? zset*1k}3muH)zs!7-9z_&%YJGs$7Qwy1Z{9kh3Edd9j3lj@G6!nbOX{y|ZGLeLoYv z#;A~x5W|Cld{|7-wYfO56)!I=sN?G@N`5+gW4l^rJvCY7@Orcc35Lf!Dw3YAaludU z$$LQ#o+Y7-dxD7uo}M+MyLiZM`5Mo|^XF~08qYiGb>fF5hB}Pzxm^5l9!#S=FMm3U zeZ6*;TjoYZ;GMVBLm&_(!>}nlz4@RMe8x0iss*MNQMcv#d>2;owFgM-;nUh+4r1Dh3ay+H)^&XnH~2UnUa0hz?}l)xFa z)3RC^L=!X}N6OL-b8md@?99AJ$|2qA81xKs~N#QCT3dYStt4gD-0|U1&GqxHY!=Ar8DxE;{hl(M%32uR6CW;z!|_ zgw2X#9&pEXcC@#L#y#(JHoSF9cW7v68x&4?9<`!PN%bz$v2$Ur0{>2`GPz;kn<|7uP+-{u!!?kzJ42or~#gw@k`e*XOFu-87rLqo1%&2KO+7uR{%&EZru zSbxs{5_QX>i;-26ydR9HW1^zchq_ITeZ0N(p(!)&d$)VvdA3Rl`6NF1?!~>TbtUDf zNL1P!{nwHWt{XxeM7G3?Izyrl#J`WG;c1e9ZW=SVAo*u=bAQ;w2>!z$qptE=zXd)p zCMjv@=r?be@UUQ@1>!bXv~OJ6pHH~aJU{;*h#Cq)hOZUEJH*$ zMB7)j*dJ5#0QAqK7|VKZ0|&pM0pX^9%B*eqM*#}}C}-sUGUPuYo6~ika%zm<(uPmh8!7$h4Z`h$>rGT-Ib^#w9gHkE-P! z(C``8$eq`Is|Yn$i2;YekB^s5T~`4p{W(8Bze%bAV4&Ftv=Pz`)6$!!a(~RlSLPX^ z7I&&HWW<$`@OU!O6JjgB+^YiT``3<+nK?IOV`!UWT1kNu!oG6g}ZfIt9 z7C`4wV8Mc~G%_M0VzxvF2krz1h(4_X&^~MYS|E^yhO1 zNU1sGdEkElU)&o+>%i2bA~2L!P%t8)`u^2f6YEbZlTEQ=m28A$=e{;5-eQ}Zn-kml zSrXn9?^qQce6W^&16GMV8@AyA=ndQ9g$M3s z%;c_suy7w8Ip0l!XqqK&S)%r<0_Vquimk3)H8O>88q4>O{9)$OJ~5H}?b|m}j0>we zUK&)g`tbQIqWcFevy$^s9X10Wyqqg)YI1z3)@J5zGGdzAWrs02cE-Rl#a;tQVqe zuR2Yuy#;C@K>zd?**>4lL(gP0H1eV)qP0jU@}k8VjR6f+CLKXU%+baGHD&2jEHm*) zFws+OHP%cvnP5KMq#?w<(vuT=_;?n~-q8cGG*`l}w|q^=mGp$`%W<><*i( z;$j_G-sKr6{P%uj5<SnJDinyfM~qv%YmLCt>qM9+o~KxVct@t zR{mIwphcthxn+~H;2lwAZR-VEQ|#nB?em#>UtV1(*Vi}{!=6q5+V%jK@Vs@Pz)bY< zzrFIl;knLRB_}gWOIoaL25NZj5LEDI8`&-@Ix%3OFFTn6d8MWRQ$Ki4vMX%!_~BCC z{?NgZWeSJ17@nxb#$W* zzVG_psX%TR93QX1_Ljfh=%ghS2e)vtJviv=1r$U@UtfL6p05$~YIX!^7qP7&!9ILH zdu7Nk=|kT`8~tF&%T5rpr33ULU2Pa?M}29-Zsrj&oj7AuO{2u>kIImB(3}DrPP55h zUUO7qB=j*pCoNA6)9c1^c}UO5e{y7n?s|KGpzSz7ea#c1&v-Rq@c_>t?7HS;Vzl6l zvA%oP0aOLZ09d{86|K2N$mE%L9iNoF))_LI_^KF+e@9AVz(N%+J5@T`zpGPmFy+4r z(Ev{n?HGVmroMRb!hX|wj)BGx5N~woX`rU`-Prr&b({nCAx3>x+IkeiWxfs-RzL~* zf|ZReb~G2hAfS!t!<0N9&IuljJ*^@##~k@qf9EHIBBd(V($aL=khB~ePg(GW^r&7D0>1<*%st zNmKcg+hkMqo;X_?CKh z$;IZ!Xf%MMy~j$78PA^8EFP^g9AHbJuHi6x*XsY?Kb&R!3v&55c{z8J53mBPb<(^~ z3u}IUPGY}F{0Vs#K9$uqAyfE|IEx$%=78mCVAf;Mj_UY-F~LN8G!(d(w8Y%zwJ%FZ z8Kt0YL8)xORY#990Dm&j&?tJeSTAwX^>R~c==1*MBx*m6 z7HL1fyhw-WyX>MSFLX-hI9kOA4#1ZX=V^85m2YHjhJo0C*Y}b8_ceGK`VyBA`=#Gi zaQIb}m#4#9o%bXtyzVs{Bka1bQhsBJ98uLC={|fHcVNy=TFKCFBDL$10~IQ-b9R9S z@NXfIEtJQDhv%W-`F~x9=D(WQNW#qduDXE`O*Ok(415hL;KVUDJNeoJg*P+gTa}8$ z#KiP0A@I_7YrImG=4x$SJ2kl*GuofxKTQCYQQwuJT=a>)JI35bn2xNz%!Lk%iTl8vLy$jLiEG z8pOr9e{fJx-PjN9ustM2i?4;8lQ{$Xmzcb?G#q62I*gRvc@px+#4tYwN-ujbvJdoU zUkFwU^H+@iud7paP`TteRTsT9oA~3&c&&q(8OH)t^gjV zrgmsZ7^(&1p_4pb997z7yjl2Z!1i~a`9u^u_$4nKtXR=7Gc*?p+KOGl z8fyPtK*T$rLtUn7sTQQ*eIjw)lAnkZ!&5`^`lUY=5h0;{i&%m2=I>r^=5>)B_F!7tmM3*y$k=<3~;bqo$JgGgO?OPjrNK((2^seI651DHcJ1R z2EG>_XTYwUx|;>7#&15J5_a_i%54Wo+CbJ@b#zNtNIZggHfU&s;PMkMjSZxMlQ)zU zKQbbj60F8$Lr3xrK!>8eEGxGu+Qik)cBxQlc!| zt?u8{3N*Tbk&zS)SNjk8(4CIm@^E~7^zjAdonduiFshgs5SfqKiL-rqh?*cmLGL_TI z%-z1T#Qw1`Mv}$R#pqcu5zyT$44v@HeI{zvF*!be>CULd|D@HiJp@~HE z?HtY6Q#)RgBS(Kv|MgrfzHxG-8YEheARqWj83W2Fc^-0|cVQFxo-AWpe7!MW8Tt;i zyTtd$wXC)V`C%D08*_|@!xcv;=&G;ZJhieg^a=vR1}jQnZcd6Ry{_884>8C~pq!Yx zV{{Sl$MCvG>By+LTJ#wxkB}=fulq8btn!g2rtkg3x9OjJ1{p}elgmY7;ZaTnpM|zy z@ku;;UE*4n&M3M)J9 zSIl2JLGY1e+#X7IH6^jZ}eap(Pe~GIW6GPJXu)iw7EPA0MfI}%+X~$`F_{)OZJh86p--@i5B6ywG~g?OZO^a{u{u{UXW1q8C7wu>$-EF2Y*v`3UHGCx7* zpKtN%VffuQHP&jy75I})n+3yb{u|;NHdrq7sn~n+Y;?ZcBn}i9{l7&r!|2_3F4NF9 z5<+$FbjxS;GO4H%mmjT*^~>?Ly5aX2C*$uBh6Hkd1AZYz;Ph!ZoNkC&1sK*Zk>0>1 z$Ki8$7qA8#w0BtKi;xy|W>SR!)dp3bX+k5@Tp^t#F$3WK zq^oL-)4beJpmFt?l3sE^0ypF|`gD~s?JzN#?@qPi&$(Vy0)-GTUFf0KMgN6|gt_n2 zGbCPW#RP7UgtBx+;pijH?uw$HaB2T{m{*6ndw3wRuI&GN!C%yO@xKGj+mnzCcfj$Z zXC|_sAwJEi8a zG|X8dv};{Qq$5KrOgw|tkiDS|qojz0D_o$Bi+KA-{Vd!K>X_fa9C7wr15R~5FdTT8 zo}-uDAiFInqjdsFPGK?IQ8%;;@gA4pbx`7K5bF~vuCc0frAH>bO?(Z9F=TugEjO6N z!AA?~)@Vg+S};O!y5J~a3QmoS}yV#)HMTfzD!zaR2Of*!=B8)NT-z{KQh{7ye2=Vg#eF)4eY&45GIyNPsI)TxT zdhvY=K04#?twCnko4EO-Hx0$-6h*!-sE2p^RKEF+pcidD!|zz=z(9UsUgw@a8fR$< z&~`ENKw&1v)2FY)kV$9bt)hnq!-6u`HQJB#;SN8-eWnOvhr`n)Q??Dp3D3dw#n4pw zVc>*444OCwBJ=m2yuh!HMD&u#MZ+ParJ6K;)@-%Aj2G#9X zFUgDN;yPW{C*?rRdm2Gbj;?^`?|D|!>&Td-ptfBOmGiN97*%4fM-h@SSNKATqE6Wp zrjg;kV3?1$cBTI{yxRTZhRMemVr21QAD6oqb>>r>vId7Hr*JF*W@&j)-K}uG?BSh= zH-hG%hG2rU9HOG);$8rv9#5(WUU=PsCT!m@H=hO2=yl}q50nirQU4t2IDKg1@I&Rk zn*kN+w#_scIZWsvd$e_vEFP&!5KRgVzAbJHdqq20mJz6$(~>Vk(u~>$2N@w|6}7-X zs?@E+U8o?7Agc*R^K$5F4q|vd4-M6P>4X7E>&}C*jb(oVMIWj0&`X9wZCFg5II?y$ z(ScDVASHgJ%B56b6n;xrcjMnI1-c)A7bT(>1A}EWuMCA&1D5PI*nb@@kDK@e1X`fK zqT%4M+J6tD&`{F(`FS*O4nk0pl9GC0xC~+c{-0Q{D3wVos_nqpGeLT;7#EB# z{_Jb(m=m*?(jLpVe{DqVd5^cD3V%q2?N%zFrsV`qG|GbS>BY)wJtZuUKQq_RsnkIB zn<(wvl)fu)U}U0| zM170CG5Dm;z_1a_8+gzzH-6sNg9cO5ZT=JhdxEF9pSCrO?KTh2VXICnkE)To6ZH^< zyQPdy)8Z5usW;Dw*lb>IzxJ!FqUIBJSP7DtIN;L@1pmz+w)|1ZSOg?ELIQ;Mt1!$0 z=Gqv>>d4DZ-0_NR-S(5Pl;=L-Pc%)HDf2PTJ1bKLut%$ctdRTB0 z-FQqGNrGt}tP~%jWafI7S;9F~PvMvW>^|j&U2-v;kfQ}gj20Lxh`9c0XWrc{Y0EsB zq4}?s{`VY0C{Vi!xF4hw#AXf!5;&mfg|+ENc_d|sbY3}9M9GnhD4y>2sWd!{D1teE@^69$$1eZkmj*lTCdLfYhq0;O)*uLq zR}Q_bN|QyQpK^=JEUM^FwluNyC1)F|!W3afvV=9<945CU9{hUst zRO6!yYn_Zo+uzP%plnRthS9*q8)f$(CqM`5Wn~y) zK>G;4sQJ5xmhl*v1ggVOB`rJq@7HCBth#MyM*ji+7g1!(V9hU&1 zpHY>^K*vv(m7JZ{Md*&-!&K0K{Y#28Gh)hdye5SIh+^#GK4+zq5&z{NeCPiVGLU;n zJVZ8|fE&Sf=gu8TzcsOp%nSp=8~9xi<D3Ygm_uqSv0Q7RW%PH*e!k2z+d`%?42<4l@h@p%yUtlbC8Mf1xik|n*u=( z0iYd%{r!=T7FNEHF#zfZCK2Qd_K!7BpJ5-ChMb}4N{wNA@f=Q&;ZTWU!|jeNWw`39 z4)2bCqVNu$Zp+8fE@rt8e^o&~I5Rto59CPpfPp8BWCuMx`m72btYM~foHS(EC9JYp z?&Ve&wF|t_+XZ!tMW>oAG3DVQJSG8u3g@tAk&iWjg3q=&0ytHkp@9qa=O$0SgP0@~ zyt*?`7p_0K+l|~o*B)mWA&A5uj#mKzfk#UNd=ZBRy$tXgqA)^!dd~Rxc$m#9nm=Yv zk@XGOe&KFodyn%1_d@kBd3DhAwj}A_U>rMk__c~7*7#;?KS3H%Yb&`3140sMj#*NQ z;>kbkp5iZ|U!49+Z$owRv|NQ)jcfLVA7B5Vdul6PlCLXxw&Z(K+;Ne&U-@iPaINyA zTGpUiW$fulyH3vUQ5SWu-r|Z5y?y&WCX@K8z0D-)J<4yl@83TKAI#hM_;iq3+7IN> z0;0blCXguS0J332!F1Guw6*acS$lenfAFG)iL~N&t9{aQ`H)=dRG`&Ro{O~TG&nY> zoh?zRMA*N0yQtOACY790rNQk5PPT@$%0<*GF7b4@Tc9m98~R zudKX)m6(#L>1jAsQTq@L149sS1`C~u@_s;6FOXt_;1NNTco}A4tp`aCtw5kX<>loC zNG&zN3lUI*l2TB7hVQ`Tc-!G$ax^~1o+~OXLYtT+{!|3sTY@so?V!_EvF7?uY>l4; z+u~`EZ2U@9k59>^qlyt5bx;>8*GXV5zUB0eG@gF$O<{qM;& zL_?z)7Tj0{a+iO5E;lSJ3gw7xz5o2`;LlWm1`ommA!hBAjEoFKGn<@Odynq0y5Ko(S!%nTwzqX$EQ+jTWjqrj=6o%om%Hz z-f&BvSRiFG=GHw0?jzWC+<(1AV|gRqiMx57C@H9Re9Z zhzNmG2ut=XL~AU zLzul?o<@-%tzP;=&_q^pJm)wT?K>=I_;a~c!})>VgQ}W`5mXPPn@30M{_Lt+B(9J3 z?R$W>3=LX9#qKc2f{1l<*tHgo9?ALqAw<2YFffbs7Nvz>X8Pj!X#gQbfJ0uK;hzj1V6#~XrMvB`SghIjBU+f6`h>SK>dpNmQ zt&%!9IYAm$Gh|XFUo+_k*ccl4{tpQK@6Y(sUMZPWG}Amw$c4xAorl-ijLPg<{A}ez z^41y&|5-P|PlaPOXB5d8+RjLMyY&B5Ao(?JNtGeP6(c7=5K9lkLVFn7up%;28SolF zXmsx$3oBgSl&~j5HEmjpuh9A29U}oKtGc?n(C}mM>~?rbR+~e`Y0JeUBf|n^e(WnK z_r5mS+1dT{S#z^lxR+T~!~lQJD=e&j=P74awqXoYY@^?uiy6hGl$;lWUZzk%!RGGn zuHSUa@5~Z%_LF`$4aBkb^!EogHa?xpoIL@NO7p+~12jVQt=&dC02GDUQwR(+?m&0> zBxEZAh$$vyqXJ`ZpsM zFrNT;m&f?2@^Fc!B>g9IFEX%zN<>GEYmWURti=lts#FvphiD8k@V-@IVY z&k1^l@$qpqy&gVTI6w2xm5jb!@qP_#rJpeM1|aPxKqx+1UK7m9%E4}#o&Qt$XKM1} zHAz|$n1-H7JdWwMtysBZtG;v5>H?72Fsq^HNIvMetf931@KXY1aa#w_5oka69{ zh}=cJs3TdH+W1=M^fo$ZW*&Cya#s4M?Mo0_|4Ik~!SlZa!!(Naf@iFs>C5u|hgFW{4slRE?X|Jx% zNA%RYxQRLR5rX4b4RYFV+uHtBdO2!vA>Rna1cQW�yy)R{|o@!>JDu=x@%?UHEnQ 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 From 1c216c248c596dff9a06316d8e805e03771ff2fd Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 17:42:20 +0100 Subject: [PATCH 159/459] Storing shared items federation domains --- webapp_profile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index bfabdbb65..c09e4432e 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1358,8 +1358,8 @@ def _htmlEditProfileSharedItems(baseDir: str, nickname: str, domain: str, editProfileForm = beginEditSection(translate['Shares']) idx = 'List of domains which can access the shared items catalog' editProfileForm += \ - editTextArea(translate[idx], 'shareDomainList', sharedItemsStr, - 200, '', False) + editTextArea(translate[idx], 'shareDomainList', + sharedItemsStr, 200, '', False) editProfileForm += endEditSection() return editProfileForm From bd468c46de51a91932089add17ce27628bb1b31e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 17:52:54 +0100 Subject: [PATCH 160/459] Remove bold --- webapp_profile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index c09e4432e..48dc77bd1 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1220,7 +1220,6 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, with open(counselorsFile, 'r') as f: counselors = f.read() roleAssignStr += \ - ' ' + \ editTextArea(translate['Counselors'], 'counselors', counselors, 200, '', False) @@ -1231,7 +1230,6 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, with open(artistsFile, 'r') as f: artists = f.read() roleAssignStr += \ - ' ' + \ editTextArea(translate['Artists'], 'artists', artists, 200, '', False) roleAssignStr += endEditSection() From cc7d7123a750a5dcbe08f40d60dc8e996761e4f4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 18:01:47 +0100 Subject: [PATCH 161/459] Field value --- daemon.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/daemon.py b/daemon.py index e21191fe4..a58d981c5 100644 --- a/daemon.py +++ b/daemon.py @@ -4834,16 +4834,17 @@ class PubServer(BaseHTTPRequestHandler): sharedItemsFederatedDomainsStr = \ getConfigParam(baseDir, "sharedItemsFederatedDomains") + if not sharedItemsFederatedDomainsStr: + sharedItemsFederatedDomainsStr = '' sharedItemsFormStr = '' - if sharedItemsFederatedDomainsStr and \ - fields.get('shareDomainList'): + if fields.get('shareDomainList'): sharedItemsList = \ sharedItemsFederatedDomainsStr.split(',') for sharedFederatedDomain in sharedItemsList: sharedItemsFormStr += \ sharedFederatedDomain.strip() + '\n' - if fields.get('shareDomainList') != \ + if fields['shareDomainList'] != \ sharedItemsFormStr: sharedItemsFormStr2 = \ sharedItemsFormStr.replace('\n', ',') @@ -4853,8 +4854,7 @@ class PubServer(BaseHTTPRequestHandler): sharedItemsFormStr2) siDomainUpdated = True else: - if not fields.get('shareDomainList') and \ - sharedItemsFederatedDomainsStr: + if sharedItemsFederatedDomainsStr: sharedItemsField = \ "sharedItemsFederatedDomains" setConfigParam(baseDir, sharedItemsField, From 92962ad187369d1e7e3cb80da178ec8128acf1e1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 18:06:44 +0100 Subject: [PATCH 162/459] Debug --- daemon.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/daemon.py b/daemon.py index a58d981c5..0bbe987e9 100644 --- a/daemon.py +++ b/daemon.py @@ -4837,6 +4837,7 @@ class PubServer(BaseHTTPRequestHandler): if not sharedItemsFederatedDomainsStr: sharedItemsFederatedDomainsStr = '' sharedItemsFormStr = '' + print('shareDomainList') if fields.get('shareDomainList'): sharedItemsList = \ sharedItemsFederatedDomainsStr.split(',') @@ -4844,10 +4845,16 @@ class PubServer(BaseHTTPRequestHandler): sharedItemsFormStr += \ sharedFederatedDomain.strip() + '\n' + print('shareDomainList1: ' + + fields['shareDomainList']) + print('shareDomainList2: ' + + sharedItemsFormStr) if fields['shareDomainList'] != \ sharedItemsFormStr: sharedItemsFormStr2 = \ sharedItemsFormStr.replace('\n', ',') + print('shareDomainList2: ' + + sharedItemsFormStr2) sharedItemsField = \ "sharedItemsFederatedDomains" setConfigParam(baseDir, sharedItemsField, From 3af2a44522b74d330e3d353940da2795656e8d73 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 18:13:44 +0100 Subject: [PATCH 163/459] Set domains list --- daemon.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index 0bbe987e9..be2aa1bb7 100644 --- a/daemon.py +++ b/daemon.py @@ -4849,11 +4849,12 @@ class PubServer(BaseHTTPRequestHandler): fields['shareDomainList']) print('shareDomainList2: ' + sharedItemsFormStr) - if fields['shareDomainList'] != \ + shareDomainList = fields['shareDomainList'] + if shareDomainList != \ sharedItemsFormStr: sharedItemsFormStr2 = \ - sharedItemsFormStr.replace('\n', ',') - print('shareDomainList2: ' + + shareDomainList.replace('\n', ',') + print('shareDomainList3: ' + sharedItemsFormStr2) sharedItemsField = \ "sharedItemsFederatedDomains" From dd9f433edbedcfb98e3aae7eef9073b3eafa8e4c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 18:18:03 +0100 Subject: [PATCH 164/459] Remove debug --- daemon.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/daemon.py b/daemon.py index be2aa1bb7..0791164c0 100644 --- a/daemon.py +++ b/daemon.py @@ -4837,7 +4837,6 @@ class PubServer(BaseHTTPRequestHandler): if not sharedItemsFederatedDomainsStr: sharedItemsFederatedDomainsStr = '' sharedItemsFormStr = '' - print('shareDomainList') if fields.get('shareDomainList'): sharedItemsList = \ sharedItemsFederatedDomainsStr.split(',') @@ -4845,17 +4844,11 @@ class PubServer(BaseHTTPRequestHandler): sharedItemsFormStr += \ sharedFederatedDomain.strip() + '\n' - print('shareDomainList1: ' + - fields['shareDomainList']) - print('shareDomainList2: ' + - sharedItemsFormStr) shareDomainList = fields['shareDomainList'] if shareDomainList != \ sharedItemsFormStr: sharedItemsFormStr2 = \ shareDomainList.replace('\n', ',') - print('shareDomainList3: ' + - sharedItemsFormStr2) sharedItemsField = \ "sharedItemsFederatedDomains" setConfigParam(baseDir, sharedItemsField, From f91d93ba05b7786ba510929f0e59b50efea1f866 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:22:34 +0100 Subject: [PATCH 165/459] Debug messages --- daemon.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/daemon.py b/daemon.py index 0791164c0..3e5908092 100644 --- a/daemon.py +++ b/daemon.py @@ -13790,21 +13790,29 @@ class PubServer(BaseHTTPRequestHandler): return -1 elif postType == 'newshare': if not fields.get('itemQty'): + print(postType + ' no itemQty') return -1 if not fields.get('itemType'): + print(postType + ' no itemType') return -1 if not fields.get('itemPrice'): + print(postType + ' no itemPrice') return -1 if not fields.get('itemCurrency'): + print(postType + ' no itemCurrency') return -1 if not fields.get('category'): + print(postType + ' no category') return -1 if not fields.get('location'): + print(postType + ' no location') return -1 if not fields.get('duration'): + print(postType + ' no duratio') return -1 if attachmentMediaType: if attachmentMediaType != 'image': + print('Attached media is not an image') return -1 durationStr = fields['duration'] if durationStr: @@ -13825,6 +13833,7 @@ class PubServer(BaseHTTPRequestHandler): itemCurrency = "EUR" if fields['itemCurrency']: itemCurrency = fields['itemCurrency'] + print('Adding shared item') addShare(self.server.baseDir, self.server.httpPrefix, nickname, From 9358e9bd2cdaa50e16168e30a3a7c637ac10d982 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:31:50 +0100 Subject: [PATCH 166/459] Required text fields --- webapp_create_post.py | 4 ++-- webapp_utils.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index d92120982..408694c7e 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -360,9 +360,9 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, 'itemQty', 1, 1, 999999, 1) extraFields += '
' + \ editTextField(translate['Type of shared item. eg. hat'] + ':', - 'itemType', '') + 'itemType', '', '', True) catStr = translate['Category of shared item. eg. clothing'] - extraFields += editTextField(catStr + ':', 'category', '') + extraFields += editTextField(catStr + ':', 'category', '', '', True) extraFields += '
' extraFields += \ editNumberField(translate['Duration of listing in days'], diff --git a/webapp_utils.py b/webapp_utils.py index 00221fe4f..428979331 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1155,7 +1155,7 @@ def endEditSection() -> str: def editTextField(label: str, name: str, value: str = "", - placeholder: str = "") -> str: + placeholder: str = "", required: bool = False) -> str: """Returns html for editing a text field """ if value is None: @@ -1163,10 +1163,13 @@ def editTextField(label: str, name: str, value: str = "", placeholderStr = '' if placeholder: placeholderStr = ' placeholder="' + placeholder + '"' + requiredStr = '' + if required: + requiredStr = ' required' return \ '
\n' + \ ' \n' + value + '"' + placeholderStr + requiredStr + '>\n' def editNumberField(label: str, name: str, value: int = 1, From b1b3d1712fa378fef7ef8efce0bbc565076bda72 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:46:10 +0100 Subject: [PATCH 167/459] No default placeholder --- webapp_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_utils.py b/webapp_utils.py index 428979331..3d05a48f3 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1195,7 +1195,7 @@ def editCurrencyField(label: str, name: str, value: str = "0.00", """ if value is None: value = '0.00' - placeholderStr = '0.00' + placeholderStr = '' if placeholder: if placeholder.isdigit(): placeholderStr = ' placeholder="' + str(placeholder) + '"' From b8496f95332726ed3d40289955215e5303e788ab Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:48:11 +0100 Subject: [PATCH 168/459] Extra quote --- webapp_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_utils.py b/webapp_utils.py index 3d05a48f3..003ead30d 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1203,7 +1203,7 @@ def editCurrencyField(label: str, name: str, value: str = "0.00", '
\n' + \ ' \n' + ' pattern="^\\d{1,3}(,\\d{3})*(\\.\\d+)?" data-type="currency">\n' def editCheckBox(label: str, name: str, checked: bool = False) -> str: From 4cc0bd03d5e2e3eb1a7880e9a3af4bbf09310f75 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:54:27 +0100 Subject: [PATCH 169/459] Currency placeholder --- daemon.py | 3 --- webapp_create_post.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/daemon.py b/daemon.py index 3e5908092..66cae4893 100644 --- a/daemon.py +++ b/daemon.py @@ -13804,9 +13804,6 @@ class PubServer(BaseHTTPRequestHandler): if not fields.get('category'): print(postType + ' no category') return -1 - if not fields.get('location'): - print(postType + ' no location') - return -1 if not fields.get('duration'): print(postType + ' no duratio') return -1 diff --git a/webapp_create_post.py b/webapp_create_post.py index 408694c7e..013d5054b 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -376,8 +376,8 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, extraFields += \ editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00') extraFields += '
' + \ - editTextField(translate['Currency'] + ':', - 'itemCurrency', 'EUR') + editTextField(translate['Currency'] + ':', 'itemCurrency', 'EUR', + 'EUR / GBP / USD ...', True) extraFields += '\n' citationsStr = '' From 91931b0e7b430174150863e3feb6598a906a7338 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 19:56:51 +0100 Subject: [PATCH 170/459] Required price field --- webapp_create_post.py | 3 ++- webapp_utils.py | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index 013d5054b..d57409bd0 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -374,7 +374,8 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, extraFields += '\n' extraFields += '

\n' extraFields += \ - editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00') + editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00', + '0.00', True) extraFields += '
' + \ editTextField(translate['Currency'] + ':', 'itemCurrency', 'EUR', 'EUR / GBP / USD ...', True) diff --git a/webapp_utils.py b/webapp_utils.py index 003ead30d..6699c0201 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1190,7 +1190,8 @@ def editNumberField(label: str, name: str, value: int = 1, def editCurrencyField(label: str, name: str, value: str = "0.00", - placeholder: str = "0.00") -> str: + placeholder: str = "0.00", + required: bool = False) -> str: """Returns html for editing a currency field """ if value is None: @@ -1199,11 +1200,15 @@ def editCurrencyField(label: str, name: str, value: str = "0.00", if placeholder: if placeholder.isdigit(): placeholderStr = ' placeholder="' + str(placeholder) + '"' + requiredStr = '' + if required: + requiredStr = ' required' return \ '
\n' + \ ' \n' + ' pattern="^\\d{1,3}(,\\d{3})*(\\.\\d+)?" data-type="currency"' + \ + requiredStr + '>\n' def editCheckBox(label: str, name: str, checked: bool = False) -> str: From 13443a914d4536d1552c52153e770d2421c9565b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:03:02 +0100 Subject: [PATCH 171/459] Create directory for shares --- daemon.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemon.py b/daemon.py index 66cae4893..acc4bad0d 100644 --- a/daemon.py +++ b/daemon.py @@ -15285,6 +15285,10 @@ def runDaemon(sharedItemsFederatedDomains: [], print('Creating archive') os.mkdir(archiveDir) + if not os.path.isdir(baseDir + '/sharefiles'): + print('Creating shared item files directory') + os.mkdir(baseDir + '/sharefiles') + print('Creating cache expiry thread') httpd.thrCache = \ threadWithTrace(target=expireCache, From 303687ba5861d4db249d61ddb1104f00a8d025fd Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:13:55 +0100 Subject: [PATCH 172/459] Use hashes in item ID --- shares.py | 4 ++-- webapp_utils.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/shares.py b/shares.py index bde0203e1..5e7b51aaf 100644 --- a/shares.py +++ b/shares.py @@ -83,7 +83,7 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: """Removes any invalid characters from the display name to produce an item ID """ - removeChars = (' ', '\n', '\r') + removeChars = (' ', '\n', '\r', '#') for ch in removeChars: displayName = displayName.replace(ch, '') removeChars2 = ('+', '/', '\\', '?', '&') @@ -91,7 +91,7 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: displayName = displayName.replace(ch, '-') displayName = displayName.replace('.', '_') displayName = displayName.replace("’", "'") - return actor + '/shareditems/' + displayName + return actor.replace('/', '#') + '#shareditems#' + displayName def removeSharedItem(baseDir: str, nickname: str, domain: str, diff --git a/webapp_utils.py b/webapp_utils.py index 6699c0201..c001cb3ca 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -381,7 +381,8 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, ctr = 0 for itemID, item in sharesJson.items(): # assign owner to the item - item['actor'] = itemID.split('/shareditems/')[0] + item['actor'] = \ + itemID.split('#shareditems#')[0].replace('#', '/') allSharesJson[str(item['published'])] = item ctr += 1 if ctr >= maxSharesPerAccount: From 82f51299b76f7d4de38cd08aeba8d942870eb585 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:17:15 +0100 Subject: [PATCH 173/459] Tidying --- webapp_utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/webapp_utils.py b/webapp_utils.py index c001cb3ca..7b2a6bd3c 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -381,8 +381,11 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, ctr = 0 for itemID, item in sharesJson.items(): # assign owner to the item - item['actor'] = \ - itemID.split('#shareditems#')[0].replace('#', '/') + shareActor = '' + if '#shareditems#' in itemID: + shareActor = itemID.split('#shareditems#')[0] + shareActor = shareActor.replace('#', '/') + item['actor'] = shareActor allSharesJson[str(item['published'])] = item ctr += 1 if ctr >= maxSharesPerAccount: From f9012cd94808eb4555ed4a26d72d203855719d3f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:23:55 +0100 Subject: [PATCH 174/459] Optional location --- webapp_search.py | 10 ++++++---- webapp_timeline.py | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index 0001f8e2d..bc80379a6 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -108,8 +108,9 @@ def _matchSharedItem(searchStrLowerList: [], """ for searchSubstr in searchStrLowerList: searchSubstr = searchSubstr.strip() - if searchSubstr in sharedItem['location'].lower(): - return True + if sharedItem.get('location'): + if searchSubstr in sharedItem['location'].lower(): + return True elif searchSubstr in sharedItem['summary'].lower(): return True elif searchSubstr in sharedItem['displayName'].lower(): @@ -143,8 +144,9 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, '' + translate['Type'] + ': ' + sharedItem['itemType'] + ' ' sharedItemsForm += \ '' + translate['Category'] + ': ' + sharedItem['category'] + ' ' - sharedItemsForm += \ - '' + translate['Location'] + ': ' + sharedItem['location'] + if sharedItem.get('location'): + sharedItemsForm += \ + '' + translate['Location'] + ': ' + sharedItem['location'] if sharedItem.get('itemPrice') and \ sharedItem.get('itemCurrency'): if isfloat(sharedItem['itemPrice']): diff --git a/webapp_timeline.py b/webapp_timeline.py index 918f02fff..e96c2da0f 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -833,8 +833,9 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, '' + translate['Type'] + ': ' + item['itemType'] + ' ' profileStr += \ '' + translate['Category'] + ': ' + item['category'] + ' ' - profileStr += \ - '' + translate['Location'] + ': ' + item['location'] + if item.get('location'): + profileStr += \ + '' + translate['Location'] + ': ' + item['location'] if item.get('itemPrice') and item.get('itemCurrency'): if isfloat(item['itemPrice']): if float(item['itemPrice']) > 0: From 4f58cc98c1eb99d02f0fd463b3aca0e72aaa1518 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:48:31 +0100 Subject: [PATCH 175/459] Replacing colon --- daemon.py | 3 +-- shares.py | 4 +++- webapp_utils.py | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index acc4bad0d..f5c5aa2fa 100644 --- a/daemon.py +++ b/daemon.py @@ -10230,8 +10230,7 @@ class PubServer(BaseHTTPRequestHandler): return True mediaStr = path.split('/sharefiles/')[1] - mediaFilename = \ - baseDir + '/sharefiles/' + mediaStr + mediaFilename = baseDir + '/sharefiles/' + mediaStr if not os.path.isfile(mediaFilename): self._404() return True diff --git a/shares.py b/shares.py index 5e7b51aaf..7c912b4ad 100644 --- a/shares.py +++ b/shares.py @@ -91,7 +91,9 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: displayName = displayName.replace(ch, '-') displayName = displayName.replace('.', '_') displayName = displayName.replace("’", "'") - return actor.replace('/', '#') + '#shareditems#' + displayName + actor = actor.replace('://', '--##') + actor = actor.replace('/', '#') + return actor + '#shareditems#' + displayName def removeSharedItem(baseDir: str, nickname: str, domain: str, diff --git a/webapp_utils.py b/webapp_utils.py index 7b2a6bd3c..5b9db3633 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -384,6 +384,7 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, shareActor = '' if '#shareditems#' in itemID: shareActor = itemID.split('#shareditems#')[0] + shareActor = shareActor.replace('--##', '://') shareActor = shareActor.replace('#', '/') item['actor'] = shareActor allSharesJson[str(item['published'])] = item From 6e7b508b804529473869b4575910ca76406a798d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 20:53:24 +0100 Subject: [PATCH 176/459] Avoid encode error --- daemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index f5c5aa2fa..ff8ac3128 100644 --- a/daemon.py +++ b/daemon.py @@ -11267,7 +11267,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.translate, self.server.baseDir, actor, shareName, - callingDomain).encode('utf-8') + callingDomain) if not msg: if callingDomain.endswith('.onion') and \ self.server.onionDomain: @@ -11278,7 +11278,7 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(actor + '/tlshares', cookie, callingDomain) return - msglen = len(msg) + msglen = len(msg.encode('utf-8')) self._set_headers('text/html', msglen, cookie, callingDomain) self._write(msg) From ad693e91f898fd7793f5cf81ac1759256fa44c2c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:14:13 +0100 Subject: [PATCH 177/459] Different substitutions --- shares.py | 4 ++-- webapp_utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/shares.py b/shares.py index 7c912b4ad..56ebb2080 100644 --- a/shares.py +++ b/shares.py @@ -91,8 +91,8 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: displayName = displayName.replace(ch, '-') displayName = displayName.replace('.', '_') displayName = displayName.replace("’", "'") - actor = actor.replace('://', '--##') - actor = actor.replace('/', '#') + actor = actor.replace('://', '___') + actor = actor.replace('/', '--') return actor + '#shareditems#' + displayName diff --git a/webapp_utils.py b/webapp_utils.py index 5b9db3633..e7984e454 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -384,8 +384,8 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, shareActor = '' if '#shareditems#' in itemID: shareActor = itemID.split('#shareditems#')[0] - shareActor = shareActor.replace('--##', '://') - shareActor = shareActor.replace('#', '/') + shareActor = shareActor.replace('___', '://') + shareActor = shareActor.replace('--', '/') item['actor'] = shareActor allSharesJson[str(item['published'])] = item ctr += 1 From ac7570473474af2ffcb4ea7ea25397f9a2e12e59 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:17:36 +0100 Subject: [PATCH 178/459] Dashes --- shares.py | 2 +- webapp_utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shares.py b/shares.py index 56ebb2080..cf0376917 100644 --- a/shares.py +++ b/shares.py @@ -93,7 +93,7 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: displayName = displayName.replace("’", "'") actor = actor.replace('://', '___') actor = actor.replace('/', '--') - return actor + '#shareditems#' + displayName + return actor + '--shareditems--' + displayName def removeSharedItem(baseDir: str, nickname: str, domain: str, diff --git a/webapp_utils.py b/webapp_utils.py index e7984e454..8573c8a15 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -382,8 +382,8 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, for itemID, item in sharesJson.items(): # assign owner to the item shareActor = '' - if '#shareditems#' in itemID: - shareActor = itemID.split('#shareditems#')[0] + if '--shareditems--' in itemID: + shareActor = itemID.split('--shareditems--')[0] shareActor = shareActor.replace('___', '://') shareActor = shareActor.replace('--', '/') item['actor'] = shareActor From 4198e12c423916eda7cd26b4afdedb346e68deed Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:26:10 +0100 Subject: [PATCH 179/459] itemPrice --- shares.py | 8 ++++---- webapp_search.py | 2 +- webapp_timeline.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shares.py b/shares.py index cf0376917..b84d6ecd3 100644 --- a/shares.py +++ b/shares.py @@ -293,8 +293,8 @@ def addShare(baseDir: str, "location": location, "published": published, "expire": durationSec, - "price": price, - "currency": currency + "itemPrice": price, + "itemCurrency": currency } saveJson(sharesJson, sharesFilename) @@ -1296,7 +1296,7 @@ def _dfcToSharesFormat(catalogJson: {}, "location": "", "published": item['DFC:startDate'], "expire": durationSec, - "price": item['DFC:price'].split(' ')[0], - "currency": item['DFC:price'].split(' ')[1] + "itemPrice": item['DFC:price'].split(' ')[0], + "itemCurrency": item['DFC:price'].split(' ')[1] } return sharesJson diff --git a/webapp_search.py b/webapp_search.py index bc80379a6..6461a4d35 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -139,7 +139,7 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, if sharedItem.get('itemQty'): sharedItemsForm += \ '' + translate['Quantity'] + \ - ': ' + str(sharedItem['itemQty']) + ' ' + ':
' + str(sharedItem['itemQty']) + '
' sharedItemsForm += \ '' + translate['Type'] + ': ' + sharedItem['itemType'] + ' ' sharedItemsForm += \ diff --git a/webapp_timeline.py b/webapp_timeline.py index e96c2da0f..734aee703 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -828,7 +828,7 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, if item.get('itemQty'): profileStr += \ '' + translate['Quantity'] + ': ' + \ - str(item['itemQty']) + ' ' + str(item['itemQty']) + '
' profileStr += \ '' + translate['Type'] + ': ' + item['itemType'] + ' ' profileStr += \ From 51ff7f8126d18886cb770ec32bff24f7acd0808f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:29:29 +0100 Subject: [PATCH 180/459] Newlines --- webapp_search.py | 7 ++++--- webapp_timeline.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index 6461a4d35..a4660b29d 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -141,12 +141,13 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, '' + translate['Quantity'] + \ ': ' + str(sharedItem['itemQty']) + '
' sharedItemsForm += \ - '' + translate['Type'] + ': ' + sharedItem['itemType'] + ' ' + '' + translate['Type'] + ': ' + sharedItem['itemType'] + '
' sharedItemsForm += \ - '' + translate['Category'] + ': ' + sharedItem['category'] + ' ' + '' + translate['Category'] + ': ' + sharedItem['category'] + '
' if sharedItem.get('location'): sharedItemsForm += \ - '' + translate['Location'] + ': ' + sharedItem['location'] + '' + translate['Location'] + ': ' + \ + sharedItem['location'] + '
' if sharedItem.get('itemPrice') and \ sharedItem.get('itemCurrency'): if isfloat(sharedItem['itemPrice']): diff --git a/webapp_timeline.py b/webapp_timeline.py index 734aee703..1d220f0bc 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -830,12 +830,13 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, '' + translate['Quantity'] + ': ' + \ str(item['itemQty']) + '
' profileStr += \ - '' + translate['Type'] + ': ' + item['itemType'] + ' ' + '' + translate['Type'] + ': ' + item['itemType'] + '
' profileStr += \ - '' + translate['Category'] + ': ' + item['category'] + ' ' + '' + translate['Category'] + ': ' + item['category'] + '
' if item.get('location'): profileStr += \ - '' + translate['Location'] + ': ' + item['location'] + '' + translate['Location'] + ': ' + \ + item['location'] + '
' if item.get('itemPrice') and item.get('itemCurrency'): if isfloat(item['itemPrice']): if float(item['itemPrice']) > 0: From 65ee4a6ceedadf42e7eef6f57ddd04060ae7663c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:31:14 +0100 Subject: [PATCH 181/459] Don't show quantity for single items --- webapp_search.py | 7 ++++--- webapp_timeline.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index a4660b29d..0b1cbf90f 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -137,9 +137,10 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, '" alt="Item image">
\n' sharedItemsForm += '

' + sharedItem['summary'] + '

\n

' if sharedItem.get('itemQty'): - sharedItemsForm += \ - '' + translate['Quantity'] + \ - ': ' + str(sharedItem['itemQty']) + '
' + if sharedItem['itemQty'] > 1: + sharedItemsForm += \ + '' + translate['Quantity'] + \ + ': ' + str(sharedItem['itemQty']) + '
' sharedItemsForm += \ '' + translate['Type'] + ': ' + sharedItem['itemType'] + '
' sharedItemsForm += \ diff --git a/webapp_timeline.py b/webapp_timeline.py index 1d220f0bc..18ba0da1c 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -826,9 +826,10 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, '" alt="' + translate['Item image'] + '">\n\n' profileStr += '

' + item['summary'] + '

\n

' if item.get('itemQty'): - profileStr += \ - '' + translate['Quantity'] + ': ' + \ - str(item['itemQty']) + '
' + if item['itemQty'] > 1: + profileStr += \ + '' + translate['Quantity'] + ': ' + \ + str(item['itemQty']) + '
' profileStr += \ '' + translate['Type'] + ': ' + item['itemType'] + '
' profileStr += \ From 8ff707fbd24aebcd5924db12002f18dd17c59572 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 21:35:55 +0100 Subject: [PATCH 182/459] Currency field --- shares.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index b84d6ecd3..fcc11f97f 100644 --- a/shares.py +++ b/shares.py @@ -835,7 +835,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] - priceStr = item['itemPrice'] + ' ' + item['currency'] + priceStr = item['itemPrice'] + ' ' + item['itemCurrency'] catalogItem = { "@id": shareId, "@type": "DFC:SuppliedProduct", @@ -919,7 +919,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] - priceStr = item['itemPrice'] + ' ' + item['currency'] + priceStr = item['itemPrice'] + ' ' + item['itemCurrency'] catalogItem = { "@id": shareId, "@type": "DFC:SuppliedProduct", From 347d769b0be6b00d5e45180775183ddb380a8366 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 22:04:49 +0100 Subject: [PATCH 183/459] User access to shared item catalog --- daemon.py | 3 ++- webapp_search.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index ff8ac3128..6f27c23bb 100644 --- a/daemon.py +++ b/daemon.py @@ -10755,7 +10755,8 @@ class PubServer(BaseHTTPRequestHandler): # shared items catalog for this instance # this is only accessible to instance members or to # other instances which present an authorization token - if self.path.startswith('/catalog'): + if self.path.startswith('/catalog') or \ + (self.path.startswith('/users/') and '/catalog' in self.path): catalogAuthorized = authorized if not catalogAuthorized: # basic auth access to shared items catalog diff --git a/webapp_search.py b/webapp_search.py index 0b1cbf90f..aec4f78ba 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -144,7 +144,8 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, sharedItemsForm += \ '' + translate['Type'] + ': ' + sharedItem['itemType'] + '
' sharedItemsForm += \ - '' + translate['Category'] + ': ' + sharedItem['category'] + '
' + '' + translate['Category'] + ': ' + \ + sharedItem['category'] + '
' if sharedItem.get('location'): sharedItemsForm += \ '' + translate['Location'] + ': ' + \ From 009d23a94804ced062fd9045b31f4e059b98730d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 22:06:22 +0100 Subject: [PATCH 184/459] Extra reserved word --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index b5debfc54..b11334770 100644 --- a/utils.py +++ b/utils.py @@ -1586,7 +1586,7 @@ def _isReservedName(nickname: str) -> bool: 'bookmark', 'bookmarks', 'tlbookmarks', 'ignores', 'linksmobile', 'newswiremobile', 'minimal', 'search', 'eventdelete', - 'searchemoji') + 'searchemoji', 'catalog') if nickname in reservedNames: return True return False From c729e85b9e2550fbeef325a708c9169ea23410ff Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 22:08:33 +0100 Subject: [PATCH 185/459] path --- daemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index 6f27c23bb..585f53d72 100644 --- a/daemon.py +++ b/daemon.py @@ -10785,7 +10785,7 @@ class PubServer(BaseHTTPRequestHandler): sharesCatalogEndpoint(self.server.baseDir, self.server.httpPrefix, self.server.domainFull, - self.server.path) + self.path) msg = json.dumps(catalogJson, ensure_ascii=False).encode('utf-8') msglen = len(msg) @@ -10799,7 +10799,7 @@ class PubServer(BaseHTTPRequestHandler): sharesCatalogCSVEndpoint(self.server.baseDir, self.server.httpPrefix, self.server.domainFull, - self.server.path) + self.path) msg = json.dumps(catalogStr, ensure_ascii=False).encode('utf-8') msglen = len(msg) From 44d8afdf41bba567df1dbe268c0af477473bca37 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 22:16:16 +0100 Subject: [PATCH 186/459] catalog defaults to json --- daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 585f53d72..f07f65528 100644 --- a/daemon.py +++ b/daemon.py @@ -10773,7 +10773,7 @@ class PubServer(BaseHTTPRequestHandler): catalogAuthorized = True # show shared items catalog for federation if self._hasAccept(callingDomain) and catalogAuthorized: - catalogType = 'html' + catalogType = 'json' if self.path.endswith('.csv') or self._requestCSV(): catalogType = 'csv' elif self.path.endswith('.json') or not self._requestHTTP(): From 0bfe005b432edb44d425073e631e118518d7e1d3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 22:28:20 +0100 Subject: [PATCH 187/459] Link to shares catalog --- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/ku.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/sw.json | 3 ++- translations/zh.json | 3 ++- webapp_column_left.py | 3 +++ 18 files changed, 37 insertions(+), 17 deletions(-) diff --git a/translations/ar.json b/translations/ar.json index 2c4af68d0..a55e1999a 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -457,5 +457,6 @@ "food": "غذاء", "Price": "السعر", "Currency": "عملة", - "List of domains which can access the shared items catalog": "قائمة المجالات التي يمكن الوصول إلى كتالوج البنود المشتركة" + "List of domains which can access the shared items catalog": "قائمة المجالات التي يمكن الوصول إلى كتالوج البنود المشتركة", + "Shares Catalog": "كتالوج الأسهم" } diff --git a/translations/ca.json b/translations/ca.json index 3d57c4d21..ee2e3f9da 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -457,5 +457,6 @@ "food": "menjar", "Price": "Preu", "Currency": "Moneda", - "List of domains which can access the shared items catalog": "Llista de dominis que poden accedir al catàleg d'articles compartits" + "List of domains which can access the shared items catalog": "Llista de dominis que poden accedir al catàleg d'articles compartits", + "Shares Catalog": "Catàleg d'accions" } diff --git a/translations/cy.json b/translations/cy.json index c379bee01..e29cf72ba 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -457,5 +457,6 @@ "food": "bwyd", "Price": "Prisia", "Currency": "Harian", - "List of domains which can access the shared items catalog": "Rhestr o barthau a all gael mynediad i'r catalog eitemau a rennir" + "List of domains which can access the shared items catalog": "Rhestr o barthau a all gael mynediad i'r catalog eitemau a rennir", + "Shares Catalog": "Catalog Cyfranddaliadau" } diff --git a/translations/de.json b/translations/de.json index 9ea05fd44..f39e414eb 100644 --- a/translations/de.json +++ b/translations/de.json @@ -457,5 +457,6 @@ "food": "lebensmittel", "Price": "Preis", "Currency": "Währung", - "List of domains which can access the shared items catalog": "Liste der Domains, die auf den gemeinsam genutzten Artikelkatalog zugreifen können" + "List of domains which can access the shared items catalog": "Liste der Domains, die auf den gemeinsam genutzten Artikelkatalog zugreifen können", + "Shares Catalog": "Aktienkatalog" } diff --git a/translations/en.json b/translations/en.json index 7386bf508..0d3f5adbf 100644 --- a/translations/en.json +++ b/translations/en.json @@ -457,5 +457,6 @@ "food": "food", "Price": "Price", "Currency": "Currency", - "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog" + "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog", + "Shares Catalog": "Shares Catalog" } diff --git a/translations/es.json b/translations/es.json index 229cf6758..70ad0578f 100644 --- a/translations/es.json +++ b/translations/es.json @@ -457,5 +457,6 @@ "food": "comida", "Price": "Precio", "Currency": "Divisa", - "List of domains which can access the shared items catalog": "Lista de dominios que pueden acceder al catálogo de artículos compartidos" + "List of domains which can access the shared items catalog": "Lista de dominios que pueden acceder al catálogo de artículos compartidos", + "Shares Catalog": "Catálogo de acciones" } diff --git a/translations/fr.json b/translations/fr.json index 2bbfe4511..b4af5ad84 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -457,5 +457,6 @@ "food": "aliments", "Price": "Prix", "Currency": "Devise", - "List of domains which can access the shared items catalog": "Liste des domaines pouvant accéder au catalogue d'éléments partagés" + "List of domains which can access the shared items catalog": "Liste des domaines pouvant accéder au catalogue d'éléments partagés", + "Shares Catalog": "Actions Catalogue" } diff --git a/translations/ga.json b/translations/ga.json index 3b14b37a6..4a341b92f 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -457,5 +457,6 @@ "food": "bia", "Price": "Praghas a chur ar", "Currency": "Airgeadra", - "List of domains which can access the shared items catalog": "Liosta na bhfearann a fhéadann rochtain a fháil ar chatalóg na míreanna comhroinnte" + "List of domains which can access the shared items catalog": "Liosta na bhfearann a fhéadann rochtain a fháil ar chatalóg na míreanna comhroinnte", + "Shares Catalog": "Scaireanna Catalóg" } diff --git a/translations/hi.json b/translations/hi.json index ffa373c19..bfd29c814 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -457,5 +457,6 @@ "food": "खाना", "Price": "कीमत", "Currency": "मुद्रा", - "List of domains which can access the shared items catalog": "डोमेन की सूची जो साझा आइटम कैटलॉग तक पहुंच सकती है" + "List of domains which can access the shared items catalog": "डोमेन की सूची जो साझा आइटम कैटलॉग तक पहुंच सकती है", + "Shares Catalog": "शेयर कैटलॉग" } diff --git a/translations/it.json b/translations/it.json index 5753261da..b861b4281 100644 --- a/translations/it.json +++ b/translations/it.json @@ -457,5 +457,6 @@ "food": "cibo", "Price": "Prezzo", "Currency": "Moneta", - "List of domains which can access the shared items catalog": "Elenco dei domini che possono accedere al catalogo articoli condivisi" + "List of domains which can access the shared items catalog": "Elenco dei domini che possono accedere al catalogo articoli condivisi", + "Shares Catalog": "Condivide il catalogo" } diff --git a/translations/ja.json b/translations/ja.json index b2dbc0b83..62cb0305a 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -457,5 +457,6 @@ "food": "食物", "Price": "価格", "Currency": "通貨", - "List of domains which can access the shared items catalog": "共有項目カタログにアクセスできるドメインのリスト" + "List of domains which can access the shared items catalog": "共有項目カタログにアクセスできるドメインのリスト", + "Shares Catalog": "カタログを共有します" } diff --git a/translations/ku.json b/translations/ku.json index d96d7030c..e8cda6ddf 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -457,5 +457,6 @@ "food": "xûrek", "Price": "Biha", "Currency": "Diravcins", - "List of domains which can access the shared items catalog": "Navnîşa domên ku dikarin bigihîjin kataloga tiştên parvekirî" + "List of domains which can access the shared items catalog": "Navnîşa domên ku dikarin bigihîjin kataloga tiştên parvekirî", + "Shares Catalog": "Kataloga Shares" } diff --git a/translations/oc.json b/translations/oc.json index fd933af58..67721c396 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -453,5 +453,6 @@ "food": "food", "Price": "Price", "Currency": "Currency", - "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog" + "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog", + "Shares Catalog": "Shares Catalog" } diff --git a/translations/pt.json b/translations/pt.json index a933ab00d..b86c62fc6 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -457,5 +457,6 @@ "food": "comida", "Price": "Preço", "Currency": "Moeda", - "List of domains which can access the shared items catalog": "Lista de domínios que podem acessar o catálogo de itens compartilhados" + "List of domains which can access the shared items catalog": "Lista de domínios que podem acessar o catálogo de itens compartilhados", + "Shares Catalog": "Catálogo de ações" } diff --git a/translations/ru.json b/translations/ru.json index 8e2a8682d..d6b34e721 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -457,5 +457,6 @@ "food": "еда", "Price": "Цена", "Currency": "Валюта", - "List of domains which can access the shared items catalog": "Список доменов, которые могут получить доступ к каталогу общих пунктов" + "List of domains which can access the shared items catalog": "Список доменов, которые могут получить доступ к каталогу общих пунктов", + "Shares Catalog": "Акции каталог" } diff --git a/translations/sw.json b/translations/sw.json index 0b7f70bed..b69c642c2 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -457,5 +457,6 @@ "food": "chakula", "Price": "Bei", "Currency": "Fedha", - "List of domains which can access the shared items catalog": "Orodha ya Domains ambayo inaweza kufikia orodha ya vitu vya pamoja" + "List of domains which can access the shared items catalog": "Orodha ya Domains ambayo inaweza kufikia orodha ya vitu vya pamoja", + "Shares Catalog": "Inashiriki orodha" } diff --git a/translations/zh.json b/translations/zh.json index 92e635694..0711931a7 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -457,5 +457,6 @@ "food": "食物", "Price": "价钱", "Currency": "货币", - "List of domains which can access the shared items catalog": "可以访问共享项目目录的域名列表" + "List of domains which can access the shared items catalog": "可以访问共享项目目录的域名列表", + "Shares Catalog": "股票目录" } diff --git a/webapp_column_left.py b/webapp_column_left.py index e96bab04e..f804cfc4e 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -252,6 +252,9 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, if firstSeparatorAdded: htmlStr += separatorStr + htmlStr += \ + '

' htmlStr += \ '\n' return profileStr diff --git a/webapp_timeline.py b/webapp_timeline.py index 18ba0da1c..b05120f23 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -813,7 +813,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, return tlStr -def htmlIndividualShare(actor: str, item: {}, translate: {}, +def htmlIndividualShare(domain: str, shareId: str, + actor: str, item: {}, translate: {}, showContact: bool, removeButton: bool) -> str: """Returns an individual shared item as html """ @@ -854,9 +855,9 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, '?replydm=sharedesc:' + sharedesc + \ '?mention=' + contactActor + '">\n' - if removeButton: + if removeButton and domain in shareId: profileStr += \ - ' \n' profileStr += '
\n' @@ -898,7 +899,8 @@ def _htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int, if item['actor'] == actor: showRemoveButton = True timelineStr += \ - htmlIndividualShare(actor, item, translate, + htmlIndividualShare(domain, item['shareId'], + actor, item, translate, showContactButton, showRemoveButton) timelineStr += separatorStr ctr += 1 diff --git a/webapp_utils.py b/webapp_utils.py index 8573c8a15..0f1a2da73 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -357,6 +357,7 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, for itemID, item in sharesJson.items(): # assign owner to the item item['actor'] = owner + item['shareId'] = itemID allSharesJson[str(item['published'])] = item ctr += 1 if ctr >= maxSharesPerAccount: @@ -387,6 +388,7 @@ def sharesTimelineJson(actor: str, pageNumber: int, itemsPerPage: int, shareActor = shareActor.replace('___', '://') shareActor = shareActor.replace('--', '/') item['actor'] = shareActor + item['shareId'] = itemID allSharesJson[str(item['published'])] = item ctr += 1 if ctr >= maxSharesPerAccount: From 53f4d59069d3b40d2face7a5280d1924826e09a2 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 09:53:53 +0100 Subject: [PATCH 191/459] Add shareId --- shares.py | 1 + 1 file changed, 1 insertion(+) diff --git a/shares.py b/shares.py index 20f390071..f530da2bc 100644 --- a/shares.py +++ b/shares.py @@ -434,6 +434,7 @@ def getSharesFeedForPerson(baseDir: str, pageCtr += 1 totalCtr += 1 if currPage == pageNumber: + item['shareId'] = itemID shares['orderedItems'].append(item) if pageCtr >= sharesPerPage: pageCtr = 0 From 95129e2e585edf93a504ac95b52c3f1a71aefa48 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 09:58:12 +0100 Subject: [PATCH 192/459] Expire field --- shares.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index f530da2bc..805427afb 100644 --- a/shares.py +++ b/shares.py @@ -838,7 +838,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, if not re.match(matchPattern, description): continue - expireDate = datetime.datetime.fromtimestamp(item['durationSec']) + expireDate = datetime.datetime.fromtimestamp(item['expire']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") shareId = getValidSharedItemID(owner, item['displayName']) @@ -925,7 +925,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, continue expireDate = \ - datetime.datetime.fromtimestamp(item['durationSec']) + datetime.datetime.fromtimestamp(item['expire']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") shareId = getValidSharedItemID(owner, item['displayName']) From 64f63aa0b653b57cd5e920b6010af191ed2f51ba Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:35:21 +0100 Subject: [PATCH 193/459] Unit test for date conversion --- shares.py | 28 +++++++++++++--------------- tests.py | 13 ++++++++++++- utils.py | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/shares.py b/shares.py index 805427afb..ec76ce0e9 100644 --- a/shares.py +++ b/shares.py @@ -20,6 +20,8 @@ from posts import getPersonBox from session import postJson from session import postImage from session import createSession +from utils import dateStringToSeconds +from utils import dateSecondsToString from utils import getConfigParam from utils import getFullDomain from utils import validNickname @@ -924,10 +926,8 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, if not re.match(matchPattern, description): continue - expireDate = \ - datetime.datetime.fromtimestamp(item['expire']) - expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") - + startDateStr = dateSecondsToString(item['published']) + expireDateStr = dateSecondsToString(item['expire']) shareId = getValidSharedItemID(owner, item['displayName']) if item['dfcId'].startswith('epicyon#'): dfcId = "epicyon:" + item['dfcId'].split('#')[1] @@ -938,7 +938,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, "@id": shareId, "@type": "DFC:SuppliedProduct", "DFC:hasType": dfcId, - "DFC:startDate": item['published'], + "DFC:startDate": startDateStr, "DFC:expiryDate": expireDateStr, "DFC:quantity": item['itemQty'], "DFC:price": priceStr, @@ -1280,15 +1280,13 @@ def _dfcToSharesFormat(catalogJson: {}, if ':' not in item['DFC:hasType']: continue - try: - expiryTime = \ - datetime.datetime.strptime(item['DFC:expiryDate'], - '%Y-%m-%dT%H:%M:%SZ') - except BaseException: + startTimeSec = dateStringToSeconds(item['DFC:startDate']) + if not startTimeSec: continue - durationSec = \ - int((expiryTime - datetime.datetime(1970, 1, 1)).total_seconds()) - if durationSec < currTime: + expiryTimeSec = dateStringToSeconds(item['DFC:expiryDate']) + if not expiryTimeSec: + continue + if expiryTimeSec < currTime: # has expired continue @@ -1314,8 +1312,8 @@ def _dfcToSharesFormat(catalogJson: {}, "itemType": itemType, "category": itemCategory, "location": "", - "published": item['DFC:startDate'], - "expire": durationSec, + "published": startTimeSec, + "expire": expiryTimeSec, "itemPrice": item['DFC:price'].split(' ')[0], "itemCurrency": item['DFC:price'].split(' ')[1] } diff --git a/tests.py b/tests.py index 25ac98858..313db4994 100644 --- a/tests.py +++ b/tests.py @@ -39,6 +39,8 @@ from follow import clearFollowers from follow import sendFollowRequestViaServer from follow import sendUnfollowRequestViaServer from siteactive import siteIsActive +from utils import dateStringToSeconds +from utils import dateSecondsToString from utils import validPassword from utils import userAgentDomain from utils import camelCaseSplit @@ -4318,9 +4320,19 @@ def _testAuthorizeSharedItems(): assert len(tokensJson['possum.domain']) == 0 +def _testDateConversions() -> None: + print('testDateConversions') + dateStr = "2021-05-16T14:37:41Z" + dateSec = dateStringToSeconds(dateStr) + dateStr2 = dateSecondsToString(dateSec) + assert dateStr == dateStr2 + + def runAllTests(): print('Running tests...') updateDefaultThemesList(os.getcwd()) + _testFunctions() + _testDateConversions() _testAuthorizeSharedItems() _testValidPassword() _testGetLinksFromContent() @@ -4328,7 +4340,6 @@ def runAllTests(): _testLimitRepetedWords() _testLimitWordLengths() _testSwitchWords() - _testFunctions() _testUserAgentDomain() _testRoles() _testSkills() diff --git a/utils.py b/utils.py index b11334770..1bba33098 100644 --- a/utils.py +++ b/utils.py @@ -2634,3 +2634,21 @@ def isfloat(value): return True except ValueError: return False + + +def dateStringToSeconds(dateStr: str) -> int: + """Converts a date string (eg "published") into seconds since epoch + """ + try: + expiryTime = \ + datetime.datetime.strptime(dateStr, '%Y-%m-%dT%H:%M:%SZ') + except BaseException: + return None + return int(datetime.datetime.timestamp(expiryTime)) + + +def dateSecondsToString(dateSec: int) -> str: + """Converts a date in seconds since epoch to a string + """ + thisDate = datetime.datetime.fromtimestamp(dateSec) + return thisDate.strftime("%Y-%m-%dT%H:%M:%SZ") From 5c9d61f68e5e64be6844f8408a23b52fcbb6936f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:37:29 +0100 Subject: [PATCH 194/459] Quantity as string --- shares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shares.py b/shares.py index ec76ce0e9..f3f170cd6 100644 --- a/shares.py +++ b/shares.py @@ -970,7 +970,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += item['DFC:hasType'] + ',' csvStr += item['DFC:startDate'] + ',' csvStr += item['DFC:expiryDate'] + ',' - csvStr += item['DFC:quantity'] + ',' + csvStr += str(item['DFC:quantity']) + ',' csvStr += item['DFC:price'] + ',' csvStr += item['DFC:Image'] + ',' csvStr += item['DFC:description'] + '\n' From ec6820dd9aaa0b5445c2f009982da385fe5eb280 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:44:19 +0100 Subject: [PATCH 195/459] Item quantity as float --- daemon.py | 4 ++-- epicyon.py | 2 +- shares.py | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/daemon.py b/daemon.py index f07f65528..e580703cf 100644 --- a/daemon.py +++ b/daemon.py @@ -13821,8 +13821,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain) itemQty = 1 if fields['itemQty']: - if fields['itemQty'].isdigit(): - itemQty = int(fields['itemQty']) + if isfloat(fields['itemQty']): + itemQty = float(fields['itemQty']) itemPrice = "0.00" if fields['itemPrice']: if isfloat(fields['itemPrice']): diff --git a/epicyon.py b/epicyon.py index 91a4b36b4..b7b9d3bfd 100644 --- a/epicyon.py +++ b/epicyon.py @@ -562,7 +562,7 @@ parser.add_argument('--summary', dest='summary', type=str, parser.add_argument('--itemImage', dest='itemImage', type=str, default=None, help='Filename of an image for an item being shared') -parser.add_argument('--itemQty', dest='itemQty', type=int, +parser.add_argument('--itemQty', dest='itemQty', type=float, default=1, help='Quantity of items being shared') parser.add_argument('--itemPrice', dest='itemPrice', type=str, diff --git a/shares.py b/shares.py index f3f170cd6..b8a6f501d 100644 --- a/shares.py +++ b/shares.py @@ -235,7 +235,7 @@ def _indicateNewShareAvailable(baseDir: str, httpPrefix: str, def addShare(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, displayName: str, summary: str, imageFilename: str, - itemQty: int, itemType: str, itemCategory: str, location: str, + itemQty: float, itemType: str, itemCategory: str, location: str, duration: str, debug: bool, city: str, price: str, currency: str, systemLanguage: str, translate: {}) -> None: @@ -295,7 +295,7 @@ def addShare(baseDir: str, "displayName": displayName, "summary": summary, "imageUrl": imageUrl, - "itemQty": itemQty, + "itemQty": float(itemQty), "dfcId": dfcId, "itemType": itemType, "category": itemCategory, @@ -457,7 +457,7 @@ def sendShareViaServer(baseDir, session, fromDomain: str, fromPort: int, httpPrefix: str, displayName: str, summary: str, imageFilename: str, - itemQty: int, itemType: str, itemCategory: str, + itemQty: float, itemType: str, itemCategory: str, location: str, duration: str, cachedWebfingers: {}, personCache: {}, debug: bool, projectVersion: str, @@ -484,7 +484,7 @@ def sendShareViaServer(baseDir, session, "type": "Offer", "displayName": displayName, "summary": summary, - "itemQty": itemQty, + "itemQty": float(itemQty), "itemType": itemType, "category": itemCategory, "location": location, @@ -711,7 +711,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['displayName'], messageJson['object']['summary'], messageJson['object']['imageFilename'], - messageJson['object']['itemQty'], + float(messageJson['object']['itemQty']), messageJson['object']['itemType'], messageJson['object']['itemCategory'], messageJson['object']['location'], @@ -855,7 +855,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, "DFC:hasType": dfcId, "DFC:startDate": item['published'], "DFC:expiryDate": expireDateStr, - "DFC:quantity": item['itemQty'], + "DFC:quantity": float(item['itemQty']), "DFC:price": priceStr, "DFC:Image": item['imageUrl'], "DFC:description": description @@ -940,7 +940,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, "DFC:hasType": dfcId, "DFC:startDate": startDateStr, "DFC:expiryDate": expireDateStr, - "DFC:quantity": item['itemQty'], + "DFC:quantity": float(item['itemQty']), "DFC:price": priceStr, "DFC:Image": item['imageUrl'], "DFC:description": description @@ -1307,7 +1307,7 @@ def _dfcToSharesFormat(catalogJson: {}, "displayName": item['DFC:description'].split(':')[0], "summary": description, "imageUrl": item['DFC:Image'], - "itemQty": item['DFC:quantity'], + "itemQty": float(item['DFC:quantity']), "dfcId": dfcId, "itemType": itemType, "category": itemCategory, From fbb9205e6e4e41afba10c4a662d46c92d86a1033 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:49:14 +0100 Subject: [PATCH 196/459] Quotes around strings --- shares.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shares.py b/shares.py index b8a6f501d..352b2d3c4 100644 --- a/shares.py +++ b/shares.py @@ -965,15 +965,15 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, 'id,type,hasType,startDate,expiryDate,' + \ 'quantity,price,Image,description\n' for item in catalogJson['DFC:supplies']: - csvStr += item['@id'] + ',' - csvStr += item['@type'] + ',' - csvStr += item['DFC:hasType'] + ',' - csvStr += item['DFC:startDate'] + ',' - csvStr += item['DFC:expiryDate'] + ',' + csvStr += '"' + item['@id'] + '",' + csvStr += '"' + item['@type'] + '",' + csvStr += '"' + item['DFC:hasType'] + '",' + csvStr += '"' + item['DFC:startDate'] + '",' + csvStr += '"' + item['DFC:expiryDate'] + '",' csvStr += str(item['DFC:quantity']) + ',' csvStr += item['DFC:price'] + ',' - csvStr += item['DFC:Image'] + ',' - csvStr += item['DFC:description'] + '\n' + csvStr += '"' + item['DFC:Image'] + '",' + csvStr += '"' + item['DFC:description'] + '"\n' return csvStr From 9130e5ce616dd7bdae62e97c4ce954c2a62baa84 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:50:27 +0100 Subject: [PATCH 197/459] Convert quotes for csv --- shares.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shares.py b/shares.py index 352b2d3c4..8f29c7593 100644 --- a/shares.py +++ b/shares.py @@ -973,7 +973,8 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += str(item['DFC:quantity']) + ',' csvStr += item['DFC:price'] + ',' csvStr += '"' + item['DFC:Image'] + '",' - csvStr += '"' + item['DFC:description'] + '"\n' + description = item['DFC:description'].replace('"', "'") + csvStr += '"' + description + '"\n' return csvStr From 9711818da985bc9523cc7cc0b4dcaca15c902002 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:52:13 +0100 Subject: [PATCH 198/459] CRLF ending for csv --- shares.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index 8f29c7593..f217eb0f9 100644 --- a/shares.py +++ b/shares.py @@ -963,7 +963,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, return '' csvStr = \ 'id,type,hasType,startDate,expiryDate,' + \ - 'quantity,price,Image,description\n' + 'quantity,price,Image,description\r\n' for item in catalogJson['DFC:supplies']: csvStr += '"' + item['@id'] + '",' csvStr += '"' + item['@type'] + '",' @@ -974,7 +974,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += item['DFC:price'] + ',' csvStr += '"' + item['DFC:Image'] + '",' description = item['DFC:description'].replace('"', "'") - csvStr += '"' + description + '"\n' + csvStr += '"' + description + '",\r\n' return csvStr From 8cd539fe8f238193c1ed447d7af165793709ef66 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:53:07 +0100 Subject: [PATCH 199/459] Extra comma --- shares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shares.py b/shares.py index f217eb0f9..01135fd54 100644 --- a/shares.py +++ b/shares.py @@ -963,7 +963,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, return '' csvStr = \ 'id,type,hasType,startDate,expiryDate,' + \ - 'quantity,price,Image,description\r\n' + 'quantity,price,Image,description,\r\n' for item in catalogJson['DFC:supplies']: csvStr += '"' + item['@id'] + '",' csvStr += '"' + item['@type'] + '",' From ef606161a198bb4ef14e0799f44a0671b0905d7b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 10:56:28 +0100 Subject: [PATCH 200/459] Separate price and currency --- shares.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index 01135fd54..16427d0a2 100644 --- a/shares.py +++ b/shares.py @@ -963,7 +963,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, return '' csvStr = \ 'id,type,hasType,startDate,expiryDate,' + \ - 'quantity,price,Image,description,\r\n' + 'quantity,price,currency,Image,description,\r\n' for item in catalogJson['DFC:supplies']: csvStr += '"' + item['@id'] + '",' csvStr += '"' + item['@type'] + '",' @@ -971,7 +971,8 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += '"' + item['DFC:startDate'] + '",' csvStr += '"' + item['DFC:expiryDate'] + '",' csvStr += str(item['DFC:quantity']) + ',' - csvStr += item['DFC:price'] + ',' + csvStr += item['DFC:price'].split(' ')[0] + ',' + csvStr += '"' + item['DFC:price'].split(' ')[1] + '",' csvStr += '"' + item['DFC:Image'] + '",' description = item['DFC:description'].replace('"', "'") csvStr += '"' + description + '",\r\n' From 35a1a3ecc9b76a69c34ac6ea495ef6dbf6ee193b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 11:04:47 +0100 Subject: [PATCH 201/459] csv encoding --- daemon.py | 3 +-- shares.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/daemon.py b/daemon.py index e580703cf..cb9096a15 100644 --- a/daemon.py +++ b/daemon.py @@ -10800,8 +10800,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix, self.server.domainFull, self.path) - msg = json.dumps(catalogStr, - ensure_ascii=False).encode('utf-8') + msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/csv', msglen, None, callingDomain) diff --git a/shares.py b/shares.py index 16427d0a2..f4abd6096 100644 --- a/shares.py +++ b/shares.py @@ -963,7 +963,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, return '' csvStr = \ 'id,type,hasType,startDate,expiryDate,' + \ - 'quantity,price,currency,Image,description,\r\n' + 'quantity,price,currency,Image,description,\n' for item in catalogJson['DFC:supplies']: csvStr += '"' + item['@id'] + '",' csvStr += '"' + item['@type'] + '",' @@ -975,7 +975,7 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += '"' + item['DFC:price'].split(' ')[1] + '",' csvStr += '"' + item['DFC:Image'] + '",' description = item['DFC:description'].replace('"', "'") - csvStr += '"' + description + '",\r\n' + csvStr += '"' + description + '",\n' return csvStr From fd092bd721b8a16a651c4e039f7be92a5ee2f199 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 11:06:37 +0100 Subject: [PATCH 202/459] Tidying --- daemon.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index cb9096a15..12337a5a9 100644 --- a/daemon.py +++ b/daemon.py @@ -10795,12 +10795,11 @@ class PubServer(BaseHTTPRequestHandler): return elif catalogType == 'csv': # catalog as a CSV file for import into a spreadsheet - catalogStr = \ + msg = \ sharesCatalogCSVEndpoint(self.server.baseDir, self.server.httpPrefix, self.server.domainFull, - self.path) - msg = msg.encode('utf-8') + self.path).encode('utf-8') msglen = len(msg) self._set_headers('text/csv', msglen, None, callingDomain) From 74a0959876384ddc3368d3d0181212a8529d4e41 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 12:03:22 +0100 Subject: [PATCH 203/459] Missing timeline ending --- webapp_timeline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp_timeline.py b/webapp_timeline.py index b05120f23..e65cc9217 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -659,6 +659,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, baseDir, actor, nickname, domain, port, maxSharesPerAccount, httpPrefix, sharedItemsFederatedDomains) + + ' \n' + ' \n' + + ' \n' + ' \n' + ' \n' + htmlFooter()) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7') From 2eec26aeaa1b370e8fbfc86521965c9027e1ccde Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 12:26:03 +0100 Subject: [PATCH 204/459] Right column on shares timeline --- shares.py | 3 +- webapp_timeline.py | 88 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/shares.py b/shares.py index f4abd6096..42d4b0306 100644 --- a/shares.py +++ b/shares.py @@ -706,12 +706,13 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, if debug: print('DEBUG: duration missing from Offer') return + itemQty = float(messageJson['object']['itemQty']) addShare(baseDir, httpPrefix, nickname, domain, port, messageJson['object']['displayName'], messageJson['object']['summary'], messageJson['object']['imageFilename'], - float(messageJson['object']['itemQty']), + itemQty, messageJson['object']['itemType'], messageJson['object']['itemCategory'], messageJson['object']['location'], diff --git a/webapp_timeline.py b/webapp_timeline.py index e65cc9217..d576376a7 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -333,6 +333,46 @@ def _htmlTimelineKeyboard(moderator: bool, textModeBanner: str, usersPath: str, None, usersPath, translate, followApprovals) +def _htmlTimelineEnd(baseDir: str, nickname: str, domainFull: str, + httpPrefix: str, translate: {}, + moderator: bool, editor: bool, + newswire: {}, positiveVoting: bool, + showPublishAsIcon: bool, + rssIconAtTop: bool, publishButtonAtTop: bool, + authorized: bool, theme: str, + defaultTimeline: str, accessKeys: {}, + boxName: str, + enableTimingLog: bool, timelineStartTime) -> str: + """Ending of the timeline, containing the right column + """ + # end of timeline-posts + tlStr = ' \n' + + # end of column-center + tlStr += ' \n' + + # right column + rightColumnStr = getRightColumnContent(baseDir, nickname, domainFull, + httpPrefix, translate, + moderator, editor, + newswire, positiveVoting, + False, None, True, + showPublishAsIcon, + rssIconAtTop, publishButtonAtTop, + authorized, True, theme, + defaultTimeline, accessKeys) + tlStr += ' ' + \ + rightColumnStr + ' \n' + tlStr += ' \n' + + _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '9') + + tlStr += ' \n' + tlStr += '\n' + return tlStr + + def htmlTimeline(cssCache: {}, defaultTimeline: str, recentPostsCache: {}, maxRecentPosts: int, translate: {}, pageNumber: int, @@ -659,8 +699,16 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, baseDir, actor, nickname, domain, port, maxSharesPerAccount, httpPrefix, sharedItemsFederatedDomains) + - ' \n' + ' \n' + - ' \n' + ' \n' + ' \n' + + _htmlTimelineEnd(baseDir, nickname, domainFull, + httpPrefix, translate, + moderator, editor, + newswire, positiveVoting, + showPublishAsIcon, + rssIconAtTop, publishButtonAtTop, + authorized, theme, + defaultTimeline, accessKeys, + boxName, + enableTimingLog, timelineStartTime) + htmlFooter()) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7') @@ -786,31 +834,17 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, elif itemCtr == 0: tlStr += _getHelpForTimeline(baseDir, boxName) - # end of timeline-posts - tlStr += ' \n' - - # end of column-center - tlStr += ' \n' - - # right column - rightColumnStr = getRightColumnContent(baseDir, nickname, domainFull, - httpPrefix, translate, - moderator, editor, - newswire, positiveVoting, - False, None, True, - showPublishAsIcon, - rssIconAtTop, publishButtonAtTop, - authorized, True, theme, - defaultTimeline, accessKeys) - tlStr += ' ' + \ - rightColumnStr + ' \n' - tlStr += ' \n' - - _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '9') - - tlStr += ' \n' - tlStr += '\n' + tlStr += \ + _htmlTimelineEnd(baseDir, nickname, domainFull, + httpPrefix, translate, + moderator, editor, + newswire, positiveVoting, + showPublishAsIcon, + rssIconAtTop, publishButtonAtTop, + authorized, theme, + defaultTimeline, accessKeys, + boxName, + enableTimingLog, timelineStartTime) tlStr += htmlFooter() return tlStr From 5e33821b418c46f978d7b5afe391fae83246a6af Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 13:08:19 +0100 Subject: [PATCH 205/459] Single function defining users paths --- follow.py | 3 ++- inbox.py | 2 +- utils.py | 11 +++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/follow.py b/follow.py index e0e3a6e33..77b5b98a9 100644 --- a/follow.py +++ b/follow.py @@ -28,6 +28,7 @@ from utils import saveJson from utils import isAccountDir from utils import getUserPaths from utils import acctDir +from utils import getUsersPaths from acceptreject import createAccept from acceptreject import createReject from webfinger import webfingerHandle @@ -108,7 +109,7 @@ def _removeFromFollowBase(baseDir: str, acceptDenyDomain = acceptOrDenyHandle.split('@')[1] # for each possible users path construct an actor and # check if it exists in teh file - usersPaths = ('users', 'profile', 'channel', 'accounts', 'u') + usersPaths = getUsersPaths() actorFound = False for usersName in usersPaths: acceptDenyActor = \ diff --git a/inbox.py b/inbox.py index 4fcdd4212..359a1300f 100644 --- a/inbox.py +++ b/inbox.py @@ -749,7 +749,7 @@ def _personReceiveUpdate(baseDir: str, ' ' + str(personJson)) domainFull = getFullDomain(domain, port) updateDomainFull = getFullDomain(updateDomain, updatePort) - usersPaths = ('users', 'profile', 'channel', 'accounts', 'u') + usersPaths = ('users', 'profile', 'channel', 'accounts', 'u', 'c') usersStrFound = False for usersStr in usersPaths: actor = updateDomainFull + '/' + usersStr + '/' + updateNickname diff --git a/utils.py b/utils.py index 1bba33098..5efba0a08 100644 --- a/utils.py +++ b/utils.py @@ -28,6 +28,13 @@ invalidCharacters = ( ) +def getUsersPaths() -> []: + """Returns the possible paths for users accounts + e.g. /users/nickname, /channel/nickname + """ + return ('users', 'profile', 'channel', 'accounts', 'u', 'c') + + def getActorLanguagesList(actorJson: {}) -> []: """Returns a list containing languages used by the given actor """ @@ -159,7 +166,7 @@ def getLockedAccount(actorJson: {}) -> bool: def hasUsersPath(pathStr: str) -> bool: """Whether there is a /users/ path (or equivalent) in the given string """ - usersList = ('users', 'accounts', 'channel', 'profile', 'u') + usersList = getUsersPaths() for usersStr in usersList: if '/' + usersStr + '/' in pathStr: return True @@ -1579,7 +1586,7 @@ def _isReservedName(nickname: str) -> bool: 'reply', 'replies', 'question', 'like', 'likes', 'users', 'statuses', 'tags', 'accounts', 'headers', - 'channels', 'profile', 'u', + 'channels', 'profile', 'u', 'c', 'updates', 'repeat', 'announce', 'shares', 'fonts', 'icons', 'avatars', 'welcome', 'helpimages', From 4e30a17d8378df040f98836a80573fc5c4cffed6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 13:37:26 +0100 Subject: [PATCH 206/459] Detecting the users path when getting actor --- person.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/person.py b/person.py index 24ad0538e..631047818 100644 --- a/person.py +++ b/person.py @@ -53,6 +53,7 @@ from utils import getImageExtensions from utils import isImageFile from utils import getUserPaths from utils import acctDir +from utils import getUsersPaths from session import createSession from session import getJson from webfinger import webfingerHandle @@ -1188,6 +1189,18 @@ def setPersonNotes(baseDir: str, nickname: str, domain: str, return True +def _detectUsersPath(url: str) -> str: + """Tries to detect the /users/ path + """ + if '/' not in url: + return '/users/' + usersPaths = getUsersPaths() + for possibleUsersPath in usersPaths: + if '/' + possibleUsersPath + '/' in url: + return '/' + possibleUsersPath + '/' + return '/users/' + + def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool, debug: bool, quiet: bool = False) -> ({}, {}): """Returns the actor json @@ -1195,8 +1208,11 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool, if debug: print('getActorJson for ' + handle) originalActor = handle + + # try to determine the users path + detectedUsersPath = _detectUsersPath(handle) if '/@' in handle or \ - '/users/' in handle or \ + detectedUsersPath in handle or \ handle.startswith('http') or \ handle.startswith('hyper'): # format: https://domain/@nick @@ -1204,12 +1220,13 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool, if not hasUsersPath(originalHandle): if not quiet or debug: print('getActorJson: Expected actor format: ' + - 'https://domain/@nick or https://domain/users/nick') + 'https://domain/@nick or https://domain' + + detectedUsersPath + 'nick') return None, None prefixes = getProtocolPrefixes() for prefix in prefixes: handle = handle.replace(prefix, '') - handle = handle.replace('/@', '/users/') + handle = handle.replace('/@', detectedUsersPath) paths = getUserPaths() userPathFound = False for userPath in paths: From 3b94048e2805537c8e5dfcc37beca37201ca5bca Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 13:49:02 +0100 Subject: [PATCH 207/459] Better support for alternative users paths --- follow.py | 6 ++---- person.py | 9 ++++----- utils.py | 14 ++++---------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/follow.py b/follow.py index 77b5b98a9..acae18889 100644 --- a/follow.py +++ b/follow.py @@ -28,7 +28,6 @@ from utils import saveJson from utils import isAccountDir from utils import getUserPaths from utils import acctDir -from utils import getUsersPaths from acceptreject import createAccept from acceptreject import createReject from webfinger import webfingerHandle @@ -109,12 +108,11 @@ def _removeFromFollowBase(baseDir: str, acceptDenyDomain = acceptOrDenyHandle.split('@')[1] # for each possible users path construct an actor and # check if it exists in teh file - usersPaths = getUsersPaths() + usersPaths = getUserPaths() actorFound = False for usersName in usersPaths: acceptDenyActor = \ - '://' + acceptDenyDomain + '/' + \ - usersName + '/' + acceptDenyNickname + '://' + acceptDenyDomain + usersName + acceptDenyNickname if acceptDenyActor in open(approveFollowsFilename).read(): actorFound = True break diff --git a/person.py b/person.py index 631047818..2b9c4cadd 100644 --- a/person.py +++ b/person.py @@ -51,9 +51,8 @@ from utils import getProtocolPrefixes from utils import hasUsersPath from utils import getImageExtensions from utils import isImageFile -from utils import getUserPaths from utils import acctDir -from utils import getUsersPaths +from utils import getUserPaths from session import createSession from session import getJson from webfinger import webfingerHandle @@ -1194,10 +1193,10 @@ def _detectUsersPath(url: str) -> str: """ if '/' not in url: return '/users/' - usersPaths = getUsersPaths() + usersPaths = getUserPaths() for possibleUsersPath in usersPaths: - if '/' + possibleUsersPath + '/' in url: - return '/' + possibleUsersPath + '/' + if possibleUsersPath in url: + return possibleUsersPath return '/users/' diff --git a/utils.py b/utils.py index 5efba0a08..1d716ade5 100644 --- a/utils.py +++ b/utils.py @@ -28,13 +28,6 @@ invalidCharacters = ( ) -def getUsersPaths() -> []: - """Returns the possible paths for users accounts - e.g. /users/nickname, /channel/nickname - """ - return ('users', 'profile', 'channel', 'accounts', 'u', 'c') - - def getActorLanguagesList(actorJson: {}) -> []: """Returns a list containing languages used by the given actor """ @@ -166,9 +159,9 @@ def getLockedAccount(actorJson: {}) -> bool: def hasUsersPath(pathStr: str) -> bool: """Whether there is a /users/ path (or equivalent) in the given string """ - usersList = getUsersPaths() + usersList = getUserPaths() for usersStr in usersList: - if '/' + usersStr + '/' in pathStr: + if usersStr in pathStr: return True if '://' in pathStr: domain = pathStr.split('://')[1] @@ -981,8 +974,9 @@ def getNicknameFromActor(actor: str) -> str: def getUserPaths() -> []: """Returns possible user paths + e.g. /users/nickname, /channel/nickname """ - return ('/users/', '/profile/', '/accounts/', '/channel/', '/u/') + return ('/users/', '/profile/', '/accounts/', '/channel/', '/u/', '/c/') def getDomainFromActor(actor: str) -> (str, int): From 35f6921d7e55ed7984eca2c4a2a4dc9294faf6ff Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 14:55:55 +0100 Subject: [PATCH 208/459] Don't attempt to follow if the actor has no followers collection --- webapp_profile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp_profile.py b/webapp_profile.py index 30a66a0d2..b14f32c66 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -202,7 +202,10 @@ def htmlProfileAfterSearch(cssCache: {}, domainFull = getFullDomain(domain, port) followIsPermitted = True - if searchNickname == 'news' and searchDomainFull == domainFull: + if not profileJson.get('followers'): + # no followers collection specified within actor + followIsPermitted = False + elif searchNickname == 'news' and searchDomainFull == domainFull: # currently the news actor is not something you can follow followIsPermitted = False elif searchNickname == nickname and searchDomainFull == domainFull: From 19ffc6d0b4cdcae3749662d9b53647ce7821e008 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 14:58:59 +0100 Subject: [PATCH 209/459] Can still view even if cannot follow --- webapp_profile.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index b14f32c66..015e5fe42 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -222,13 +222,15 @@ def htmlProfileAfterSearch(cssCache: {}, personUrl + '">\n' + \ ' \n' + \ - ' \n' + \ - '
\n' + \ - ' \n' + \ - '\n' + translate['Follow'] + '\n' + + profileStr += \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + '\n' i = 0 for item in parseUserFeed(session, outboxUrl, asHeader, From edeb8f99608b8f757dd072ebab5eab29d9412221 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 15:02:07 +0100 Subject: [PATCH 210/459] View only style --- webapp_profile.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index 015e5fe42..ff4f9a564 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -222,15 +222,27 @@ def htmlProfileAfterSearch(cssCache: {}, personUrl + '">\n' + \ ' \n' - - profileStr += \ - ' \n' + \ - ' \n' + \ - ' \n' + \ - '\n' + translate['Follow'] + '\n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + '\n' + else: + profileStr += \ + '
\n' + \ + '
\n' + \ + '
\n' + \ + ' \n' + \ + ' \n' + \ + '
\n' + \ + '
\n' + \ + '
\n' i = 0 for item in parseUserFeed(session, outboxUrl, asHeader, From 4972c68773c586328c8f532c68754fecc56782c8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 15:15:54 +0100 Subject: [PATCH 211/459] Debug --- daemon.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/daemon.py b/daemon.py index 12337a5a9..45a27dfee 100644 --- a/daemon.py +++ b/daemon.py @@ -11256,18 +11256,22 @@ class PubServer(BaseHTTPRequestHandler): # remove a shared item if htmlGET and '?rmshare=' in self.path: + print('Debug rmshare: 1: ' + self.path) shareName = self.path.split('?rmshare=')[1] shareName = urllib.parse.unquote_plus(shareName.strip()) + print('Debug rmshare: 2: ' + shareName) usersPath = self.path.split('?rmshare=')[0] actor = \ self.server.httpPrefix + '://' + \ self.server.domainFull + usersPath + print('Debug rmshare: 3: ' + actor) msg = htmlConfirmRemoveSharedItem(self.server.cssCache, self.server.translate, self.server.baseDir, actor, shareName, callingDomain) if not msg: + print('Debug rmshare: no msg ' + actor) if callingDomain.endswith('.onion') and \ self.server.onionDomain: actor = 'http://' + self.server.onionDomain + usersPath From c41aacba6f94196018b8282054e79c379bfb9c6c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 15:22:06 +0100 Subject: [PATCH 212/459] Removing share via its itemID --- daemon.py | 4 ---- webapp_confirm.py | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/daemon.py b/daemon.py index 45a27dfee..12337a5a9 100644 --- a/daemon.py +++ b/daemon.py @@ -11256,22 +11256,18 @@ class PubServer(BaseHTTPRequestHandler): # remove a shared item if htmlGET and '?rmshare=' in self.path: - print('Debug rmshare: 1: ' + self.path) shareName = self.path.split('?rmshare=')[1] shareName = urllib.parse.unquote_plus(shareName.strip()) - print('Debug rmshare: 2: ' + shareName) usersPath = self.path.split('?rmshare=')[0] actor = \ self.server.httpPrefix + '://' + \ self.server.domainFull + usersPath - print('Debug rmshare: 3: ' + actor) msg = htmlConfirmRemoveSharedItem(self.server.cssCache, self.server.translate, self.server.baseDir, actor, shareName, callingDomain) if not msg: - print('Debug rmshare: no msg ' + actor) if callingDomain.endswith('.onion') and \ self.server.onionDomain: actor = 'http://' + self.server.onionDomain + usersPath diff --git a/webapp_confirm.py b/webapp_confirm.py index c7f34bbe6..cdd51b1e1 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -104,11 +104,10 @@ def htmlConfirmDelete(cssCache: {}, def htmlConfirmRemoveSharedItem(cssCache: {}, translate: {}, baseDir: str, - actor: str, shareName: str, + actor: str, itemID: str, callingDomain: str) -> str: """Shows a screen asking to confirm the removal of a shared item """ - itemID = getValidSharedItemID(actor, shareName) nickname = getNicknameFromActor(actor) domain, port = getDomainFromActor(actor) domainFull = getFullDomain(domain, port) @@ -153,7 +152,7 @@ def htmlConfirmRemoveSharedItem(cssCache: {}, translate: {}, baseDir: str, sharesStr += \ ' \n' sharesStr += ' \n' + itemID + '">\n' sharesStr += \ ' \n' From a5a6f2da892e17ac808da3eff529f54a9d43a4dc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 15:27:20 +0100 Subject: [PATCH 213/459] Removing a shared item --- daemon.py | 14 +++++++------- shares.py | 3 +-- webapp_confirm.py | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/daemon.py b/daemon.py index 12337a5a9..59fa85c7f 100644 --- a/daemon.py +++ b/daemon.py @@ -3351,14 +3351,14 @@ class PubServer(BaseHTTPRequestHandler): shareActor = removeShareConfirmParams.split('actor=')[1] if '&' in shareActor: shareActor = shareActor.split('&')[0] - shareName = removeShareConfirmParams.split('shareName=')[1] - if '&' in shareName: - shareName = shareName.split('&')[0] + itemID = removeShareConfirmParams.split('itemID=')[1] + if '&' in itemID: + itemID = itemID.split('&')[0] shareNickname = getNicknameFromActor(shareActor) if shareNickname: shareDomain, sharePort = getDomainFromActor(shareActor) removeSharedItem(baseDir, - shareNickname, shareDomain, shareName, + shareNickname, shareDomain, itemID, httpPrefix, domainFull) if callingDomain.endswith('.onion') and onionDomain: @@ -11256,8 +11256,8 @@ class PubServer(BaseHTTPRequestHandler): # remove a shared item if htmlGET and '?rmshare=' in self.path: - shareName = self.path.split('?rmshare=')[1] - shareName = urllib.parse.unquote_plus(shareName.strip()) + itemID = self.path.split('?rmshare=')[1] + itemID = urllib.parse.unquote_plus(itemID.strip()) usersPath = self.path.split('?rmshare=')[0] actor = \ self.server.httpPrefix + '://' + \ @@ -11265,7 +11265,7 @@ class PubServer(BaseHTTPRequestHandler): msg = htmlConfirmRemoveSharedItem(self.server.cssCache, self.server.translate, self.server.baseDir, - actor, shareName, + actor, itemID, callingDomain) if not msg: if callingDomain.endswith('.onion') and \ diff --git a/shares.py b/shares.py index 42d4b0306..c7bb5412f 100644 --- a/shares.py +++ b/shares.py @@ -99,7 +99,7 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: def removeSharedItem(baseDir: str, nickname: str, domain: str, - displayName: str, + itemID: str, httpPrefix: str, domainFull: str) -> None: """Removes a share for a person """ @@ -114,7 +114,6 @@ def removeSharedItem(baseDir: str, nickname: str, domain: str, return actor = httpPrefix + '://' + domainFull + '/users/' + nickname - itemID = getValidSharedItemID(actor, displayName) if sharesJson.get(itemID): # remove any image for the item itemIDfile = baseDir + '/sharefiles/' + nickname + '/' + itemID diff --git a/webapp_confirm.py b/webapp_confirm.py index cdd51b1e1..fc9a15f9c 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -151,7 +151,7 @@ def htmlConfirmRemoveSharedItem(cssCache: {}, translate: {}, baseDir: str, sharesStr += '
\n' sharesStr += \ ' \n' - sharesStr += ' \n' sharesStr += \ ' \n' - if actor.endswith('/users/' + contactNickname): - sharedItemsForm += \ - ' \n' - sharedItemsForm += '

\n' - return sharedItemsForm - - def _htmlSearchResultSharePage(actor: str, domainFull: str, callingDomain: str, pageNumber: int, searchStrLower: str, translate: {}, @@ -227,10 +173,10 @@ def _htmlSharesResult(sharesJson: {}, pageNumber: int, resultsPerPage: int, if currPage == pageNumber: # show individual search result sharedItemsForm += \ - _htmlSearchResultShare(sharedItem, translate, - httpPrefix, domainFull, - contactNickname, - name, actor) + htmlSearchResultShare(sharedItem, translate, + httpPrefix, domainFull, + contactNickname, + name, actor) if not resultsExist and currPage > 1: # show the previous page button sharedItemsForm += \ diff --git a/webapp_utils.py b/webapp_utils.py index 0f1a2da73..b3457de1f 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -18,6 +18,8 @@ from utils import loadJson from utils import getCachedPostFilename from utils import getConfigParam from utils import acctDir +from utils import getNicknameFromActor +from utils import isfloat from cache import storePersonInCache from content import addHtmlTags from content import replaceEmojiFromTags @@ -1249,3 +1251,120 @@ def editTextArea(label: str, name: str, value: str = "", text += 'spellcheck="' + str(spellcheck).lower() + '">' text += value + '\n' return text + + +def htmlSearchResultShare(sharedItem: {}, translate: {}, + httpPrefix: str, domainFull: str, + contactNickname: str, name: str, + actor: str) -> str: + """Returns the html for an individual shared item + """ + sharedItemsForm = '
\n' + sharedItemsForm += \ + '\n' + if sharedItem.get('imageUrl'): + sharedItemsForm += \ + '\n' + sharedItemsForm += \ + 'Item image\n' + sharedItemsForm += '

' + sharedItem['summary'] + '

\n

' + if sharedItem.get('itemQty'): + if sharedItem['itemQty'] > 1: + sharedItemsForm += \ + '' + translate['Quantity'] + \ + ': ' + str(sharedItem['itemQty']) + '
' + sharedItemsForm += \ + '' + translate['Type'] + ': ' + sharedItem['itemType'] + '
' + sharedItemsForm += \ + '' + translate['Category'] + ': ' + \ + sharedItem['category'] + '
' + if sharedItem.get('location'): + sharedItemsForm += \ + '' + translate['Location'] + ': ' + \ + sharedItem['location'] + '
' + if sharedItem.get('itemPrice') and \ + sharedItem.get('itemCurrency'): + if isfloat(sharedItem['itemPrice']): + if float(sharedItem['itemPrice']) > 0: + sharedItemsForm += \ + ' ' + translate['Price'] + \ + ': ' + sharedItem['itemPrice'] + \ + ' ' + sharedItem['itemCurrency'] + sharedItemsForm += '

\n' + contactActor = \ + httpPrefix + '://' + domainFull + '/users/' + contactNickname + sharedItemsForm += \ + '

\n' + if actor.endswith('/users/' + contactNickname): + sharedItemsForm += \ + ' \n' + sharedItemsForm += '

\n' + return sharedItemsForm + + +def htmlShowShare(baseDir: str, domain: str, nickname: str, + httpPrefix: str, domainFull: str, + itemID: str, translate: {}, + sharedItemsFederatedDomains: []) -> str: + """Shows an individual shared item after selecting it from the left column + """ + sharesJson = None + + shareUrl = itemID.replace('___', '://').replace('--', '/') + contactNickname = getNicknameFromActor(shareUrl) + if not contactNickname: + return None + + if '://' + domainFull + '/' in shareUrl: + # shared item on this instance + sharesFilename = \ + acctDir(baseDir, contactNickname, domain) + '/shares.json' + if not os.path.isfile(sharesFilename): + return None + sharesJson = loadJson(sharesFilename) + else: + # federated shared item + catalogsDir = baseDir + '/cache/catalogs' + if not os.path.isdir(catalogsDir): + return None + for subdir, dirs, files in os.walk(catalogsDir): + for f in files: + if '#' in f: + continue + if not f.endswith('.shares.json'): + continue + federatedDomain = f.split('.')[0] + if federatedDomain not in sharedItemsFederatedDomains: + continue + sharesFilename = catalogsDir + '/' + f + sharesJson = loadJson(sharesFilename) + if not sharesJson: + continue + if sharesJson.get(itemID): + break + break + + if not sharesJson: + return None + if not sharesJson.get(itemID): + return None + sharedItem = sharesJson[itemID] + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + shareStr = \ + htmlSearchResultShare(sharedItem, translate, httpPrefix, + domainFull, contactNickname, itemID, + actor) + + cssFilename = baseDir + '/epicyon-profile.css' + if os.path.isfile(baseDir + '/epicyon.css'): + cssFilename = baseDir + '/epicyon.css' + instanceTitle = \ + getConfigParam(baseDir, 'instanceTitle') + + return htmlHeaderWithExternalStyle(cssFilename, instanceTitle) + \ + shareStr + htmlFooter() From 28b3d5b0200cdf29724bbaa367360468f08660cd Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 20:05:19 +0100 Subject: [PATCH 222/459] Get nickname from path --- daemon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index 38569277f..e72e4f264 100644 --- a/daemon.py +++ b/daemon.py @@ -11256,13 +11256,14 @@ class PubServer(BaseHTTPRequestHandler): 'blog post 2 done') # after selecting a shared item from the left column then show it - if htmlGET and '?showshare=' in self.path: + if htmlGET and '?showshare=' in self.path and '/users/' in self.path: itemID = self.path.split('?showshare=')[1] usersPath = self.path.split('?showshare=')[0] + nickname = usersPath.replace('/users/', '') itemID = urllib.parse.unquote_plus(itemID.strip()) msg = \ htmlShowShare(self.server.baseDir, - self.server.domain, self.server.nickname, + self.server.domain, nickname, self.server.httpPrefix, self.server.domainFull, itemID, self.server.translate, self.server.sharedItemsFederatedDomains) From 4a13a0685d8a7368743aaac9ea996b907f31686d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 28 Jul 2021 20:16:42 +0100 Subject: [PATCH 223/459] Open left column shared item in same tab --- webapp_column_left.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/webapp_column_left.py b/webapp_column_left.py index b13bf03a2..5e31af0b2 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -168,6 +168,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, if linksList and sharesList: linksList = sharesList + linksList + newTabStr = ' target="_blank" rel="nofollow noopener noreferrer"' if linksList: htmlStr += '
' deletePostStr += \ diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index a1c575029..d95719c07 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -32,7 +32,7 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Shows posts on the front screen of a news instance These should only be public blog posts from the features timeline which is the blog timeline of the news actor @@ -73,7 +73,7 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, False, False, True, False) if postStr: profileStr += postStr + separatorStr @@ -98,6 +98,7 @@ def htmlFrontScreen(rssIconAtTop: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, accessKeys: {}, + systemLanguage: str, extraJson: {} = None, pageNumber: int = None, maxItemsPerPage: int = None) -> str: @@ -167,7 +168,7 @@ def htmlFrontScreen(rssIconAtTop: bool, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - theme) + licenseStr + theme, systemLanguage) + licenseStr # Footer which is only used for system accounts profileFooterStr = ' \n' diff --git a/webapp_moderation.py b/webapp_moderation.py index a58b95f3e..99b9049f1 100644 --- a/webapp_moderation.py +++ b/webapp_moderation.py @@ -47,7 +47,7 @@ def htmlModeration(cssCache: {}, defaultTimeline: str, theme: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the moderation feed as html This is what you see when selecting the "mod" timeline """ @@ -63,7 +63,7 @@ def htmlModeration(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, moderationActionStr, theme, peertubeInstances, allowLocalNetworkAccess, - textModeBanner, accessKeys) + textModeBanner, accessKeys, systemLanguage) def htmlAccountInfo(cssCache: {}, translate: {}, diff --git a/webapp_post.py b/webapp_post.py index 5eee1a3bc..de18a7135 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -22,6 +22,7 @@ from posts import postIsMuted from posts import getPersonBox from posts import downloadAnnounce from posts import populateRepliesJson +from utils import getContentFromPost from utils import hasObjectDict from utils import updateAnnounceCollection from utils import isPGPEncrypted @@ -273,7 +274,7 @@ def _getAvatarImageHtml(showAvatarOptions: bool, def _getReplyIconHtml(nickname: str, isPublicRepeat: bool, showIcons: bool, commentsEnabled: bool, postJsonObject: {}, pageNumberParam: str, - translate: {}) -> str: + translate: {}, systemLanguage: str) -> str: """Returns html for the reply icon/button """ replyStr = '' @@ -286,9 +287,9 @@ def _getReplyIconHtml(nickname: str, isPublicRepeat: bool, if isinstance(postJsonObject['object']['attributedTo'], str): replyToLink += \ '?mention=' + postJsonObject['object']['attributedTo'] - if postJsonObject['object'].get('content'): - mentionedActors = \ - getMentionsFromHtml(postJsonObject['object']['content']) + content = getContentFromPost(postJsonObject, systemLanguage) + if content: + mentionedActors = getMentionsFromHtml(content) if mentionedActors: for actorUrl in mentionedActors: if '?mention=' + actorUrl not in replyToLink: @@ -1160,7 +1161,7 @@ def individualPostAsHtml(allowDownloads: bool, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str, + themeName: str, systemLanguage: str, showRepeats: bool = True, showIcons: bool = False, manuallyApprovesFollowers: bool = False, @@ -1410,7 +1411,7 @@ def individualPostAsHtml(allowDownloads: bool, replyStr = _getReplyIconHtml(nickname, isPublicRepeat, showIcons, commentsEnabled, postJsonObject, pageNumberParam, - translate) + translate, systemLanguage) _logPostTiming(enableTimingLog, postStartTime, '10') @@ -1588,20 +1589,21 @@ def individualPostAsHtml(allowDownloads: bool, postJsonObject['object']['content'] = \ E2EEdecryptMessageFromDevice(postJsonObject['object']) - if not postJsonObject['object'].get('content'): + contentStr = getContentFromPost(postJsonObject, systemLanguage) + if not contentStr: return '' isPatch = isGitPatch(baseDir, nickname, domain, postJsonObject['object']['type'], postJsonObject['object']['summary'], - postJsonObject['object']['content']) + contentStr) _logPostTiming(enableTimingLog, postStartTime, '16') - if not isPGPEncrypted(postJsonObject['object']['content']): + if not isPGPEncrypted(contentStr): if not isPatch: objectContent = \ - removeLongWords(postJsonObject['object']['content'], 40, []) + removeLongWords(contentStr, 40, []) objectContent = removeTextFormatting(objectContent) objectContent = limitRepeatedWords(objectContent, 6) objectContent = \ @@ -1609,8 +1611,7 @@ def individualPostAsHtml(allowDownloads: bool, objectContent = htmlReplaceEmailQuote(objectContent) objectContent = htmlReplaceQuoteMarks(objectContent) else: - objectContent = \ - postJsonObject['object']['content'] + objectContent = contentStr else: objectContent = '🔒 ' + translate['Encrypted'] @@ -1719,7 +1720,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Show an individual post as html """ postStr = '' @@ -1761,6 +1762,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, themeName, + systemLanguage, False, authorized, False, False, False) messageId = removeIdEnding(postJsonObject['id']) @@ -1788,7 +1790,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, authorized, False, False, False) + postStr @@ -1819,7 +1821,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, authorized, False, False, False) cssFilename = baseDir + '/epicyon-profile.css' @@ -1842,7 +1844,7 @@ def htmlPostReplies(cssCache: {}, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Show the replies to an individual post as html """ repliesStr = '' @@ -1861,7 +1863,7 @@ def htmlPostReplies(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, False, False, False, False) cssFilename = baseDir + '/epicyon-profile.css' diff --git a/webapp_profile.py b/webapp_profile.py index b25891efb..71f697eda 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -73,7 +73,8 @@ def htmlProfileAfterSearch(cssCache: {}, peertubeInstances: [], allowLocalNetworkAccess: bool, themeName: str, - accessKeys: {}) -> str: + accessKeys: {}, + systemLanguage: str) -> str: """Show a profile page after a search for a fediverse address """ http = False @@ -246,7 +247,7 @@ def htmlProfileAfterSearch(cssCache: {}, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, False, False, False, False) i += 1 if i >= 20: @@ -447,6 +448,7 @@ def htmlProfile(rssIconAtTop: bool, allowLocalNetworkAccess: bool, textModeBanner: str, debug: bool, accessKeys: {}, city: str, + systemLanguage: str, extraJson: {} = None, pageNumber: int = None, maxItemsPerPage: int = None) -> str: """Show the profile page as html @@ -467,6 +469,7 @@ def htmlProfile(rssIconAtTop: bool, showPublishedDateOnly, newswire, theme, extraJson, allowLocalNetworkAccess, accessKeys, + systemLanguage, pageNumber, maxItemsPerPage) domain, port = getDomainFromActor(profileJson['id']) @@ -796,7 +799,7 @@ def htmlProfile(rssIconAtTop: bool, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - theme) + licenseStr + theme, systemLanguage) + licenseStr elif selected == 'following': profileStr += \ _htmlProfileFollowing(translate, baseDir, httpPrefix, @@ -850,7 +853,7 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Shows posts on the profile screen These should only be public posts """ @@ -890,7 +893,7 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, False, False, False, True, False) if postStr: profileStr += postStr + separatorStr diff --git a/webapp_search.py b/webapp_search.py index 9f2202add..f82894690 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -536,7 +536,8 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str, boxName: str) -> str: + themeName: str, boxName: str, + systemLanguage: str) -> str: """Show a page containing search results for your post history """ if historysearch.startswith('!'): @@ -616,7 +617,7 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, showIndividualPostIcons, showIndividualPostIcons, False, False, False) @@ -640,7 +641,7 @@ def htmlHashtagSearch(cssCache: {}, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, - themeName: str) -> str: + themeName: str, systemLanguage: str) -> str: """Show a page containing search results for a hashtag """ if hashtag.startswith('#'): @@ -790,7 +791,7 @@ def htmlHashtagSearch(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - themeName, + themeName, systemLanguage, showRepeats, showIcons, manuallyApprovesFollowers, showPublicOnly, diff --git a/webapp_timeline.py b/webapp_timeline.py index 7ade66c82..1b1552b09 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -358,7 +358,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the timeline as html """ enableTimingLog = False @@ -743,7 +743,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, - theme, + theme, systemLanguage, boxName != 'dm', showIndividualPostIcons, manuallyApproveFollowers, @@ -919,7 +919,7 @@ def htmlShares(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the shares timeline as html """ manuallyApproveFollowers = \ @@ -941,7 +941,7 @@ def htmlShares(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInbox(cssCache: {}, defaultTimeline: str, @@ -964,7 +964,7 @@ def htmlInbox(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the inbox as html """ manuallyApproveFollowers = \ @@ -986,7 +986,7 @@ def htmlInbox(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlBookmarks(cssCache: {}, defaultTimeline: str, @@ -1009,7 +1009,7 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the bookmarks as html """ manuallyApproveFollowers = \ @@ -1031,7 +1031,7 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxDMs(cssCache: {}, defaultTimeline: str, @@ -1054,7 +1054,7 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the DM timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1071,7 +1071,7 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxReplies(cssCache: {}, defaultTimeline: str, @@ -1094,7 +1094,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the replies timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1112,7 +1112,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxMedia(cssCache: {}, defaultTimeline: str, @@ -1135,7 +1135,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the media timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1153,7 +1153,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, @@ -1176,7 +1176,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the blogs timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1194,7 +1194,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, @@ -1218,7 +1218,7 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the features timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1236,7 +1236,7 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlInboxNews(cssCache: {}, defaultTimeline: str, @@ -1259,7 +1259,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the news timeline as html """ return htmlTimeline(cssCache, defaultTimeline, @@ -1277,7 +1277,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) def htmlOutbox(cssCache: {}, defaultTimeline: str, @@ -1300,7 +1300,7 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str, peertubeInstances: [], allowLocalNetworkAccess: bool, textModeBanner: str, - accessKeys: {}) -> str: + accessKeys: {}, systemLanguage: str) -> str: """Show the Outbox as html """ manuallyApproveFollowers = \ @@ -1319,4 +1319,4 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str, iconsAsButtons, rssIconAtTop, publishButtonAtTop, authorized, None, theme, peertubeInstances, allowLocalNetworkAccess, textModeBanner, - accessKeys) + accessKeys, systemLanguage) From 50096bff11d34645b31a67419bf5a60df163514f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 18 Jul 2021 15:15:16 +0100 Subject: [PATCH 018/459] Improve handling of contentMap within posts --- blog.py | 52 ++++++++++++++---------- daemon.py | 60 ++++++++++++++++++--------- desktop_client.py | 25 +++++++----- epicyon.py | 26 +++++++++--- happening.py | 3 ++ inbox.py | 92 ++++++++++++++---------------------------- media.py | 13 +++--- newsdaemon.py | 29 +++++++------ newswire.py | 16 +++++--- outbox.py | 13 +++--- posts.py | 54 +++++++++++++++---------- schedule.py | 2 +- socnet.py | 5 ++- tests.py | 6 +++ utils.py | 37 +---------------- webapp_column_right.py | 6 ++- webapp_moderation.py | 5 ++- webapp_post.py | 14 +++---- webapp_search.py | 6 ++- 19 files changed, 245 insertions(+), 219 deletions(-) diff --git a/blog.py b/blog.py index 7823ae3b0..79076f35b 100644 --- a/blog.py +++ b/blog.py @@ -16,6 +16,7 @@ from webapp_utils import htmlHeaderWithBlogMarkup from webapp_utils import htmlFooter from webapp_utils import getPostAttachmentsAsHtml from webapp_media import addEmbeddedElements +from utils import getContentFromPost from utils import isAccountDir from utils import removeHtml from utils import getConfigParam @@ -164,7 +165,8 @@ def _htmlBlogPostContent(authorized: bool, postJsonObject: {}, handle: str, restrictToDomain: bool, peertubeInstances: [], - blogSeparator='
') -> str: + systemLanguage: str, + blogSeparator: str = '
') -> str: """Returns the content for a single blog post """ linkedAuthor = False @@ -235,9 +237,9 @@ def _htmlBlogPostContent(authorized: bool, if attachmentStr: blogStr += '
' + attachmentStr + '
' - if postJsonObject['object'].get('content'): - contentStr = addEmbeddedElements(translate, - postJsonObject['object']['content'], + jsonContent = getContentFromPost(postJsonObject, systemLanguage) + if jsonContent: + contentStr = addEmbeddedElements(translate, jsonContent, peertubeInstances) if postJsonObject['object'].get('tag'): contentStr = replaceEmojiFromTags(contentStr, @@ -312,7 +314,8 @@ def _htmlBlogPostRSS2(authorized: bool, baseDir: str, httpPrefix: str, translate: {}, nickname: str, domain: str, domainFull: str, postJsonObject: {}, - handle: str, restrictToDomain: bool) -> str: + handle: str, restrictToDomain: bool, + systemLanguage: str) -> str: """Returns the RSS version 2 feed for a single blog post """ rssStr = '' @@ -327,7 +330,7 @@ def _htmlBlogPostRSS2(authorized: bool, pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") titleStr = postJsonObject['object']['summary'] rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) description = firstParagraphFromString(content) rssStr = ' ' rssStr += ' ' + titleStr + '' @@ -343,7 +346,8 @@ def _htmlBlogPostRSS3(authorized: bool, baseDir: str, httpPrefix: str, translate: {}, nickname: str, domain: str, domainFull: str, postJsonObject: {}, - handle: str, restrictToDomain: bool) -> str: + handle: str, restrictToDomain: bool, + systemLanguage: str) -> str: """Returns the RSS version 3 feed for a single blog post """ rssStr = '' @@ -358,7 +362,7 @@ def _htmlBlogPostRSS3(authorized: bool, pubDate = datetime.strptime(published, "%Y-%m-%dT%H:%M:%SZ") titleStr = postJsonObject['object']['summary'] rssDateStr = pubDate.strftime("%a, %d %b %Y %H:%M:%S UT") - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) description = firstParagraphFromString(content) rssStr = 'title: ' + titleStr + '\n' rssStr += 'link: ' + messageLink + '\n' @@ -379,10 +383,10 @@ def _htmlBlogRemoveCwButton(blogStr: str, translate: {}) -> str: return blogStr -def _getSnippetFromBlogContent(postJsonObject: {}) -> str: +def _getSnippetFromBlogContent(postJsonObject: {}, systemLanguage: str) -> str: """Returns a snippet of text from the blog post as a preview """ - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) if '

' in content: content = content.split('

', 1)[1] if '

' in content: @@ -412,7 +416,7 @@ def htmlBlogPost(authorized: bool, getConfigParam(baseDir, 'instanceTitle') published = postJsonObject['object']['published'] title = postJsonObject['object']['summary'] - snippet = _getSnippetFromBlogContent(postJsonObject) + snippet = _getSnippetFromBlogContent(postJsonObject, systemLanguage) blogStr = htmlHeaderWithBlogMarkup(cssFilename, instanceTitle, httpPrefix, domainFull, nickname, systemLanguage, published, @@ -424,7 +428,7 @@ def htmlBlogPost(authorized: bool, nickname, domain, domainFull, postJsonObject, None, False, - peertubeInstances) + peertubeInstances, systemLanguage) # show rss links blogStr += '

' @@ -452,7 +456,7 @@ def htmlBlogPage(authorized: bool, session, baseDir: str, httpPrefix: str, translate: {}, nickname: str, domain: str, port: int, noOfItems: int, pageNumber: int, - peertubeInstances: []) -> str: + peertubeInstances: [], systemLanguage: str) -> str: """Returns a html blog page containing posts """ if ' ' in nickname or '@' in nickname or \ @@ -514,7 +518,8 @@ def htmlBlogPage(authorized: bool, session, nickname, domain, domainFull, item, None, True, - peertubeInstances) + peertubeInstances, + systemLanguage) if len(timelineJson['orderedItems']) >= noOfItems: blogStr += navigateStr @@ -542,7 +547,7 @@ def htmlBlogPageRSS2(authorized: bool, session, baseDir: str, httpPrefix: str, translate: {}, nickname: str, domain: str, port: int, noOfItems: int, pageNumber: int, - includeHeader: bool) -> str: + includeHeader: bool, systemLanguage: str) -> str: """Returns an RSS version 2 feed containing posts """ if ' ' in nickname or '@' in nickname or \ @@ -585,7 +590,7 @@ def htmlBlogPageRSS2(authorized: bool, session, httpPrefix, translate, nickname, domain, domainFull, item, - None, True) + None, True, systemLanguage) if includeHeader: return blogRSS2 + rss2Footer() @@ -596,7 +601,8 @@ def htmlBlogPageRSS2(authorized: bool, session, def htmlBlogPageRSS3(authorized: bool, session, baseDir: str, httpPrefix: str, translate: {}, nickname: str, domain: str, port: int, - noOfItems: int, pageNumber: int) -> str: + noOfItems: int, pageNumber: int, + systemLanguage: str) -> str: """Returns an RSS version 3 feed containing posts """ if ' ' in nickname or '@' in nickname or \ @@ -630,7 +636,8 @@ def htmlBlogPageRSS3(authorized: bool, session, httpPrefix, translate, nickname, domain, domainFull, item, - None, True) + None, True, + systemLanguage) return blogRSS3 @@ -670,7 +677,7 @@ def htmlBlogView(authorized: bool, session, baseDir: str, httpPrefix: str, translate: {}, domain: str, port: int, noOfItems: int, - peertubeInstances: []) -> str: + peertubeInstances: [], systemLanguage: str) -> str: """Show the blog main page """ blogStr = '' @@ -688,7 +695,8 @@ def htmlBlogView(authorized: bool, return htmlBlogPage(authorized, session, baseDir, httpPrefix, translate, nickname, domain, port, - noOfItems, 1, peertubeInstances) + noOfItems, 1, peertubeInstances, + systemLanguage) domainFull = getFullDomain(domain, port) @@ -714,7 +722,7 @@ def htmlEditBlog(mediaInstance: bool, translate: {}, path: str, pageNumber: int, nickname: str, domain: str, - postUrl: str) -> str: + postUrl: str, systemLanguage: str) -> str: """Edit a blog post after it was created """ postFilename = locatePost(baseDir, nickname, domain, postUrl) @@ -832,7 +840,7 @@ def htmlEditBlog(mediaInstance: bool, translate: {}, placeholderMessage + '' messageBoxHeight = 800 - contentStr = postJsonObject['object']['content'] + contentStr = getContentFromPost(postJsonObject, systemLanguage) contentStr = contentStr.replace('

', '').replace('

', '\n') editBlogForm += \ diff --git a/daemon.py b/daemon.py index df4165008..16a229ce5 100644 --- a/daemon.py +++ b/daemon.py @@ -208,6 +208,7 @@ from shares import addShare from shares import removeShare from shares import expireShares from categories import setHashtagCategory +from utils import getContentFromPost from utils import acctDir from utils import getImageExtensionFromMimeType from utils import getImageMimeType @@ -1110,7 +1111,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.allowLocalNetworkAccess, - city) + city, self.server.systemLanguage) def _postToOutboxThread(self, messageJson: {}) -> bool: """Creates a thread to send a post @@ -1308,7 +1309,8 @@ class PubServer(BaseHTTPRequestHandler): headersDict, self.path, self.server.debug, - self.server.blockedCache) + self.server.blockedCache, + self.server.systemLanguage) if queueFilename: # add json to the queue if queueFilename not in self.server.inboxQueue: @@ -1696,7 +1698,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, searchHandle, - self.server.debug) + self.server.debug, + self.server.systemLanguage) else: msg = \ htmlModerationInfo(self.server.cssCache, @@ -2344,7 +2347,8 @@ class PubServer(BaseHTTPRequestHandler): domain, self.server.port, optionsActor, - self.server.debug).encode('utf-8') + self.server.debug, + self.server.systemLanguage).encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, cookie, callingDomain) @@ -3910,6 +3914,8 @@ class PubServer(BaseHTTPRequestHandler): newsPostTitle postJsonObject['object']['content'] = \ newsPostContent + contentMap = postJsonObject['object']['contentMap'] + contentMap[self.server.systemLanguage] = newsPostContent # update newswire pubDate = postJsonObject['object']['published'] publishedDate = \ @@ -5619,7 +5625,8 @@ class PubServer(BaseHTTPRequestHandler): domain, port, maxPostsInRSSFeed, 1, - True) + True, + self.server.systemLanguage) if msg is not None: msg = msg.encode('utf-8') msglen = len(msg) @@ -5674,7 +5681,8 @@ class PubServer(BaseHTTPRequestHandler): domain, port, maxPostsInRSSFeed, 1, - False) + False, + self.server.systemLanguage) break if msg: msg = rss2Header(httpPrefix, @@ -5776,7 +5784,7 @@ class PubServer(BaseHTTPRequestHandler): baseDir: str, httpPrefix: str, domain: str, port: int, proxyType: str, GETstartTime, GETtimings: {}, - debug: bool) -> None: + debug: bool, systemLanguage: str) -> None: """Returns an RSS3 feed """ nickname = path.split('/blog/')[1] @@ -5800,7 +5808,8 @@ class PubServer(BaseHTTPRequestHandler): baseDir, httpPrefix, self.server.translate, nickname, domain, port, - maxPostsInRSSFeed, 1) + maxPostsInRSSFeed, 1, + systemLanguage) if msg is not None: msg = msg.encode('utf-8') msglen = len(msg) @@ -6233,7 +6242,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.personCache, httpPrefix, self.server.projectVersion, - self.server.YTReplacementDomain) + self.server.YTReplacementDomain, + self.server.systemLanguage) if hashtagStr: msg = hashtagStr.encode('utf-8') msglen = len(msg) @@ -9767,7 +9777,8 @@ class PubServer(BaseHTTPRequestHandler): nickname, domain, port, maxPostsInBlogsFeed, pageNumber, - self.server.peertubeInstances) + self.server.peertubeInstances, + self.server.systemLanguage) if msg is not None: msg = msg.encode('utf-8') msglen = len(msg) @@ -10384,7 +10395,8 @@ class PubServer(BaseHTTPRequestHandler): translate, baseDir, path, domain, port, httpPrefix, - postUrl).encode('utf-8') + postUrl, + self.server.systemLanguage).encode('utf-8') if msg: msglen = len(msg) self._set_headers('text/html', msglen, @@ -10732,7 +10744,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.port, self.server.proxyType, GETstartTime, GETtimings, - self.server.debug) + self.server.debug, + self.server.systemLanguage) return usersInPath = False @@ -10894,7 +10907,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, maxPostsInBlogsFeed, - self.server.peertubeInstances) + self.server.peertubeInstances, + self.server.systemLanguage) if msg is not None: msg = msg.encode('utf-8') msglen = len(msg) @@ -12265,7 +12279,7 @@ class PubServer(BaseHTTPRequestHandler): self.path, replyPageNumber, nickname, self.server.domain, - postUrl) + postUrl, self.server.systemLanguage) if msg: msg = msg.encode('utf-8') msglen = len(msg) @@ -12643,7 +12657,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, searchHandle, - self.server.debug) + self.server.debug, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._login_headers('text/html', @@ -12677,7 +12692,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.domain, self.server.port, searchHandle, - self.server.debug) + self.server.debug, + self.server.systemLanguage) msg = msg.encode('utf-8') msglen = len(msg) self._login_headers('text/html', @@ -13122,9 +13138,11 @@ class PubServer(BaseHTTPRequestHandler): if fields['schedulePost']: return 1 if pinToProfile: + contentStr = \ + getContentFromPost(messageJson, + self.server.systemLanguage) pinPost(self.server.baseDir, - nickname, self.server.domain, - messageJson['object']['content']) + nickname, self.server.domain, contentStr) return 1 if self._postToOutbox(messageJson, __version__, nickname): populateReplies(self.server.baseDir, @@ -13251,6 +13269,9 @@ class PubServer(BaseHTTPRequestHandler): tags, 'content') postJsonObject['object']['content'] = fields['message'] + contentMap = postJsonObject['object']['contentMap'] + contentMap[self.server.systemLanguage] = \ + fields['message'] imgDescription = '' if fields.get('imageDescription'): @@ -13274,7 +13295,8 @@ class PubServer(BaseHTTPRequestHandler): city) replaceYouTube(postJsonObject, - self.server.YTReplacementDomain) + self.server.YTReplacementDomain, + self.server.systemLanguage) saveJson(postJsonObject, postFilename) # also save to the news actor if nickname != 'news': diff --git a/desktop_client.py b/desktop_client.py index 44dd245c7..728395ba1 100644 --- a/desktop_client.py +++ b/desktop_client.py @@ -16,6 +16,7 @@ import webbrowser import urllib.parse from pathlib import Path from random import randint +from utils import getContentFromPost from utils import hasObjectDict from utils import getFullDomain from utils import isDM @@ -689,15 +690,16 @@ def _readLocalBoxPost(session, nickname: str, domain: str, __version__, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, False) + recentPostsCache, False, + systemLanguage) if postJsonObject2: if hasObjectDict(postJsonObject2): if postJsonObject2['object'].get('attributedTo') and \ postJsonObject2['object'].get('content'): attributedTo = postJsonObject2['object']['attributedTo'] - content = postJsonObject2['object']['content'] - if isinstance(attributedTo, str) and \ - isinstance(content, str): + content = \ + getContentFromPost(postJsonObject2, systemLanguage) + if isinstance(attributedTo, str) and content: actor = attributedTo nameStr += ' ' + translate['announces'] + ' ' + \ getNicknameFromActor(actor) @@ -721,7 +723,7 @@ def _readLocalBoxPost(session, nickname: str, domain: str, attributedTo = postJsonObject['object']['attributedTo'] if not attributedTo: return {} - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) if not isinstance(attributedTo, str) or \ not isinstance(content, str): return {} @@ -1044,7 +1046,8 @@ def _desktopShowBox(indent: str, published = _formatPublished(postJsonObject['published']) - content = _textOnlyContent(postJsonObject['object']['content']) + contentStr = getContentFromPost(postJsonObject, systemLanguage) + content = _textOnlyContent(contentStr) if boxName != 'dm': if isDM(postJsonObject): content = '📧' + content @@ -2321,11 +2324,13 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, __version__, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, False) + recentPostsCache, False, + systemLanguage) if postJsonObject2: postJsonObject = postJsonObject2 if postJsonObject: - content = postJsonObject['object']['content'] + content = \ + getContentFromPost(postJsonObject, systemLanguage) messageStr, detectedLinks = \ speakableText(baseDir, content, translate) linkOpened = False @@ -2381,7 +2386,9 @@ def runDesktopClient(baseDir: str, proxyType: str, httpPrefix: str, print('') if postJsonObject['object'].get('summary'): print(postJsonObject['object']['summary']) - print(postJsonObject['object']['content']) + contentStr = getContentFromPost(postJsonObject, + systemLanguage) + print(contentStr) print('') sayStr = 'Confirm delete, yes or no?' _sayCommand(sayStr, sayStr, screenreader, diff --git a/epicyon.py b/epicyon.py index cc21d48fd..59364581f 100644 --- a/epicyon.py +++ b/epicyon.py @@ -631,9 +631,11 @@ if args.posts: args.port = 80 elif args.gnunet: proxyType = 'gnunet' + if not args.language: + args.language = 'en' getPublicPostsOfPerson(baseDir, nickname, domain, False, True, proxyType, args.port, httpPrefix, debug, - __version__) + __version__, args.language) sys.exit() if args.postDomains: @@ -663,12 +665,15 @@ if args.postDomains: proxyType = 'gnunet' wordFrequency = {} domainList = [] + if not args.language: + args.language = 'en' domainList = getPublicPostDomains(None, baseDir, nickname, domain, proxyType, args.port, httpPrefix, debug, __version__, - wordFrequency, domainList) + wordFrequency, domainList, + args.language) for postDomain in domainList: print(postDomain) sys.exit() @@ -703,12 +708,15 @@ if args.postDomainsBlocked: proxyType = 'gnunet' wordFrequency = {} domainList = [] + if not args.language: + args.language = 'en' domainList = getPublicPostDomainsBlocked(None, baseDir, nickname, domain, proxyType, args.port, httpPrefix, debug, __version__, - wordFrequency, domainList) + wordFrequency, domainList, + args.language) for postDomain in domainList: print(postDomain) sys.exit() @@ -741,12 +749,14 @@ if args.checkDomains: elif args.gnunet: proxyType = 'gnunet' maxBlockedDomains = 0 + if not args.language: + args.language = 'en' checkDomains(None, baseDir, nickname, domain, proxyType, args.port, httpPrefix, debug, __version__, - maxBlockedDomains, False) + maxBlockedDomains, False, args.language) sys.exit() if args.socnet: @@ -758,10 +768,12 @@ if args.socnet: if not args.http: args.port = 443 proxyType = 'tor' + if not args.language: + args.language = 'en' dotGraph = instancesGraph(baseDir, args.socnet, proxyType, args.port, httpPrefix, debug, - __version__) + __version__, args.language) try: with open('socnet.dot', 'w+') as fp: fp.write(dotGraph) @@ -785,9 +797,11 @@ if args.postsraw: proxyType = 'i2p' elif args.gnunet: proxyType = 'gnunet' + if not args.language: + args.language = 'en' getPublicPostsOfPerson(baseDir, nickname, domain, False, False, proxyType, args.port, httpPrefix, debug, - __version__) + __version__, args.language) sys.exit() if args.json: diff --git a/happening.py b/happening.py index 8583ac0b2..a3d877528 100644 --- a/happening.py +++ b/happening.py @@ -55,6 +55,9 @@ def saveEventPost(baseDir: str, handle: str, postId: str, See https://framagit.org/framasoft/mobilizon/-/blob/ master/lib/federation/activity_stream/converter/event.ex """ + if not os.path.isdir(baseDir + '/accounts/' + handle): + print('WARN: Account does not exist at ' + + baseDir + '/accounts/' + handle) calendarPath = baseDir + '/accounts/' + handle + '/calendar' if not os.path.isdir(calendarPath): os.mkdir(calendarPath) diff --git a/inbox.py b/inbox.py index f45f6f72a..23c4bdcff 100644 --- a/inbox.py +++ b/inbox.py @@ -13,6 +13,7 @@ import datetime import time import random from linked_data_sig import verifyJsonSignature +from utils import getContentFromPost from utils import acctDir from utils import removeDomainPort from utils import getPortFromDomain @@ -23,7 +24,6 @@ from utils import getConfigParam from utils import hasUsersPath from utils import validPostDate from utils import getFullDomain -from utils import isEventPost from utils import removeIdEnding from utils import getProtocolPrefixes from utils import isBlogPost @@ -351,7 +351,7 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str, messageBytes: str, httpHeaders: {}, postPath: str, debug: bool, - blockedCache: []) -> str: + blockedCache: [], systemLanguage: str) -> str: """Saves the give json to the inbox queue for the person keyId specifies the actor sending the post """ @@ -415,9 +415,9 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str, replyNickname + '@' + replyDomain) return None if postJsonObject['object'].get('content'): - if isinstance(postJsonObject['object']['content'], str): - if isFiltered(baseDir, nickname, domain, - postJsonObject['object']['content']): + contentStr = getContentFromPost(postJsonObject, systemLanguage) + if contentStr: + if isFiltered(baseDir, nickname, domain, contentStr): if debug: print('WARN: post was filtered out due to content') return None @@ -730,25 +730,6 @@ def _receiveUndo(session, baseDir: str, httpPrefix: str, return False -def _receiveEventPost(recentPostsCache: {}, session, baseDir: str, - httpPrefix: str, domain: str, port: int, - sendThreads: [], postLog: [], cachedWebfingers: {}, - personCache: {}, messageJson: {}, federationList: [], - nickname: str, debug: bool) -> bool: - """Receive a mobilizon-type event activity - See https://framagit.org/framasoft/mobilizon/-/blob/ - master/lib/federation/activity_stream/converter/event.ex - """ - if not isEventPost(messageJson): - return - print('Receiving event: ' + str(messageJson['object'])) - handle = getFullDomain(nickname + '@' + domain, port) - - postId = removeIdEnding(messageJson['id']).replace('/', '#') - - saveEventPost(baseDir, handle, postId, messageJson['object']) - - def _personReceiveUpdate(baseDir: str, domain: str, port: int, updateNickname: str, updateDomain: str, @@ -1305,7 +1286,7 @@ def _receiveAnnounce(recentPostsCache: {}, debug: bool, translate: {}, YTReplacementDomain: str, allowLocalNetworkAccess: bool, - themeName: str) -> bool: + themeName: str, systemLanguage: str) -> bool: """Receives an announce activity within the POST section of HTTPServer """ if messageJson['type'] != 'Announce': @@ -1392,7 +1373,8 @@ def _receiveAnnounce(recentPostsCache: {}, __version__, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, debug) + recentPostsCache, debug, + systemLanguage) if not postJsonObject: notInOnion = True if onionDomain: @@ -1616,7 +1598,8 @@ def _estimateNumberOfEmoji(content: str) -> int: def _validPostContent(baseDir: str, nickname: str, domain: str, messageJson: {}, maxMentions: int, maxEmoji: int, - allowLocalNetworkAccess: bool, debug: bool) -> bool: + allowLocalNetworkAccess: bool, debug: bool, + systemLanguage: str) -> bool: """Is the content of a received post valid? Check for bad html Check for hellthreads @@ -1651,27 +1634,27 @@ def _validPostContent(baseDir: str, nickname: str, domain: str, messageJson['object']['content']): return True - if dangerousMarkup(messageJson['object']['content'], - allowLocalNetworkAccess): + contentStr = getContentFromPost(messageJson, systemLanguage) + if dangerousMarkup(contentStr, allowLocalNetworkAccess): if messageJson['object'].get('id'): print('REJECT ARBITRARY HTML: ' + messageJson['object']['id']) print('REJECT ARBITRARY HTML: bad string in post - ' + - messageJson['object']['content']) + contentStr) return False # check (rough) number of mentions - mentionsEst = _estimateNumberOfMentions(messageJson['object']['content']) + mentionsEst = _estimateNumberOfMentions(contentStr) if mentionsEst > maxMentions: if messageJson['object'].get('id'): print('REJECT HELLTHREAD: ' + messageJson['object']['id']) print('REJECT HELLTHREAD: Too many mentions in post - ' + - messageJson['object']['content']) + contentStr) return False - if _estimateNumberOfEmoji(messageJson['object']['content']) > maxEmoji: + if _estimateNumberOfEmoji(contentStr) > maxEmoji: if messageJson['object'].get('id'): print('REJECT EMOJI OVERLOAD: ' + messageJson['object']['id']) print('REJECT EMOJI OVERLOAD: Too many emoji in post - ' + - messageJson['object']['content']) + contentStr) return False # check number of tags if messageJson['object'].get('tag'): @@ -1685,8 +1668,7 @@ def _validPostContent(baseDir: str, nickname: str, domain: str, messageJson['object']['tag']) return False # check for filtered content - if isFiltered(baseDir, nickname, domain, - messageJson['object']['content']): + if isFiltered(baseDir, nickname, domain, contentStr): print('REJECT: content filtered') return False if messageJson['object'].get('inReplyTo'): @@ -1926,7 +1908,8 @@ def _sendToGroupMembers(session, baseDir: str, handle: str, port: int, postJsonObject: {}, httpPrefix: str, federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, - personCache: {}, debug: bool) -> None: + personCache: {}, debug: bool, + systemLanguage: str) -> None: """When a post arrives for a group send it out to the group members """ followersFile = baseDir + '/accounts/' + handle + '/followers.txt' @@ -1947,9 +1930,12 @@ def _sendToGroupMembers(session, baseDir: str, handle: str, port: int, sendingActorDomainFull = \ getFullDomain(sendingActorDomain, sendingActorPort) senderStr = '@' + sendingActorNickname + '@' + sendingActorDomainFull - if not postJsonObject['object']['content'].startswith(senderStr): + contentStr = getContentFromPost(postJsonObject, systemLanguage) + if not contentStr.startswith(senderStr): postJsonObject['object']['content'] = \ - senderStr + ' ' + postJsonObject['object']['content'] + senderStr + ' ' + contentStr + postJsonObject['object']['contentMap'][systemLanguage] = \ + senderStr + ' ' + contentStr # add mention to tag list if not postJsonObject['object']['tag']: postJsonObject['object']['tag'] = [] @@ -2367,7 +2353,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, debug, translate, YTReplacementDomain, allowLocalNetworkAccess, - themeName): + themeName, systemLanguage): if debug: print('DEBUG: Announce accepted from ' + actor) @@ -2416,7 +2402,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, jsonObj = None if _validPostContent(baseDir, nickname, domain, postJsonObject, maxMentions, maxEmoji, - allowLocalNetworkAccess, debug): + allowLocalNetworkAccess, debug, + systemLanguage): if postJsonObject.get('object'): jsonObj = postJsonObject['object'] @@ -2449,7 +2436,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, return False # replace YouTube links, so they get less tracking data - replaceYouTube(postJsonObject, YTReplacementDomain) + replaceYouTube(postJsonObject, YTReplacementDomain, systemLanguage) # list of indexes to be updated updateIndexList = ['inbox'] @@ -2519,7 +2506,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, nickname, domain, postJsonObject, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, debug): + recentPostsCache, debug, systemLanguage): # media index will be updated updateIndexList.append('tlmedia') if isBlogPost(postJsonObject): @@ -2613,7 +2600,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, postJsonObject, httpPrefix, federationList, sendThreads, postLog, cachedWebfingers, personCache, - debug) + debug, systemLanguage) # if the post wasn't saved if not os.path.isfile(destinationFilename): @@ -3149,23 +3136,6 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, queue.pop(0) continue - if _receiveEventPost(recentPostsCache, session, - baseDir, httpPrefix, - domain, port, - sendThreads, postLog, - cachedWebfingers, - personCache, - queueJson['post'], - federationList, - queueJson['postNickname'], - debug): - print('Queue: Event activity accepted from ' + keyId) - if os.path.isfile(queueFilename): - os.remove(queueFilename) - if len(queue) > 0: - queue.pop(0) - continue - if _receiveUpdate(recentPostsCache, session, baseDir, httpPrefix, domain, port, diff --git a/media.py b/media.py index dc4ba5346..659dd1c30 100644 --- a/media.py +++ b/media.py @@ -13,6 +13,7 @@ import subprocess from random import randint from hashlib import sha1 from auth import createPassword +from utils import getContentFromPost from utils import getFullDomain from utils import getImageExtensions from utils import getVideoExtensions @@ -26,7 +27,8 @@ from shutil import move from city import spoofGeolocation -def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None: +def replaceYouTube(postJsonObject: {}, replacementDomain: str, + systemLanguage: str) -> None: """Replace YouTube with a replacement domain This denies Google some, but not all, tracking data """ @@ -36,11 +38,12 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None: return if not postJsonObject['object'].get('content'): return - if 'www.youtube.com' not in postJsonObject['object']['content']: + contentStr = getContentFromPost(postJsonObject, systemLanguage) + if 'www.youtube.com' not in contentStr: return - postJsonObject['object']['content'] = \ - postJsonObject['object']['content'].replace('www.youtube.com', - replacementDomain) + contentStr = contentStr.replace('www.youtube.com', replacementDomain) + postJsonObject['object']['content'] = contentStr + postJsonObject['object']['contentMap'][systemLanguage] = contentStr def _removeMetaData(imageFilename: str, outputFilename: str) -> None: diff --git a/newsdaemon.py b/newsdaemon.py index 647eecd16..716ded56e 100644 --- a/newsdaemon.py +++ b/newsdaemon.py @@ -25,6 +25,7 @@ from newswire import getDictFromNewswire from posts import createNewsPost from posts import archivePostsForPerson from content import validHashTag +from utils import getContentFromPost from utils import removeHtml from utils import getFullDomain from utils import loadJson @@ -279,7 +280,7 @@ def hashtagRuleTree(operators: [], def _hashtagAdd(baseDir: str, httpPrefix: str, domainFull: str, postJsonObject: {}, - actionStr: str, hashtags: []) -> None: + actionStr: str, hashtags: [], systemLanguage: str) -> None: """Adds a hashtag via a hashtag rule """ addHashtag = actionStr.split('add ', 1)[1].strip() @@ -313,7 +314,7 @@ def _hashtagAdd(baseDir: str, httpPrefix: str, domainFull: str, hashtagHtml = \ "
#" + htId + "" - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) if hashtagHtml in content: return @@ -328,7 +329,7 @@ def _hashtagAdd(baseDir: str, httpPrefix: str, domainFull: str, def _hashtagRemove(httpPrefix: str, domainFull: str, postJsonObject: {}, - actionStr: str, hashtags: []) -> None: + actionStr: str, hashtags: [], systemLanguage: str) -> None: """Removes a hashtag via a hashtag rule """ rmHashtag = actionStr.split('remove ', 1)[1].strip() @@ -343,10 +344,11 @@ def _hashtagRemove(httpPrefix: str, domainFull: str, postJsonObject: {}, hashtagHtml = \ "#" + htId + "" - content = postJsonObject['object']['content'] + content = getContentFromPost(postJsonObject, systemLanguage) if hashtagHtml in content: content = content.replace(hashtagHtml, '').replace(' ', ' ') postJsonObject['object']['content'] = content + postJsonObject['object']['contentMap'][systemLanguage] = content rmTagObject = None for t in postJsonObject['object']['tag']: if t.get('type') and t.get('name'): @@ -365,7 +367,8 @@ def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {}, cachedWebfingers: {}, federationList: [], sendThreads: [], postLog: [], - moderated: bool, url: str) -> bool: + moderated: bool, url: str, + systemLanguage: str) -> bool: """Applies hashtag rules to a news post. Returns true if the post should be saved to the news timeline of this instance @@ -382,7 +385,7 @@ def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {}, # get the full text content of the post content = '' if postJsonObject['object'].get('content'): - content += postJsonObject['object']['content'] + content += getContentFromPost(postJsonObject, systemLanguage) if postJsonObject['object'].get('summary'): content += ' ' + postJsonObject['object']['summary'] content = content.lower() @@ -409,11 +412,11 @@ def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {}, if actionStr.startswith('add '): # add a hashtag _hashtagAdd(baseDir, httpPrefix, domainFull, - postJsonObject, actionStr, hashtags) + postJsonObject, actionStr, hashtags, systemLanguage) elif actionStr.startswith('remove '): # remove a hashtag _hashtagRemove(httpPrefix, domainFull, postJsonObject, - actionStr, hashtags) + actionStr, hashtags, systemLanguage) elif actionStr.startswith('block') or actionStr.startswith('drop'): # Block this item return False @@ -627,7 +630,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, blog['object']['published'] = dateStr blog['object']['content'] = rssDescription - blog['object']['contentMap']['en'] = rssDescription + blog['object']['contentMap'][systemLanguage] = rssDescription domainFull = getFullDomain(domain, port) @@ -642,7 +645,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, personCache, cachedWebfingers, federationList, sendThreads, postLog, - moderated, url) + moderated, url, systemLanguage) # save the post and update the index if savePost: @@ -664,7 +667,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, "\" class=\"addedHashtag\" " + \ "rel=\"tag\">#" + \ htId + "" - content = blog['object']['content'] + content = getContentFromPost(blog, systemLanguage) if hashtagHtml not in content: if content.endswith('

'): content = \ @@ -673,6 +676,7 @@ def _convertRSStoActivityPub(baseDir: str, httpPrefix: str, else: content += hashtagHtml blog['object']['content'] = content + blog['object']['contentMap'][systemLanguage] = content # update the newswire tags if new ones have been found by # _newswireHashtagProcessing @@ -749,7 +753,8 @@ def runNewswireDaemon(baseDir: str, httpd, httpd.maxTags, httpd.maxFeedItemSizeKb, httpd.maxNewswirePosts, - httpd.maxCategoriesFeedItemSizeKb) + httpd.maxCategoriesFeedItemSizeKb, + httpd.systemLanguage) if not httpd.newswire: if os.path.isfile(newswireStateFilename): diff --git a/newswire.py b/newswire.py index 5fba2748e..40558d586 100644 --- a/newswire.py +++ b/newswire.py @@ -18,6 +18,7 @@ from datetime import timezone from collections import OrderedDict from utils import validPostDate from categories import setHashtagCategory +from utils import getContentFromPost from utils import hasObjectDict from utils import firstParagraphFromString from utils import isPublicPost @@ -909,7 +910,7 @@ def _addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str, newswire: {}, maxBlogsPerAccount: int, indexFilename: str, - maxTags: int) -> None: + maxTags: int, systemLanguage: str) -> None: """Adds blogs for the given account to the newswire """ if not os.path.isfile(indexFilename): @@ -961,7 +962,8 @@ def _addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str, votes = [] if os.path.isfile(fullPostFilename + '.votes'): votes = loadJson(fullPostFilename + '.votes') - content = postJsonObject['object']['content'] + content = \ + getContentFromPost(postJsonObject, systemLanguage) description = firstParagraphFromString(content) description = removeHtml(description) tagsFromPost = _getHashtagsFromPost(postJsonObject) @@ -981,7 +983,7 @@ def _addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str, def _addBlogsToNewswire(baseDir: str, domain: str, newswire: {}, maxBlogsPerAccount: int, - maxTags: int) -> None: + maxTags: int, systemLanguage: str) -> None: """Adds blogs from each user account into the newswire """ moderationDict = {} @@ -1009,7 +1011,8 @@ def _addBlogsToNewswire(baseDir: str, domain: str, newswire: {}, domain = handle.split('@')[1] _addAccountBlogsToNewswire(baseDir, nickname, domain, newswire, maxBlogsPerAccount, - blogsIndex, maxTags) + blogsIndex, maxTags, + systemLanguage) break # sort the moderation dict into chronological order, latest first @@ -1029,7 +1032,8 @@ def getDictFromNewswire(session, baseDir: str, domain: str, maxPostsPerSource: int, maxFeedSizeKb: int, maxTags: int, maxFeedItemSizeKb: int, maxNewswirePosts: int, - maxCategoriesFeedItemSizeKb: int) -> {}: + maxCategoriesFeedItemSizeKb: int, + systemLanguage: str) -> {}: """Gets rss feeds as a dictionary from newswire file """ subscriptionsFilename = baseDir + '/accounts/newswire.txt' @@ -1077,7 +1081,7 @@ def getDictFromNewswire(session, baseDir: str, domain: str, # add blogs from each user account _addBlogsToNewswire(baseDir, domain, result, - maxPostsPerSource, maxTags) + maxPostsPerSource, maxTags, systemLanguage) # sort into chronological order, latest first sortedResult = OrderedDict(sorted(result.items(), reverse=True)) diff --git a/outbox.py b/outbox.py index 25bc2d163..b72aff995 100644 --- a/outbox.py +++ b/outbox.py @@ -16,6 +16,7 @@ from posts import outboxMessageCreateWrap from posts import savePostToBox from posts import sendToFollowersThread from posts import sendToNamedAddresses +from utils import getContentFromPost from utils import hasObjectDict from utils import getLocalNetworkAddresses from utils import getFullDomain @@ -178,7 +179,7 @@ def postMessageToOutbox(session, translate: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, allowLocalNetworkAccess: bool, - city: str) -> bool: + city: str, systemLanguage: str) -> bool: """post is received by the outbox Client to server message post https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery @@ -201,9 +202,9 @@ def postMessageToOutbox(session, translate: {}, # check that the outgoing post doesn't contain any markup # which can be used to implement exploits if hasObjectDict(messageJson): - if messageJson['object'].get('content'): - if dangerousMarkup(messageJson['object']['content'], - allowLocalNetworkAccess): + contentStr = getContentFromPost(messageJson, systemLanguage) + if contentStr: + if dangerousMarkup(contentStr, allowLocalNetworkAccess): print('POST to outbox contains dangerous markup: ' + str(messageJson)) return False @@ -264,7 +265,7 @@ def postMessageToOutbox(session, translate: {}, print('DEBUG: domain is blocked: ' + messageJson['actor']) return False # replace youtube, so that google gets less tracking data - replaceYouTube(messageJson, YTReplacementDomain) + replaceYouTube(messageJson, YTReplacementDomain, systemLanguage) # https://www.w3.org/TR/activitypub/#create-activity-outbox messageJson['object']['attributedTo'] = messageJson['actor'] if messageJson['object'].get('attachment'): @@ -390,7 +391,7 @@ def postMessageToOutbox(session, translate: {}, messageJson, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, debug): + recentPostsCache, debug, systemLanguage): inboxUpdateIndex('tlmedia', baseDir, postToNickname + '@' + domain, savedFilename, debug) diff --git a/posts.py b/posts.py index 8f1f886fe..79cf3a3f7 100644 --- a/posts.py +++ b/posts.py @@ -31,6 +31,7 @@ from session import postImage from webfinger import webfingerHandle from httpsig import createSignedHeader from siteactive import siteIsActive +from utils import getContentFromPost from utils import removeDomainPort from utils import getPortFromDomain from utils import hasObjectDict @@ -322,7 +323,7 @@ def _getPosts(session, outboxUrl: str, maxPosts: int, personCache: {}, raw: bool, simple: bool, debug: bool, projectVersion: str, httpPrefix: str, - domain: str) -> {}: + domain: str, systemLanguage: str) -> {}: """Gets public posts from an outbox """ personPosts = {} @@ -385,8 +386,8 @@ def _getPosts(session, outboxUrl: str, maxPosts: int, if not isPublic: continue - content = \ - item['object']['content'].replace(''', "'") + content = getContentFromPost(item, systemLanguage) + content = content.replace(''', "'") mentions = [] emoji = {} @@ -538,7 +539,7 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int, projectVersion: str, httpPrefix: str, domain: str, wordFrequency: {}, - domainList=[]) -> []: + domainList: [], systemLanguage: str) -> []: """Returns a list of domains referenced within public posts """ if not outboxUrl: @@ -563,9 +564,9 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int, break if not hasObjectDict(item): continue - if item['object'].get('content'): - _updateWordFrequency(item['object']['content'], - wordFrequency) + contentStr = getContentFromPost(item, systemLanguage) + if contentStr: + _updateWordFrequency(contentStr, wordFrequency) if item['object'].get('inReplyTo'): if isinstance(item['object']['inReplyTo'], str): postDomain, postPort = \ @@ -2932,7 +2933,8 @@ def isImageMedia(session, baseDir: str, httpPrefix: str, postJsonObject: {}, translate: {}, YTReplacementDomain: str, allowLocalNetworkAccess: bool, - recentPostsCache: {}, debug: bool) -> bool: + recentPostsCache: {}, debug: bool, + systemLanguage: str) -> bool: """Returns true if the given post has attached image media """ if postJsonObject['type'] == 'Announce': @@ -2942,7 +2944,8 @@ def isImageMedia(session, baseDir: str, httpPrefix: str, __version__, translate, YTReplacementDomain, allowLocalNetworkAccess, - recentPostsCache, debug) + recentPostsCache, debug, + systemLanguage) if postJsonAnnounce: postJsonObject = postJsonAnnounce if postJsonObject['type'] != 'Create': @@ -3500,7 +3503,8 @@ def archivePostsForPerson(httpPrefix: str, nickname: str, domain: str, def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str, raw: bool, simple: bool, proxyType: str, port: int, httpPrefix: str, - debug: bool, projectVersion: str) -> None: + debug: bool, projectVersion: str, + systemLanguage: str) -> None: """ This is really just for test purposes """ print('Starting new session for getting public posts') @@ -3536,13 +3540,14 @@ def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str, _getPosts(session, personUrl, 30, maxMentions, maxEmoji, maxAttachments, federationList, personCache, raw, simple, debug, - projectVersion, httpPrefix, domain) + projectVersion, httpPrefix, domain, systemLanguage) def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, - wordFrequency: {}, domainList=[]) -> []: + wordFrequency: {}, domainList: [], + systemLanguage: str) -> []: """ Returns a list of domains referenced within public posts """ if not session: @@ -3580,7 +3585,7 @@ def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str, maxAttachments, federationList, personCache, debug, projectVersion, httpPrefix, domain, - wordFrequency, domainList) + wordFrequency, domainList, systemLanguage) postDomains.sort() return postDomains @@ -3622,7 +3627,7 @@ def downloadFollowCollection(followType: str, def getPublicPostInfo(session, baseDir: str, nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, - wordFrequency: {}) -> []: + wordFrequency: {}, systemLanguage: str) -> []: """ Returns a dict of domains referenced within public posts """ if not session: @@ -3661,7 +3666,7 @@ def getPublicPostInfo(session, baseDir: str, nickname: str, domain: str, maxAttachments, federationList, personCache, debug, projectVersion, httpPrefix, domain, - wordFrequency, []) + wordFrequency, [], systemLanguage) postDomains.sort() domainsInfo = {} for d in postDomains: @@ -3687,7 +3692,8 @@ def getPublicPostDomainsBlocked(session, baseDir: str, nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, - wordFrequency: {}, domainList=[]) -> []: + wordFrequency: {}, domainList: [], + systemLanguage: str) -> []: """ Returns a list of domains referenced within public posts which are globally blocked on this instance """ @@ -3695,7 +3701,7 @@ def getPublicPostDomainsBlocked(session, baseDir: str, getPublicPostDomains(session, baseDir, nickname, domain, proxyType, port, httpPrefix, debug, projectVersion, - wordFrequency, domainList) + wordFrequency, domainList, systemLanguage) if not postDomains: return [] @@ -3743,7 +3749,8 @@ def checkDomains(session, baseDir: str, nickname: str, domain: str, proxyType: str, port: int, httpPrefix: str, debug: bool, projectVersion: str, - maxBlockedDomains: int, singleCheck: bool) -> None: + maxBlockedDomains: int, singleCheck: bool, + systemLanguage: str) -> None: """Checks follower accounts for references to globally blocked domains """ wordFrequency = {} @@ -3771,7 +3778,8 @@ def checkDomains(session, baseDir: str, nonMutualDomain, proxyType, port, httpPrefix, debug, projectVersion, - wordFrequency, []) + wordFrequency, [], + systemLanguage) if blockedDomains: if len(blockedDomains) > maxBlockedDomains: followerWarningStr += handle + '\n' @@ -3791,7 +3799,8 @@ def checkDomains(session, baseDir: str, nonMutualDomain, proxyType, port, httpPrefix, debug, projectVersion, - wordFrequency, []) + wordFrequency, [], + systemLanguage) if blockedDomains: print(handle) for d in blockedDomains: @@ -3889,7 +3898,8 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str, postJsonObject: {}, projectVersion: str, translate: {}, YTReplacementDomain: str, allowLocalNetworkAccess: bool, - recentPostsCache: {}, debug: bool) -> {}: + recentPostsCache: {}, debug: bool, + systemLanguage: str) -> {}: """Download the post referenced by an announce """ if not postJsonObject.get('object'): @@ -4074,7 +4084,7 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str, recentPostsCache) return None postJsonObject = announcedJson - replaceYouTube(postJsonObject, YTReplacementDomain) + replaceYouTube(postJsonObject, YTReplacementDomain, systemLanguage) if saveJson(postJsonObject, announceFilename): return postJsonObject return None diff --git a/schedule.py b/schedule.py index de3564d45..e01ccb874 100644 --- a/schedule.py +++ b/schedule.py @@ -112,7 +112,7 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd, httpd.YTReplacementDomain, httpd.showPublishedDateOnly, httpd.allowLocalNetworkAccess, - httpd.city): + httpd.city, httpd.systemLanguage): indexLines.remove(line) os.remove(postFilename) continue diff --git a/socnet.py b/socnet.py index 4ae5d3e1c..36d49bd75 100644 --- a/socnet.py +++ b/socnet.py @@ -17,7 +17,8 @@ from utils import getFullDomain def instancesGraph(baseDir: str, handles: str, proxyType: str, port: int, httpPrefix: str, - debug: bool, projectVersion: str) -> str: + debug: bool, projectVersion: str, + systemLanguage: str) -> str: """ Returns a dot graph of federating instances based upon a few sample handles. The handles argument should contain a comma separated list @@ -74,7 +75,7 @@ def instancesGraph(baseDir: str, handles: str, maxAttachments, federationList, personCache, debug, projectVersion, httpPrefix, domain, - wordFrequency, []) + wordFrequency, [], systemLanguage) postDomains.sort() for fedDomain in postDomains: dotLineStr = ' "' + domain + '" -> "' + fedDomain + '";\n' diff --git a/tests.py b/tests.py index 58bfa3d7b..baf303151 100644 --- a/tests.py +++ b/tests.py @@ -926,6 +926,8 @@ def testPostMessageBetweenServers(): assert receivedJson assert 'Why is a mouse when it spins?' in \ receivedJson['object']['content'] + assert 'Why is a mouse when it spins?' in \ + receivedJson['object']['contentMap'][systemLanguage] assert 'यह एक परीक्षण है' in receivedJson['object']['content'] print('Check that message received from Alice contains an attachment') assert receivedJson['object']['attachment'] @@ -2913,6 +2915,7 @@ def _testReplyToPublicPost() -> None: '@ninjarodent' + \ ' This is a test.

' + reply['object']['contentMap'][systemLanguage] = reply['object']['content'] assert reply['object']['tag'][0]['type'] == 'Mention' assert reply['object']['tag'][0]['name'] == '@ninjarodent@rat.site' assert reply['object']['tag'][0]['href'] == \ @@ -3444,6 +3447,8 @@ def _testLinksWithinPost() -> None: 'rel="nofollow noopener noreferrer" target="_blank">' + \ '' + \ 'freedombone.net

' + assert postJsonObject['object']['content'] == \ + postJsonObject['object']['contentMap'][systemLanguage] content = "

Some text

Other text

More text

" + \ "
Errno::EOHNOES (No such file or rodent @ " + \
@@ -3467,6 +3472,7 @@ def _testLinksWithinPost() -> None:
                          testEventDate, testEventTime, testLocation,
                          testIsArticle, systemLanguage)
     assert postJsonObject['object']['content'] == content
+    assert postJsonObject['object']['contentMap'][systemLanguage] == content
 
 
 def _testMastoApi():
diff --git a/utils.py b/utils.py
index 70be68a75..2f8c9f5d3 100644
--- a/utils.py
+++ b/utils.py
@@ -42,6 +42,8 @@ def getContentFromPost(postJsonObject: {}, systemLanguage: str) -> str:
             if thisPostJson['contentMap'].get(systemLanguage):
                 if isinstance(thisPostJson['contentMap'][systemLanguage], str):
                     return thisPostJson['contentMap'][systemLanguage]
+    if not isinstance(content, str):
+        return ''
     return content
 
 
@@ -1732,41 +1734,6 @@ def getCSS(baseDir: str, cssFilename: str, cssCache: {}) -> str:
     return None
 
 
-def isEventPost(messageJson: {}) -> bool:
-    """Is the given post a mobilizon-type event activity?
-    See https://framagit.org/framasoft/mobilizon/-/blob/
-    master/lib/federation/activity_stream/converter/event.ex
-    """
-    if not messageJson.get('id'):
-        return False
-    if not messageJson.get('actor'):
-        return False
-    if not hasObjectDict(messageJson):
-        return False
-    if not messageJson['object'].get('type'):
-        return False
-    if messageJson['object']['type'] != 'Event':
-        return False
-    print('Event arriving')
-    if not messageJson['object'].get('startTime'):
-        print('No event start time')
-        return False
-    if not messageJson['object'].get('actor'):
-        print('No event actor')
-        return False
-    if not messageJson['object'].get('content'):
-        print('No event content')
-        return False
-    if not messageJson['object'].get('name'):
-        print('No event name')
-        return False
-    if not messageJson['object'].get('uuid'):
-        print('No event UUID')
-        return False
-    print('Event detected')
-    return True
-
-
 def isBlogPost(postJsonObject: {}) -> bool:
     """Is the given post a blog post?
     """
diff --git a/webapp_column_right.py b/webapp_column_right.py
index ad89d56e0..d3a581b1f 100644
--- a/webapp_column_right.py
+++ b/webapp_column_right.py
@@ -11,6 +11,7 @@ import os
 from datetime import datetime
 from content import removeLongWords
 from content import limitRepeatedWords
+from utils import getContentFromPost
 from utils import removeHtml
 from utils import locatePost
 from utils import loadJson
@@ -636,7 +637,8 @@ def htmlEditNewswire(cssCache: {}, translate: {}, baseDir: str, path: str,
 
 def htmlEditNewsPost(cssCache: {}, translate: {}, baseDir: str, path: str,
                      domain: str, port: int,
-                     httpPrefix: str, postUrl: str) -> str:
+                     httpPrefix: str, postUrl: str,
+                     systemLanguage: str) -> str:
     """Edits a news post on the news/features timeline
     """
     if '/users/' not in path:
@@ -696,7 +698,7 @@ def htmlEditNewsPost(cssCache: {}, translate: {}, baseDir: str, path: str,
         '  
\n' - newsPostContent = postJsonObject['object']['content'] + newsPostContent = getContentFromPost(postJsonObject, systemLanguage) editNewsPostForm += \ ' \n' + + def htmlProfileAfterSearch(cssCache: {}, recentPostsCache: {}, maxRecentPosts: int, translate: {}, @@ -205,24 +229,22 @@ def htmlProfileAfterSearch(cssCache: {}, followIsPermitted = False if followIsPermitted: - profileStr += '
\n' - profileStr += '
\n' - profileStr += '
\n' profileStr += \ + '
\n' + \ + ' \n' + \ + '
\n' + \ ' \n' - profileStr += \ + personUrl + '">\n' + \ ' \n' - profileStr += \ + translate['Follow'] + '\n' + \ ' \n' - profileStr += '
\n' - profileStr += ' \n' - profileStr += '
\n' + translate['View'] + '\n' + \ + '
\n' + \ + ' \n' + \ + '
\n' i = 0 for item in parseUserFeed(session, outboxUrl, asHeader, @@ -277,15 +299,15 @@ def _getProfileHeader(baseDir: str, httpPrefix: str, """The header of the profile screen, containing background image and avatar """ - htmlStr = '\n\n
\n' - htmlStr += ' \n' + \ + ' \n' - htmlStr += ' \n' + \ + ' \n' - htmlStr += '
\n' - htmlStr += \ + 'src="/users/' + nickname + '/image_' + theme + '.png" />\n' + \ + '
\n' + \ ' \n' + \ @@ -344,13 +366,13 @@ def _getProfileHeader(baseDir: str, httpPrefix: str, '/qrcode.png" alt="' + translate['QR Code'] + '" title="' + \ translate['QR Code'] + '">' + \ '' + translate['QR Code'] + \
-        '

\n' - htmlStr += '

' + profileDescriptionShort + '

\n' - htmlStr += loginButton + '" src="/icons/qrcode.png" />

\n' + \ + '

' + profileDescriptionShort + '

\n' + loginButton if pinnedContent: htmlStr += pinnedContent.replace('

', '

📎', 1) - htmlStr += '

\n' - htmlStr += '
\n\n' + htmlStr += \ + ' \n' + \ + ' \n\n' return htmlStr @@ -370,24 +392,25 @@ def _getProfileHeaderAfterSearch(baseDir: str, """The header of a searched for handle, containing background image and avatar """ - htmlStr = '\n\n
\n' - htmlStr += ' \n' + \ + ' \n' - htmlStr += ' \n' + \ + ' \n' - htmlStr += '
\n' + 'src="' + imageUrl + '" />\n' + \ + '
\n' if avatarUrl: - htmlStr += ' \n' htmlStr += \ + ' \n' + \ ' \n' - htmlStr += '

' + displayName + '

\n' htmlStr += \ + '

' + displayName + '

\n' + \ '

@' + searchNickname + '@' + searchDomainFull + '
\n' if joinedDate: htmlStr += '

' + translate['Joined'] + ' ' + \ @@ -428,9 +451,10 @@ def _getProfileHeaderAfterSearch(baseDir: str, if ctr > 0: htmlStr += otherAccountshtml - htmlStr += '

' + profileDescriptionShort + '

\n' - htmlStr += '
\n' - htmlStr += '
\n\n' + htmlStr += \ + '

' + profileDescriptionShort + '

\n' + \ + ' \n' + \ + ' \n\n' return htmlStr @@ -1156,12 +1180,9 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, instanceStr += \ ' ' - instanceStr += \ - '
\n' - - instanceStr += \ + translate['Instance Logo'] + '' + \ + '
\n' + \ '

\n' @@ -1216,20 +1237,17 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, # Instance type instanceStr += \ '

\n' - instanceStr += \ + translate['Type of instance'] + '
\n' + \ ' ' + \ - translate['This is a media instance'] + '
\n' - instanceStr += \ + translate['This is a media instance'] + '
\n' + \ ' ' + \ - translate['This is a blogging instance'] + '
\n' - instanceStr += \ + translate['This is a blogging instance'] + '
\n' + \ ' ' + \ - translate['This is a news instance'] + '
\n' - instanceStr += ' \n' + translate['This is a news instance'] + '
\n' + \ + ' \n' # Role assignments section moderators = '' @@ -1238,14 +1256,14 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, with open(moderatorsFile, 'r') as f: moderators = f.read() # site moderators - roleAssignStr = '
' + \ - translate['Role Assignment'] + '\n' - roleAssignStr += '
' - roleAssignStr += '
\n' - roleAssignStr += ' ' + \ - translate['A list of moderator nicknames. One per line.'] - roleAssignStr += \ + roleAssignStr = \ + '
' + \ + translate['Role Assignment'] + '\n' + \ + '
' + \ + '
\n' + \ + ' ' + \ + translate['A list of moderator nicknames. One per line.'] + \ ' ' @@ -1272,9 +1290,9 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, if os.path.isfile(counselorsFile): with open(counselorsFile, 'r') as f: counselors = f.read() - roleAssignStr += '
\n' roleAssignStr += \ + '
\n' + \ ' ' - roleAssignStr += '
\n' + artists + '' + \ + '
\n' # Video section - peertubeStr = '
' + \ - translate['Video Settings'] + '\n' - peertubeStr += '
\n' - peertubeStr += \ - ' \n' idx = 'Show video previews for the following Peertube sites.' - peertubeStr += \ + peertubeStr = \ + '
' + \ + translate['Video Settings'] + '\n' + \ + '
\n' + \ + ' \n' + \ '
\n' peertubeInstancesStr = '' @@ -1312,8 +1329,7 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, peertubeStr += \ ' \n' - peertubeStr += \ + peertubeInstancesStr + '\n' + \ '
\n' YTReplacementDomain = getConfigParam(baseDir, "youtubedomain") @@ -1321,8 +1337,8 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, YTReplacementDomain = '' peertubeStr += \ ' \n' - peertubeStr += '
\n' + YTReplacementDomain + '">\n' + \ + '
\n' libretranslateUrl = getConfigParam(baseDir, 'libretranslateUrl') libretranslateApiKey = getConfigParam(baseDir, 'libretranslateApiKey') @@ -1337,16 +1353,16 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, def _htmlEditProfileDangerZone(translate: {}) -> str: """danger zone section of Edit Profile screen """ - editProfileForm = '
' + \ - translate['Danger Zone'] + '\n' - editProfileForm += '
\n' - editProfileForm += '
\n' - editProfileForm += \ + editProfileForm = \ + '
' + \ + translate['Danger Zone'] + '\n' + \ + '
\n' + \ + '
\n' + \ ' ' + \ - translate['Deactivate this account'] + '
\n' - editProfileForm += '
\n' + translate['Deactivate this account'] + '
\n' + \ + '
\n' return editProfileForm @@ -1364,8 +1380,7 @@ def _htmlEditProfileSkills(baseDir: str, nickname: str, domain: str, skillsStr += \ '

' - skillsStr += \ + '" value="' + skillDesc + '" style="width:40%">' + \ '

' @@ -1374,26 +1389,24 @@ def _htmlEditProfileSkills(baseDir: str, nickname: str, domain: str, skillsStr += \ '

' - skillsStr += \ + '" value="" style="width:40%">' + \ '

' - skillsStr += ' \n' + str(skillCtr) + '" value="50">

' + \ + ' \n' - editProfileForm = '
' + \ - translate['Skills'] + '\n' - editProfileForm += '
\n' - editProfileForm += \ - '
\n' idx = 'If you want to participate within organizations then you ' + \ 'can indicate some skills that you have and approximate ' + \ 'proficiency levels. This helps organizers to construct ' + \ 'teams with an appropriate combination of skills.' - editProfileForm += ' \n' - editProfileForm += skillsStr + editProfileForm = \ + '
' + \ + translate['Skills'] + '\n' + \ + '
\n' + \ + '
\n' + \ + ' \n' + skillsStr return editProfileForm @@ -1408,18 +1421,17 @@ def _htmlEditProfileGitProjects(baseDir: str, nickname: str, domain: str, with open(gitProjectsFilename, 'r') as gitProjectsFile: gitProjectsStr = gitProjectsFile.read() - editProfileForm = '
' + \ - translate['Git Projects'] + '\n' - editProfileForm += '
\n' idx = 'List of project names that you wish to receive git patches for' - editProfileForm += \ + editProfileForm = \ + '
' + \ + translate['Git Projects'] + '\n' + \ + '
\n' + \ ' \n' - editProfileForm += \ + translate[idx] + '\n' + \ ' \n' - editProfileForm += '
\n' + gitProjectsStr + '\n' + \ + '
\n' return editProfileForm @@ -1517,71 +1529,56 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, editProfileForm += \ ' \n' - editProfileForm += '
\n' - editProfileForm += ' \n' - - editProfileForm += \ + filterStr + '\n' + \ '
\n' - editProfileForm += '
\n' - editProfileForm += \ + translate['Word Replacements'] + '
\n' + \ + '
\n' + \ ' \n' - - editProfileForm += \ + switchStr + '\n' + \ '
\n' - editProfileForm += '
\n' - editProfileForm += \ + translate['Autogenerated Hashtags'] + '
\n' + \ + '
\n' + \ ' \n' - - editProfileForm += \ + autoTags + '\n' + \ '
\n' - editProfileForm += '
\n' - editProfileForm += \ + translate['Autogenerated Content Warnings'] + '\n' + \ + '
\n' + \ ' \n' - editProfileForm += \ - '
\n' idx = 'Blocked accounts, one per line, in the form ' + \ 'nickname@domain or *@blockeddomain' editProfileForm += \ - '
\n' - editProfileForm += \ + '
\n' + \ + '
\n' + \ ' \n' - editProfileForm += \ - '
\n' idx = 'Direct messages are always allowed from these instances.' editProfileForm += \ + '
\n' + \ '
\n' - editProfileForm += \ + translate[idx] + '\n' + \ ' \n' - editProfileForm += \ - '
\n' idx = 'Federate only with a defined set of instances. ' + \ 'One domain name per line.' editProfileForm += \ + '
\n' + \ '
\n' - editProfileForm += \ + translate[idx] + '\n' + \ ' \n' @@ -1593,8 +1590,7 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, userAgentsBlockedStr += ua editProfileForm += \ '
\n' - editProfileForm += \ + translate['Blocked User Agents'] + '\n' + \ ' \n' @@ -1662,29 +1658,21 @@ def _htmlEditProfileBackground(newsInstance: bool, translate: {}) -> str: ' \n' + \ ' \n' - - editProfileForm += \ + ' accept="' + imageFormats + '">\n' + \ '
\n' + \ ' \n' - - editProfileForm += \ + ' accept="' + imageFormats + '">\n' + \ '
\n' + \ ' \n' - - editProfileForm += \ + ' accept="' + imageFormats + '">\n' + \ '
\n' + \ ' \n' - - editProfileForm += \ + ' accept="' + imageFormats + '">\n' + \ '
\n' + \ ' str: return editProfileForm +def _beginEditSection(label: str) -> str: + """returns the html for begining a dropdown section on edit profile screen + """ + return \ + '
' + label + '\n' + \ + '
' + + +def _endEditSection() -> str: + """returns the html for ending a dropdown section on edit profile screen + """ + return '
\n' + + def _htmlEditProfileContactInfo(nickname: str, emailAddress: str, xmppAddress: str, @@ -1709,63 +1711,30 @@ def _htmlEditProfileContactInfo(nickname: str, translate: {}) -> str: """Contact Information section of edit profile screen """ - editProfileForm = '
' + \ - translate['Contact Details'] + '\n' - editProfileForm += '
' - editProfileForm += '
\n' - editProfileForm += \ - ' \n' - editProfileForm += \ - '
\n' - editProfileForm += \ - ' \n' - editProfileForm += '
\n' - editProfileForm += \ - ' \n' + editProfileForm = _beginEditSection(translate['Contact Details']) + editProfileForm += _editText(translate['Email'], + 'email', emailAddress) + editProfileForm += _editText(translate['XMPP'], + 'xmppAddress', xmppAddress) + editProfileForm += _editText(translate['Matrix'], + 'matrixAddress', matrixAddress) + editProfileForm += _editText('SSB', 'ssbAddress', ssbAddress) + editProfileForm += _editText('Tox', 'toxAddress', toxAddress) + editProfileForm += _editText('Briar', 'briarAddress', briarAddress) + editProfileForm += _editText('Jami', 'jamiAddress', jamiAddress) + editProfileForm += _editText('Cwtch', 'cwtchAddress', cwtchAddress) + editProfileForm += _editText(translate['PGP Fingerprint'], + 'openpgp', PGPfingerprint) editProfileForm += \ - '
\n' + \ - ' \n' - + _editTextArea(translate['PGP'], 'pgp', PGPpubKey, 600, + '-----BEGIN PGP PUBLIC KEY BLOCK-----', False) editProfileForm += \ - '
\n' + \ - ' \n' - - editProfileForm += \ - '
\n' + \ - ' \n' - - editProfileForm += \ - '
\n' + \ - ' \n' - - editProfileForm += \ - '
\n' + \ - ' \n' - - editProfileForm += \ - '
\n' + \ - ' \n' + \ - '
\n' + \ - ' \n' + \ '
\n' + \ - '
\n' + translate['Following'] + '
\n' + + editProfileForm += _endEditSection() return editProfileForm From 1d11b177d88fd5256b80e8bd72ed4dc5f291681e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 22 Jul 2021 13:36:31 +0100 Subject: [PATCH 079/459] Tidying of profile edit fields --- webapp_profile.py | 243 +++++++++++++++++++--------------------------- 1 file changed, 102 insertions(+), 141 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index cb4146239..16bfc14f2 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -60,21 +60,51 @@ from webapp_post import individualPostAsHtml from webapp_timeline import htmlIndividualShare +def _beginEditSection(label: str) -> str: + """returns the html for begining a dropdown section on edit profile screen + """ + return \ + '
' + label + '\n' + \ + '
' + + +def _endEditSection() -> str: + """returns the html for ending a dropdown section on edit profile screen + """ + return '
\n' + + def _editText(label: str, name: str, value: str = "") -> str: """Returns html for editing a text field """ + if value is None: + value = '' return \ '
\n' + \ ' \n' +def _editCheckBox(label: str, name: str, checked: bool = False) -> str: + """Returns html for editing a checkbox field + """ + checkedStr = '' + if checked: + checkedStr = ' checked' + + return \ + ' ' + label + '
\n' + + def _editTextArea(label: str, name: str, value: str = "", height: int = 600, placeholder: str = "", spellcheck: bool = False) -> str: """Returns html for editing a textarea field """ + if value is None: + value = '' return \ '
\n' + \ ' ' - else: - instanceStr += \ - ' ' + instanceStr = _beginEditSection(translate['Instance Settings']) instanceStr += \ - ' ' - if customSubmitText: - instanceStr += \ - '
' - else: - instanceStr += \ - '
' - + _editText(translate['Instance Title'], + 'instanceTitle', instanceTitle) + instanceStr += '
\n' + instanceStr += \ + _editText(translate['Instance Short Description'], + 'instanceDescriptionShort', instanceDescriptionShort) + instanceStr += '
\n' + instanceStr += \ + _editTextArea(translate['Instance Description'], + 'instanceDescription', instanceDescription, 200, + '', True) + instanceStr += \ + _editText(translate['Custom post submit button text'], + 'customSubmitText', customSubmitText) + instanceStr += '
\n' instanceStr += \ ' ' + \ @@ -1190,64 +1182,51 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, translate['Show numbers of accounts within instance metadata'] if getConfigParam(baseDir, "showNodeInfoAccounts"): instanceStr += \ - ' ' + \ - nodeInfoStr + '
\n' + _editCheckBox(nodeInfoStr, 'showNodeInfoAccounts', True) else: instanceStr += \ - ' ' + \ - nodeInfoStr + '
\n' + _editCheckBox(nodeInfoStr, 'showNodeInfoAccounts', False) nodeInfoStr = \ translate['Show version number within instance metadata'] if getConfigParam(baseDir, "showNodeInfoVersion"): instanceStr += \ - ' ' + \ - nodeInfoStr + '
\n' + _editCheckBox(nodeInfoStr, 'showNodeInfoVersion', True) else: instanceStr += \ - ' ' + \ - nodeInfoStr + '
\n' + _editCheckBox(nodeInfoStr, 'showNodeInfoVersion', False) if getConfigParam(baseDir, "verifyAllSignatures"): instanceStr += \ - ' ' + \ - translate['Verify all signatures'] + '
\n' + _editCheckBox(translate['Verify all signatures'], + 'verifyallsignatures', True) else: instanceStr += \ - ' ' + \ - translate['Verify all signatures'] + '
\n' + _editCheckBox(translate['Verify all signatures'], + 'verifyallsignatures', False) instanceStr += translate['Enabling broch mode'] + '
\n' if getConfigParam(baseDir, "brochMode"): instanceStr += \ - ' ' + \ - translate['Broch mode'] + '
\n' + _editCheckBox(translate['Broch mode'], 'brochMode', True) else: instanceStr += \ - ' ' + \ - translate['Broch mode'] + '
\n' + _editCheckBox(translate['Broch mode'], 'brochMode', False) # Instance type instanceStr += \ '

\n' + \ - ' ' + \ - translate['This is a media instance'] + '
\n' + \ - ' ' + \ - translate['This is a blogging instance'] + '
\n' + \ - ' ' + \ - translate['This is a news instance'] + '
\n' + \ - '
\n' + translate['Type of instance'] + '
\n' + instanceStr += \ + _editCheckBox(translate['This is a media instance'], + 'mediaInstance', mediaInstanceStr) + instanceStr += \ + _editCheckBox(translate['This is a blogging instance'], + 'blogsInstance', blogsInstanceStr) + instanceStr += \ + _editCheckBox(translate['This is a news instance'], + 'newsInstance', newsInstanceStr) + + instanceStr += _endEditSection() # Role assignments section moderators = '' @@ -1330,15 +1309,14 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, ' \n' + \ - '
\n' + '
\n' YTReplacementDomain = getConfigParam(baseDir, "youtubedomain") if not YTReplacementDomain: YTReplacementDomain = '' peertubeStr += \ - ' \n' + \ - '
\n' + _editText(translate['YouTube Replacement Domain'], + 'ytdomain', YTReplacementDomain) + peertubeStr += _endEditSection() libretranslateUrl = getConfigParam(baseDir, 'libretranslateUrl') libretranslateApiKey = getConfigParam(baseDir, 'libretranslateApiKey') @@ -1356,12 +1334,17 @@ def _htmlEditProfileDangerZone(translate: {}) -> str: editProfileForm = \ '
' + \ translate['Danger Zone'] + '\n' + \ - '
\n' + \ + '
\n' + + editProfileForm = \ '
\n' + \ - ' ' + \ - translate['Deactivate this account'] + '
\n' + \ + translate['Danger Zone'] + '
\n' + + editProfileForm = \ + _editCheckBox(translate['Deactivate this account'], + 'deactivateThisAccount', False) + + editProfileForm = \ '
\n' return editProfileForm @@ -1683,20 +1666,6 @@ def _htmlEditProfileBackground(newsInstance: bool, translate: {}) -> str: return editProfileForm -def _beginEditSection(label: str) -> str: - """returns the html for begining a dropdown section on edit profile screen - """ - return \ - '
' + label + '\n' + \ - '
' - - -def _endEditSection() -> str: - """returns the html for ending a dropdown section on edit profile screen - """ - return '
\n' - - def _htmlEditProfileContactInfo(nickname: str, emailAddress: str, xmppAddress: str, @@ -1747,33 +1716,26 @@ def _htmlEditProfileOptions(manuallyApprovesFollowers: str, """ editProfileForm = '
\n' editProfileForm += \ - ' ' + translate['Approve follower requests'] + '
\n' + _editCheckBox(translate['Approve follower requests'], + 'approveFollowers', manuallyApprovesFollowers) editProfileForm += \ - ' ' + translate['This is a bot account'] + '
\n' + _editCheckBox(translate['This is a bot account'], + 'isBot', isBot) editProfileForm += \ - ' ' + \ - translate['This is a group account'] + '
\n' + _editCheckBox(translate['This is a group account'], + 'isGroup', isGroup) editProfileForm += \ - ' ' + \ - translate['Only people I follow can send me DMs'] + '
\n' + _editCheckBox(translate['Only people I follow can send me DMs'], + 'followDMs', followDMs) editProfileForm += \ - ' ' + \ - translate['Remove Twitter posts'] + '
\n' + _editCheckBox(translate['Remove Twitter posts'], + 'removeTwitter', removeTwitter) editProfileForm += \ - ' ' + \ - translate['Notify when posts are liked'] + '
\n' + _editCheckBox(translate['Notify when posts are liked'], + 'notifyLikes', notifyLikes) editProfileForm += \ - ' ' + \ - translate["Don't show the Like button"] + '
\n' + _editCheckBox(translate["Don't show the Like button"], + 'hideLikeButton', hideLikeButton) editProfileForm += '
\n' return editProfileForm @@ -1917,9 +1879,8 @@ def _htmlEditProfileTopBanner(baseDir: str, if scheduledPostsExist(baseDir, nickname, domain): editProfileForm += '
\n' editProfileForm += \ - ' ' + \ - translate['Remove scheduled posts'] + '
\n' + _editCheckBox(translate['Remove scheduled posts'], + 'removeScheduledPosts', False) editProfileForm += '
\n' return editProfileForm From 588b80af3caffe850d504b2e4bac43dbf5c30667 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 22 Jul 2021 14:26:43 +0100 Subject: [PATCH 080/459] Tidying of text fields --- webapp_profile.py | 72 +++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index 16bfc14f2..cda99216a 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -74,15 +74,19 @@ def _endEditSection() -> str: return ' \n' -def _editText(label: str, name: str, value: str = "") -> str: +def _editText(label: str, name: str, value: str = "", + placeholder: str = "") -> str: """Returns html for editing a text field """ if value is None: value = '' + placeholderStr = '' + if placeholder: + placeholderStr = ' placeholder="' + placeholder + '"' return \ '
\n' + \ ' \n' + value + '"' + placeholderStr + '>\n' def _editCheckBox(label: str, name: str, checked: bool = False) -> str: @@ -1605,22 +1609,15 @@ def _htmlEditProfileLibreTranslate(translate: {}, libretranslateApiKey: str) -> str: """Change automatic translation settings """ - if libretranslateUrl is None: - libretranslateUrl = '' - if libretranslateApiKey is None: - libretranslateApiKey = '' + editProfileForm = _beginEditSection('LibreTranslate') - editProfileForm = \ - '
LibreTranslate\n' + \ - '
\n' + \ - '
\n' + \ - '
\n' + \ - '
\n' + \ - ' \n' + \ - '
\n' + editProfileForm += \ + _editText('URL', 'libretranslateUrl', libretranslateUrl, + 'http://0.0.0.0:5000') + editProfileForm += \ + _editText('API Key', 'libretranslateApiKey', libretranslateApiKey) + + editProfileForm += _endEditSection() return editProfileForm @@ -1776,10 +1773,7 @@ def _htmlEditProfileMain(baseDir: str, displayNickname: str, bioStr: str, editProfileForm = '
\n' editProfileForm += \ - ' \n' + \ - '
\n' + _editText(translate['Nickname'], 'displayNickname', displayNickname) editProfileForm += \ '
\n' return editProfileForm From e66891f8abb282cf794ac4f90c8d7d3efb8374bc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 22 Jul 2021 14:37:41 +0100 Subject: [PATCH 081/459] Beginning and end of dropdown sections on edit profile screen --- webapp_profile.py | 62 +++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 42 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index cda99216a..e40197347 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1116,9 +1116,8 @@ def _htmlEditProfileGraphicDesign(baseDir: str, translate: {}) -> str: """ themeFormats = '.zip, .gz' - graphicsStr = '
' + \ - translate['Graphic Design'] + '\n' - graphicsStr += '
' + graphicsStr = _beginEditSection(translate['Graphic Design']) + graphicsStr += _htmlThemesDropdown(baseDir, translate) graphicsStr += \ '
\n' + graphicsStr += _endEditSection() return graphicsStr @@ -1240,9 +1239,7 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, moderators = f.read() # site moderators roleAssignStr = \ - '
' + \ - translate['Role Assignment'] + '\n' + \ - '
' + \ + _beginEditSection(translate['Role Assignment']) + \ '
\n' + \ ' ' + \ @@ -1293,15 +1290,12 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, ' ' + \ - '
\n' + artists + '' + _endEditSection() # Video section idx = 'Show video previews for the following Peertube sites.' peertubeStr = \ - '
' + \ - translate['Video Settings'] + '\n' + \ - '
\n' + \ + _beginEditSection(translate['Video Settings']) + \ ' \n' + \ '
\n' + str(skillCtr) + '" value="50">

' + _endEditSection() idx = 'If you want to participate within organizations then you ' + \ 'can indicate some skills that you have and approximate ' + \ 'proficiency levels. This helps organizers to construct ' + \ 'teams with an appropriate combination of skills.' editProfileForm = \ - '
' + \ - translate['Skills'] + '\n' + \ - '
\n' + \ + _beginEditSection(translate['Skills']) + \ '
\n' + \ '
\n' grayscale = _grayscaleEnabled(baseDir) themesDropdown += \ - _editCheckBox(translate['Grayscale'], 'grayscale', grayscale) + editCheckBox(translate['Grayscale'], 'grayscale', grayscale) themesDropdown += ' ' + _endEditSection() + artists + '' + endEditSection() # Video section idx = 'Show video previews for the following Peertube sites.' peertubeStr = \ - _beginEditSection(translate['Video Settings']) + \ + beginEditSection(translate['Video Settings']) + \ ' \n' + \ '
\n' return htmlStr + + +def beginEditSection(label: str) -> str: + """returns the html for begining a dropdown section on edit profile screen + """ + return \ + '
' + label + '\n' + \ + '
' + + +def endEditSection() -> str: + """returns the html for ending a dropdown section on edit profile screen + """ + return '
\n' + + +def editTextField(label: str, name: str, value: str = "", + placeholder: str = "") -> str: + """Returns html for editing a text field + """ + if value is None: + value = '' + placeholderStr = '' + if placeholder: + placeholderStr = ' placeholder="' + placeholder + '"' + return \ + '
\n' + \ + ' \n' + + +def editCheckBox(label: str, name: str, checked: bool = False) -> str: + """Returns html for editing a checkbox field + """ + checkedStr = '' + if checked: + checkedStr = ' checked' + + return \ + ' ' + label + '
\n' + + +def editTextArea(label: str, name: str, value: str = "", + height: int = 600, + placeholder: str = "", + spellcheck: bool = False) -> str: + """Returns html for editing a textarea field + """ + if value is None: + value = '' + return \ + '
\n' + \ + ' \n' diff --git a/webapp_welcome_profile.py b/webapp_welcome_profile.py index 76aff001a..416644475 100644 --- a/webapp_welcome_profile.py +++ b/webapp_welcome_profile.py @@ -17,6 +17,7 @@ from utils import getImageFormats from utils import acctDir from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlFooter +from webapp_utils import editTextField from markdown import markdownToHtml @@ -102,10 +103,9 @@ def htmlWelcomeProfile(baseDir: str, nickname: str, domain: str, actorJson = loadJson(actorFilename) displayNickname = actorJson['name'] profileForm += '
\n' - profileForm += '
\n' - profileForm += '
\n' + profileForm += \ + editTextField(translate['Nickname'], 'displayNickname', + displayNickname) bioStr = \ actorJson['summary'].replace('

', '').replace('

', '') From 947f7131ef60220d792a9a4d7c082b26011cc956 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 22 Jul 2021 19:35:45 +0100 Subject: [PATCH 083/459] Tidying --- blog.py | 9 +++--- webapp_profile.py | 73 ++++++++++++++++------------------------------- webapp_utils.py | 7 +++-- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/blog.py b/blog.py index 9adf15063..12d753906 100644 --- a/blog.py +++ b/blog.py @@ -15,6 +15,7 @@ from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithBlogMarkup from webapp_utils import htmlFooter from webapp_utils import getPostAttachmentsAsHtml +from webapp_utils import editTextArea from webapp_media import addEmbeddedElements from utils import getActorLanguagesList from utils import getBaseContentFromPost @@ -854,17 +855,15 @@ def htmlEditBlog(mediaInstance: bool, translate: {}, editBlogForm += \ ' ' editBlogForm += '' - editBlogForm += '
' + editBlogForm += '
' messageBoxHeight = 800 contentStr = getBaseContentFromPost(postJsonObject, systemLanguage) contentStr = contentStr.replace('

', '').replace('

', '\n') editBlogForm += \ - ' ' + editTextArea(placeholderMessage, 'message', contentStr, + messageBoxHeight, '', True) editBlogForm += dateAndLocation if not mediaInstance: editBlogForm += editBlogImageSection diff --git a/webapp_profile.py b/webapp_profile.py index 9020c7f81..d542de00c 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1218,12 +1218,9 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, with open(counselorsFile, 'r') as f: counselors = f.read() roleAssignStr += \ - '
\n' + \ - ' ' + ' ' + \ + editTextArea(translate['Counselors'], 'counselors', counselors, + 200, '', False) # artists artists = '' @@ -1232,28 +1229,20 @@ def _htmlEditProfileInstance(baseDir: str, translate: {}, with open(artistsFile, 'r') as f: artists = f.read() roleAssignStr += \ - '
\n' + \ - ' ' + endEditSection() + ' ' + \ + editTextArea(translate['Artists'], 'artists', artists, + 200, '', False) + roleAssignStr += endEditSection() # Video section - idx = 'Show video previews for the following Peertube sites.' - peertubeStr = \ - beginEditSection(translate['Video Settings']) + \ - ' \n' + \ - '
\n' + peertubeStr = beginEditSection(translate['Video Settings']) peertubeInstancesStr = '' for url in peertubeInstances: peertubeInstancesStr += url + '\n' peertubeStr += \ - ' \n' + \ + editTextArea(translate['Peertube Instances'], 'ptInstances', + peertubeInstancesStr, 200, '', False) + peertubeStr += \ '
\n' YTReplacementDomain = getConfigParam(baseDir, "youtubedomain") if not YTReplacementDomain: @@ -1343,13 +1332,11 @@ def _htmlEditProfileGitProjects(baseDir: str, nickname: str, domain: str, gitProjectsStr = gitProjectsFile.read() idx = 'List of project names that you wish to receive git patches for' + editProfileForm = beginEditSection(translate['Git Projects']) editProfileForm = \ - beginEditSection(translate['Git Projects']) + \ - ' \n' + \ - ' \n' + endEditSection() + editTextArea(translate[idx], 'gitProjects', gitProjectsStr, + 100, '', False) + editProfileForm = endEditSection() return editProfileForm @@ -1472,21 +1459,14 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, idx = 'Blocked accounts, one per line, in the form ' + \ 'nickname@domain or *@blockeddomain' editProfileForm += \ - '
\n' + \ - '
\n' + \ - ' \n' + editTextArea(translate['Blocked accounts'], 'blocked', blockedStr, + 200, '', False) idx = 'Direct messages are always allowed from these instances.' editProfileForm += \ - '
\n' + \ - '
\n' + \ - ' \n' + editTextArea(translate['Direct Message permitted instances'], + 'dmAllowedInstances', dmAllowedInstancesStr, + 200, '', False) idx = 'Federate only with a defined set of instances. ' + \ 'One domain name per line.' @@ -1505,11 +1485,9 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, userAgentsBlockedStr += '\n' userAgentsBlockedStr += ua editProfileForm += \ - '
\n' + \ - ' \n' + editTextArea(translate['Blocked User Agents'], + 'userAgentsBlockedStr', userAgentsBlockedStr, + 200, '', False) editProfileForm += endEditSection() return editProfileForm @@ -1702,10 +1680,7 @@ def _htmlEditProfileMain(baseDir: str, displayNickname: str, bioStr: str, displayNickname) editProfileForm += \ - ' \n' + \ - ' \n' + editTextArea(translate['Your bio'], 'bio', bioStr, 200, '', True) editProfileForm += \ '
\n' @@ -211,7 +216,8 @@ def htmlAccountInfo(cssCache: {}, translate: {}, followerNickname = getNicknameFromActor(actor) followerDomain, followerPort = getDomainFromActor(actor) followerDomainFull = getFullDomain(followerDomain, followerPort) - infoForm += '' + \ + infoForm += '' + \ followerNickname + '@' + followerDomainFull + '

\n' infoForm += '\n' From 946c10e3aa2de697a4c3229f04acaef7078a0e78 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 23 Jul 2021 15:32:21 +0100 Subject: [PATCH 097/459] Handle posts without text --- utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils.py b/utils.py index ea0be45c5..6d0bea5f6 100644 --- a/utils.py +++ b/utils.py @@ -852,6 +852,8 @@ def _genderFromString(translate: {}, text: str) -> str: """Given some text, does it contain a gender description? """ gender = None + if not text: + return None textOrig = text text = text.lower() if translate['He/Him'].lower() in text or \ From 234f7c1efcc8de9841bda40186c31bf9ce221f3e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 23 Jul 2021 20:22:56 +0100 Subject: [PATCH 098/459] Increase font size --- theme/hacker/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/hacker/theme.json b/theme/hacker/theme.json index 244b494ae..6bc48bcc5 100644 --- a/theme/hacker/theme.json +++ b/theme/hacker/theme.json @@ -8,7 +8,7 @@ "font-size-newswire": "16px", "font-size-newswire-mobile": "36px", "font-size-dropdown-header": "26px", - "font-size-mobile": "20px", + "font-size-mobile": "40px", "font-size": "26px", "font-size2": "16px", "font-size3": "36px", From d70badb76bd80d96165053cb4446ba14b89529b3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 11:13:40 +0100 Subject: [PATCH 099/459] Deprecate event posts --- webapp_create_post.py | 159 ++++++------------------------------------ 1 file changed, 22 insertions(+), 137 deletions(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index 3dbe35f9c..083dd93f7 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -11,7 +11,6 @@ import os from utils import isPublicPostFromUrl from utils import getNicknameFromActor from utils import getDomainFromActor -from utils import getImageFormats from utils import getMediaFormats from utils import getConfigParam from utils import acctDir @@ -273,7 +272,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, pathBase = path.replace('/newreport', '').replace('/newpost', '') pathBase = pathBase.replace('/newblog', '').replace('/newshare', '') pathBase = pathBase.replace('/newunlisted', '') - pathBase = pathBase.replace('/newevent', '') pathBase = pathBase.replace('/newreminder', '') pathBase = pathBase.replace('/newfollowers', '').replace('/newdm', '') @@ -281,19 +279,10 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, newPostImageSection += \ editTextField(translate['Image description'], 'imageDescription', '') - if path.endswith('/newevent'): - newPostImageSection += \ - ' \n' - newPostImageSection += \ - ' \n' - else: - newPostImageSection += \ - ' \n' + newPostImageSection += \ + ' \n' newPostImageSection += ' \n' scopeIcon = 'scope_public.png' @@ -334,12 +323,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, scopeIcon = 'scope_reminder.png' scopeDescription = translate['Reminder'] endpoint = 'newreminder' - elif path.endswith('/newevent'): - scopeIcon = 'scope_event.png' - scopeDescription = translate['Event'] - endpoint = 'newevent' - placeholderSubject = translate['Event name'] - placeholderMessage = translate['Describe the event'] + '...' elif path.endswith('/newreport'): scopeIcon = 'scope_report.png' scopeDescription = translate['Report'] @@ -417,69 +400,11 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, if endpoint != 'newshare' and \ endpoint != 'newreport' and \ endpoint != 'newquestion': - dateAndLocation = '
\n' - - if endpoint == 'newevent': - # event status - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '
\n' - dateAndLocation += '
\n' - # maximum attendees - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '
\n' - # event joining options - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '
\n' - dateAndLocation += '
\n' - # Event posts don't allow replies - they're just an announcement. - # They also have a few more checkboxes - dateAndLocation += \ - '

\n' - dateAndLocation += \ - '

' + \ - '

\n' - else: - dateAndLocation += \ - '

\n' + dateAndLocation = \ + '
\n' + \ + '

\n' if endpoint == 'newpost': dateAndLocation += \ @@ -487,64 +412,25 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, 'name="pinToProfile">

\n' - if not inReplyTo and endpoint != 'newevent': + if not inReplyTo: dateAndLocation += \ '

\n' - if endpoint != 'newevent': - dateAndLocation += \ - '

\n' - # select a date and time for this post - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '

\n' - else: - dateAndLocation += '
\n' - dateAndLocation += '
\n' - dateAndLocation += \ - '

\n' - # select start time for the event - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '

\n' - # select end time for the event - dateAndLocation += \ - '
\n' - dateAndLocation += '\n' - dateAndLocation += '\n' - dateAndLocation += '\n' + dateAndLocation += \ + '

\n' + # select a date and time for this post + dateAndLocation += '\n' + dateAndLocation += '\n' + dateAndLocation += '

\n' - if endpoint == 'newevent': - dateAndLocation += '
\n' - dateAndLocation += '
\n' - dateAndLocation += '
\n' - dateAndLocation += \ - ' \n' dateAndLocation += '
\n' dateAndLocation += '
\n' dateAndLocation += \ @@ -588,7 +474,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, dropdownUnlistedSuffix = '/newunlisted' dropdownFollowersSuffix = '/newfollowers' dropdownDMSuffix = '/newdm' - dropdownEventSuffix = '/newevent' dropdownReminderSuffix = '/newreminder' dropdownReportSuffix = '/newreport' if inReplyTo or mentions: From 67747bad72b023d193ec0129d0a9a16c3b2db604 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:30:46 +0100 Subject: [PATCH 100/459] Add quantity to shared items --- daemon.py | 8 +++++++- epicyon.py | 8 ++++++++ shares.py | 11 +++++++++-- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/ku.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/sw.json | 3 ++- translations/zh.json | 3 ++- webapp_create_post.py | 3 +++ webapp_search.py | 7 +++++-- webapp_timeline.py | 5 ++++- 23 files changed, 70 insertions(+), 23 deletions(-) diff --git a/daemon.py b/daemon.py index cec24bb9c..00c46ffef 100644 --- a/daemon.py +++ b/daemon.py @@ -13627,6 +13627,8 @@ class PubServer(BaseHTTPRequestHandler): return 1 return -1 elif postType == 'newshare': + if not fields.get('itemQty'): + return -1 if not fields.get('itemType'): return -1 if not fields.get('category'): @@ -13646,6 +13648,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.baseDir, nickname, self.server.domain) + itemQty = 1 + if fields['itemQty']: + if fields['itemQty'].isdigit(): + itemQty = int(fields['itemQty']) addShare(self.server.baseDir, self.server.httpPrefix, nickname, @@ -13653,7 +13659,7 @@ class PubServer(BaseHTTPRequestHandler): fields['subject'], fields['message'], filename, - fields['itemType'], + itemQty, fields['itemType'], fields['category'], fields['location'], durationStr, diff --git a/epicyon.py b/epicyon.py index 7ce570793..684ae8521 100644 --- a/epicyon.py +++ b/epicyon.py @@ -558,6 +558,9 @@ parser.add_argument('--summary', dest='summary', type=str, parser.add_argument('--itemImage', dest='itemImage', type=str, default=None, help='Filename of an image for an item being shared') +parser.add_argument('--itemQty', dest='itemQty', type=int, + default=1, + help='Quantity of items being shared') parser.add_argument('--itemType', dest='itemType', type=str, default=None, help='Type of item being shared') @@ -1246,6 +1249,10 @@ if args.itemName: 'with the --summary option') sys.exit() + if not args.itemQty: + print('Specify a quantity of shared items with the --itemQty option') + sys.exit() + if not args.itemType: print('Specify a type of shared item with the --itemType option') sys.exit() @@ -1277,6 +1284,7 @@ if args.itemName: args.itemName, args.summary, args.itemImage, + args.itemQty, args.itemType, args.itemCategory, args.location, diff --git a/shares.py b/shares.py index 20605c12a..9e10e6aa3 100644 --- a/shares.py +++ b/shares.py @@ -97,7 +97,7 @@ def _addShareDurationSec(duration: str, published: str) -> int: def addShare(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, displayName: str, summary: str, imageFilename: str, - itemType: str, itemCategory: str, location: str, + itemQty: int, itemType: str, itemCategory: str, location: str, duration: str, debug: bool, city: str) -> None: """Adds a new share """ @@ -151,6 +151,7 @@ def addShare(baseDir: str, "displayName": displayName, "summary": summary, "imageUrl": imageUrl, + "itemQty": itemQty, "itemType": itemType, "category": itemCategory, "location": location, @@ -321,7 +322,7 @@ def sendShareViaServer(baseDir, session, fromDomain: str, fromPort: int, httpPrefix: str, displayName: str, summary: str, imageFilename: str, - itemType: str, itemCategory: str, + itemQty: int, itemType: str, itemCategory: str, location: str, duration: str, cachedWebfingers: {}, personCache: {}, debug: bool, projectVersion: str) -> {}: @@ -347,6 +348,7 @@ def sendShareViaServer(baseDir, session, "type": "Offer", "displayName": displayName, "summary": summary, + "itemQty": itemQty, "itemType": itemType, "category": itemCategory, "location": location, @@ -545,6 +547,10 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, if debug: print('DEBUG: summary missing from Offer') return + if not messageJson['object'].get('itemQty'): + if debug: + print('DEBUG: itemQty missing from Offer') + return if not messageJson['object'].get('itemType'): if debug: print('DEBUG: itemType missing from Offer') @@ -566,6 +572,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['displayName'], messageJson['object']['summary'], messageJson['object']['imageFilename'], + messageJson['object']['itemQty'], messageJson['object']['itemType'], messageJson['object']['itemCategory'], messageJson['object']['location'], diff --git a/translations/ar.json b/translations/ar.json index 01994da3e..f6e54fb4a 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -452,5 +452,6 @@ "Blocked User Agents": "عوامل المستخدم المحظورة", "Notify me when this account posts": "أعلمني عندما ينشر الحساب هذا", "Languages": "اللغات", - "Translated": "تترجم" + "Translated": "تترجم", + "Quantity": "كمية" } diff --git a/translations/ca.json b/translations/ca.json index 3b7911ba9..0c4e49f54 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Agents d'usuari bloquejats", "Notify me when this account posts": "Aviseu-me quan publiqui aquest compte", "Languages": "Idiomes", - "Translated": "Traduït" + "Translated": "Traduït", + "Quantity": "Quantitat" } diff --git a/translations/cy.json b/translations/cy.json index 3bfb2b4e3..8eb9088d3 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Asiantau defnyddwyr wedi'u blocio", "Notify me when this account posts": "Rhoi gwybod i mi pan fydd y cyfrifon cyfrif hwn", "Languages": "Ieithoedd", - "Translated": "Chyfieithwyd" + "Translated": "Chyfieithwyd", + "Quantity": "Symiau" } diff --git a/translations/de.json b/translations/de.json index 38b233dda..f6b501be1 100644 --- a/translations/de.json +++ b/translations/de.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Blockierte Benutzeragenten", "Notify me when this account posts": "Benachrichtigen Sie mich, wenn dieses Konto postet", "Languages": "Sprachen", - "Translated": "Übersetzt" + "Translated": "Übersetzt", + "Quantity": "Menge" } diff --git a/translations/en.json b/translations/en.json index 644d41b83..e358967ba 100644 --- a/translations/en.json +++ b/translations/en.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Blocked User Agents", "Notify me when this account posts": "Notify me when this account posts", "Languages": "Languages", - "Translated": "Translated" + "Translated": "Translated", + "Quantity": "Quantity" } diff --git a/translations/es.json b/translations/es.json index c1d9ea13d..0627c523c 100644 --- a/translations/es.json +++ b/translations/es.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Agentes de usuario bloqueados", "Notify me when this account posts": "Notifíqueme cuando se publique esta cuenta", "Languages": "Idiomas", - "Translated": "Traducida" + "Translated": "Traducida", + "Quantity": "Cantidad" } diff --git a/translations/fr.json b/translations/fr.json index 015e6ed11..0e73dcd27 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Agents d'utilisateur bloqués", "Notify me when this account posts": "Avertissez-moi quand ce compte publie", "Languages": "Langues", - "Translated": "Traduite" + "Translated": "Traduite", + "Quantity": "Quantité" } diff --git a/translations/ga.json b/translations/ga.json index a7008e056..6841a30d8 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Gníomhairí úsáideora blocáilte", "Notify me when this account posts": "Cuir in iúl dom nuair a phostófar an cuntas seo", "Languages": "Teangacha", - "Translated": "Aistrithe" + "Translated": "Aistrithe", + "Quantity": "Cainníocht" } diff --git a/translations/hi.json b/translations/hi.json index 5f389a268..fd07f2407 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -452,5 +452,6 @@ "Blocked User Agents": "अवरुद्ध उपयोगकर्ता एजेंट", "Notify me when this account posts": "यह खाता पोस्ट होने पर मुझे सूचित करें", "Languages": "बोली", - "Translated": "अनुवाद" + "Translated": "अनुवाद", + "Quantity": "मात्रा" } diff --git a/translations/it.json b/translations/it.json index 103a3aef5..98fc2abb4 100644 --- a/translations/it.json +++ b/translations/it.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Agenti utente bloccati", "Notify me when this account posts": "Avvisami quando questo account messaggi", "Languages": "Le lingue", - "Translated": "Tradotto" + "Translated": "Tradotto", + "Quantity": "Quantità" } diff --git a/translations/ja.json b/translations/ja.json index c8f022fd2..237c40538 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -452,5 +452,6 @@ "Blocked User Agents": "ブロックされたユーザーエージェント", "Notify me when this account posts": "この口座投稿を通知する", "Languages": "言語", - "Translated": "翻訳" + "Translated": "翻訳", + "Quantity": "量" } diff --git a/translations/ku.json b/translations/ku.json index 8053032d2..ddd15e33e 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Karmendên bikarhêner asteng kirin", "Notify me when this account posts": "Dema ku ev postên hesabê min agahdar bikin", "Languages": "Ziman", - "Translated": "Wergerandin" + "Translated": "Wergerandin", + "Quantity": "Jimarî" } diff --git a/translations/oc.json b/translations/oc.json index c16657c80..53843d5aa 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -448,5 +448,6 @@ "Blocked User Agents": "Blocked User Agents", "Notify me when this account posts": "Notify me when this account posts", "Languages": "Languages", - "Translated": "Translated" + "Translated": "Translated", + "Quantity": "Quantity" } diff --git a/translations/pt.json b/translations/pt.json index 13ef8a929..b2bfcbf1a 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Agentes de usuário bloqueados", "Notify me when this account posts": "Notifique-me quando esta conta posts", "Languages": "Línguas", - "Translated": "Traduzida" + "Translated": "Traduzida", + "Quantity": "Quantidade" } diff --git a/translations/ru.json b/translations/ru.json index 71eaf6606..2284d4d13 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Заблокированные пользовательские агенты", "Notify me when this account posts": "Сообщите мне, когда эта учетная запись", "Languages": "Языки", - "Translated": "Перевод" + "Translated": "Перевод", + "Quantity": "Количество" } diff --git a/translations/sw.json b/translations/sw.json index 29537cef9..2b010a9a2 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -452,5 +452,6 @@ "Blocked User Agents": "Wakala wa watumiaji waliozuiwa", "Notify me when this account posts": "Nijulishe wakati akaunti hii ya akaunti.", "Languages": "Lugha", - "Translated": "Ilitafsiriwa" + "Translated": "Ilitafsiriwa", + "Quantity": "Wingi" } diff --git a/translations/zh.json b/translations/zh.json index 54cd7658e..828b32382 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -452,5 +452,6 @@ "Blocked User Agents": "阻止用户代理商", "Notify me when this account posts": "此帐户帖子时通知我", "Languages": "语言", - "Translated": "翻译" + "Translated": "翻译", + "Quantity": "数量" } diff --git a/webapp_create_post.py b/webapp_create_post.py index 083dd93f7..408a22b96 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -354,6 +354,9 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, translate['Description of the item being shared'] + '...' endpoint = 'newshare' extraFields = '
\n' + extraFields += \ + editTextField(translate['Quantity'] + ':', + 'itemQty', '1') extraFields += \ editTextField(translate['Type of shared item. eg. hat'] + ':', 'itemType', '') diff --git a/webapp_search.py b/webapp_search.py index f0d2df47a..e22c3140e 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -175,9 +175,12 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, sharedItem['imageUrl'] + \ '" alt="Item image">\n' sharedItemsForm += \ - '

' + sharedItem['summary'] + '

\n' + '

' + sharedItem['summary'] + '

\n

' sharedItemsForm += \ - '

' + translate['Type'] + \ + '' + translate['Quantity'] + \ + ': ' + str(sharedItem['itemQty']) + ' ' + sharedItemsForm += \ + '' + translate['Type'] + \ ': ' + sharedItem['itemType'] + ' ' sharedItemsForm += \ '' + translate['Category'] + \ diff --git a/webapp_timeline.py b/webapp_timeline.py index 1b1552b09..5e472b856 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -821,8 +821,11 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, '' + translate['Item image'] + '\n\n' profileStr += '

' + item['summary'] + '

\n' + profileStr += '

' profileStr += \ - '

' + translate['Type'] + ': ' + item['itemType'] + ' ' + '' + translate['Quantity'] + ': ' + str(item['itemQty']) + ' ' + profileStr += \ + '' + translate['Type'] + ': ' + item['itemType'] + ' ' profileStr += \ '' + translate['Category'] + ': ' + item['category'] + ' ' profileStr += \ From c4db90c66f6b2f92d0e6f29475f89af2789d5dde Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:34:05 +0100 Subject: [PATCH 101/459] Check that quantity value exists --- webapp_search.py | 7 ++++--- webapp_timeline.py | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index e22c3140e..b8221de7e 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -176,9 +176,10 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, '" alt="Item image">\n' sharedItemsForm += \ '

' + sharedItem['summary'] + '

\n

' - sharedItemsForm += \ - '' + translate['Quantity'] + \ - ': ' + str(sharedItem['itemQty']) + ' ' + if sharedItem.get('itemQty'): + sharedItemsForm += \ + '' + translate['Quantity'] + \ + ': ' + str(sharedItem['itemQty']) + ' ' sharedItemsForm += \ '' + translate['Type'] + \ ': ' + sharedItem['itemType'] + ' ' diff --git a/webapp_timeline.py b/webapp_timeline.py index 5e472b856..e7fc5e2e5 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -820,10 +820,11 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, profileStr += \ '' + translate['Item image'] + '\n\n' - profileStr += '

' + item['summary'] + '

\n' - profileStr += '

' - profileStr += \ - '' + translate['Quantity'] + ': ' + str(item['itemQty']) + ' ' + profileStr += '

' + item['summary'] + '

\n

' + if item.get('itemQty'): + profileStr += \ + '' + translate['Quantity'] + ': ' + \ + str(item['itemQty']) + ' ' profileStr += \ '' + translate['Type'] + ': ' + item['itemType'] + ' ' profileStr += \ From 7d47bf4a3831eb5f347602e753df92e1363518f8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:47:51 +0100 Subject: [PATCH 102/459] Fix tests --- epicyon.py | 6 +++--- webapp_create_post.py | 11 +++++++---- webapp_utils.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/epicyon.py b/epicyon.py index 684ae8521..b80ad04df 100644 --- a/epicyon.py +++ b/epicyon.py @@ -2327,8 +2327,8 @@ if args.testdata: httpPrefix, nickname, domain, port, "spanner", "It's a spanner", - "img/shares1.png", - "tool", + "img/shares1.png", + 1, "tool", "mechanical", "City", "2 months", @@ -2338,7 +2338,7 @@ if args.testdata: "witch hat", "Spooky", "img/shares2.png", - "hat", + 1, "hat", "clothing", "City", "3 months", diff --git a/webapp_create_post.py b/webapp_create_post.py index 408a22b96..d83324465 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -18,6 +18,7 @@ from webapp_utils import getBannerFile from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlFooter from webapp_utils import editTextField +from webapp_utils import editNumberField def _htmlFollowingDataList(baseDir: str, nickname: str, @@ -354,6 +355,9 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, translate['Description of the item being shared'] + '...' endpoint = 'newshare' extraFields = '

\n' + extraFields += \ + editNumberField(translate['Quantity'], + 'itemQty', 1, 1, 999999, 1) extraFields += \ editTextField(translate['Quantity'] + ':', 'itemQty', '1') @@ -362,11 +366,10 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, 'itemType', '') catStr = translate['Category of shared item. eg. clothing'] extraFields += editTextField(catStr + ':', 'category', '') + extraFields += '
' extraFields += \ - '
\n' - extraFields += ' \n' + editNumberField(translate['Duration of listing in days'], + 'duration', 14, 1, 365, 1) extraFields += '
\n' extraFields += '
\n' cityOrLocStr = translate['City or location of the shared item'] diff --git a/webapp_utils.py b/webapp_utils.py index 9d9135006..1a22efa36 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1142,6 +1142,23 @@ def editTextField(label: str, name: str, value: str = "", value + '"' + placeholderStr + '>\n' +def editNumberField(label: str, name: str, value: int = 1, + minValue: int = 1, maxValue: int = 999999, + placeholder: int = 1) -> str: + """Returns html for editing an integer number field + """ + if value is None: + value = '' + placeholderStr = '' + if placeholder: + placeholderStr = ' placeholder="' + str(placeholder) + '"' + return \ + '
\n' + \ + ' \n' + + def editCheckBox(label: str, name: str, checked: bool = False) -> str: """Returns html for editing a checkbox field """ From a6e1723b4c0e2bd58d2abf042e3074aec1bfbc04 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:53:45 +0100 Subject: [PATCH 103/459] Remove unused parameter --- epicyon.py | 2 +- webapp_create_post.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/epicyon.py b/epicyon.py index b80ad04df..6657737bf 100644 --- a/epicyon.py +++ b/epicyon.py @@ -2327,7 +2327,7 @@ if args.testdata: httpPrefix, nickname, domain, port, "spanner", "It's a spanner", - "img/shares1.png", + "img/shares1.png", 1, "tool", "mechanical", "City", diff --git a/webapp_create_post.py b/webapp_create_post.py index d83324465..a40ca4798 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -73,7 +73,6 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str, dropdownFollowersSuffix: str, dropdownDMSuffix: str, dropdownReminderSuffix: str, - dropdownEventSuffix: str, dropdownReportSuffix: str, noDropDown: bool, accessKeys: {}) -> str: @@ -488,7 +487,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, dropdownUnlistedSuffix = '' dropdownFollowersSuffix = '' dropdownDMSuffix = '' - dropdownEventSuffix = '' dropdownReminderSuffix = '' dropdownReportSuffix = '' if inReplyTo: @@ -520,7 +518,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, dropdownFollowersSuffix, dropdownDMSuffix, dropdownReminderSuffix, - dropdownEventSuffix, dropdownReportSuffix, noDropDown, accessKeys) else: From 004dea3e71b47732eca03c05ece057d2f2a7e257 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:55:34 +0100 Subject: [PATCH 104/459] Remove duplicate field --- webapp_create_post.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index a40ca4798..99fac93d1 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -357,9 +357,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, extraFields += \ editNumberField(translate['Quantity'], 'itemQty', 1, 1, 999999, 1) - extraFields += \ - editTextField(translate['Quantity'] + ':', - 'itemQty', '1') extraFields += \ editTextField(translate['Type of shared item. eg. hat'] + ':', 'itemType', '') From ef21e1e3292f680b247315d8c7686aad1cef193f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 12:58:01 +0100 Subject: [PATCH 105/459] Newline --- webapp_create_post.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index 99fac93d1..d5bc22bc5 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -357,7 +357,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, extraFields += \ editNumberField(translate['Quantity'], 'itemQty', 1, 1, 999999, 1) - extraFields += \ + extraFields += '
' + \ editTextField(translate['Type of shared item. eg. hat'] + ':', 'itemType', '') catStr = translate['Category of shared item. eg. clothing'] From 84bcc6d103df7189ce124a9f8a440b69bce9e46e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 15:38:43 +0100 Subject: [PATCH 106/459] Assign product Ids to shared items --- daemon.py | 3 +- epicyon.py | 4 +- ontology/productTypes.json | 3689 ++++++++++++++++++++++++++++++++++++ ontology/units.json | 28 + outbox.py | 5 +- shares.py | 88 +- 6 files changed, 3808 insertions(+), 9 deletions(-) create mode 100644 ontology/productTypes.json create mode 100644 ontology/units.json diff --git a/daemon.py b/daemon.py index 00c46ffef..fa9e798d1 100644 --- a/daemon.py +++ b/daemon.py @@ -13664,7 +13664,8 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], durationStr, self.server.debug, - city) + city, + self.server.systemLanguage) if filename: if os.path.isfile(filename): os.remove(filename) diff --git a/epicyon.py b/epicyon.py index 6657737bf..fc32df676 100644 --- a/epicyon.py +++ b/epicyon.py @@ -2332,7 +2332,7 @@ if args.testdata: "mechanical", "City", "2 months", - debug, city) + debug, city, args.language) addShare(baseDir, httpPrefix, nickname, domain, port, "witch hat", @@ -2342,7 +2342,7 @@ if args.testdata: "clothing", "City", "3 months", - debug, city) + debug, city, args.language) deleteAllPosts(baseDir, nickname, domain, 'inbox') deleteAllPosts(baseDir, nickname, domain, 'outbox') diff --git a/ontology/productTypes.json b/ontology/productTypes.json new file mode 100644 index 000000000..d4e5283f0 --- /dev/null +++ b/ontology/productTypes.json @@ -0,0 +1,3689 @@ +{ + "@context": { + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "dfc-b": "http://static.datafoodconsortium.org/ontologies/DFC_BusinessOntology.owl#", + "dfc-p": "http://static.datafoodconsortium.org/ontologies/DFC_ProductOntology.owl#", + "dfc-t": "http://static.datafoodconsortium.org/ontologies/DFC_TechnicalOntology.owl#", + "dfc-u": "http://static.datafoodconsortium.org/data/units.rdf#", + "dfc-p:specialize": { + "@type": "@id" + } + }, + "@graph": [ + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#soft-drink", + "rdfs:label": [ + { + "@value": "Boisson non alcoolisée", + "@language": "fr" + }, + { + "@value": "Soft drink", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#drink", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "rdfs:label": [ + { + "@value": "Salade", + "@language": "fr" + }, + { + "@value": "Salad", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#egg", + "rdfs:label": [ + { + "@value": "Oeuf", + "@language": "fr" + }, + { + "@value": "Egg", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#old-variety-squash", + "rdfs:label": [ + { + "@value": "Variété ancienne", + "@language": "fr" + }, + { + "@value": "Old variety squash", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fig", + "rdfs:label": [ + { + "@value": "Figue", + "@language": "fr" + }, + { + "@value": "Fig", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rocket", + "rdfs:label": [ + { + "@value": "Roquette", + "@language": "fr" + }, + { + "@value": "Rocket", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#beef", + "rdfs:label": [ + { + "@value": "Viande bovine", + "@language": "fr" + }, + { + "@value": "Beef", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-mature-cheese", + "rdfs:label": [ + { + "@value": "Fromage affinés", + "@language": "fr" + }, + { + "@value": "Sheep Mature cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-milk", + "rdfs:label": [ + { + "@value": "Lait", + "@language": "fr" + }, + { + "@value": "Sheep Milk", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chanterelle-mushroom", + "rdfs:label": [ + { + "@value": "Chanterelle", + "@language": "fr" + }, + { + "@value": "Chanterelle mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-sweet-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt sucré", + "@language": "fr" + }, + { + "@value": "Goat sweet yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rabbit", + "rdfs:label": [ + { + "@value": "Lapin", + "@language": "fr" + }, + { + "@value": "Rabbit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pie-pastry", + "rdfs:label": [ + { + "@value": "Pâte à tarte", + "@language": "fr" + }, + { + "@value": "Pie Pastry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#jam", + "rdfs:label": [ + { + "@value": "Confiture", + "@language": "fr" + }, + { + "@value": "Jam", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fourth-range-vegetable", + "rdfs:label": [ + { + "@value": "Légume quatrième gamme", + "@language": "fr" + }, + { + "@value": "Fourth range vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "rdfs:label": [ + { + "@value": "Champignon", + "@language": "fr" + }, + { + "@value": "Mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#beer", + "rdfs:label": [ + { + "@value": "Bière", + "@language": "fr" + }, + { + "@value": "Beer", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chilli-pepper", + "rdfs:label": [ + { + "@value": "Piment", + "@language": "fr" + }, + { + "@value": "Chilli pepper", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#non-local-vegetable", + "rdfs:label": [ + { + "@value": "Légume non local", + "@language": "fr" + }, + { + "@value": "Non local vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chicory", + "rdfs:label": [ + { + "@value": "Chicorée", + "@language": "fr" + }, + { + "@value": "Chicory", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#other-cheese", + "rdfs:label": [ + { + "@value": "Fromage", + "@language": "fr" + }, + { + "@value": "Other Cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#other-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cream-cheese", + "rdfs:label": [ + { + "@value": "Fromage blanc", + "@language": "fr" + }, + { + "@value": "Cream cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#peach", + "rdfs:label": [ + { + "@value": "Pêche", + "@language": "fr" + }, + { + "@value": "Peach", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#almond", + "rdfs:label": [ + { + "@value": "Amande", + "@language": "fr" + }, + { + "@value": "Almond", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#milk", + "rdfs:label": [ + { + "@value": "Lait", + "@language": "fr" + }, + { + "@value": "Milk", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#romanesco-cabbage", + "rdfs:label": [ + { + "@value": "Chou romanesco", + "@language": "fr" + }, + { + "@value": "Romanesco cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen", + "rdfs:label": [ + { + "@value": "Surgelé", + "@language": "fr" + }, + { + "@value": "Frozen", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fresh-meat", + "rdfs:label": [ + { + "@value": "Viande fraîche", + "@language": "fr" + }, + { + "@value": "Fresh meat", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#pork", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#semolina", + "rdfs:label": [ + { + "@value": "Semoule", + "@language": "fr" + }, + { + "@value": "Semolina", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#turkey", + "rdfs:label": [ + { + "@value": "Dinde", + "@language": "fr" + }, + { + "@value": "Turkey", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#plant", + "rdfs:label": [ + { + "@value": "Plant", + "@language": "fr" + }, + { + "@value": "Plant", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#inedible", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#kale-cabbage", + "rdfs:label": [ + { + "@value": "Chou kale", + "@language": "fr" + }, + { + "@value": "Kale cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#asparagus", + "rdfs:label": [ + { + "@value": "Asperge", + "@language": "fr" + }, + { + "@value": "Asparagus", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-yogurt-with-fruits", + "rdfs:label": [ + { + "@value": "Yaourt aux fruits", + "@language": "fr" + }, + { + "@value": "Goat yogurt with fruits", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#wine", + "rdfs:label": [ + { + "@value": "Vin", + "@language": "fr" + }, + { + "@value": "Wine", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-yogurt-on-a-bed-of-fruit", + "rdfs:label": [ + { + "@value": "Yaourt sur lit de fruit", + "@language": "fr" + }, + { + "@value": "Sheep yogurt on a bed of fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#crepe-and-galette", + "rdfs:label": [ + { + "@value": "Crêpe et galette", + "@language": "fr" + }, + { + "@value": "Crepe and galette", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "rdfs:label": [ + { + "@value": "Produits laitiers de chèvre", + "@language": "fr" + }, + { + "@value": "Goat dairy product", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#oyster-mushroom", + "rdfs:label": [ + { + "@value": "Pleurote", + "@language": "fr" + }, + { + "@value": "Oyster mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#seashell", + "rdfs:label": [ + { + "@value": "Coquillage", + "@language": "fr" + }, + { + "@value": "Seashell", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fishery-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#radish", + "rdfs:label": [ + { + "@value": "Radis", + "@language": "fr" + }, + { + "@value": "Radish", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#tomato", + "rdfs:label": [ + { + "@value": "Tomate", + "@language": "fr" + }, + { + "@value": "Tomato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pork", + "rdfs:label": [ + { + "@value": "Porc", + "@language": "fr" + }, + { + "@value": "Pork", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#thyme", + "rdfs:label": [ + { + "@value": "Thym", + "@language": "fr" + }, + { + "@value": "Thyme", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cider", + "rdfs:label": [ + { + "@value": "Cidre", + "@language": "fr" + }, + { + "@value": "Cider", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mint", + "rdfs:label": [ + { + "@value": "Menthe", + "@language": "fr" + }, + { + "@value": "Mint", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#parsley", + "rdfs:label": [ + { + "@value": "Persil", + "@language": "fr" + }, + { + "@value": "Parsley", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-fresh-cheese", + "rdfs:label": [ + { + "@value": "Fromage frais", + "@language": "fr" + }, + { + "@value": "Sheep Fresh cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "rdfs:label": [ + { + "@value": "Produit laitier de vache", + "@language": "fr" + }, + { + "@value": "Cow dairy product", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#jerusalem-artichoke", + "rdfs:label": [ + { + "@value": "Topinambour", + "@language": "fr" + }, + { + "@value": "Jerusalem artichoke", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#ripe", + "rdfs:label": [ + { + "@value": "Mûre", + "@language": "fr" + }, + { + "@value": "Ripe", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad-mix", + "rdfs:label": [ + { + "@value": "Mélange salades", + "@language": "fr" + }, + { + "@value": "Salad mix", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#parsnip", + "rdfs:label": [ + { + "@value": "Panais", + "@language": "fr" + }, + { + "@value": "Parsnip", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#broccoli-cabbage", + "rdfs:label": [ + { + "@value": "Chou brocoli", + "@language": "fr" + }, + { + "@value": "Broccoli cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#salt", + "rdfs:label": [ + { + "@value": "Sel", + "@language": "fr" + }, + { + "@value": "Salt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#yam", + "rdfs:label": [ + { + "@value": "Patate douce", + "@language": "fr" + }, + { + "@value": "Yam", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-yogurt-with-fruits", + "rdfs:label": [ + { + "@value": "Yaourt aux fruits", + "@language": "fr" + }, + { + "@value": "Sheep yogurt with fruits", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#green-garlic", + "rdfs:label": [ + { + "@value": "Aillet", + "@language": "fr" + }, + { + "@value": "Green Garlic", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chestnut", + "rdfs:label": [ + { + "@value": "Marron", + "@language": "fr" + }, + { + "@value": "Chestnut", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chard", + "rdfs:label": [ + { + "@value": "Blette", + "@language": "fr" + }, + { + "@value": "Chard", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#flower", + "rdfs:label": [ + { + "@value": "Fleur", + "@language": "fr" + }, + { + "@value": "Flower", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#inedible", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fennel", + "rdfs:label": [ + { + "@value": "Fenouil", + "@language": "fr" + }, + { + "@value": "Fennel", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fish", + "rdfs:label": [ + { + "@value": "Poisson", + "@language": "fr" + }, + { + "@value": "Fish", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fishery-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#shallot", + "rdfs:label": [ + { + "@value": "Échalote", + "@language": "fr" + }, + { + "@value": "Shallot", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-fruit", + "rdfs:label": [ + { + "@value": "Fruit séché", + "@language": "fr" + }, + { + "@value": "Dried fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#kiwi", + "rdfs:label": [ + { + "@value": "Kiwi", + "@language": "fr" + }, + { + "@value": "Kiwi", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "rdfs:label": [ + { + "@value": "Petit fruit", + "@language": "fr" + }, + { + "@value": "Berry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#inedible", + "rdfs:label": [ + { + "@value": "Non alimentaire", + "@language": "fr" + }, + { + "@value": "Inedible", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chewed-up", + "rdfs:label": [ + { + "@value": "Mâche", + "@language": "fr" + }, + { + "@value": "Chewed up", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#endive", + "rdfs:label": [ + { + "@value": "Endive", + "@language": "fr" + }, + { + "@value": "Endive", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#celeriac", + "rdfs:label": [ + { + "@value": "Céleri-rave", + "@language": "fr" + }, + { + "@value": "Celeriac", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#kohlrabi", + "rdfs:label": [ + { + "@value": "Chou rave", + "@language": "fr" + }, + { + "@value": "Kohlrabi", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dandelion", + "rdfs:label": [ + { + "@value": "Pissenlit", + "@language": "fr" + }, + { + "@value": "Dandelion", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#guinea-fowl", + "rdfs:label": [ + { + "@value": "Pintade", + "@language": "fr" + }, + { + "@value": "Guinea fowl", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cassis", + "rdfs:label": [ + { + "@value": "Cassis", + "@language": "fr" + }, + { + "@value": "Cassis", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#duck", + "rdfs:label": [ + { + "@value": "Canard", + "@language": "fr" + }, + { + "@value": "Duck", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#deaths-trumpet", + "rdfs:label": [ + { + "@value": "Trompette de la mort", + "@language": "fr" + }, + { + "@value": "Death's trumpet", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cosmetic", + "rdfs:label": [ + { + "@value": "Cosmétique", + "@language": "fr" + }, + { + "@value": "Cosmetic", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#inedible", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#festive-poultry", + "rdfs:label": [ + { + "@value": "Volaille festive", + "@language": "fr" + }, + { + "@value": "Festive poultry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#melon", + "rdfs:label": [ + { + "@value": "Melon", + "@language": "fr" + }, + { + "@value": "Melon", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "rdfs:label": [ + { + "@value": "Epicerie sucrée", + "@language": "fr" + }, + { + "@value": "Sweet groceries", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#smoothie", + "rdfs:label": [ + { + "@value": "Smoothie", + "@language": "fr" + }, + { + "@value": "Smoothie", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#soft-drink", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#blueberry", + "rdfs:label": [ + { + "@value": "Myrtille", + "@language": "fr" + }, + { + "@value": "Blueberry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#gooseberry", + "rdfs:label": [ + { + "@value": "Groseille à maquereau", + "@language": "fr" + }, + { + "@value": "Gooseberry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#girolle-mushroom", + "rdfs:label": [ + { + "@value": "Girolle", + "@language": "fr" + }, + { + "@value": "Girolle mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#grape", + "rdfs:label": [ + { + "@value": "Raisin", + "@language": "fr" + }, + { + "@value": "Grape", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "rdfs:label": [ + { + "@value": "Aromate", + "@language": "fr" + }, + { + "@value": "Aromatic", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-fresh-cheese", + "rdfs:label": [ + { + "@value": "Fromage frais", + "@language": "fr" + }, + { + "@value": "Goat Fresh cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-flavored-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt aromatisé", + "@language": "fr" + }, + { + "@value": "Sheep flavored yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#quinoa", + "rdfs:label": [ + { + "@value": "Quinoa", + "@language": "fr" + }, + { + "@value": "Quinoa", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#ready-meal", + "rdfs:label": [ + { + "@value": "Plat cuisiné", + "@language": "fr" + }, + { + "@value": "Ready meal", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rhubarb", + "rdfs:label": [ + { + "@value": "Rhubarbe", + "@language": "fr" + }, + { + "@value": "Rhubarb", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rosemary", + "rdfs:label": [ + { + "@value": "Romarin", + "@language": "fr" + }, + { + "@value": "Rosemary", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chinese-cabbage", + "rdfs:label": [ + { + "@value": "Chou chinois", + "@language": "fr" + }, + { + "@value": "Chinese cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#brussels-sprouts", + "rdfs:label": [ + { + "@value": "Chou de Bruxelles", + "@language": "fr" + }, + { + "@value": "Brussels sprouts", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "rdfs:label": [ + { + "@value": "Boisson alcoolisée", + "@language": "fr" + }, + { + "@value": "Alcoholic beverage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#drink", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#paris-mushroom", + "rdfs:label": [ + { + "@value": "Champignon de Paris", + "@language": "fr" + }, + { + "@value": "Paris mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bottled-fruit", + "rdfs:label": [ + { + "@value": "Fruit en bocal", + "@language": "fr" + }, + { + "@value": "Bottled fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pepper", + "rdfs:label": [ + { + "@value": "Poivron", + "@language": "fr" + }, + { + "@value": "Pepper", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#round-tomato", + "rdfs:label": [ + { + "@value": "Tomate ronde", + "@language": "fr" + }, + { + "@value": "Round tomato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#tomato", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goose", + "rdfs:label": [ + { + "@value": "Oie", + "@language": "fr" + }, + { + "@value": "Goose", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bottled-vegetable", + "rdfs:label": [ + { + "@value": "Légume en bocal", + "@language": "fr" + }, + { + "@value": "Bottled vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cooked-meat", + "rdfs:label": [ + { + "@value": "Viande cuite", + "@language": "fr" + }, + { + "@value": "Cooked meat", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#pork", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "rdfs:label": [ + { + "@value": "Légume", + "@language": "fr" + }, + { + "@value": "Vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#quail", + "rdfs:label": [ + { + "@value": "Caille", + "@language": "fr" + }, + { + "@value": "Quail", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mature-cheese", + "rdfs:label": [ + { + "@value": "Fromage affiné", + "@language": "fr" + }, + { + "@value": "Mature cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#lemon", + "rdfs:label": [ + { + "@value": "Citron", + "@language": "fr" + }, + { + "@value": "Lemon", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pear", + "rdfs:label": [ + { + "@value": "Poire", + "@language": "fr" + }, + { + "@value": "Pear", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen-fruit", + "rdfs:label": [ + { + "@value": "Fruit surgelé", + "@language": "fr" + }, + { + "@value": "Frozen fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#peas", + "rdfs:label": [ + { + "@value": "Pois", + "@language": "fr" + }, + { + "@value": "Peas", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#salting", + "rdfs:label": [ + { + "@value": "Salaison", + "@language": "fr" + }, + { + "@value": "Salting", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#pork", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#honey", + "rdfs:label": [ + { + "@value": "Miel", + "@language": "fr" + }, + { + "@value": "Honey", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen-meal", + "rdfs:label": [ + { + "@value": "Plat surgelé", + "@language": "fr" + }, + { + "@value": "Frozen meal", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pasta", + "rdfs:label": [ + { + "@value": "Pâte", + "@language": "fr" + }, + { + "@value": "Pasta", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cherry-tomato", + "rdfs:label": [ + { + "@value": "Tomate cerise", + "@language": "fr" + }, + { + "@value": "Cherry tomato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#tomato", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen-meat", + "rdfs:label": [ + { + "@value": "Viande surgelée", + "@language": "fr" + }, + { + "@value": "Frozen meat", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#non-local-fruit", + "rdfs:label": [ + { + "@value": "Fruit non local", + "@language": "fr" + }, + { + "@value": "Non-local fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#clementine", + "rdfs:label": [ + { + "@value": "Clémentine", + "@language": "fr" + }, + { + "@value": "Clementine", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#other-milk", + "rdfs:label": [ + { + "@value": "Lait", + "@language": "fr" + }, + { + "@value": "Other Milk", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#other-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dill", + "rdfs:label": [ + { + "@value": "Aneth", + "@language": "fr" + }, + { + "@value": "Dill", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#lemonade", + "rdfs:label": [ + { + "@value": "Limonade", + "@language": "fr" + }, + { + "@value": "Lemonade", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#soft-drink", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rice", + "rdfs:label": [ + { + "@value": "Riz", + "@language": "fr" + }, + { + "@value": "Rice", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#snails", + "rdfs:label": [ + { + "@value": "Escargots", + "@language": "fr" + }, + { + "@value": "Snails", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fifth-range-vegetable", + "rdfs:label": [ + { + "@value": "Légume cinquième gamme", + "@language": "fr" + }, + { + "@value": "Fifth range vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#turnip", + "rdfs:label": [ + { + "@value": "Navet", + "@language": "fr" + }, + { + "@value": "Turnip", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cluster-tomato", + "rdfs:label": [ + { + "@value": "Tomate grappe", + "@language": "fr" + }, + { + "@value": "Cluster tomato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#tomato", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "rdfs:label": [ + { + "@value": "Chou", + "@language": "fr" + }, + { + "@value": "Cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit-in-compote", + "rdfs:label": [ + { + "@value": "Fruit en compote", + "@language": "fr" + }, + { + "@value": "Fruit in compote", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#digestive", + "rdfs:label": [ + { + "@value": "Digestif", + "@language": "fr" + }, + { + "@value": "Digestive", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#truffle", + "rdfs:label": [ + { + "@value": "Truffe", + "@language": "fr" + }, + { + "@value": "Truffle", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-yogurt-on-a-bed-of-fruit", + "rdfs:label": [ + { + "@value": "Yaourt sur lit de fruit", + "@language": "fr" + }, + { + "@value": "Goat yogurt on a bed of fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#flour", + "rdfs:label": [ + { + "@value": "Farine", + "@language": "fr" + }, + { + "@value": "Flour", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#eggplant", + "rdfs:label": [ + { + "@value": "Aubergine", + "@language": "fr" + }, + { + "@value": "Eggplant", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cress", + "rdfs:label": [ + { + "@value": "Cresson", + "@language": "fr" + }, + { + "@value": "Cress", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#quince", + "rdfs:label": [ + { + "@value": "Coing", + "@language": "fr" + }, + { + "@value": "Quince", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt sucré", + "@language": "fr" + }, + { + "@value": "Sweet yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#laurel", + "rdfs:label": [ + { + "@value": "Laurier", + "@language": "fr" + }, + { + "@value": "Laurel", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-dessert", + "rdfs:label": [ + { + "@value": "Desserts lactés", + "@language": "fr" + }, + { + "@value": "Sheep Dairy dessert", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#raspberry", + "rdfs:label": [ + { + "@value": "Framboise", + "@language": "fr" + }, + { + "@value": "Raspberry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#hazelnut", + "rdfs:label": [ + { + "@value": "Noisette", + "@language": "fr" + }, + { + "@value": "Hazelnut", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#delicatessen", + "rdfs:label": [ + { + "@value": "Charcuterie", + "@language": "fr" + }, + { + "@value": "Delicatessen", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#pork", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-natural-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt nature", + "@language": "fr" + }, + { + "@value": "Sheep natural yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bakery", + "rdfs:label": [ + { + "@value": "Boulangerie", + "@language": "fr" + }, + { + "@value": "Bakery", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#lettuce", + "rdfs:label": [ + { + "@value": "Laitue", + "@language": "fr" + }, + { + "@value": "Lettuce", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#veal", + "rdfs:label": [ + { + "@value": "Veau", + "@language": "fr" + }, + { + "@value": "Veal", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#drink", + "rdfs:label": [ + { + "@value": "Boisson", + "@language": "fr" + }, + { + "@value": "Drink", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#tarragon", + "rdfs:label": [ + { + "@value": "Estragon", + "@language": "fr" + }, + { + "@value": "Tarragon", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "rdfs:label": [ + { + "@value": "Produit carné", + "@language": "fr" + }, + { + "@value": "Meat product", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#natural-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt nature", + "@language": "fr" + }, + { + "@value": "Natural yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-dessert", + "rdfs:label": [ + { + "@value": "Dessert lacté", + "@language": "fr" + }, + { + "@value": "Goat Dairy dessert", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#milky-mushroom", + "rdfs:label": [ + { + "@value": "Lactaire", + "@language": "fr" + }, + { + "@value": "Milky mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cherry", + "rdfs:label": [ + { + "@value": "Cerise", + "@language": "fr" + }, + { + "@value": "Cherry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#flavored-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt aromatisé", + "@language": "fr" + }, + { + "@value": "Flavored yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash-melon", + "rdfs:label": [ + { + "@value": "Pâtisson", + "@language": "fr" + }, + { + "@value": "Squash melon", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chervil", + "rdfs:label": [ + { + "@value": "Cerfeuil", + "@language": "fr" + }, + { + "@value": "Chervil", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "rdfs:label": [ + { + "@value": "Epicerie salée", + "@language": "fr" + }, + { + "@value": "Savory groceries", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "rdfs:label": [ + { + "@value": "Epicerie locale", + "@language": "fr" + }, + { + "@value": "Local grocery store", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-fruit", + "rdfs:label": [ + { + "@value": "Fruit transformé", + "@language": "fr" + }, + { + "@value": "Processed fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#other-dairy-product", + "rdfs:label": [ + { + "@value": "Produit laitier autre", + "@language": "fr" + }, + { + "@value": "Other Dairy product", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#potato", + "rdfs:label": [ + { + "@value": "Pomme de terre", + "@language": "fr" + }, + { + "@value": "Potato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#coriander", + "rdfs:label": [ + { + "@value": "Coriandre", + "@language": "fr" + }, + { + "@value": "Coriander", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#beans", + "rdfs:label": [ + { + "@value": "Fèves", + "@language": "fr" + }, + { + "@value": "Beans", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#viennoiserie-", + "rdfs:label": [ + { + "@value": "Viennoiserie", + "@language": "fr" + }, + { + "@value": "Viennoiserie ", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#bakery", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-flavored-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt aromatisé", + "@language": "fr" + }, + { + "@value": "Goat flavored yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#currant", + "rdfs:label": [ + { + "@value": "Groseille", + "@language": "fr" + }, + { + "@value": "Currant", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#berry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#nectarine", + "rdfs:label": [ + { + "@value": "Nectarine", + "@language": "fr" + }, + { + "@value": "Nectarine", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bread", + "rdfs:label": [ + { + "@value": "Pain", + "@language": "fr" + }, + { + "@value": "Bread", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#bakery", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-sweet-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt sucré", + "@language": "fr" + }, + { + "@value": "Sheep sweet yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#smooth-cabbage", + "rdfs:label": [ + { + "@value": "Chou lisse", + "@language": "fr" + }, + { + "@value": "Smooth cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#courgette", + "rdfs:label": [ + { + "@value": "Courgette", + "@language": "fr" + }, + { + "@value": "Courgette", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#strawberry", + "rdfs:label": [ + { + "@value": "Fraise", + "@language": "fr" + }, + { + "@value": "Strawberry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-natural-yogurt", + "rdfs:label": [ + { + "@value": "Yaourt nature", + "@language": "fr" + }, + { + "@value": "Goat natural yogurt", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pastry", + "rdfs:label": [ + { + "@value": "Pâtisserie", + "@language": "fr" + }, + { + "@value": "Pastry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fresh-cheese", + "rdfs:label": [ + { + "@value": "Fromage frais", + "@language": "fr" + }, + { + "@value": "Fresh cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#canned-vegetable", + "rdfs:label": [ + { + "@value": "Légume en conserve", + "@language": "fr" + }, + { + "@value": "Canned vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#yogurt-on-a-bed-of-fruit", + "rdfs:label": [ + { + "@value": "Yaourt sur lit de fruit", + "@language": "fr" + }, + { + "@value": "Yogurt on a bed of fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#aperitif", + "rdfs:label": [ + { + "@value": "Apéritif", + "@language": "fr" + }, + { + "@value": "Aperitif", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#alcoholic-beverage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#kale", + "rdfs:label": [ + { + "@value": "Chou frisé", + "@language": "fr" + }, + { + "@value": "Kale", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#grilling-meat", + "rdfs:label": [ + { + "@value": "Viande à griller", + "@language": "fr" + }, + { + "@value": "Grilling meat", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#beef", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-vegetable", + "rdfs:label": [ + { + "@value": "Légume transformé", + "@language": "fr" + }, + { + "@value": "Processed vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#local-grocery-store", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#spinach", + "rdfs:label": [ + { + "@value": "Épinard", + "@language": "fr" + }, + { + "@value": "Spinach", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#morel", + "rdfs:label": [ + { + "@value": "Morille", + "@language": "fr" + }, + { + "@value": "Morel", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cucumber", + "rdfs:label": [ + { + "@value": "Concombre", + "@language": "fr" + }, + { + "@value": "Cucumber", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#onion", + "rdfs:label": [ + { + "@value": "Oignon", + "@language": "fr" + }, + { + "@value": "Onion", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#artichoke", + "rdfs:label": [ + { + "@value": "Artichaut", + "@language": "fr" + }, + { + "@value": "Artichoke", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-milk", + "rdfs:label": [ + { + "@value": "Lait", + "@language": "fr" + }, + { + "@value": "Goat Milk", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "rdfs:label": [ + { + "@value": "Fruit à coque", + "@language": "fr" + }, + { + "@value": "Nut", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-product", + "rdfs:label": [ + { + "@value": "Produit laitier", + "@language": "fr" + }, + { + "@value": "Dairy product", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#red-kuri-squash", + "rdfs:label": [ + { + "@value": "Potimarron", + "@language": "fr" + }, + { + "@value": "Red kuri squash", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "rdfs:label": [ + { + "@value": "Courge", + "@language": "fr" + }, + { + "@value": "Squash", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen-vegetable", + "rdfs:label": [ + { + "@value": "Légume surgelé", + "@language": "fr" + }, + { + "@value": "Frozen vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#frozen", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sage", + "rdfs:label": [ + { + "@value": "Sauge", + "@language": "fr" + }, + { + "@value": "Sage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#salsify", + "rdfs:label": [ + { + "@value": "Salsifis", + "@language": "fr" + }, + { + "@value": "Salsify", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bean", + "rdfs:label": [ + { + "@value": "Haricot", + "@language": "fr" + }, + { + "@value": "Bean", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#plum", + "rdfs:label": [ + { + "@value": "Prune", + "@language": "fr" + }, + { + "@value": "Plum", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pigeon", + "rdfs:label": [ + { + "@value": "Pigeon", + "@language": "fr" + }, + { + "@value": "Pigeon", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#basil", + "rdfs:label": [ + { + "@value": "Basilic", + "@language": "fr" + }, + { + "@value": "Basil", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#red-cabbage", + "rdfs:label": [ + { + "@value": "Chou rouge", + "@language": "fr" + }, + { + "@value": "Red cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#butternut", + "rdfs:label": [ + { + "@value": "Butternut", + "@language": "fr" + }, + { + "@value": "Butternut", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#celery-branch", + "rdfs:label": [ + { + "@value": "Céleri-branche", + "@language": "fr" + }, + { + "@value": "Celery branch", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#coulemelle-mushroom", + "rdfs:label": [ + { + "@value": "Coulemelle", + "@language": "fr" + }, + { + "@value": "Coulemelle mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#apple-cabbage", + "rdfs:label": [ + { + "@value": "Chou pomme", + "@language": "fr" + }, + { + "@value": "Apple cabbage", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cereal", + "rdfs:label": [ + { + "@value": "Céréale", + "@language": "fr" + }, + { + "@value": "Cereal", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#canned-fruit", + "rdfs:label": [ + { + "@value": "Fruit en conserve", + "@language": "fr" + }, + { + "@value": "Canned fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#apples", + "rdfs:label": [ + { + "@value": "Pomme", + "@language": "fr" + }, + { + "@value": "Apples", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#rutabaga", + "rdfs:label": [ + { + "@value": "Rutabaga", + "@language": "fr" + }, + { + "@value": "Rutabaga", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mousseron", + "rdfs:label": [ + { + "@value": "Mousseron", + "@language": "fr" + }, + { + "@value": "Mousseron", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#cauliflower", + "rdfs:label": [ + { + "@value": "Chou-fleur", + "@language": "fr" + }, + { + "@value": "Cauliflower", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cabbage", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chicken", + "rdfs:label": [ + { + "@value": "Poulet", + "@language": "fr" + }, + { + "@value": "Chicken", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#soup", + "rdfs:label": [ + { + "@value": "Soupe", + "@language": "fr" + }, + { + "@value": "Soup", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#processed-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "rdfs:label": [ + { + "@value": "Fruit", + "@language": "fr" + }, + { + "@value": "Fruit", + "@language": "en" + } + ], + "dfc-p:specialize": "undefined", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#confectionery", + "rdfs:label": [ + { + "@value": "Confiserie", + "@language": "fr" + }, + { + "@value": "Confectionery", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#oil", + "rdfs:label": [ + { + "@value": "Huile", + "@language": "fr" + }, + { + "@value": "Oil", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#savory-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fresh-cream", + "rdfs:label": [ + { + "@value": "Crème Fraîche", + "@language": "fr" + }, + { + "@value": "Fresh cream", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheep-dairy-product", + "rdfs:label": [ + { + "@value": "Produits laitiers de brebis", + "@language": "fr" + }, + { + "@value": "Sheep dairy product", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mandarin", + "rdfs:label": [ + { + "@value": "Mandarine", + "@language": "fr" + }, + { + "@value": "Mandarin", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#garlic", + "rdfs:label": [ + { + "@value": "Ail", + "@language": "fr" + }, + { + "@value": "Garlic", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#yogurt-with-fruits", + "rdfs:label": [ + { + "@value": "Yaourt aux fruits", + "@language": "fr" + }, + { + "@value": "Yogurt with fruits", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#butter", + "rdfs:label": [ + { + "@value": "Beurre", + "@language": "fr" + }, + { + "@value": "Butter", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#poultry", + "rdfs:label": [ + { + "@value": "Volaille", + "@language": "fr" + }, + { + "@value": "Poultry", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fishery-product", + "rdfs:label": [ + { + "@value": "Produit de la pêche", + "@language": "fr" + }, + { + "@value": "Fishery product", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#porcini", + "rdfs:label": [ + { + "@value": "Cèpe", + "@language": "fr" + }, + { + "@value": "Porcini", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#bluefoot-mushroom", + "rdfs:label": [ + { + "@value": "Pied-bleu", + "@language": "fr" + }, + { + "@value": "Bluefoot mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dairy-dessert", + "rdfs:label": [ + { + "@value": "Dessert lacté", + "@language": "fr" + }, + { + "@value": "Dairy dessert", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#cow-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#leek", + "rdfs:label": [ + { + "@value": "Poireau", + "@language": "fr" + }, + { + "@value": "Leek", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#beetroot", + "rdfs:label": [ + { + "@value": "Betterave rouge", + "@language": "fr" + }, + { + "@value": "Beetroot", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#lentils", + "rdfs:label": [ + { + "@value": "Lentilles", + "@language": "fr" + }, + { + "@value": "Lentils", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#medlar", + "rdfs:label": [ + { + "@value": "Nèfle", + "@language": "fr" + }, + { + "@value": "Medlar", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#chive", + "rdfs:label": [ + { + "@value": "Ciboulette", + "@language": "fr" + }, + { + "@value": "Chive", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#aromatic", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#apricot", + "rdfs:label": [ + { + "@value": "Abricot", + "@language": "fr" + }, + { + "@value": "Apricot", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#orange", + "rdfs:label": [ + { + "@value": "Orange", + "@language": "fr" + }, + { + "@value": "Orange", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#old-variety-tomato", + "rdfs:label": [ + { + "@value": "Tomate ancienne", + "@language": "fr" + }, + { + "@value": "Old variety tomato", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#tomato", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#walnut", + "rdfs:label": [ + { + "@value": "Noix", + "@language": "fr" + }, + { + "@value": "Walnut", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#nut", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#shellfish", + "rdfs:label": [ + { + "@value": "Crustacé", + "@language": "fr" + }, + { + "@value": "Shellfish", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fishery-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#sheepfoot-mushroom", + "rdfs:label": [ + { + "@value": "Pied de mouton", + "@language": "fr" + }, + { + "@value": "Sheepfoot mushroom", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#mushroom", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#prune", + "rdfs:label": [ + { + "@value": "Pruneau", + "@language": "fr" + }, + { + "@value": "Prune", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#simmering-meat", + "rdfs:label": [ + { + "@value": "Viande à mijoter", + "@language": "fr" + }, + { + "@value": "Simmering meat", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#beef", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#dried-vegetable", + "rdfs:label": [ + { + "@value": "Légume sec", + "@language": "fr" + }, + { + "@value": "Dried vegetable", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#lamb", + "rdfs:label": [ + { + "@value": "Agneau", + "@language": "fr" + }, + { + "@value": "Lamb", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#meat-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#fruit-juice", + "rdfs:label": [ + { + "@value": "Jus de fruit", + "@language": "fr" + }, + { + "@value": "Fruit juice", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#soft-drink", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-mature-cheese", + "rdfs:label": [ + { + "@value": "Fromage affinés", + "@language": "fr" + }, + { + "@value": "Goat Mature cheese", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#goat-dairy-product", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#biscuit", + "rdfs:label": [ + { + "@value": "Biscuit", + "@language": "fr" + }, + { + "@value": "Biscuit", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#sweet-groceries", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#pumpkin", + "rdfs:label": [ + { + "@value": "Potiron", + "@language": "fr" + }, + { + "@value": "Pumpkin", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#squash", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#mesclun", + "rdfs:label": [ + { + "@value": "Mesclun", + "@language": "fr" + }, + { + "@value": "Mesclun", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#salad", + "@type": "dfc-p:ProductType" + }, + { + "@id": "http://static.datafoodconsortium.org/data/productTypes.rdf#carrot", + "rdfs:label": [ + { + "@value": "Carotte", + "@language": "fr" + }, + { + "@value": "Carrot", + "@language": "en" + } + ], + "dfc-p:specialize": "http://static.datafoodconsortium.org/data/productTypes.rdf#vegetable", + "@type": "dfc-p:ProductType" + } + ] +} diff --git a/ontology/units.json b/ontology/units.json new file mode 100644 index 000000000..066fecd65 --- /dev/null +++ b/ontology/units.json @@ -0,0 +1,28 @@ +{ + "@context":{ + "dfc-p": "http://static.datafoodconsortium.org/ontologies/dfc_ProductGlossary.owl#", + "dfc-u":"http://static.datafoodconsortium.org/data/units.rdf#" + }, + "@graph":[ + { + "@id":"dfc-u:kg", + "@type":"dfc-p:Unit", + "rdfs:label":"kilogramme" + }, + { + "@id":"dfc-u:u", + "@type":"dfc-p:Unit", + "rdfs:label":"unité" + }, + { + "@id":"dfc-u:g", + "@type":"dfc-p:Unit", + "rdfs:label":"gramme" + }, + { + "@id":"dfc-u:l", + "@type":"dfc-p:Unit", + "rdfs:label":"litre" + } + ] +} diff --git a/outbox.py b/outbox.py index d7f4ef493..bfff4f7a3 100644 --- a/outbox.py +++ b/outbox.py @@ -548,9 +548,8 @@ def postMessageToOutbox(session, translate: {}, if debug: print('DEBUG: handle share uploads') - outboxShareUpload(baseDir, httpPrefix, - postToNickname, domain, - port, messageJson, debug, city) + outboxShareUpload(baseDir, httpPrefix, postToNickname, domain, + port, messageJson, debug, city, systemLanguage) if debug: print('DEBUG: handle undo share uploads') diff --git a/shares.py b/shares.py index 9e10e6aa3..c581bc1d1 100644 --- a/shares.py +++ b/shares.py @@ -26,6 +26,51 @@ from utils import acctDir from media import processMetaData +def _loadProductIds(baseDir: str, systemLanguage: str) -> {}: + """Loads the product types ontology + This is used to add an id to shared items + """ + productTypesFilename = baseDir + '/ontology/customProductTypes.json' + if not os.path.isfile(productTypesFilename): + productTypesFilename = baseDir + '/ontology/productTypes.json' + productTypes = loadJson(productTypesFilename) + if not productTypes: + return None + if not productTypes.get('@graph'): + return None + if len(productTypes['@graph']) == 0: + return None + if not productTypes['@graph'][0].get('rdfs:label'): + return None + languageExists = False + for label in productTypes['@graph'][0]['rdfs:label']: + if not label.get('@language'): + continue + if productTypes['@graph'][0]['rdfs:label']['@language'] == \ + systemLanguage: + languageExists = True + break + if not languageExists: + print('productTypes ontology does not contain the language ' + + systemLanguage) + return None + productIds = {} + for item in productTypes['@graph']: + if not item.get('@id'): + continue + if not item.get('rdfs:label'): + continue + for label in item['rdfs:label']: + if not label.get('@language'): + continue + if not label.get('@value'): + continue + if label['@language'] == systemLanguage: + productIds[label['@value'].lower()] = item['@id'] + break + return productIds + + def getValidSharedItemID(displayName: str) -> str: """Removes any invalid characters from the display name to produce an item ID @@ -94,11 +139,45 @@ def _addShareDurationSec(duration: str, published: str) -> int: return 0 +def _getshareProductId(baseDir: str, systemLanguage: str, + itemType: str) -> str: + """Attempts to obtain an Id for the shared item, + based upon productTypes ontology. + See https://github.com/datafoodconsortium/ontology + """ + productIds = _loadProductIds(baseDir, systemLanguage) + if not productIds: + return '' + itemTypeLower = itemType.lower() + matchName = '' + matchId = '' + for name, uri in productIds.items(): + if name not in itemTypeLower: + continue + if len(name) > len(matchName): + matchName = name + matchId = uri + if not matchId: + # bag of words match + maxMatchedWords = 0 + for name, uri in productIds.items(): + words = name.split(' ') + score = 0 + for wrd in words: + if wrd in itemTypeLower: + score += 1 + if score > maxMatchedWords: + maxMatchedWords = score + matchId = uri + return matchId + + def addShare(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, displayName: str, summary: str, imageFilename: str, itemQty: int, itemType: str, itemCategory: str, location: str, - duration: str, debug: bool, city: str) -> None: + duration: str, debug: bool, city: str, + systemLanguage: str) -> None: """Adds a new share """ sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' @@ -111,6 +190,7 @@ def addShare(baseDir: str, durationSec = _addShareDurationSec(duration, published) itemID = getValidSharedItemID(displayName) + productId = _getshareProductId(baseDir, systemLanguage, itemType) # has an image for this share been uploaded? imageUrl = None @@ -152,6 +232,7 @@ def addShare(baseDir: str, "summary": summary, "imageUrl": imageUrl, "itemQty": itemQty, + "productId": productId, "itemType": itemType, "category": itemCategory, "location": location, @@ -522,7 +603,8 @@ def sendUndoShareViaServer(baseDir: str, session, def outboxShareUpload(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, - messageJson: {}, debug: bool, city: str) -> None: + messageJson: {}, debug: bool, city: str, + systemLanguage: str) -> None: """ When a shared item is received by the outbox from c2s """ if not messageJson.get('type'): @@ -577,7 +659,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['itemCategory'], messageJson['object']['location'], messageJson['object']['duration'], - debug, city) + debug, city, systemLanguage) if debug: print('DEBUG: shared item received via c2s') From f1d42b62db8bfb32ae96ebfe3854ccbe0aba4e92 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 15:43:52 +0100 Subject: [PATCH 107/459] Change terminology to be less ambiguous --- shares.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/shares.py b/shares.py index c581bc1d1..826936b1f 100644 --- a/shares.py +++ b/shares.py @@ -26,7 +26,7 @@ from utils import acctDir from media import processMetaData -def _loadProductIds(baseDir: str, systemLanguage: str) -> {}: +def _loadDfcIds(baseDir: str, systemLanguage: str) -> {}: """Loads the product types ontology This is used to add an id to shared items """ @@ -54,7 +54,7 @@ def _loadProductIds(baseDir: str, systemLanguage: str) -> {}: print('productTypes ontology does not contain the language ' + systemLanguage) return None - productIds = {} + dfcIds = {} for item in productTypes['@graph']: if not item.get('@id'): continue @@ -66,9 +66,9 @@ def _loadProductIds(baseDir: str, systemLanguage: str) -> {}: if not label.get('@value'): continue if label['@language'] == systemLanguage: - productIds[label['@value'].lower()] = item['@id'] + dfcIds[label['@value'].lower()] = item['@id'] break - return productIds + return dfcIds def getValidSharedItemID(displayName: str) -> str: @@ -139,19 +139,18 @@ def _addShareDurationSec(duration: str, published: str) -> int: return 0 -def _getshareProductId(baseDir: str, systemLanguage: str, - itemType: str) -> str: - """Attempts to obtain an Id for the shared item, +def _getshareDfcId(baseDir: str, systemLanguage: str, itemType: str) -> str: + """Attempts to obtain a DFC Id for the shared item, based upon productTypes ontology. See https://github.com/datafoodconsortium/ontology """ - productIds = _loadProductIds(baseDir, systemLanguage) - if not productIds: + dfcIds = _loadDfcIds(baseDir, systemLanguage) + if not dfcIds: return '' itemTypeLower = itemType.lower() matchName = '' matchId = '' - for name, uri in productIds.items(): + for name, uri in dfcIds.items(): if name not in itemTypeLower: continue if len(name) > len(matchName): @@ -160,7 +159,7 @@ def _getshareProductId(baseDir: str, systemLanguage: str, if not matchId: # bag of words match maxMatchedWords = 0 - for name, uri in productIds.items(): + for name, uri in dfcIds.items(): words = name.split(' ') score = 0 for wrd in words: @@ -190,7 +189,7 @@ def addShare(baseDir: str, durationSec = _addShareDurationSec(duration, published) itemID = getValidSharedItemID(displayName) - productId = _getshareProductId(baseDir, systemLanguage, itemType) + dfcId = _getshareDfcId(baseDir, systemLanguage, itemType) # has an image for this share been uploaded? imageUrl = None @@ -232,7 +231,7 @@ def addShare(baseDir: str, "summary": summary, "imageUrl": imageUrl, "itemQty": itemQty, - "productId": productId, + "dfcId": dfcId, "itemType": itemType, "category": itemCategory, "location": location, From 723e6e7a5ec836a8b1787ce9f30c76b5b4c4c7d1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 16:05:21 +0100 Subject: [PATCH 108/459] Check for food category before assigning dfc id --- daemon.py | 3 ++- epicyon.py | 4 ++-- outbox.py | 3 ++- shares.py | 15 ++++++++++----- translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/ku.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/sw.json | 3 ++- translations/zh.json | 3 ++- 21 files changed, 50 insertions(+), 26 deletions(-) diff --git a/daemon.py b/daemon.py index fa9e798d1..265d5d5b0 100644 --- a/daemon.py +++ b/daemon.py @@ -13665,7 +13665,8 @@ class PubServer(BaseHTTPRequestHandler): durationStr, self.server.debug, city, - self.server.systemLanguage) + self.server.systemLanguage, + self.server.translate) if filename: if os.path.isfile(filename): os.remove(filename) diff --git a/epicyon.py b/epicyon.py index fc32df676..2bc7987d4 100644 --- a/epicyon.py +++ b/epicyon.py @@ -2332,7 +2332,7 @@ if args.testdata: "mechanical", "City", "2 months", - debug, city, args.language) + debug, city, args.language, {}) addShare(baseDir, httpPrefix, nickname, domain, port, "witch hat", @@ -2342,7 +2342,7 @@ if args.testdata: "clothing", "City", "3 months", - debug, city, args.language) + debug, city, args.language, {}) deleteAllPosts(baseDir, nickname, domain, 'inbox') deleteAllPosts(baseDir, nickname, domain, 'outbox') diff --git a/outbox.py b/outbox.py index bfff4f7a3..0d12c8212 100644 --- a/outbox.py +++ b/outbox.py @@ -549,7 +549,8 @@ def postMessageToOutbox(session, translate: {}, if debug: print('DEBUG: handle share uploads') outboxShareUpload(baseDir, httpPrefix, postToNickname, domain, - port, messageJson, debug, city, systemLanguage) + port, messageJson, debug, city, + systemLanguage, translate) if debug: print('DEBUG: handle undo share uploads') diff --git a/shares.py b/shares.py index 826936b1f..348246502 100644 --- a/shares.py +++ b/shares.py @@ -139,11 +139,15 @@ def _addShareDurationSec(duration: str, published: str) -> int: return 0 -def _getshareDfcId(baseDir: str, systemLanguage: str, itemType: str) -> str: +def _getshareDfcId(baseDir: str, systemLanguage: str, + itemType: str, itemCategory: str, + translate: {}) -> str: """Attempts to obtain a DFC Id for the shared item, based upon productTypes ontology. See https://github.com/datafoodconsortium/ontology """ + if translate['food'] not in itemCategory.lower(): + return '' dfcIds = _loadDfcIds(baseDir, systemLanguage) if not dfcIds: return '' @@ -176,7 +180,7 @@ def addShare(baseDir: str, displayName: str, summary: str, imageFilename: str, itemQty: int, itemType: str, itemCategory: str, location: str, duration: str, debug: bool, city: str, - systemLanguage: str) -> None: + systemLanguage: str, translate: {}) -> None: """Adds a new share """ sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' @@ -189,7 +193,8 @@ def addShare(baseDir: str, durationSec = _addShareDurationSec(duration, published) itemID = getValidSharedItemID(displayName) - dfcId = _getshareDfcId(baseDir, systemLanguage, itemType) + dfcId = _getshareDfcId(baseDir, systemLanguage, + itemType, itemCategory, translate) # has an image for this share been uploaded? imageUrl = None @@ -603,7 +608,7 @@ def sendUndoShareViaServer(baseDir: str, session, def outboxShareUpload(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, messageJson: {}, debug: bool, city: str, - systemLanguage: str) -> None: + systemLanguage: str, translate: {}) -> None: """ When a shared item is received by the outbox from c2s """ if not messageJson.get('type'): @@ -658,7 +663,7 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['itemCategory'], messageJson['object']['location'], messageJson['object']['duration'], - debug, city, systemLanguage) + debug, city, systemLanguage, translate) if debug: print('DEBUG: shared item received via c2s') diff --git a/translations/ar.json b/translations/ar.json index f6e54fb4a..5106215ba 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "أعلمني عندما ينشر الحساب هذا", "Languages": "اللغات", "Translated": "تترجم", - "Quantity": "كمية" + "Quantity": "كمية", + "food": "غذاء" } diff --git a/translations/ca.json b/translations/ca.json index 0c4e49f54..e238d3270 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Aviseu-me quan publiqui aquest compte", "Languages": "Idiomes", "Translated": "Traduït", - "Quantity": "Quantitat" + "Quantity": "Quantitat", + "food": "menjar" } diff --git a/translations/cy.json b/translations/cy.json index 8eb9088d3..093334c80 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Rhoi gwybod i mi pan fydd y cyfrifon cyfrif hwn", "Languages": "Ieithoedd", "Translated": "Chyfieithwyd", - "Quantity": "Symiau" + "Quantity": "Symiau", + "food": "bwyd" } diff --git a/translations/de.json b/translations/de.json index f6b501be1..52eaaaa29 100644 --- a/translations/de.json +++ b/translations/de.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Benachrichtigen Sie mich, wenn dieses Konto postet", "Languages": "Sprachen", "Translated": "Übersetzt", - "Quantity": "Menge" + "Quantity": "Menge", + "food": "lebensmittel" } diff --git a/translations/en.json b/translations/en.json index e358967ba..bfbb2ec5e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Notify me when this account posts", "Languages": "Languages", "Translated": "Translated", - "Quantity": "Quantity" + "Quantity": "Quantity", + "food": "food" } diff --git a/translations/es.json b/translations/es.json index 0627c523c..7af7d0862 100644 --- a/translations/es.json +++ b/translations/es.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Notifíqueme cuando se publique esta cuenta", "Languages": "Idiomas", "Translated": "Traducida", - "Quantity": "Cantidad" + "Quantity": "Cantidad", + "food": "comida" } diff --git a/translations/fr.json b/translations/fr.json index 0e73dcd27..e1f026a73 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Avertissez-moi quand ce compte publie", "Languages": "Langues", "Translated": "Traduite", - "Quantity": "Quantité" + "Quantity": "Quantité", + "food": "aliments" } diff --git a/translations/ga.json b/translations/ga.json index 6841a30d8..0d039b369 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Cuir in iúl dom nuair a phostófar an cuntas seo", "Languages": "Teangacha", "Translated": "Aistrithe", - "Quantity": "Cainníocht" + "Quantity": "Cainníocht", + "food": "bia" } diff --git a/translations/hi.json b/translations/hi.json index fd07f2407..cade54185 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "यह खाता पोस्ट होने पर मुझे सूचित करें", "Languages": "बोली", "Translated": "अनुवाद", - "Quantity": "मात्रा" + "Quantity": "मात्रा", + "food": "खाना" } diff --git a/translations/it.json b/translations/it.json index 98fc2abb4..74a7399ee 100644 --- a/translations/it.json +++ b/translations/it.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Avvisami quando questo account messaggi", "Languages": "Le lingue", "Translated": "Tradotto", - "Quantity": "Quantità" + "Quantity": "Quantità", + "food": "cibo" } diff --git a/translations/ja.json b/translations/ja.json index 237c40538..10f678667 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "この口座投稿を通知する", "Languages": "言語", "Translated": "翻訳", - "Quantity": "量" + "Quantity": "量", + "food": "食物" } diff --git a/translations/ku.json b/translations/ku.json index ddd15e33e..22c792d13 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Dema ku ev postên hesabê min agahdar bikin", "Languages": "Ziman", "Translated": "Wergerandin", - "Quantity": "Jimarî" + "Quantity": "Jimarî", + "food": "xûrek" } diff --git a/translations/oc.json b/translations/oc.json index 53843d5aa..f6e1a2673 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -449,5 +449,6 @@ "Notify me when this account posts": "Notify me when this account posts", "Languages": "Languages", "Translated": "Translated", - "Quantity": "Quantity" + "Quantity": "Quantity", + "food": "food" } diff --git a/translations/pt.json b/translations/pt.json index b2bfcbf1a..cbf9f0975 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Notifique-me quando esta conta posts", "Languages": "Línguas", "Translated": "Traduzida", - "Quantity": "Quantidade" + "Quantity": "Quantidade", + "food": "comida" } diff --git a/translations/ru.json b/translations/ru.json index 2284d4d13..f64dce626 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Сообщите мне, когда эта учетная запись", "Languages": "Языки", "Translated": "Перевод", - "Quantity": "Количество" + "Quantity": "Количество", + "food": "еда" } diff --git a/translations/sw.json b/translations/sw.json index 2b010a9a2..1d83ff995 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "Nijulishe wakati akaunti hii ya akaunti.", "Languages": "Lugha", "Translated": "Ilitafsiriwa", - "Quantity": "Wingi" + "Quantity": "Wingi", + "food": "chakula" } diff --git a/translations/zh.json b/translations/zh.json index 828b32382..ec5d25507 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -453,5 +453,6 @@ "Notify me when this account posts": "此帐户帖子时通知我", "Languages": "语言", "Translated": "翻译", - "Quantity": "数量" + "Quantity": "数量", + "food": "食物" } From 5d5dab6d2fb812d7e25e8fd5002d2f165f0294e1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 23:08:11 +0100 Subject: [PATCH 109/459] Functions for shares catalog endpoints --- daemon.py | 13 +++- epicyon.py | 13 +++- shares.py | 165 ++++++++++++++++++++++++++++++++++++------ tests.py | 4 +- translations/ar.json | 4 +- translations/ca.json | 4 +- translations/cy.json | 4 +- translations/de.json | 4 +- translations/en.json | 4 +- translations/es.json | 4 +- translations/fr.json | 4 +- translations/ga.json | 4 +- translations/hi.json | 4 +- translations/it.json | 4 +- translations/ja.json | 4 +- translations/ku.json | 4 +- translations/oc.json | 4 +- translations/pt.json | 4 +- translations/ru.json | 4 +- translations/sw.json | 4 +- translations/zh.json | 4 +- webapp_create_post.py | 8 ++ webapp_search.py | 11 ++- webapp_timeline.py | 9 ++- webapp_utils.py | 17 +++++ 25 files changed, 262 insertions(+), 46 deletions(-) diff --git a/daemon.py b/daemon.py index 265d5d5b0..56cf7888c 100644 --- a/daemon.py +++ b/daemon.py @@ -13631,6 +13631,10 @@ class PubServer(BaseHTTPRequestHandler): return -1 if not fields.get('itemType'): return -1 + if not fields.get('itemPrice'): + return -1 + if not fields.get('itemCurrency'): + return -1 if not fields.get('category'): return -1 if not fields.get('location'): @@ -13652,6 +13656,13 @@ class PubServer(BaseHTTPRequestHandler): if fields['itemQty']: if fields['itemQty'].isdigit(): itemQty = int(fields['itemQty']) + itemPrice = "0" + if fields['itemPrice']: + if fields['itemPrice'].isdigit(): + itemPrice = fields['itemPrice'] + itemCurrency = "EUR" + if fields['itemCurrency']: + itemCurrency = fields['itemCurrency'] addShare(self.server.baseDir, self.server.httpPrefix, nickname, @@ -13664,7 +13675,7 @@ class PubServer(BaseHTTPRequestHandler): fields['location'], durationStr, self.server.debug, - city, + city, itemPrice, itemCurrency, self.server.systemLanguage, self.server.translate) if filename: diff --git a/epicyon.py b/epicyon.py index 2bc7987d4..574203e50 100644 --- a/epicyon.py +++ b/epicyon.py @@ -561,6 +561,12 @@ parser.add_argument('--itemImage', dest='itemImage', type=str, parser.add_argument('--itemQty', dest='itemQty', type=int, default=1, help='Quantity of items being shared') +parser.add_argument('--itemPrice', dest='itemPrice', type=str, + default="0", + help='Total price of items being shared') +parser.add_argument('--itemCurrency', dest='itemCurrency', type=str, + default="EUR", + help='Currency of items being shared') parser.add_argument('--itemType', dest='itemType', type=str, default=None, help='Type of item being shared') @@ -1290,7 +1296,8 @@ if args.itemName: args.location, args.duration, cachedWebfingers, personCache, - debug, __version__) + debug, __version__, + args.itemPrice, args.itemCurrency) for i in range(10): # TODO detect send success/fail time.sleep(1) @@ -2330,7 +2337,7 @@ if args.testdata: "img/shares1.png", 1, "tool", "mechanical", - "City", + "City", "0", "GBP", "2 months", debug, city, args.language, {}) addShare(baseDir, @@ -2340,7 +2347,7 @@ if args.testdata: "img/shares2.png", 1, "hat", "clothing", - "City", + "City", "0", "GBP", "3 months", debug, city, args.language, {}) diff --git a/shares.py b/shares.py index 348246502..33dbd6e65 100644 --- a/shares.py +++ b/shares.py @@ -9,6 +9,7 @@ __module_group__ = "Timeline" import os import time +import datetime from webfinger import webfingerHandle from auth import createBasicAuthHeader from posts import getPersonBox @@ -180,6 +181,7 @@ def addShare(baseDir: str, displayName: str, summary: str, imageFilename: str, itemQty: int, itemType: str, itemCategory: str, location: str, duration: str, debug: bool, city: str, + price: str, currency: str, systemLanguage: str, translate: {}) -> None: """Adds a new share """ @@ -241,7 +243,9 @@ def addShare(baseDir: str, "category": itemCategory, "location": location, "published": published, - "expire": durationSec + "expire": durationSec, + "price": "0", + "currency": "" } saveJson(sharesJson, sharesFilename) @@ -283,25 +287,27 @@ def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None: handleDomain = removeDomainPort(domain) handle = nickname + '@' + handleDomain sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' - if os.path.isfile(sharesFilename): - sharesJson = loadJson(sharesFilename) - if sharesJson: - currTime = int(time.time()) - deleteItemID = [] - for itemID, item in sharesJson.items(): - if currTime > item['expire']: - deleteItemID.append(itemID) - if deleteItemID: - for itemID in deleteItemID: - del sharesJson[itemID] - # remove any associated images - itemIDfile = \ - baseDir + '/sharefiles/' + nickname + '/' + itemID - formats = getImageExtensions() - for ext in formats: - if os.path.isfile(itemIDfile + '.' + ext): - os.remove(itemIDfile + '.' + ext) - saveJson(sharesJson, sharesFilename) + if not os.path.isfile(sharesFilename): + return + sharesJson = loadJson(sharesFilename) + if not sharesJson: + return + currTime = int(time.time()) + deleteItemID = [] + for itemID, item in sharesJson.items(): + if currTime > item['expire']: + deleteItemID.append(itemID) + if not deleteItemID: + return + for itemID in deleteItemID: + del sharesJson[itemID] + # remove any associated images + itemIDfile = baseDir + '/sharefiles/' + nickname + '/' + itemID + formats = getImageExtensions() + for ext in formats: + if os.path.isfile(itemIDfile + '.' + ext): + os.remove(itemIDfile + '.' + ext) + saveJson(sharesJson, sharesFilename) def getSharesFeedForPerson(baseDir: str, @@ -410,7 +416,8 @@ def sendShareViaServer(baseDir, session, itemQty: int, itemType: str, itemCategory: str, location: str, duration: str, cachedWebfingers: {}, personCache: {}, - debug: bool, projectVersion: str) -> {}: + debug: bool, projectVersion: str, + itemPrice: str, itemCurrency: str) -> {}: """Creates an item share via c2s """ if not session: @@ -438,6 +445,8 @@ def sendShareViaServer(baseDir, session, "category": itemCategory, "location": location, "duration": duration, + "itemPrice": itemPrice, + "itemCurrency": itemCurrency, 'to': [toUrl], 'cc': [ccUrl] }, @@ -663,7 +672,10 @@ def outboxShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['itemCategory'], messageJson['object']['location'], messageJson['object']['duration'], - debug, city, systemLanguage, translate) + debug, city, + messageJson['object']['itemPrice'], + messageJson['object']['itemCurrency'], + systemLanguage, translate) if debug: print('DEBUG: shared item received via c2s') @@ -695,3 +707,112 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, messageJson['object']['displayName']) if debug: print('DEBUG: shared item removed via c2s') + + +def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, + nickname: str, domain: str, + domainFull: str, + path: str) -> {}: + """Returns the endpoint for the shares catalog of a particular account + """ + dfcUrl = \ + "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" + owner = httpPrefix + '://' + domainFull + '/users/' + nickname + dfcInstanceId = owner + '/catalog' + endpoint = { + "@context": { + "DFC": dfcUrl, + "@base": "http://maPlateformeNationale" + }, + "@id": dfcInstanceId, + "@type": "DFC:Entreprise", + "DFC:supplies": [] + } + + sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' + if not os.path.isfile(sharesFilename): + return endpoint + sharesJson = loadJson(sharesFilename) + if not sharesJson: + return endpoint + + for itemID, item in sharesJson.items(): + if not item.get('dfcId'): + continue + + expireDate = datetime.datetime.fromtimestamp(item['durationSec']) + expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") + + catalogItem = { + "@id": item['dfcId'], + "DFC:offeredThrough": owner, + "DFC:startDate": item['published'], + "DFC:expiryDate": expireDateStr, + "DFC:quantity": item['itemQty'], + "DFC:totalTheoriticalStock": item['itemQty'], + "DFC:price": "0", + "DFC:Image": item['imageUrl'], + "DFC:description": item['displayName'] + ': ' + item['summary'] + } + endpoint['DFC:supplies'].append(catalogItem) + + return endpoint + + +def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, + domainFull: str, + path: str) -> {}: + """Returns the endpoint for the shares catalog for the instance + """ + dfcUrl = \ + "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" + dfcInstanceId = httpPrefix + '://' + domainFull + '/catalog' + endpoint = { + "@context": { + "DFC": dfcUrl, + "@base": "http://maPlateformeNationale" + }, + "@id": dfcInstanceId, + "@type": "DFC:Entreprise", + "DFC:supplies": [] + } + + for subdir, dirs, files in os.walk(baseDir + '/accounts'): + for acct in dirs: + if not isAccountDir(acct): + continue + nickname = acct.split('@')[0] + domain = acct.split('@')[1] + owner = httpPrefix + '://' + domainFull + '/users/' + nickname + + sharesFilename = \ + acctDir(baseDir, nickname, domain) + '/shares.json' + if not os.path.isfile(sharesFilename): + continue + sharesJson = loadJson(sharesFilename) + if not sharesJson: + continue + + for itemID, item in sharesJson.items(): + if not item.get('dfcId'): + continue + + expireDate = \ + datetime.datetime.fromtimestamp(item['durationSec']) + expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") + + description = item['displayName'] + ': ' + item['summary'] + catalogItem = { + "@id": item['dfcId'], + "DFC:offeredThrough": owner, + "DFC:startDate": item['published'], + "DFC:expiryDate": expireDateStr, + "DFC:quantity": item['itemQty'], + "DFC:totalTheoriticalStock": item['itemQty'], + "DFC:price": "0", + "DFC:Image": item['imageUrl'], + "DFC:description": description + } + endpoint['DFC:supplies'].append(catalogItem) + + return endpoint diff --git a/tests.py b/tests.py index e44517da4..f483b4be8 100644 --- a/tests.py +++ b/tests.py @@ -3272,7 +3272,9 @@ def _testFunctions(): 'E2EEremoveDevice', 'setOrganizationScheme', 'fill_headers', - '_nothing' + '_nothing', + 'sharesCatalogEndpoint', + 'sharesCatalogAccountEndpoint' ] excludeImports = [ 'link', diff --git a/translations/ar.json b/translations/ar.json index 5106215ba..fb361feb6 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -454,5 +454,7 @@ "Languages": "اللغات", "Translated": "تترجم", "Quantity": "كمية", - "food": "غذاء" + "food": "غذاء", + "Price": "السعر", + "Currency": "عملة" } diff --git a/translations/ca.json b/translations/ca.json index e238d3270..083bbab60 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -454,5 +454,7 @@ "Languages": "Idiomes", "Translated": "Traduït", "Quantity": "Quantitat", - "food": "menjar" + "food": "menjar", + "Price": "Preu", + "Currency": "Moneda" } diff --git a/translations/cy.json b/translations/cy.json index 093334c80..001716b71 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -454,5 +454,7 @@ "Languages": "Ieithoedd", "Translated": "Chyfieithwyd", "Quantity": "Symiau", - "food": "bwyd" + "food": "bwyd", + "Price": "Prisia", + "Currency": "Harian" } diff --git a/translations/de.json b/translations/de.json index 52eaaaa29..ff9b3ad46 100644 --- a/translations/de.json +++ b/translations/de.json @@ -454,5 +454,7 @@ "Languages": "Sprachen", "Translated": "Übersetzt", "Quantity": "Menge", - "food": "lebensmittel" + "food": "lebensmittel", + "Price": "Preis", + "Currency": "Währung" } diff --git a/translations/en.json b/translations/en.json index bfbb2ec5e..d211aee2c 100644 --- a/translations/en.json +++ b/translations/en.json @@ -454,5 +454,7 @@ "Languages": "Languages", "Translated": "Translated", "Quantity": "Quantity", - "food": "food" + "food": "food", + "Price": "Price", + "Currency": "Currency" } diff --git a/translations/es.json b/translations/es.json index 7af7d0862..bf3ec917c 100644 --- a/translations/es.json +++ b/translations/es.json @@ -454,5 +454,7 @@ "Languages": "Idiomas", "Translated": "Traducida", "Quantity": "Cantidad", - "food": "comida" + "food": "comida", + "Price": "Precio", + "Currency": "Divisa" } diff --git a/translations/fr.json b/translations/fr.json index e1f026a73..8481d0ecd 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -454,5 +454,7 @@ "Languages": "Langues", "Translated": "Traduite", "Quantity": "Quantité", - "food": "aliments" + "food": "aliments", + "Price": "Prix", + "Currency": "Devise" } diff --git a/translations/ga.json b/translations/ga.json index 0d039b369..7055f288d 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -454,5 +454,7 @@ "Languages": "Teangacha", "Translated": "Aistrithe", "Quantity": "Cainníocht", - "food": "bia" + "food": "bia", + "Price": "Praghas a chur ar", + "Currency": "Airgeadra" } diff --git a/translations/hi.json b/translations/hi.json index cade54185..6c46aa3fd 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -454,5 +454,7 @@ "Languages": "बोली", "Translated": "अनुवाद", "Quantity": "मात्रा", - "food": "खाना" + "food": "खाना", + "Price": "कीमत", + "Currency": "मुद्रा" } diff --git a/translations/it.json b/translations/it.json index 74a7399ee..dcc91184a 100644 --- a/translations/it.json +++ b/translations/it.json @@ -454,5 +454,7 @@ "Languages": "Le lingue", "Translated": "Tradotto", "Quantity": "Quantità", - "food": "cibo" + "food": "cibo", + "Price": "Prezzo", + "Currency": "Moneta" } diff --git a/translations/ja.json b/translations/ja.json index 10f678667..1bf47aef8 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -454,5 +454,7 @@ "Languages": "言語", "Translated": "翻訳", "Quantity": "量", - "food": "食物" + "food": "食物", + "Price": "価格", + "Currency": "通貨" } diff --git a/translations/ku.json b/translations/ku.json index 22c792d13..17b8bca0a 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -454,5 +454,7 @@ "Languages": "Ziman", "Translated": "Wergerandin", "Quantity": "Jimarî", - "food": "xûrek" + "food": "xûrek", + "Price": "Biha", + "Currency": "Diravcins" } diff --git a/translations/oc.json b/translations/oc.json index f6e1a2673..5eb5c0953 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -450,5 +450,7 @@ "Languages": "Languages", "Translated": "Translated", "Quantity": "Quantity", - "food": "food" + "food": "food", + "Price": "Price", + "Currency": "Currency" } diff --git a/translations/pt.json b/translations/pt.json index cbf9f0975..74de6dca5 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -454,5 +454,7 @@ "Languages": "Línguas", "Translated": "Traduzida", "Quantity": "Quantidade", - "food": "comida" + "food": "comida", + "Price": "Preço", + "Currency": "Moeda" } diff --git a/translations/ru.json b/translations/ru.json index f64dce626..3d01c70d6 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -454,5 +454,7 @@ "Languages": "Языки", "Translated": "Перевод", "Quantity": "Количество", - "food": "еда" + "food": "еда", + "Price": "Цена", + "Currency": "Валюта" } diff --git a/translations/sw.json b/translations/sw.json index 1d83ff995..7ff101685 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -454,5 +454,7 @@ "Languages": "Lugha", "Translated": "Ilitafsiriwa", "Quantity": "Wingi", - "food": "chakula" + "food": "chakula", + "Price": "Bei", + "Currency": "Fedha" } diff --git a/translations/zh.json b/translations/zh.json index ec5d25507..f8cb6d8a9 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -454,5 +454,7 @@ "Languages": "语言", "Translated": "翻译", "Quantity": "数量", - "food": "食物" + "food": "食物", + "Price": "价钱", + "Currency": "货币" } diff --git a/webapp_create_post.py b/webapp_create_post.py index d5bc22bc5..d92120982 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -19,6 +19,7 @@ from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlFooter from webapp_utils import editTextField from webapp_utils import editNumberField +from webapp_utils import editCurrencyField def _htmlFollowingDataList(baseDir: str, nickname: str, @@ -371,6 +372,13 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, cityOrLocStr = translate['City or location of the shared item'] extraFields += editTextField(cityOrLocStr + ':', 'location', '') extraFields += '
\n' + extraFields += '
\n' + extraFields += \ + editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00') + extraFields += '
' + \ + editTextField(translate['Currency'] + ':', + 'itemCurrency', 'EUR') + extraFields += '
\n' citationsStr = '' if endpoint == 'newblog': diff --git a/webapp_search.py b/webapp_search.py index b8221de7e..013fff788 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -188,7 +188,16 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, ':
' + sharedItem['category'] + ' ' sharedItemsForm += \ '' + translate['Location'] + \ - ': ' + sharedItem['location'] + '

\n' + ':
' + sharedItem['location'] + if sharedItem.get('itemPrice') and \ + sharedItem.get('itemCurrency'): + if sharedItem['itemPrice'].isdigit(): + if float(sharedItem['itemPrice']) > 0: + sharedItemsForm += \ + ' ' + translate['Price'] + \ + ': ' + sharedItem['itemPrice'] + \ + ' ' + sharedItem['itemCurrency'] + sharedItemsForm += '

\n' contactActor = \ httpPrefix + '://' + domainFull + \ '/users/' + contactNickname diff --git a/webapp_timeline.py b/webapp_timeline.py index e7fc5e2e5..265634b12 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -830,7 +830,14 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, profileStr += \ '' + translate['Category'] + ': ' + item['category'] + ' ' profileStr += \ - '' + translate['Location'] + ': ' + item['location'] + '

\n' + '' + translate['Location'] + ': ' + item['location'] + if item.get('itemPrice') and item.get('itemCurrency'): + if item['itemPrice'].isdigit(): + if float(item['itemPrice']) > 0: + profileStr += ' ' + \ + '' + translate['Price'] + ': ' + \ + item['itemPrice'] + ' ' + item['itemCurrency'] + profileStr += '

\n' sharedesc = item['displayName'] if '<' not in sharedesc and '?' not in sharedesc: if showContact: diff --git a/webapp_utils.py b/webapp_utils.py index 1a22efa36..265f2e1b8 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -1159,6 +1159,23 @@ def editNumberField(label: str, name: str, value: int = 1, 'min="' + str(minValue) + '" max="' + str(maxValue) + '" step="1">\n' +def editCurrencyField(label: str, name: str, value: str = "0.00", + placeholder: str = "0.00") -> str: + """Returns html for editing a currency field + """ + if value is None: + value = '0.00' + placeholderStr = '0.00' + if placeholder: + if placeholder.isdigit(): + placeholderStr = ' placeholder="' + str(placeholder) + '"' + return \ + '
\n' + \ + ' \n' + + def editCheckBox(label: str, name: str, checked: bool = False) -> str: """Returns html for editing a checkbox field """ From bba4e461e40f919b0b5fd2057e84b63baa8847fe Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 24 Jul 2021 23:12:26 +0100 Subject: [PATCH 110/459] Comments --- shares.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shares.py b/shares.py index 33dbd6e65..d53c22f09 100644 --- a/shares.py +++ b/shares.py @@ -714,6 +714,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, domainFull: str, path: str) -> {}: """Returns the endpoint for the shares catalog of a particular account + See https://github.com/datafoodconsortium/ontology """ dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" @@ -763,6 +764,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, domainFull: str, path: str) -> {}: """Returns the endpoint for the shares catalog for the instance + See https://github.com/datafoodconsortium/ontology """ dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" From 5421facdb39d31d66b682c85cd4c71b864d7a14a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 10:33:59 +0100 Subject: [PATCH 111/459] Unique share ID for the instance --- daemon.py | 7 ++-- shares.py | 84 ++++++++++++++++++++++++++++++++--------------- webapp_confirm.py | 2 +- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/daemon.py b/daemon.py index 56cf7888c..9147de8a0 100644 --- a/daemon.py +++ b/daemon.py @@ -205,7 +205,7 @@ from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal from shares import getSharesFeedForPerson from shares import addShare -from shares import removeShare +from shares import removeSharedItem from shares import expireShares from categories import setHashtagCategory from languages import getActorLanguages @@ -3332,8 +3332,9 @@ class PubServer(BaseHTTPRequestHandler): shareNickname = getNicknameFromActor(shareActor) if shareNickname: shareDomain, sharePort = getDomainFromActor(shareActor) - removeShare(baseDir, - shareNickname, shareDomain, shareName) + removeSharedItem(baseDir, + shareNickname, shareDomain, shareName, + httpPrefix, domainFull) if callingDomain.endswith('.onion') and onionDomain: originPathStr = 'http://' + onionDomain + usersPath diff --git a/shares.py b/shares.py index d53c22f09..6e11721ca 100644 --- a/shares.py +++ b/shares.py @@ -72,7 +72,7 @@ def _loadDfcIds(baseDir: str, systemLanguage: str) -> {}: return dfcIds -def getValidSharedItemID(displayName: str) -> str: +def getValidSharedItemID(actor: str, displayName: str) -> str: """Removes any invalid characters from the display name to produce an item ID """ @@ -84,11 +84,12 @@ def getValidSharedItemID(displayName: str) -> str: displayName = displayName.replace(ch, '-') displayName = displayName.replace('.', '_') displayName = displayName.replace("’", "'") - return displayName + return actor + '/item/' + displayName -def removeShare(baseDir: str, nickname: str, domain: str, - displayName: str) -> None: +def removeSharedItem(baseDir: str, nickname: str, domain: str, + displayName: str, + httpPrefix: str, domainFull: str) -> None: """Removes a share for a person """ sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' @@ -101,7 +102,8 @@ def removeShare(baseDir: str, nickname: str, domain: str, print('ERROR: shares.json could not be loaded from ' + sharesFilename) return - itemID = getValidSharedItemID(displayName) + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + itemID = getValidSharedItemID(actor, displayName) if sharesJson.get(itemID): # remove any image for the item itemIDfile = baseDir + '/sharefiles/' + nickname + '/' + itemID @@ -176,6 +178,28 @@ def _getshareDfcId(baseDir: str, systemLanguage: str, return matchId +def _indicateNewShareAvailable(baseDir: str, httpPrefix: str, + domainFull: str) -> None: + """Indicate to each account that a new share is available + """ + for subdir, dirs, files in os.walk(baseDir + '/accounts'): + for handle in dirs: + if not isAccountDir(handle): + continue + accountDir = baseDir + '/accounts/' + handle + newShareFile = accountDir + '/.newShare' + if os.path.isfile(newShareFile): + continue + nickname = handle.split('@')[0] + try: + with open(newShareFile, 'w+') as fp: + fp.write(httpPrefix + '://' + domainFull + + '/users/' + nickname + '/tlshares') + except BaseException: + pass + break + + def addShare(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, displayName: str, summary: str, imageFilename: str, @@ -194,7 +218,9 @@ def addShare(baseDir: str, published = int(time.time()) durationSec = _addShareDurationSec(duration, published) - itemID = getValidSharedItemID(displayName) + domainFull = getFullDomain(domain, port) + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + itemID = getValidSharedItemID(actor, displayName) dfcId = _getshareDfcId(baseDir, systemLanguage, itemType, itemCategory, translate) @@ -244,28 +270,13 @@ def addShare(baseDir: str, "location": location, "published": published, "expire": durationSec, - "price": "0", - "currency": "" + "price": price, + "currency": currency } saveJson(sharesJson, sharesFilename) - # indicate that a new share is available - for subdir, dirs, files in os.walk(baseDir + '/accounts'): - for handle in dirs: - if not isAccountDir(handle): - continue - accountDir = baseDir + '/accounts/' + handle - newShareFile = accountDir + '/.newShare' - if not os.path.isfile(newShareFile): - nickname = handle.split('@')[0] - try: - with open(newShareFile, 'w+') as fp: - fp.write(httpPrefix + '://' + domainFull + - '/users/' + nickname + '/tlshares') - except BaseException: - pass - break + _indicateNewShareAvailable(baseDir, httpPrefix, domainFull) def expireShares(baseDir: str) -> None: @@ -703,8 +714,10 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, if debug: print('DEBUG: displayName missing from Offer') return - removeShare(baseDir, nickname, domain, - messageJson['object']['displayName']) + domainFull = getFullDomain(domain, port) + removeSharedItem(baseDir, nickname, domain, + messageJson['object']['displayName'], + httpPrefix, domainFull) if debug: print('DEBUG: shared item removed via c2s') @@ -718,11 +731,14 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, """ dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" + dfcPtUrl = \ + "http://static.datafoodconsortium.org/data/productTypes.rdf#" owner = httpPrefix + '://' + domainFull + '/users/' + nickname dfcInstanceId = owner + '/catalog' endpoint = { "@context": { "DFC": dfcUrl, + "dfc-pt": dfcPtUrl, "@base": "http://maPlateformeNationale" }, "@id": dfcInstanceId, @@ -740,12 +756,17 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, for itemID, item in sharesJson.items(): if not item.get('dfcId'): continue + if '#' not in item['dfcId']: + continue expireDate = datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") + dfcId = item['dfcId'].split('#')[1] catalogItem = { "@id": item['dfcId'], + "@type": "DFC:SuppliedProduct", + "DFC:hasType": "dfc-pt:" + dfcId, "DFC:offeredThrough": owner, "DFC:startDate": item['published'], "DFC:expiryDate": expireDateStr, @@ -768,10 +789,13 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, """ dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" + dfcPtUrl = \ + "http://static.datafoodconsortium.org/data/productTypes.rdf#" dfcInstanceId = httpPrefix + '://' + domainFull + '/catalog' endpoint = { "@context": { "DFC": dfcUrl, + "dfc-pt": dfcPtUrl, "@base": "http://maPlateformeNationale" }, "@id": dfcInstanceId, @@ -798,14 +822,20 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, for itemID, item in sharesJson.items(): if not item.get('dfcId'): continue + if '#' not in item['dfcId']: + continue expireDate = \ datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") description = item['displayName'] + ': ' + item['summary'] + shareId = getValidSharedItemID(owner, item['displayName']) + dfcId = item['dfcId'].split('#')[1] catalogItem = { - "@id": item['dfcId'], + "@id": shareId, + "@type": "DFC:SuppliedProduct", + "DFC:hasType": "dfc-pt:" + dfcId, "DFC:offeredThrough": owner, "DFC:startDate": item['published'], "DFC:expiryDate": expireDateStr, diff --git a/webapp_confirm.py b/webapp_confirm.py index 00b570392..c7f34bbe6 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -108,7 +108,7 @@ def htmlConfirmRemoveSharedItem(cssCache: {}, translate: {}, baseDir: str, callingDomain: str) -> str: """Shows a screen asking to confirm the removal of a shared item """ - itemID = getValidSharedItemID(shareName) + itemID = getValidSharedItemID(actor, shareName) nickname = getNicknameFromActor(actor) domain, port = getDomainFromActor(actor) domainFull = getFullDomain(domain, port) From 312e8bbdfa00156dd6042a1b04baad828b9d2da0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 10:34:49 +0100 Subject: [PATCH 112/459] Unique share ID for the instance --- shares.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shares.py b/shares.py index 6e11721ca..8577f7c6d 100644 --- a/shares.py +++ b/shares.py @@ -762,9 +762,10 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, expireDate = datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") + shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] catalogItem = { - "@id": item['dfcId'], + "@id": shareId, "@type": "DFC:SuppliedProduct", "DFC:hasType": "dfc-pt:" + dfcId, "DFC:offeredThrough": owner, From 5cd8854a48d8b1e41fca851470847252379e1d4f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 10:39:21 +0100 Subject: [PATCH 113/459] Include price in catalog --- shares.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/shares.py b/shares.py index 8577f7c6d..e99017b3d 100644 --- a/shares.py +++ b/shares.py @@ -764,16 +764,15 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] + priceStr = item['itemPrice'] + ' ' + item['currency'] catalogItem = { "@id": shareId, "@type": "DFC:SuppliedProduct", "DFC:hasType": "dfc-pt:" + dfcId, - "DFC:offeredThrough": owner, "DFC:startDate": item['published'], "DFC:expiryDate": expireDateStr, "DFC:quantity": item['itemQty'], - "DFC:totalTheoriticalStock": item['itemQty'], - "DFC:price": "0", + "DFC:price": priceStr, "DFC:Image": item['imageUrl'], "DFC:description": item['displayName'] + ': ' + item['summary'] } @@ -833,16 +832,15 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, description = item['displayName'] + ': ' + item['summary'] shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] + priceStr = item['itemPrice'] + ' ' + item['currency'] catalogItem = { "@id": shareId, "@type": "DFC:SuppliedProduct", "DFC:hasType": "dfc-pt:" + dfcId, - "DFC:offeredThrough": owner, "DFC:startDate": item['published'], "DFC:expiryDate": expireDateStr, "DFC:quantity": item['itemQty'], - "DFC:totalTheoriticalStock": item['itemQty'], - "DFC:price": "0", + "DFC:price": priceStr, "DFC:Image": item['imageUrl'], "DFC:description": description } From 0e5753147e65a8574586cc10ae5b151f981301f4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 10:56:57 +0100 Subject: [PATCH 114/459] Option to show shared for today --- shares.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index e99017b3d..fa965877f 100644 --- a/shares.py +++ b/shares.py @@ -725,7 +725,7 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, - path: str) -> {}: + path: str, today: bool) -> {}: """Returns the endpoint for the shares catalog of a particular account See https://github.com/datafoodconsortium/ontology """ @@ -746,6 +746,9 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, "DFC:supplies": [] } + currDate = datetime.datetime.utcnow() + currDateStr = currDate.strftime("%Y-%m-%d") + sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' if not os.path.isfile(sharesFilename): return endpoint @@ -758,6 +761,9 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, continue if '#' not in item['dfcId']: continue + if today: + if not item['published'].startswith(currDateStr): + continue expireDate = datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") @@ -783,7 +789,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, domainFull: str, - path: str) -> {}: + path: str, today: bool) -> {}: """Returns the endpoint for the shares catalog for the instance See https://github.com/datafoodconsortium/ontology """ @@ -803,6 +809,9 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, "DFC:supplies": [] } + currDate = datetime.datetime.utcnow() + currDateStr = currDate.strftime("%Y-%m-%d") + for subdir, dirs, files in os.walk(baseDir + '/accounts'): for acct in dirs: if not isAccountDir(acct): @@ -824,6 +833,9 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, continue if '#' not in item['dfcId']: continue + if today: + if not item['published'].startswith(currDateStr): + continue expireDate = \ datetime.datetime.fromtimestamp(item['durationSec']) From 5fb57cf04efb446da946686f88d881b10f58c61e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 11:17:39 +0100 Subject: [PATCH 115/459] Parameter for matching description --- shares.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/shares.py b/shares.py index fa965877f..10ccdc7f4 100644 --- a/shares.py +++ b/shares.py @@ -8,6 +8,7 @@ __status__ = "Production" __module_group__ = "Timeline" import os +import re import time import datetime from webfinger import webfingerHandle @@ -725,7 +726,9 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, - path: str, today: bool) -> {}: + path: str, today: bool, + minPrice: float, maxPrice: float, + matchPattern: str) -> {}: """Returns the endpoint for the shares catalog of a particular account See https://github.com/datafoodconsortium/ontology """ @@ -764,6 +767,16 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, if today: if not item['published'].startswith(currDateStr): continue + if minPrice is not None: + if float(item['itemPrice']) < minPrice: + continue + if maxPrice is not None: + if float(item['itemPrice']) > maxPrice: + continue + description = item['displayName'] + ': ' + item['summary'] + if matchPattern: + if not re.match(matchPattern, description): + continue expireDate = datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") @@ -780,7 +793,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, "DFC:quantity": item['itemQty'], "DFC:price": priceStr, "DFC:Image": item['imageUrl'], - "DFC:description": item['displayName'] + ': ' + item['summary'] + "DFC:description": description } endpoint['DFC:supplies'].append(catalogItem) @@ -789,7 +802,9 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, domainFull: str, - path: str, today: bool) -> {}: + path: str, today: bool, + minPrice: float, maxPrice: float, + matchPattern: str) -> {}: """Returns the endpoint for the shares catalog for the instance See https://github.com/datafoodconsortium/ontology """ @@ -836,12 +851,21 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, if today: if not item['published'].startswith(currDateStr): continue + if minPrice is not None: + if float(item['itemPrice']) < minPrice: + continue + if maxPrice is not None: + if float(item['itemPrice']) > maxPrice: + continue + description = item['displayName'] + ': ' + item['summary'] + if matchPattern: + if not re.match(matchPattern, description): + continue expireDate = \ datetime.datetime.fromtimestamp(item['durationSec']) expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ") - description = item['displayName'] + ': ' + item['summary'] shareId = getValidSharedItemID(owner, item['displayName']) dfcId = item['dfcId'].split('#')[1] priceStr = item['itemPrice'] + ' ' + item['currency'] From dc7851ec6c68f40770e685ef25f102185291681f Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:09:39 +0100 Subject: [PATCH 116/459] Authorized access to catalog --- daemon.py | 35 ++++++++++++++++++++++++++++++++--- epicyon.py | 2 +- shares.py | 46 ++++++++++++++++++++++++++++++++++++++-------- tests.py | 1 - utils.py | 8 ++++++++ webapp_search.py | 3 ++- webapp_timeline.py | 3 ++- 7 files changed, 83 insertions(+), 15 deletions(-) diff --git a/daemon.py b/daemon.py index 9147de8a0..a20c696a3 100644 --- a/daemon.py +++ b/daemon.py @@ -207,9 +207,11 @@ from shares import getSharesFeedForPerson from shares import addShare from shares import removeSharedItem from shares import expireShares +from shares import sharesCatalogEndpoint from categories import setHashtagCategory from languages import getActorLanguages from languages import setActorLanguages +from utils import isfloat from utils import validPassword from utils import removeLineEndings from utils import getBaseContentFromPost @@ -10618,7 +10620,6 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime, GETtimings, 'show logout', 'get cookie') - # manifest for progressive web apps if '/manifest.json' in self.path: if self._hasAccept(callingDomain): if not self._requestHTTP(): @@ -10654,6 +10655,34 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime, GETtimings, 'show logout', 'isAuthorized') + if self.path.startswith('/dfc-catalog'): + catalogAuthorized = False + if authorized: + catalogAuthorized = True + else: + if self.headers.get('Authorization'): + if authorize(self.server.baseDir, self.path, + self.headers['Authorization'], + self.server.debug): + catalogAuthorized = True + # show shared items DFC catalog + if self._hasAccept(callingDomain) and catalogAuthorized: + if not self._requestHTTP(): + catalogJson = \ + sharesCatalogEndpoint(self.server.baseDir, + self.server.httpPrefix, + self.server.domainFull, + self.server.path) + msg = json.dumps(catalogJson, + ensure_ascii=False).encode('utf-8') + msglen = len(msg) + self._set_headers('application/json', + msglen, None, callingDomain) + self._write(msg) + return + else: + self.path = '/' + # minimal mastodon api if self._mastoApi(self.path, callingDomain, authorized, self.server.httpPrefix, @@ -13657,9 +13686,9 @@ class PubServer(BaseHTTPRequestHandler): if fields['itemQty']: if fields['itemQty'].isdigit(): itemQty = int(fields['itemQty']) - itemPrice = "0" + itemPrice = "0.00" if fields['itemPrice']: - if fields['itemPrice'].isdigit(): + if isfloat(fields['itemPrice']): itemPrice = fields['itemPrice'] itemCurrency = "EUR" if fields['itemCurrency']: diff --git a/epicyon.py b/epicyon.py index 574203e50..f65c29786 100644 --- a/epicyon.py +++ b/epicyon.py @@ -562,7 +562,7 @@ parser.add_argument('--itemQty', dest='itemQty', type=int, default=1, help='Quantity of items being shared') parser.add_argument('--itemPrice', dest='itemPrice', type=str, - default="0", + default="0.00", help='Total price of items being shared') parser.add_argument('--itemCurrency', dest='itemCurrency', type=str, default="EUR", diff --git a/shares.py b/shares.py index 10ccdc7f4..da878e2fc 100644 --- a/shares.py +++ b/shares.py @@ -25,6 +25,7 @@ from utils import hasObjectDict from utils import removeDomainPort from utils import isAccountDir from utils import acctDir +from utils import isfloat from media import processMetaData @@ -723,21 +724,51 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str, print('DEBUG: shared item removed via c2s') +def _sharesCatalogParams(path: str) -> (bool, float, float, str): + """Returns parameters when accessing the shares catalog + """ + today = False + minPrice = 0 + maxPrice = 9999999 + matchPattern = None + if '?' not in path: + return today, minPrice, maxPrice, matchPattern + args = path.split('?', 1)[1] + argList = args.split('?') + for arg in argList: + if '=' not in arg: + continue + key = arg.split('=')[0].lower() + value = arg.split('=')[1] + if key == 'today': + value = value.lower() + if 'true' in value or 'y' in value or '1' in value: + today = True + elif key.startswith('min'): + if isfloat(value): + minPrice = float(value) + elif key.startswith('max'): + if isfloat(value): + maxPrice = float(value) + elif key.startswith('match'): + matchPattern = value + return today, minPrice, maxPrice, matchPattern + + def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, - path: str, today: bool, - minPrice: float, maxPrice: float, - matchPattern: str) -> {}: + path: str) -> {}: """Returns the endpoint for the shares catalog of a particular account See https://github.com/datafoodconsortium/ontology """ + today, minPrice, maxPrice, matchPattern = _sharesCatalogParams(path) dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" dfcPtUrl = \ "http://static.datafoodconsortium.org/data/productTypes.rdf#" owner = httpPrefix + '://' + domainFull + '/users/' + nickname - dfcInstanceId = owner + '/catalog' + dfcInstanceId = owner + '/dfc-catalog' endpoint = { "@context": { "DFC": dfcUrl, @@ -802,17 +833,16 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, domainFull: str, - path: str, today: bool, - minPrice: float, maxPrice: float, - matchPattern: str) -> {}: + path: str) -> {}: """Returns the endpoint for the shares catalog for the instance See https://github.com/datafoodconsortium/ontology """ + today, minPrice, maxPrice, matchPattern = _sharesCatalogParams(path) dfcUrl = \ "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" dfcPtUrl = \ "http://static.datafoodconsortium.org/data/productTypes.rdf#" - dfcInstanceId = httpPrefix + '://' + domainFull + '/catalog' + dfcInstanceId = httpPrefix + '://' + domainFull + '/dfc-catalog' endpoint = { "@context": { "DFC": dfcUrl, diff --git a/tests.py b/tests.py index f483b4be8..c8f3b3b54 100644 --- a/tests.py +++ b/tests.py @@ -3273,7 +3273,6 @@ def _testFunctions(): 'setOrganizationScheme', 'fill_headers', '_nothing', - 'sharesCatalogEndpoint', 'sharesCatalogAccountEndpoint' ] excludeImports = [ diff --git a/utils.py b/utils.py index 6d0bea5f6..b5debfc54 100644 --- a/utils.py +++ b/utils.py @@ -2626,3 +2626,11 @@ def validPassword(password: str) -> bool: if not re.match("^[a-zA-Z0-9!]*$", password): return False return True + + +def isfloat(value): + try: + float(value) + return True + except ValueError: + return False diff --git a/webapp_search.py b/webapp_search.py index 013fff788..a031a4333 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -25,6 +25,7 @@ from utils import firstParagraphFromString from utils import searchBoxPosts from utils import getAltPath from utils import acctDir +from utils import isfloat from skills import noOfActorSkills from skills import getSkillsFromList from categories import getHashtagCategory @@ -191,7 +192,7 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, ': ' + sharedItem['location'] if sharedItem.get('itemPrice') and \ sharedItem.get('itemCurrency'): - if sharedItem['itemPrice'].isdigit(): + if isfloat(sharedItem['itemPrice']): if float(sharedItem['itemPrice']) > 0: sharedItemsForm += \ ' ' + translate['Price'] + \ diff --git a/webapp_timeline.py b/webapp_timeline.py index 265634b12..8175ff10f 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -16,6 +16,7 @@ from utils import getFullDomain from utils import isEditor from utils import removeIdEnding from utils import acctDir +from utils import isfloat from follow import followerApprovalActive from person import isPersonSnoozed from markdown import markdownToHtml @@ -832,7 +833,7 @@ def htmlIndividualShare(actor: str, item: {}, translate: {}, profileStr += \ '' + translate['Location'] + ': ' + item['location'] if item.get('itemPrice') and item.get('itemCurrency'): - if item['itemPrice'].isdigit(): + if isfloat(item['itemPrice']): if float(item['itemPrice']) > 0: profileStr += ' ' + \ '' + translate['Price'] + ': ' + \ From a47faed9941d6f3b4901df6d98a7f3fd08d665d3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:12:31 +0100 Subject: [PATCH 117/459] Comment --- daemon.py | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon.py b/daemon.py index a20c696a3..02ba043ba 100644 --- a/daemon.py +++ b/daemon.py @@ -10660,6 +10660,7 @@ class PubServer(BaseHTTPRequestHandler): if authorized: catalogAuthorized = True else: + # basic auth access to catalog if self.headers.get('Authorization'): if authorize(self.server.baseDir, self.path, self.headers['Authorization'], From 2c1cd07dcd01d9af83062e1408fbc2d666c34511 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:30:42 +0100 Subject: [PATCH 118/459] csv catalog endpoint --- daemon.py | 34 +++++++++++++++++++++++++++------- shares.py | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/daemon.py b/daemon.py index 02ba043ba..c84b20566 100644 --- a/daemon.py +++ b/daemon.py @@ -208,6 +208,7 @@ from shares import addShare from shares import removeSharedItem from shares import expireShares from shares import sharesCatalogEndpoint +from shares import sharesCatalogCSVEndpoint from categories import setHashtagCategory from languages import getActorLanguages from languages import setActorLanguages @@ -517,6 +518,16 @@ class PubServer(BaseHTTPRequestHandler): print('Blocked User agent: ' + agentDomain) return blockedUA + def _requestCSV(self) -> bool: + """Should a csv response be given? + """ + if not self.headers.get('Accept'): + return False + acceptStr = self.headers['Accept'] + if 'text/csv' in acceptStr: + return True + return False + def _requestHTTP(self) -> bool: """Should a http response be given? """ @@ -10669,13 +10680,22 @@ class PubServer(BaseHTTPRequestHandler): # show shared items DFC catalog if self._hasAccept(callingDomain) and catalogAuthorized: if not self._requestHTTP(): - catalogJson = \ - sharesCatalogEndpoint(self.server.baseDir, - self.server.httpPrefix, - self.server.domainFull, - self.server.path) - msg = json.dumps(catalogJson, - ensure_ascii=False).encode('utf-8') + if self._requestCSV(): + catalogStr = \ + sharesCatalogCSVEndpoint(self.server.baseDir, + self.server.httpPrefix, + self.server.domainFull, + self.server.path) + msg = json.dumps(catalogStr, + ensure_ascii=False).encode('utf-8') + else: + catalogJson = \ + sharesCatalogEndpoint(self.server.baseDir, + self.server.httpPrefix, + self.server.domainFull, + self.server.path) + msg = json.dumps(catalogJson, + ensure_ascii=False).encode('utf-8') msglen = len(msg) self._set_headers('application/json', msglen, None, callingDomain) diff --git a/shares.py b/shares.py index da878e2fc..30db312fd 100644 --- a/shares.py +++ b/shares.py @@ -913,3 +913,30 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, endpoint['DFC:supplies'].append(catalogItem) return endpoint + + +def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, + domainFull: str, + path: str) -> str: + """Returns a CSV version of the shares catalog + """ + catalogJson = \ + sharesCatalogEndpoint(baseDir, httpPrefix, domainFull, path) + if not catalogJson: + return '' + if not catalogJson.get('DFC:supplies'): + return '' + csvStr = \ + 'id,type,hasType,startDate,expiryDate,' + \ + 'quantity,price,Image,description\n' + for item in catalogJson['DFC:supplies']: + csvStr += item['@id'] + ',' + csvStr += item['@type'] + ',' + csvStr += item['DFC:hasType'] + ',' + csvStr += item['DFC:startDate'] + ',' + csvStr += item['DFC:expiryDate'] + ',' + csvStr += item['DFC:quantity'] + ',' + csvStr += item['DFC:price'] + ',' + csvStr += item['DFC:Image'] + ',' + csvStr += item['DFC:description'] + '\n' + return csvStr From 52e3bc1e8dceb2d4d6aec3feed6e4177a6ca9c97 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:43:28 +0100 Subject: [PATCH 119/459] csv header --- daemon.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index c84b20566..9687057e5 100644 --- a/daemon.py +++ b/daemon.py @@ -10688,6 +10688,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.path) msg = json.dumps(catalogStr, ensure_ascii=False).encode('utf-8') + msglen = len(msg) + self._set_headers('text/csv', + msglen, None, callingDomain) else: catalogJson = \ sharesCatalogEndpoint(self.server.baseDir, @@ -10696,9 +10699,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.path) msg = json.dumps(catalogJson, ensure_ascii=False).encode('utf-8') - msglen = len(msg) - self._set_headers('application/json', - msglen, None, callingDomain) + msglen = len(msg) + self._set_headers('application/json', + msglen, None, callingDomain) self._write(msg) return else: From 102eaf3a005fa668e05d5ab21db64f80f797a338 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:47:34 +0100 Subject: [PATCH 120/459] Comments --- daemon.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon.py b/daemon.py index 9687057e5..06816ebb9 100644 --- a/daemon.py +++ b/daemon.py @@ -10681,6 +10681,7 @@ class PubServer(BaseHTTPRequestHandler): if self._hasAccept(callingDomain) and catalogAuthorized: if not self._requestHTTP(): if self._requestCSV(): + # catalog as a CSV file for import into a spreadsheet catalogStr = \ sharesCatalogCSVEndpoint(self.server.baseDir, self.server.httpPrefix, @@ -10692,6 +10693,7 @@ class PubServer(BaseHTTPRequestHandler): self._set_headers('text/csv', msglen, None, callingDomain) else: + # catalog as a json catalogJson = \ sharesCatalogEndpoint(self.server.baseDir, self.server.httpPrefix, From 3433c03f0dc6bfefccdcb9853eb95906a2537794 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:48:55 +0100 Subject: [PATCH 121/459] Separate parameters with semicolons --- shares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shares.py b/shares.py index 30db312fd..612f16c97 100644 --- a/shares.py +++ b/shares.py @@ -734,7 +734,7 @@ def _sharesCatalogParams(path: str) -> (bool, float, float, str): if '?' not in path: return today, minPrice, maxPrice, matchPattern args = path.split('?', 1)[1] - argList = args.split('?') + argList = args.split(';') for arg in argList: if '=' not in arg: continue From 035b0e2f942d1c7b3fe4d12365aae7b2fd43f721 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:50:03 +0100 Subject: [PATCH 122/459] Single character for true --- shares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shares.py b/shares.py index 612f16c97..06da0eef1 100644 --- a/shares.py +++ b/shares.py @@ -742,7 +742,7 @@ def _sharesCatalogParams(path: str) -> (bool, float, float, str): value = arg.split('=')[1] if key == 'today': value = value.lower() - if 'true' in value or 'y' in value or '1' in value: + if 't' in value or 'y' in value or '1' in value: today = True elif key.startswith('min'): if isfloat(value): From 504101d17e2c663595a12493585d1401ff1ec082 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 14:57:45 +0100 Subject: [PATCH 123/459] Path on failure --- daemon.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index 06816ebb9..c43fd0a7f 100644 --- a/daemon.py +++ b/daemon.py @@ -10706,8 +10706,7 @@ class PubServer(BaseHTTPRequestHandler): msglen, None, callingDomain) self._write(msg) return - else: - self.path = '/' + self.path = '/' # minimal mastodon api if self._mastoApi(self.path, callingDomain, authorized, From 336c7e8bbb89f75a8f4504c65afcd2f0dec5dd8d Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 15:19:04 +0100 Subject: [PATCH 124/459] Use file extension to access different catalog formats --- daemon.py | 57 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/daemon.py b/daemon.py index c43fd0a7f..e7db68ba0 100644 --- a/daemon.py +++ b/daemon.py @@ -10679,31 +10679,38 @@ class PubServer(BaseHTTPRequestHandler): catalogAuthorized = True # show shared items DFC catalog if self._hasAccept(callingDomain) and catalogAuthorized: - if not self._requestHTTP(): - if self._requestCSV(): - # catalog as a CSV file for import into a spreadsheet - catalogStr = \ - sharesCatalogCSVEndpoint(self.server.baseDir, - self.server.httpPrefix, - self.server.domainFull, - self.server.path) - msg = json.dumps(catalogStr, - ensure_ascii=False).encode('utf-8') - msglen = len(msg) - self._set_headers('text/csv', - msglen, None, callingDomain) - else: - # catalog as a json - catalogJson = \ - sharesCatalogEndpoint(self.server.baseDir, - self.server.httpPrefix, - self.server.domainFull, - self.server.path) - msg = json.dumps(catalogJson, - ensure_ascii=False).encode('utf-8') - msglen = len(msg) - self._set_headers('application/json', - msglen, None, callingDomain) + catalogType = 'html' + if self.path.endswith('.csv') or self._requestCSV(): + catalogType = 'csv' + elif self.path.endswith('.json') or not self._requestHTTP(): + catalogType = 'json' + + if catalogType == 'json': + # catalog as a json + catalogJson = \ + sharesCatalogEndpoint(self.server.baseDir, + self.server.httpPrefix, + self.server.domainFull, + self.server.path) + msg = json.dumps(catalogJson, + ensure_ascii=False).encode('utf-8') + msglen = len(msg) + self._set_headers('application/json', + msglen, None, callingDomain) + self._write(msg) + return + elif catalogType == 'csv': + # catalog as a CSV file for import into a spreadsheet + catalogStr = \ + sharesCatalogCSVEndpoint(self.server.baseDir, + self.server.httpPrefix, + self.server.domainFull, + self.server.path) + msg = json.dumps(catalogStr, + ensure_ascii=False).encode('utf-8') + msglen = len(msg) + self._set_headers('text/csv', + msglen, None, callingDomain) self._write(msg) return self.path = '/' From 6ee7f2bfbf1a554be14a38a9dc2f9b5542f9e174 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 15:27:27 +0100 Subject: [PATCH 125/459] Return http code if not authorized --- daemon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index e7db68ba0..6dfdcc433 100644 --- a/daemon.py +++ b/daemon.py @@ -10713,7 +10713,8 @@ class PubServer(BaseHTTPRequestHandler): msglen, None, callingDomain) self._write(msg) return - self.path = '/' + self._400() + return # minimal mastodon api if self._mastoApi(self.path, callingDomain, authorized, From f142d9f0011c2fb6b356595320c06d6897e5e7c3 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 15:28:58 +0100 Subject: [PATCH 126/459] Return http code if not authorized --- daemon.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/daemon.py b/daemon.py index 6dfdcc433..5b110b177 100644 --- a/daemon.py +++ b/daemon.py @@ -10713,6 +10713,8 @@ class PubServer(BaseHTTPRequestHandler): msglen, None, callingDomain) self._write(msg) return + self._404() + return self._400() return From 5ce777eb1b234b10dd5af3828230f05c9ef98011 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 22:18:38 +0100 Subject: [PATCH 127/459] Authentication from shared items federation token list --- auth.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++- daemon.py | 15 +++++++++----- epicyon.py | 20 ++++++++++++++++++- tests.py | 12 +++++++++--- 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/auth.py b/auth.py index 5103365f3..893f814bd 100644 --- a/auth.py +++ b/auth.py @@ -89,7 +89,7 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, """ if ' ' not in authHeader: if debug: - print('DEBUG: basic auth - Authorixation header does not ' + + print('DEBUG: basic auth - Authorisation header does not ' + 'contain a space character') return False if not hasUsersPath(path): @@ -147,6 +147,61 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, return False +def authorizeDFC(sharedItemsFederatedDomains: [], + baseDir: str, + callingDomain: str, + authHeader: str, + debug: bool) -> bool: + """HTTP basic auth for shared item federation + """ + if callingDomain not in sharedItemsFederatedDomains: + if debug: + print(callingDomain + + ' is not in the shared items federation list') + return False + if 'Basic ' not in authHeader: + if debug: + print('DEBUG: DFC basic auth - Authorisation header does not ' + + 'contain a space character') + return False + base64Str = \ + authHeader.split(' ')[1].replace('\n', '').replace('\r', '') + plain = base64.b64decode(base64Str).decode('utf-8') + if ':' not in plain: + if debug: + print('DEBUG: DFC basic auth header does not contain a ":" ' + + 'separator for username:password') + return False + basicAuthDomain = plain.split(':')[0] + if basicAuthDomain != callingDomain: + if debug: + print('DEBUG: DFC calling domain does not match ' + + 'the one in the Authorization header (' + + basicAuthDomain + ')') + return False + passwordFile = baseDir + '/accounts/sharedItemsFederationTokens' + if not os.path.isfile(passwordFile): + if debug: + print('DEBUG: shared item federation tokens file missing ' + + passwordFile) + return False + providedPassword = plain.split(':')[1] + passfile = open(passwordFile, 'r') + for line in passfile: + if line.startswith(basicAuthDomain + ':'): + storedPassword = \ + line.split(':')[1].replace('\n', '').replace('\r', '') + success = _verifyPassword(storedPassword, providedPassword) + if not success: + if debug: + print('DEBUG: DFC password check failed for ' + + basicAuthDomain) + return success + print('DEBUG: DFC did not find credentials for ' + basicAuthDomain + + ' in ' + passwordFile) + return False + + def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool: """Stores login credentials to a file """ diff --git a/daemon.py b/daemon.py index 5b110b177..17793de0a 100644 --- a/daemon.py +++ b/daemon.py @@ -105,6 +105,7 @@ from skills import actorSkillValue from skills import setActorSkillLevel from auth import recordLoginFailure from auth import authorize +from auth import authorizeDFC from auth import createPassword from auth import createBasicAuthHeader from auth import authorizeBasic @@ -10671,11 +10672,13 @@ class PubServer(BaseHTTPRequestHandler): if authorized: catalogAuthorized = True else: - # basic auth access to catalog + # basic auth access to shared items catalog if self.headers.get('Authorization'): - if authorize(self.server.baseDir, self.path, - self.headers['Authorization'], - self.server.debug): + if authorizeDFC(self.server.sharedItemsFederatedDomains, + self.server.baseDir, + callingDomain, + self.headers['Authorization'], + self.server.debug): catalogAuthorized = True # show shared items DFC catalog if self._hasAccept(callingDomain) and catalogAuthorized: @@ -14808,7 +14811,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None: break -def runDaemon(userAgentsBlocked: [], +def runDaemon(sharedItemsFederatedDomains: [], + userAgentsBlocked: [], logLoginFailures: bool, city: str, showNodeInfoAccounts: bool, @@ -15067,6 +15071,7 @@ def runDaemon(userAgentsBlocked: [], httpd.httpPrefix = httpPrefix httpd.debug = debug httpd.federationList = fedList.copy() + httpd.sharedItemsFederatedDomains = sharedItemsFederatedDomains.copy() httpd.baseDir = baseDir httpd.instanceId = instanceId httpd.personCache = {} diff --git a/epicyon.py b/epicyon.py index f65c29786..6c8fa6c3f 100644 --- a/epicyon.py +++ b/epicyon.py @@ -264,6 +264,10 @@ parser.add_argument('--rss', dest='rss', type=str, default=None, help='Show an rss feed for a given url') parser.add_argument('-f', '--federate', nargs='+', dest='federationList', help='Specify federation list separated by spaces') +parser.add_argument('--federateshares', nargs='+', + dest='sharedItemsFederatedDomains', + help='Specify federation list for shared items, ' + + 'separated by spaces') parser.add_argument("--following", "--followingList", dest='followingList', type=str2bool, nargs='?', @@ -1024,6 +1028,16 @@ else: if configFederationList: federationList = configFederationList +sharedItemsFederatedDomains = [] +if args.sharedItemsFederatedDomains: + setConfigParam(baseDir, 'sharedItemsFederatedDomains', + sharedItemsFederatedDomains) +else: + configSharedItemsFederatedDomains = \ + getConfigParam(baseDir, 'sharedItemsFederatedDomains') + if configSharedItemsFederatedDomains: + sharedItemsFederatedDomains = configSharedItemsFederatedDomains + proxyType = None if args.tor or domain.endswith('.onion'): proxyType = 'tor' @@ -2142,6 +2156,9 @@ if args.desktop: if federationList: print('Federating with: ' + str(federationList)) +if sharedItemsFederatedDomains: + print('Federating shared items with: ' + + str(sharedItemsFederatedDomains)) if args.block: if not nickname: @@ -2657,7 +2674,8 @@ if args.registration: print('New registrations closed') if __name__ == "__main__": - runDaemon(userAgentsBlocked, + runDaemon(sharedItemsFederatedDomains, + userAgentsBlocked, args.logLoginFailures, args.city, args.showNodeInfoAccounts, diff --git a/tests.py b/tests.py index c8f3b3b54..0e79f10bd 100644 --- a/tests.py +++ b/tests.py @@ -457,6 +457,7 @@ def createServerAlice(path: str, domain: str, port: int, shutil.rmtree(path) os.mkdir(path) os.chdir(path) + sharedItemsFederatedDomains = [] systemLanguage = 'en' nickname = 'alice' httpPrefix = 'http' @@ -554,7 +555,8 @@ def createServerAlice(path: str, domain: str, port: int, logLoginFailures = False userAgentsBlocked = [] print('Server running: Alice') - runDaemon(userAgentsBlocked, + runDaemon(sharedItemsFederatedDomains, + userAgentsBlocked, logLoginFailures, city, showNodeInfoAccounts, showNodeInfoVersion, @@ -585,6 +587,7 @@ def createServerBob(path: str, domain: str, port: int, shutil.rmtree(path) os.mkdir(path) os.chdir(path) + sharedItemsFederatedDomains = [] systemLanguage = 'en' nickname = 'bob' httpPrefix = 'http' @@ -680,7 +683,8 @@ def createServerBob(path: str, domain: str, port: int, logLoginFailures = False userAgentsBlocked = [] print('Server running: Bob') - runDaemon(userAgentsBlocked, + runDaemon(sharedItemsFederatedDomains, + userAgentsBlocked, logLoginFailures, city, showNodeInfoAccounts, showNodeInfoVersion, @@ -710,6 +714,7 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], shutil.rmtree(path) os.mkdir(path) os.chdir(path) + sharedItemsFederatedDomains = [] nickname = 'eve' httpPrefix = 'http' proxyType = None @@ -740,7 +745,8 @@ def createServerEve(path: str, domain: str, port: int, federationList: [], logLoginFailures = False userAgentsBlocked = [] print('Server running: Eve') - runDaemon(userAgentsBlocked, + runDaemon(sharedItemsFederatedDomains, + userAgentsBlocked, logLoginFailures, city, showNodeInfoAccounts, showNodeInfoVersion, From cf9bffbed7cc1ca7d810585b0189c53e79869de6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sun, 25 Jul 2021 23:47:42 +0100 Subject: [PATCH 128/459] Generate tokens for shared item federation --- auth.py | 63 ++++++++++++++++++++++++++++++++++++++++++------------- daemon.py | 5 +++++ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/auth.py b/auth.py index 893f814bd..0ab2e6a61 100644 --- a/auth.py +++ b/auth.py @@ -132,9 +132,10 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, print('DEBUG: passwords file missing') return False providedPassword = plain.split(':')[1] - passfile = open(passwordFile, 'r') - for line in passfile: - if line.startswith(nickname + ':'): + with open(passwordFile, 'r') as passfile: + for line in passfile: + if not line.startswith(nickname + ':'): + continue storedPassword = \ line.split(':')[1].replace('\n', '').replace('\r', '') success = _verifyPassword(storedPassword, providedPassword) @@ -147,6 +148,37 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, return False +def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], + baseDir: str) -> None: + """Generates tokens for shared item federated domains + """ + if not sharedItemsFederatedDomains: + return + tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' + if not os.path.isfile(tokensFile): + with open(tokensFile, 'w+') as fp: + fp.write('') + tokens = [] + with open(tokensFile, 'r') as fp: + tokens = fp.read().split('\n') + tokensAdded = False + for domain in sharedItemsFederatedDomains: + domainFound = False + for line in tokens: + if line.startswith(domain + ':'): + domainFound = True + break + if not domainFound: + newLine = domain + ':' + createPassword(64) + tokens.append(newLine) + tokensAdded = True + if not tokensAdded: + return + with open(tokensFile, 'w+') as fp: + for line in tokens: + fp.write(line + '\n') + + def authorizeDFC(sharedItemsFederatedDomains: [], baseDir: str, callingDomain: str, @@ -179,26 +211,27 @@ def authorizeDFC(sharedItemsFederatedDomains: [], 'the one in the Authorization header (' + basicAuthDomain + ')') return False - passwordFile = baseDir + '/accounts/sharedItemsFederationTokens' - if not os.path.isfile(passwordFile): + tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' + if not os.path.isfile(tokensFile): if debug: print('DEBUG: shared item federation tokens file missing ' + - passwordFile) + tokensFile) return False - providedPassword = plain.split(':')[1] - passfile = open(passwordFile, 'r') - for line in passfile: - if line.startswith(basicAuthDomain + ':'): - storedPassword = \ + providedToken = plain.split(':')[1] + with open(tokensFile, 'r') as tokfile: + for line in tokfile: + if not line.startswith(basicAuthDomain + ':'): + continue + storedToken = \ line.split(':')[1].replace('\n', '').replace('\r', '') - success = _verifyPassword(storedPassword, providedPassword) + success = _verifyPassword(storedToken, providedToken) if not success: if debug: - print('DEBUG: DFC password check failed for ' + + print('DEBUG: DFC token check failed for ' + basicAuthDomain) return success - print('DEBUG: DFC did not find credentials for ' + basicAuthDomain + - ' in ' + passwordFile) + print('DEBUG: DFC did not find token for ' + basicAuthDomain + + ' in ' + tokensFile) return False diff --git a/daemon.py b/daemon.py index 17793de0a..0015cbb4b 100644 --- a/daemon.py +++ b/daemon.py @@ -103,6 +103,7 @@ from skills import noOfActorSkills from skills import actorHasSkill from skills import actorSkillValue from skills import setActorSkillLevel +from auth import generateSharedItemFederationTokens from auth import recordLoginFailure from auth import authorize from auth import authorizeDFC @@ -15210,6 +15211,10 @@ def runDaemon(sharedItemsFederatedDomains: [], httpd.iconsCache = {} httpd.fontsCache = {} + # create tokens used for shared item federation + generateSharedItemFederationTokens(httpd.sharedItemsFederatedDomains, + baseDir) + # load peertube instances from file into a list httpd.peertubeInstances = [] loadPeertubeInstances(baseDir, httpd.peertubeInstances) From 4c8b96efc194abc9fb8ca0704319edb99841068a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 10:08:36 +0100 Subject: [PATCH 129/459] Shared item federation uses a simple token --- auth.py | 56 +++++++++++++++++++++++++++---------------------------- daemon.py | 22 +++++++++++----------- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/auth.py b/auth.py index 0ab2e6a61..94da132fc 100644 --- a/auth.py +++ b/auth.py @@ -179,37 +179,34 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], fp.write(line + '\n') -def authorizeDFC(sharedItemsFederatedDomains: [], - baseDir: str, - callingDomain: str, - authHeader: str, - debug: bool) -> bool: - """HTTP basic auth for shared item federation +def authorizeSharedItems(sharedItemsFederatedDomains: [], + baseDir: str, + callingDomain: str, + authHeader: str, + debug: bool) -> bool: + """HTTP simple token check for shared item federation """ + if not sharedItemsFederatedDomains: + # no shared item federation + return False if callingDomain not in sharedItemsFederatedDomains: if debug: print(callingDomain + ' is not in the shared items federation list') return False - if 'Basic ' not in authHeader: + if 'Basic ' in authHeader: if debug: - print('DEBUG: DFC basic auth - Authorisation header does not ' + - 'contain a space character') + print('DEBUG: shared item federation should not use basic auth') return False - base64Str = \ - authHeader.split(' ')[1].replace('\n', '').replace('\r', '') - plain = base64.b64decode(base64Str).decode('utf-8') - if ':' not in plain: + providedToken = authHeader.replace('\n', '').replace('\r', '').strip() + if not providedToken: if debug: - print('DEBUG: DFC basic auth header does not contain a ":" ' + - 'separator for username:password') + print('DEBUG: shared item federation token is empty') return False - basicAuthDomain = plain.split(':')[0] - if basicAuthDomain != callingDomain: + if len(providedToken) < 60: if debug: - print('DEBUG: DFC calling domain does not match ' + - 'the one in the Authorization header (' + - basicAuthDomain + ')') + print('DEBUG: shared item federation token is too small ' + + providedToken) return False tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' if not os.path.isfile(tokensFile): @@ -217,21 +214,22 @@ def authorizeDFC(sharedItemsFederatedDomains: [], print('DEBUG: shared item federation tokens file missing ' + tokensFile) return False - providedToken = plain.split(':')[1] + # check the tokens file with open(tokensFile, 'r') as tokfile: for line in tokfile: - if not line.startswith(basicAuthDomain + ':'): + if not line.startswith(callingDomain + ':'): continue storedToken = \ line.split(':')[1].replace('\n', '').replace('\r', '') - success = _verifyPassword(storedToken, providedToken) - if not success: + if constantTimeStringCheck(storedToken, providedToken): + return True + else: if debug: - print('DEBUG: DFC token check failed for ' + - basicAuthDomain) - return success - print('DEBUG: DFC did not find token for ' + basicAuthDomain + - ' in ' + tokensFile) + print('DEBUG: shared item federation token ' + + 'check failed for ' + callingDomain) + return False + print('DEBUG: shared item federation token for ' + callingDomain + + ' not found in ' + tokensFile) return False diff --git a/daemon.py b/daemon.py index 0015cbb4b..abf126b91 100644 --- a/daemon.py +++ b/daemon.py @@ -106,7 +106,7 @@ from skills import setActorSkillLevel from auth import generateSharedItemFederationTokens from auth import recordLoginFailure from auth import authorize -from auth import authorizeDFC +from auth import authorizeSharedItems from auth import createPassword from auth import createBasicAuthHeader from auth import authorizeBasic @@ -10669,19 +10669,19 @@ class PubServer(BaseHTTPRequestHandler): 'show logout', 'isAuthorized') if self.path.startswith('/dfc-catalog'): - catalogAuthorized = False - if authorized: - catalogAuthorized = True - else: + catalogAuthorized = authorized + if not catalogAuthorized: # basic auth access to shared items catalog if self.headers.get('Authorization'): - if authorizeDFC(self.server.sharedItemsFederatedDomains, - self.server.baseDir, - callingDomain, - self.headers['Authorization'], - self.server.debug): + permittedDomains = \ + self.server.sharedItemsFederatedDomains + if authorizeSharedItems(permittedDomains, + self.server.baseDir, + callingDomain, + self.headers['Authorization'], + self.server.debug): catalogAuthorized = True - # show shared items DFC catalog + # show shared items catalog for federation if self._hasAccept(callingDomain) and catalogAuthorized: catalogType = 'html' if self.path.endswith('.csv') or self._requestCSV(): From 47575a52a7f8dfc01e70edcd0fe3219c4825b3eb Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 10:20:49 +0100 Subject: [PATCH 130/459] Use url token function to generate shared items federation token --- auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth.py b/auth.py index 94da132fc..ef4940240 100644 --- a/auth.py +++ b/auth.py @@ -169,7 +169,7 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], domainFound = True break if not domainFound: - newLine = domain + ':' + createPassword(64) + newLine = domain + ':' + secrets.token_urlsafe(64) tokens.append(newLine) tokensAdded = True if not tokensAdded: From 646254c8a9305ba242c1976faf58595eaa30feb1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 10:40:51 +0100 Subject: [PATCH 131/459] Move token generation function --- auth.py | 31 ------------------------------- daemon.py | 2 +- shares.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/auth.py b/auth.py index ef4940240..eb25035d5 100644 --- a/auth.py +++ b/auth.py @@ -148,37 +148,6 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, return False -def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], - baseDir: str) -> None: - """Generates tokens for shared item federated domains - """ - if not sharedItemsFederatedDomains: - return - tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' - if not os.path.isfile(tokensFile): - with open(tokensFile, 'w+') as fp: - fp.write('') - tokens = [] - with open(tokensFile, 'r') as fp: - tokens = fp.read().split('\n') - tokensAdded = False - for domain in sharedItemsFederatedDomains: - domainFound = False - for line in tokens: - if line.startswith(domain + ':'): - domainFound = True - break - if not domainFound: - newLine = domain + ':' + secrets.token_urlsafe(64) - tokens.append(newLine) - tokensAdded = True - if not tokensAdded: - return - with open(tokensFile, 'w+') as fp: - for line in tokens: - fp.write(line + '\n') - - def authorizeSharedItems(sharedItemsFederatedDomains: [], baseDir: str, callingDomain: str, diff --git a/daemon.py b/daemon.py index abf126b91..89b656329 100644 --- a/daemon.py +++ b/daemon.py @@ -103,7 +103,6 @@ from skills import noOfActorSkills from skills import actorHasSkill from skills import actorSkillValue from skills import setActorSkillLevel -from auth import generateSharedItemFederationTokens from auth import recordLoginFailure from auth import authorize from auth import authorizeSharedItems @@ -205,6 +204,7 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import generateSharedItemFederationTokens from shares import getSharesFeedForPerson from shares import addShare from shares import removeSharedItem diff --git a/shares.py b/shares.py index 06da0eef1..dc25b10bb 100644 --- a/shares.py +++ b/shares.py @@ -9,6 +9,7 @@ __module_group__ = "Timeline" import os import re +import secrets import time import datetime from webfinger import webfingerHandle @@ -940,3 +941,34 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, csvStr += item['DFC:Image'] + ',' csvStr += item['DFC:description'] + '\n' return csvStr + + +def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], + baseDir: str) -> None: + """Generates tokens for shared item federated domains + """ + if not sharedItemsFederatedDomains: + return + tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' + if not os.path.isfile(tokensFile): + with open(tokensFile, 'w+') as fp: + fp.write('') + tokens = [] + with open(tokensFile, 'r') as fp: + tokens = fp.read().split('\n') + tokensAdded = False + for domain in sharedItemsFederatedDomains: + domainFound = False + for line in tokens: + if line.startswith(domain + ':'): + domainFound = True + break + if not domainFound: + newLine = domain + ':' + secrets.token_urlsafe(64) + tokens.append(newLine) + tokensAdded = True + if not tokensAdded: + return + with open(tokensFile, 'w+') as fp: + for line in tokens: + fp.write(line + '\n') From 3c8e4bb5fbd52f532f129665c1bcb2bf5d8be546 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 11:00:54 +0100 Subject: [PATCH 132/459] Shared item federation tokens stored as json --- auth.py | 54 ------------------------------------ daemon.py | 2 +- shares.py | 83 +++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 73 deletions(-) diff --git a/auth.py b/auth.py index eb25035d5..de23e6130 100644 --- a/auth.py +++ b/auth.py @@ -148,60 +148,6 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str, return False -def authorizeSharedItems(sharedItemsFederatedDomains: [], - baseDir: str, - callingDomain: str, - authHeader: str, - debug: bool) -> bool: - """HTTP simple token check for shared item federation - """ - if not sharedItemsFederatedDomains: - # no shared item federation - return False - if callingDomain not in sharedItemsFederatedDomains: - if debug: - print(callingDomain + - ' is not in the shared items federation list') - return False - if 'Basic ' in authHeader: - if debug: - print('DEBUG: shared item federation should not use basic auth') - return False - providedToken = authHeader.replace('\n', '').replace('\r', '').strip() - if not providedToken: - if debug: - print('DEBUG: shared item federation token is empty') - return False - if len(providedToken) < 60: - if debug: - print('DEBUG: shared item federation token is too small ' + - providedToken) - return False - tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' - if not os.path.isfile(tokensFile): - if debug: - print('DEBUG: shared item federation tokens file missing ' + - tokensFile) - return False - # check the tokens file - with open(tokensFile, 'r') as tokfile: - for line in tokfile: - if not line.startswith(callingDomain + ':'): - continue - storedToken = \ - line.split(':')[1].replace('\n', '').replace('\r', '') - if constantTimeStringCheck(storedToken, providedToken): - return True - else: - if debug: - print('DEBUG: shared item federation token ' + - 'check failed for ' + callingDomain) - return False - print('DEBUG: shared item federation token for ' + callingDomain + - ' not found in ' + tokensFile) - return False - - def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool: """Stores login credentials to a file """ diff --git a/daemon.py b/daemon.py index 89b656329..cba31349b 100644 --- a/daemon.py +++ b/daemon.py @@ -105,7 +105,6 @@ from skills import actorSkillValue from skills import setActorSkillLevel from auth import recordLoginFailure from auth import authorize -from auth import authorizeSharedItems from auth import createPassword from auth import createBasicAuthHeader from auth import authorizeBasic @@ -204,6 +203,7 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import authorizeSharedItems from shares import generateSharedItemFederationTokens from shares import getSharesFeedForPerson from shares import addShare diff --git a/shares.py b/shares.py index dc25b10bb..81a28b6ab 100644 --- a/shares.py +++ b/shares.py @@ -14,6 +14,7 @@ import time import datetime from webfinger import webfingerHandle from auth import createBasicAuthHeader +from auth import constantTimeStringCheck from posts import getPersonBox from session import postJson from session import postImage @@ -949,26 +950,72 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], """ if not sharedItemsFederatedDomains: return - tokensFile = baseDir + '/accounts/sharedItemsFederationTokens' - if not os.path.isfile(tokensFile): - with open(tokensFile, 'w+') as fp: - fp.write('') - tokens = [] - with open(tokensFile, 'r') as fp: - tokens = fp.read().split('\n') + + tokensFilename = baseDir + '/accounts/sharedItemsFederationTokens.json' + tokensJson = {} + if not os.path.isfile(tokensFilename): + tokensJson = loadJson(tokensFilename) + tokensAdded = False for domain in sharedItemsFederatedDomains: - domainFound = False - for line in tokens: - if line.startswith(domain + ':'): - domainFound = True - break - if not domainFound: - newLine = domain + ':' + secrets.token_urlsafe(64) - tokens.append(newLine) + if not tokensJson.get(domain): + tokensJson[domain] = secrets.token_urlsafe(64) tokensAdded = True + if not tokensAdded: return - with open(tokensFile, 'w+') as fp: - for line in tokens: - fp.write(line + '\n') + saveJson(tokensJson, tokensFilename) + + +def authorizeSharedItems(sharedItemsFederatedDomains: [], + baseDir: str, + callingDomain: str, + authHeader: str, + debug: bool, + tokensJson: {} = None) -> bool: + """HTTP simple token check for shared item federation + """ + if not sharedItemsFederatedDomains: + # no shared item federation + return False + if callingDomain not in sharedItemsFederatedDomains: + if debug: + print(callingDomain + + ' is not in the shared items federation list') + return False + if 'Basic ' in authHeader: + if debug: + print('DEBUG: shared item federation should not use basic auth') + return False + providedToken = authHeader.replace('\n', '').replace('\r', '').strip() + if not providedToken: + if debug: + print('DEBUG: shared item federation token is empty') + return False + if len(providedToken) < 60: + if debug: + print('DEBUG: shared item federation token is too small ' + + providedToken) + return False + if not tokensJson: + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if not os.path.isfile(tokensFilename): + if debug: + print('DEBUG: shared item federation tokens file missing ' + + tokensFilename) + return False + tokensJson = loadJson(tokensFilename) + if not tokensJson: + return False + if not tokensJson.get(callingDomain): + if debug: + print('DEBUG: shared item federation token ' + + 'check failed for ' + callingDomain) + return False + if not constantTimeStringCheck(tokensJson[callingDomain], providedToken): + if debug: + print('DEBUG: shared item federation token ' + + 'mismatch for ' + callingDomain) + return False + return True From ceaefc6ee9e0393e291f402ed62a67070fd5bc2e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 11:17:06 +0100 Subject: [PATCH 133/459] Unit test for shared item federation tokens --- shares.py | 16 ++++++++++------ tests.py | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/shares.py b/shares.py index 81a28b6ab..4e27d1f3c 100644 --- a/shares.py +++ b/shares.py @@ -945,16 +945,18 @@ def sharesCatalogCSVEndpoint(baseDir: str, httpPrefix: str, def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], - baseDir: str) -> None: + baseDir: str) -> {}: """Generates tokens for shared item federated domains """ if not sharedItemsFederatedDomains: return - tokensFilename = baseDir + '/accounts/sharedItemsFederationTokens.json' tokensJson = {} - if not os.path.isfile(tokensFilename): - tokensJson = loadJson(tokensFilename) + if baseDir: + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if not os.path.isfile(tokensFilename): + tokensJson = loadJson(tokensFilename) tokensAdded = False for domain in sharedItemsFederatedDomains: @@ -963,8 +965,10 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], tokensAdded = True if not tokensAdded: - return - saveJson(tokensJson, tokensFilename) + return tokensJson + if baseDir: + saveJson(tokensJson, tokensFilename) + return tokensJson def authorizeSharedItems(sharedItemsFederatedDomains: [], diff --git a/tests.py b/tests.py index 0e79f10bd..b8203b0ff 100644 --- a/tests.py +++ b/tests.py @@ -128,6 +128,8 @@ from languages import setActorLanguages from languages import getActorLanguages from languages import getLinksFromContent from languages import addLinksToContent +from shares import authorizeSharedItems +from shares import generateSharedItemFederationTokens testServerAliceRunning = False testServerBobRunning = False @@ -4268,9 +4270,31 @@ def _testValidPassword(): assert validPassword('Abcdef!g123456') +def _testAuthorizeSharedItems(): + print('testAuthorizeSharedItems') + sharedItemsFederatedDomains = \ + ['dog.domain', 'cat.domain', 'birb.domain'] + tokensJson = \ + generateSharedItemFederationTokens(sharedItemsFederatedDomains, None) + assert tokensJson + assert tokensJson.get('dog.domain') + assert tokensJson.get('cat.domain') + assert tokensJson.get('birb.domain') + assert len(tokensJson['dog.domain']) >= 64 + assert len(tokensJson['cat.domain']) >= 64 + assert len(tokensJson['birb.domain']) >= 64 + assert not authorizeSharedItems(sharedItemsFederatedDomains, None, + 'dog.domain', 'w' * 86, + False, tokensJson) + assert authorizeSharedItems(sharedItemsFederatedDomains, None, + 'dog.domain', tokensJson['dog.domain'], + False, tokensJson) + + def runAllTests(): print('Running tests...') updateDefaultThemesList(os.getcwd()) + _testAuthorizeSharedItems() _testValidPassword() _testGetLinksFromContent() _testSetActorLanguages() From 2cfd87d802aceecadd6f649502afb0555d87544b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 12:13:59 +0100 Subject: [PATCH 134/459] Load shared items federation tokens from file --- daemon.py | 8 ++++++++ epicyon.py | 27 +++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/daemon.py b/daemon.py index cba31349b..16a108a4e 100644 --- a/daemon.py +++ b/daemon.py @@ -15072,7 +15072,15 @@ def runDaemon(sharedItemsFederatedDomains: [], httpd.httpPrefix = httpPrefix httpd.debug = debug httpd.federationList = fedList.copy() + if not sharedItemsFederatedDomains: + sharedItemsTokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if os.path.isfile(sharedItemsTokensFilename): + sharedItemsFederatedDomains = loadJson(sharedItemsTokensFilename) httpd.sharedItemsFederatedDomains = sharedItemsFederatedDomains.copy() + if httpd.sharedItemsFederatedDomains: + if domain not in httpd.sharedItemsFederatedDomains: + httpd.sharedItemsFederatedDomains.append(domain) httpd.baseDir = baseDir httpd.instanceId = instanceId httpd.personCache = {} diff --git a/epicyon.py b/epicyon.py index 6c8fa6c3f..91a4b36b4 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1028,16 +1028,6 @@ else: if configFederationList: federationList = configFederationList -sharedItemsFederatedDomains = [] -if args.sharedItemsFederatedDomains: - setConfigParam(baseDir, 'sharedItemsFederatedDomains', - sharedItemsFederatedDomains) -else: - configSharedItemsFederatedDomains = \ - getConfigParam(baseDir, 'sharedItemsFederatedDomains') - if configSharedItemsFederatedDomains: - sharedItemsFederatedDomains = configSharedItemsFederatedDomains - proxyType = None if args.tor or domain.endswith('.onion'): proxyType = 'tor' @@ -2156,9 +2146,22 @@ if args.desktop: if federationList: print('Federating with: ' + str(federationList)) -if sharedItemsFederatedDomains: +if args.sharedItemsFederatedDomains: print('Federating shared items with: ' + - str(sharedItemsFederatedDomains)) + args.sharedItemsFederatedDomains) + +sharedItemsFederatedDomains = [] +if args.sharedItemsFederatedDomains: + sharedItemsFederatedDomainsStr = args.sharedItemsFederatedDomains + setConfigParam(baseDir, 'sharedItemsFederatedDomains', + sharedItemsFederatedDomainsStr) +else: + sharedItemsFederatedDomainsStr = \ + getConfigParam(baseDir, 'sharedItemsFederatedDomains') +if sharedItemsFederatedDomainsStr: + sharedItemsFederatedDomainsList = sharedItemsFederatedDomainsStr.split(',') + for sharedFederatedDomain in sharedItemsFederatedDomainsList: + sharedItemsFederatedDomains.append(sharedFederatedDomain.strip()) if args.block: if not nickname: From 43d7071ab8a765e757999e0a042168e68c316eb8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 12:19:49 +0100 Subject: [PATCH 135/459] Federation tokens in memory --- daemon.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/daemon.py b/daemon.py index 16a108a4e..92666bbed 100644 --- a/daemon.py +++ b/daemon.py @@ -15072,15 +15072,7 @@ def runDaemon(sharedItemsFederatedDomains: [], httpd.httpPrefix = httpPrefix httpd.debug = debug httpd.federationList = fedList.copy() - if not sharedItemsFederatedDomains: - sharedItemsTokensFilename = \ - baseDir + '/accounts/sharedItemsFederationTokens.json' - if os.path.isfile(sharedItemsTokensFilename): - sharedItemsFederatedDomains = loadJson(sharedItemsTokensFilename) httpd.sharedItemsFederatedDomains = sharedItemsFederatedDomains.copy() - if httpd.sharedItemsFederatedDomains: - if domain not in httpd.sharedItemsFederatedDomains: - httpd.sharedItemsFederatedDomains.append(domain) httpd.baseDir = baseDir httpd.instanceId = instanceId httpd.personCache = {} @@ -15220,8 +15212,9 @@ def runDaemon(sharedItemsFederatedDomains: [], httpd.fontsCache = {} # create tokens used for shared item federation - generateSharedItemFederationTokens(httpd.sharedItemsFederatedDomains, - baseDir) + httpd.sharedItemFederationTokens = \ + generateSharedItemFederationTokens(httpd.sharedItemsFederatedDomains, + baseDir) # load peertube instances from file into a list httpd.peertubeInstances = [] From 060efe5851439ecd0ab27061b6385fe3bbf686d5 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 12:24:23 +0100 Subject: [PATCH 136/459] fast authorization of shared item tokens --- daemon.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 92666bbed..2a4d86167 100644 --- a/daemon.py +++ b/daemon.py @@ -10675,11 +10675,13 @@ class PubServer(BaseHTTPRequestHandler): if self.headers.get('Authorization'): permittedDomains = \ self.server.sharedItemsFederatedDomains + sharedItemTokens = self.server.sharedItemFederationTokens if authorizeSharedItems(permittedDomains, self.server.baseDir, callingDomain, self.headers['Authorization'], - self.server.debug): + self.server.debug, + sharedItemTokens): catalogAuthorized = True # show shared items catalog for federation if self._hasAccept(callingDomain) and catalogAuthorized: From d9e32a0719a99543b54d4909ae222a13613111f0 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 12:33:08 +0100 Subject: [PATCH 137/459] Alternative enpoint names --- daemon.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 2a4d86167..0f575338b 100644 --- a/daemon.py +++ b/daemon.py @@ -10668,7 +10668,9 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime, GETtimings, 'show logout', 'isAuthorized') - if self.path.startswith('/dfc-catalog'): + if self.path.startswith('/dfc-catalog') or \ + self.path.startswith('/shareditems') or \ + self.path.startswith('/catalog'): catalogAuthorized = authorized if not catalogAuthorized: # basic auth access to shared items catalog From ebac11f502e41c96f83a59be5e44878288e3eab4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 13:20:07 +0100 Subject: [PATCH 138/459] Invert logic --- daemon.py | 4 ++++ shares.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++-------- tests.py | 20 ++++++++++++------ 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/daemon.py b/daemon.py index 0f575338b..4651ae921 100644 --- a/daemon.py +++ b/daemon.py @@ -203,6 +203,7 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import createSharedItemFederationToken from shares import authorizeSharedItems from shares import generateSharedItemFederationTokens from shares import getSharesFeedForPerson @@ -15219,6 +15220,9 @@ def runDaemon(sharedItemsFederatedDomains: [], httpd.sharedItemFederationTokens = \ generateSharedItemFederationTokens(httpd.sharedItemsFederatedDomains, baseDir) + httpd.sharedItemFederationTokens = \ + createSharedItemFederationToken(baseDir, domain, + httpd.sharedItemFederationTokens) # load peertube instances from file into a list httpd.peertubeInstances = [] diff --git a/shares.py b/shares.py index 4e27d1f3c..8a4c1ebe9 100644 --- a/shares.py +++ b/shares.py @@ -216,7 +216,7 @@ def addShare(baseDir: str, sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' sharesJson = {} if os.path.isfile(sharesFilename): - sharesJson = loadJson(sharesFilename) + sharesJson = loadJson(sharesFilename, 1, 2) duration = duration.lower() published = int(time.time()) @@ -304,7 +304,7 @@ def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None: sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' if not os.path.isfile(sharesFilename): return - sharesJson = loadJson(sharesFilename) + sharesJson = loadJson(sharesFilename, 1, 2) if not sharesJson: return currTime = int(time.time()) @@ -788,7 +788,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json' if not os.path.isfile(sharesFilename): return endpoint - sharesJson = loadJson(sharesFilename) + sharesJson = loadJson(sharesFilename, 1, 2) if not sharesJson: return endpoint @@ -871,7 +871,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, acctDir(baseDir, nickname, domain) + '/shares.json' if not os.path.isfile(sharesFilename): continue - sharesJson = loadJson(sharesFilename) + sharesJson = loadJson(sharesFilename, 1, 2) if not sharesJson: continue @@ -949,19 +949,21 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], """Generates tokens for shared item federated domains """ if not sharedItemsFederatedDomains: - return + return {} tokensJson = {} if baseDir: tokensFilename = \ baseDir + '/accounts/sharedItemsFederationTokens.json' - if not os.path.isfile(tokensFilename): - tokensJson = loadJson(tokensFilename) + if os.path.isfile(tokensFilename): + tokensJson = loadJson(tokensFilename, 1, 2) + if tokensJson is None: + tokensJson = {} tokensAdded = False for domain in sharedItemsFederatedDomains: if not tokensJson.get(domain): - tokensJson[domain] = secrets.token_urlsafe(64) + tokensJson[domain] = '' tokensAdded = True if not tokensAdded: @@ -971,6 +973,47 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], return tokensJson +def updateSharedItemFederationToken(baseDir: str, + tokenDomain: str, newToken: str, + tokensJson: {} = None) -> {}: + """Updates a token for shared item federation + """ + if not tokensJson: + tokensJson = {} + if baseDir: + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if os.path.isfile(tokensFilename): + tokensJson = loadJson(tokensFilename, 1, 2) + if tokensJson is None: + tokensJson = {} + tokensJson[tokenDomain] = newToken + if baseDir: + saveJson(tokensJson, tokensFilename) + return tokensJson + + +def createSharedItemFederationToken(baseDir: str, + tokenDomain: str, + tokensJson: {} = None) -> {}: + """Updates a token for shared item federation + """ + if not tokensJson: + tokensJson = {} + if baseDir: + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if os.path.isfile(tokensFilename): + tokensJson = loadJson(tokensFilename, 1, 2) + if tokensJson is None: + tokensJson = {} + if not tokensJson.get(tokenDomain): + tokensJson[tokenDomain] = secrets.token_urlsafe(64) + if baseDir: + saveJson(tokensJson, tokensFilename) + return tokensJson + + def authorizeSharedItems(sharedItemsFederatedDomains: [], baseDir: str, callingDomain: str, @@ -1009,7 +1052,7 @@ def authorizeSharedItems(sharedItemsFederatedDomains: [], print('DEBUG: shared item federation tokens file missing ' + tokensFilename) return False - tokensJson = loadJson(tokensFilename) + tokensJson = loadJson(tokensFilename, 1, 2) if not tokensJson: return False if not tokensJson.get(callingDomain): diff --git a/tests.py b/tests.py index b8203b0ff..229be046d 100644 --- a/tests.py +++ b/tests.py @@ -130,6 +130,8 @@ from languages import getLinksFromContent from languages import addLinksToContent from shares import authorizeSharedItems from shares import generateSharedItemFederationTokens +from shares import createSharedItemFederationToken +from shares import updateSharedItemFederationToken testServerAliceRunning = False testServerBobRunning = False @@ -4276,19 +4278,25 @@ def _testAuthorizeSharedItems(): ['dog.domain', 'cat.domain', 'birb.domain'] tokensJson = \ generateSharedItemFederationTokens(sharedItemsFederatedDomains, None) + tokensJson = \ + createSharedItemFederationToken(None, 'cat.domain', tokensJson) assert tokensJson - assert tokensJson.get('dog.domain') + assert not tokensJson.get('dog.domain') assert tokensJson.get('cat.domain') - assert tokensJson.get('birb.domain') - assert len(tokensJson['dog.domain']) >= 64 + assert not tokensJson.get('birb.domain') + assert len(tokensJson['dog.domain']) == 0 assert len(tokensJson['cat.domain']) >= 64 - assert len(tokensJson['birb.domain']) >= 64 + assert len(tokensJson['birb.domain']) == 0 assert not authorizeSharedItems(sharedItemsFederatedDomains, None, - 'dog.domain', 'w' * 86, + 'cat.domain', 'M' * 86, False, tokensJson) assert authorizeSharedItems(sharedItemsFederatedDomains, None, - 'dog.domain', tokensJson['dog.domain'], + 'cat.domain', tokensJson['cat.domain'], False, tokensJson) + tokensJson = \ + updateSharedItemFederationToken(None, + 'dog.domain', 'testToken', tokensJson) + assert tokensJson['dog.domain'] == 'testToken' def runAllTests(): From 84ad6d9f4f02459b6d7cc74bb1cd8d42a349da84 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 14:12:51 +0100 Subject: [PATCH 139/459] Send shared items token to domains within the approved federation --- announce.py | 2 +- daemon.py | 4 +++- follow.py | 6 +++--- inbox.py | 10 +++++++--- like.py | 2 +- outbox.py | 12 +++++++++--- posts.py | 49 +++++++++++++++++++++++++++++++++++++++++-------- schedule.py | 4 +++- 8 files changed, 68 insertions(+), 21 deletions(-) diff --git a/announce.py b/announce.py index 92c412c5c..096f2d982 100644 --- a/announce.py +++ b/announce.py @@ -169,7 +169,7 @@ def createAnnounce(session, baseDir: str, federationList: [], announceNickname, announceDomain, announcePort, None, httpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, personCache, - debug, projectVersion) + debug, projectVersion, None) return newAnnounce diff --git a/daemon.py b/daemon.py index 4651ae921..89314779f 100644 --- a/daemon.py +++ b/daemon.py @@ -1131,7 +1131,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.allowLocalNetworkAccess, - city, self.server.systemLanguage) + city, self.server.systemLanguage, + self.server.sharedItemsFederatedDomains, + self.server.sharedItemFederationTokens) def _postToOutboxThread(self, messageJson: {}) -> bool: """Creates a thread to send a post diff --git a/follow.py b/follow.py index 2654989e4..e0e3a6e33 100644 --- a/follow.py +++ b/follow.py @@ -821,7 +821,7 @@ def followedAccountAccepts(session, baseDir: str, httpPrefix: str, httpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) + personCache, debug, projectVersion, None) def followedAccountRejects(session, baseDir: str, httpPrefix: str, @@ -882,7 +882,7 @@ def followedAccountRejects(session, baseDir: str, httpPrefix: str, httpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) + personCache, debug, projectVersion, None) def sendFollowRequest(session, baseDir: str, @@ -941,7 +941,7 @@ def sendFollowRequest(session, baseDir: str, httpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, personCache, - debug, projectVersion) + debug, projectVersion, None) return newFollowJson diff --git a/inbox.py b/inbox.py index d50486114..4fcdd4212 100644 --- a/inbox.py +++ b/inbox.py @@ -2000,7 +2000,7 @@ def _sendToGroupMembers(session, baseDir: str, handle: str, port: int, memberNickname, memberDomain, memberPort, cc, httpPrefix, False, False, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, __version__) + personCache, debug, __version__, None) def _inboxUpdateCalendar(baseDir: str, handle: str, @@ -2178,7 +2178,7 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str, senderNickname, senderDomain, senderPort, cc, httpPrefix, False, False, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, __version__) + personCache, debug, __version__, None) return True @@ -2476,6 +2476,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, # if the votes on a question have changed then # send out an update questionJson['type'] = 'Update' + sharedItemsFederatedDomains = [] + sharedItemFederationTokens = {} sendToFollowersThread(session, baseDir, nickname, domain, onionDomain, i2pDomain, port, @@ -2483,7 +2485,9 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, sendThreads, postLog, cachedWebfingers, personCache, postJsonObject, debug, - __version__) + __version__, + sharedItemsFederatedDomains, + sharedItemFederationTokens) isReplyToMutedPost = False diff --git a/like.py b/like.py index ad0bff5da..91f3e77f4 100644 --- a/like.py +++ b/like.py @@ -113,7 +113,7 @@ def _like(recentPostsCache: {}, 'https://www.w3.org/ns/activitystreams#Public', httpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, personCache, - debug, projectVersion) + debug, projectVersion, None) return newLikeJson diff --git a/outbox.py b/outbox.py index 0d12c8212..ef9bbc55e 100644 --- a/outbox.py +++ b/outbox.py @@ -190,7 +190,9 @@ def postMessageToOutbox(session, translate: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, allowLocalNetworkAccess: bool, - city: str, systemLanguage: str) -> bool: + city: str, systemLanguage: str, + sharedItemsFederatedDomains: [], + sharedItemFederationTokens: {}) -> bool: """post is received by the outbox Client to server message post https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery @@ -462,7 +464,9 @@ def postMessageToOutbox(session, translate: {}, cachedWebfingers, personCache, messageJson, debug, - version) + version, + sharedItemsFederatedDomains, + sharedItemFederationTokens) followersThreads.append(followersThread) if debug: @@ -584,5 +588,7 @@ def postMessageToOutbox(session, translate: {}, cachedWebfingers, personCache, messageJson, debug, - version) + version, + sharedItemsFederatedDomains, + sharedItemFederationTokens) return True diff --git a/posts.py b/posts.py index f2318279a..9701c4e2f 100644 --- a/posts.py +++ b/posts.py @@ -2211,7 +2211,8 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, httpPrefix: str, saveToFile: bool, clientToServer: bool, federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, - personCache: {}, debug: bool, projectVersion: str) -> int: + personCache: {}, debug: bool, projectVersion: str, + sharedItemsToken: str) -> int: """Sends a signed json object to an inbox/outbox """ if debug: @@ -2336,6 +2337,10 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str, createSignedHeader(privateKeyPem, nickname, domain, port, toDomain, toPort, postPath, httpPrefix, withDigest, postJsonStr) + # optionally add a token so that the receiving instance may access + # your shared items catalog + if sharedItemsToken: + signatureHeaderJson['SharesCatalog'] = sharedItemsToken # Keep the number of threads being used small while len(sendThreads) > 1000: @@ -2446,7 +2451,9 @@ def sendToNamedAddresses(session, baseDir: str, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, postJsonObject: {}, debug: bool, - projectVersion: str) -> None: + projectVersion: str, + sharedItemsFederatedDomains: [], + sharedItemFederationTokens: {}) -> None: """sends a post to the specific named addresses in to/cc """ if not session: @@ -2562,13 +2569,23 @@ def sendToNamedAddresses(session, baseDir: str, fromDomain = i2pDomain fromHttpPrefix = 'http' cc = [] + + # if the "to" domain is within the shared items + # federation list then send the token for this domain + # so that it can request a catalog + sharedItemsToken = None + if toDomain in sharedItemsFederatedDomains: + if sharedItemFederationTokens.get(fromDomain): + sharedItemsToken = sharedItemFederationTokens[fromDomain] + sendSignedJson(postJsonObject, session, baseDir, nickname, fromDomain, port, toNickname, toDomain, toPort, cc, fromHttpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) + personCache, debug, projectVersion, + sharedItemsToken) def _hasSharedInbox(session, httpPrefix: str, domain: str, @@ -2616,7 +2633,9 @@ def sendToFollowers(session, baseDir: str, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, postJsonObject: {}, debug: bool, - projectVersion: str) -> None: + projectVersion: str, + sharedItemsFederatedDomains: [], + sharedItemFederationTokens: {}) -> None: """sends a post to the followers of the given nickname """ print('sendToFollowers') @@ -2656,6 +2675,14 @@ def sendToFollowers(session, baseDir: str, if debug: pprint(followerHandles) + # if the followers domain is within the shared items + # federation list then send the token for this domain + # so that it can request a catalog + sharedItemsToken = None + if followerDomain in sharedItemsFederatedDomains: + if sharedItemFederationTokens.get(domain): + sharedItemsToken = sharedItemFederationTokens[domain] + # check that the follower's domain is active followerDomainUrl = httpPrefix + '://' + followerDomain if not siteIsActive(followerDomainUrl): @@ -2720,7 +2747,8 @@ def sendToFollowers(session, baseDir: str, cc, fromHttpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) + personCache, debug, projectVersion, + sharedItemsToken) else: # send to individual followers without using a shared inbox for handle in followerHandles: @@ -2742,7 +2770,8 @@ def sendToFollowers(session, baseDir: str, cc, fromHttpPrefix, True, clientToServer, federationList, sendThreads, postLog, cachedWebfingers, - personCache, debug, projectVersion) + personCache, debug, projectVersion, + sharedItemsToken) time.sleep(4) @@ -2762,7 +2791,9 @@ def sendToFollowersThread(session, baseDir: str, sendThreads: [], postLog: [], cachedWebfingers: {}, personCache: {}, postJsonObject: {}, debug: bool, - projectVersion: str): + projectVersion: str, + sharedItemsFederatedDomains: [], + sharedItemFederationTokens: {}): """Returns a thread used to send a post to followers """ sendThread = \ @@ -2774,7 +2805,9 @@ def sendToFollowersThread(session, baseDir: str, sendThreads, postLog, cachedWebfingers, personCache, postJsonObject.copy(), debug, - projectVersion), daemon=True) + projectVersion, + sharedItemsFederatedDomains, + sharedItemFederationTokens), daemon=True) try: sendThread.start() except SocketError as e: diff --git a/schedule.py b/schedule.py index e01ccb874..9205dd0fc 100644 --- a/schedule.py +++ b/schedule.py @@ -112,7 +112,9 @@ def _updatePostSchedule(baseDir: str, handle: str, httpd, httpd.YTReplacementDomain, httpd.showPublishedDateOnly, httpd.allowLocalNetworkAccess, - httpd.city, httpd.systemLanguage): + httpd.city, httpd.systemLanguage, + httpd.sharedItemsFederatedDomains, + httpd.sharedItemFederationTokens): indexLines.remove(line) os.remove(postFilename) continue From c194c3996b2e3d552c16c55f617241b54e7b20eb Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 15:41:06 +0100 Subject: [PATCH 140/459] Update shared item tokens from inbox posts --- daemon.py | 18 ++++++++++++++++++ shares.py | 13 ++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index 89314779f..709471e1b 100644 --- a/daemon.py +++ b/daemon.py @@ -203,6 +203,7 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import updateSharedItemFederationToken from shares import createSharedItemFederationToken from shares import authorizeSharedItems from shares import generateSharedItemFederationTokens @@ -14692,6 +14693,23 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkPOSTtimings(POSTstartTime, POSTtimings, 23) + # update the shared item federation token for the calling domain + # if it is within the permitted federation + if self.headers.get('SharesCatalog') and \ + callingDomain != self.server.domain and \ + callingDomain != self.server.domainFull and \ + callingDomain in self.server.sharedItemsFederatedDomains: + if self.server.debug: + print('DEBUG: ' + + 'POST updating shared item federation token for ' + + callingDomain) + sharedItemTokens = self.server.sharedItemFederationTokens + self.server.sharedItemFederationTokens = \ + updateSharedItemFederationToken(self.server.baseDir, + callingDomain, + self.headers['SharesCatalog'], + sharedItemTokens) + if self.server.debug: print('DEBUG: POST saving to inbox queue') if usersInPath: diff --git a/shares.py b/shares.py index 8a4c1ebe9..e17a0f8db 100644 --- a/shares.py +++ b/shares.py @@ -987,9 +987,16 @@ def updateSharedItemFederationToken(baseDir: str, tokensJson = loadJson(tokensFilename, 1, 2) if tokensJson is None: tokensJson = {} - tokensJson[tokenDomain] = newToken - if baseDir: - saveJson(tokensJson, tokensFilename) + updateRequired = False + if tokensJson.get(tokenDomain): + if tokensJson[tokenDomain] != newToken: + updateRequired = True + else: + updateRequired = True + if updateRequired: + tokensJson[tokenDomain] = newToken + if baseDir: + saveJson(tokensJson, tokensFilename) return tokensJson From a5b97f1d92c49e429f539d2d4a6c55ddd68d5b68 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 18:54:13 +0100 Subject: [PATCH 141/459] Daemon for federating shared items --- daemon.py | 16 ++++++++ shares.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests.py | 2 + 3 files changed, 125 insertions(+) diff --git a/daemon.py b/daemon.py index 709471e1b..10ea43b84 100644 --- a/daemon.py +++ b/daemon.py @@ -203,6 +203,8 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import runFederatedSharesDaemon +from shares import runFederatedSharesWatchdog from shares import updateSharedItemFederationToken from shares import createSharedItemFederationToken from shares import authorizeSharedItems @@ -15285,6 +15287,13 @@ def runDaemon(sharedItemsFederatedDomains: [], httpPrefix, domain, port, httpd.translate), daemon=True) + print('Creating federated shares thread') + httpd.thrFederatedSharesDaemon = \ + threadWithTrace(target=runFederatedSharesDaemon, + args=(baseDir, httpd, + httpPrefix, domain, + proxyType, debug), daemon=True) + # flags used when restarting the inbox queue httpd.restartInboxQueueInProgress = False httpd.restartInboxQueue = False @@ -15310,9 +15319,16 @@ def runDaemon(sharedItemsFederatedDomains: [], threadWithTrace(target=runNewswireWatchdog, args=(projectVersion, httpd), daemon=True) httpd.thrNewswireWatchdog.start() + + print('Creating federated shares watchdog') + httpd.thrFederatedSharesWatchdog = \ + threadWithTrace(target=runFederatedSharesWatchdog, + args=(projectVersion, httpd), daemon=True) + httpd.thrFederatedSharesWatchdog.start() else: httpd.thrInboxQueue.start() httpd.thrPostSchedule.start() + httpd.thrFederatedSharesDaemon.start() if clientToServer: print('Running ActivityPub client on ' + diff --git a/shares.py b/shares.py index e17a0f8db..5574f5129 100644 --- a/shares.py +++ b/shares.py @@ -12,12 +12,15 @@ import re import secrets import time import datetime +from session import getJson from webfinger import webfingerHandle from auth import createBasicAuthHeader from auth import constantTimeStringCheck from posts import getPersonBox from session import postJson from session import postImage +from session import createSession +from utils import getConfigParam from utils import getFullDomain from utils import validNickname from utils import loadJson @@ -1073,3 +1076,107 @@ def authorizeSharedItems(sharedItemsFederatedDomains: [], 'mismatch for ' + callingDomain) return False return True + + +def _updateFederatedSharesCache(session, sharedItemsFederatedDomains: [], + baseDir: str, domain: str, + httpPrefix: str, + tokensJson: {}, debug: bool) -> None: + """Updates the cache of federated shares for the instance. + This enables shared items to be available even when other instances + might not be online + """ + # create directories where catalogs will be stored + cacheDir = baseDir + '/cache' + if not os.path.isdir(cacheDir): + os.mkdir(cacheDir) + catalogsDir = cacheDir + '/catalogs' + if not os.path.isdir(catalogsDir): + os.mkdir(catalogsDir) + + asHeader = { + 'Accept': 'application/ld+json' + } + for otherDomain in sharedItemsFederatedDomains: + # NOTE: otherDomain does not have a port extension, + # so may not work in some situations + if otherDomain.startswith(domain): + # only download from instances other than this one + continue + if not tokensJson.get(otherDomain): + # token has been obtained for the other domain + continue + url = httpPrefix + '://' + otherDomain + '/catalog' + asHeader['Authorization'] = tokensJson[otherDomain] + catalogJson = getJson(session, url, asHeader, None, + debug, __version__, httpPrefix, None) + if not catalogJson: + print('WARN: failed to download shared items catalog for ' + + otherDomain) + continue + catalogFilename = catalogsDir + '/' + otherDomain + '.json' + if saveJson(catalogJson, catalogFilename): + print('Downloaded shared items catalog for ' + otherDomain) + else: + time.sleep(2) + + +def runFederatedSharesWatchdog(projectVersion: str, httpd) -> None: + """This tries to keep the federated shares update thread + running even if it dies + """ + print('Starting federated shares watchdog') + federatedSharesOriginal = \ + httpd.thrPostSchedule.clone(runFederatedSharesDaemon) + httpd.thrFederatedSharesDaemon.start() + while True: + time.sleep(55) + if httpd.thrFederatedSharesDaemon.is_alive(): + continue + httpd.thrFederatedSharesDaemon.kill() + httpd.thrFederatedSharesDaemon = \ + federatedSharesOriginal.clone(runFederatedSharesDaemon) + httpd.thrFederatedSharesDaemon.start() + print('Restarting federated shares daemon...') + + +def runFederatedSharesDaemon(baseDir: str, httpd, httpPrefix: str, + domain: str, proxyType: str, debug: bool) -> None: + """Runs the daemon used to update federated shared items + """ + secondsPerHour = 60 * 60 + fileCheckIntervalSec = 120 + time.sleep(60) + while True: + sharedItemsFederatedDomainsStr = \ + getConfigParam(baseDir, 'sharedItemsFederatedDomains') + if not sharedItemsFederatedDomainsStr: + time.sleep(fileCheckIntervalSec) + continue + + # get a list of the domains within the shared items federation + sharedItemsFederatedDomains = [] + sharedItemsFederatedDomainsList = \ + sharedItemsFederatedDomainsStr.split(',') + for sharedFederatedDomain in sharedItemsFederatedDomainsList: + sharedItemsFederatedDomains.append(sharedFederatedDomain.strip()) + if not sharedItemsFederatedDomains: + time.sleep(fileCheckIntervalSec) + continue + + # load the tokens + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + if not os.path.isfile(tokensFilename): + time.sleep(fileCheckIntervalSec) + continue + tokensJson = loadJson(tokensFilename, 1, 2) + if not tokensJson: + time.sleep(fileCheckIntervalSec) + continue + + session = createSession(proxyType) + _updateFederatedSharesCache(session, sharedItemsFederatedDomains, + baseDir, domain, httpPrefix, tokensJson, + debug) + time.sleep(secondsPerHour * 6) diff --git a/tests.py b/tests.py index 229be046d..c73ed639e 100644 --- a/tests.py +++ b/tests.py @@ -3265,6 +3265,8 @@ def _testFunctions(): 'str2bool', 'runNewswireDaemon', 'runNewswireWatchdog', + 'runFederatedSharesWatchdog', + 'runFederatedSharesDaemon', 'threadSendPost', 'sendToFollowers', 'expireCache', From d14055e3045809114530cd0840f46ec181e336cf Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 19:03:33 +0100 Subject: [PATCH 142/459] Comments --- daemon.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/daemon.py b/daemon.py index 10ea43b84..7b0cd85dd 100644 --- a/daemon.py +++ b/daemon.py @@ -10674,9 +10674,10 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime, GETtimings, 'show logout', 'isAuthorized') - if self.path.startswith('/dfc-catalog') or \ - self.path.startswith('/shareditems') or \ - self.path.startswith('/catalog'): + # shared items catalog for this instance + # this is only accessible to instance members or to + # other instances which present an authorization token + if self.path.startswith('/catalog'): catalogAuthorized = authorized if not catalogAuthorized: # basic auth access to shared items catalog From b3b4f9433d2294ad06342d016b186851dd504ae6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 19:06:08 +0100 Subject: [PATCH 143/459] catalog endpoint --- shares.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shares.py b/shares.py index 5574f5129..53e0ead73 100644 --- a/shares.py +++ b/shares.py @@ -773,7 +773,7 @@ def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str, dfcPtUrl = \ "http://static.datafoodconsortium.org/data/productTypes.rdf#" owner = httpPrefix + '://' + domainFull + '/users/' + nickname - dfcInstanceId = owner + '/dfc-catalog' + dfcInstanceId = owner + '/catalog' endpoint = { "@context": { "DFC": dfcUrl, @@ -847,7 +847,7 @@ def sharesCatalogEndpoint(baseDir: str, httpPrefix: str, "http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#" dfcPtUrl = \ "http://static.datafoodconsortium.org/data/productTypes.rdf#" - dfcInstanceId = httpPrefix + '://' + domainFull + '/dfc-catalog' + dfcInstanceId = httpPrefix + '://' + domainFull + '/catalog' endpoint = { "@context": { "DFC": dfcUrl, From 50849490eb43eee96840fdd04f0953067b5b17c8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 19:09:26 +0100 Subject: [PATCH 144/459] Shared item id path --- shares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shares.py b/shares.py index 53e0ead73..b11929cbf 100644 --- a/shares.py +++ b/shares.py @@ -91,7 +91,7 @@ def getValidSharedItemID(actor: str, displayName: str) -> str: displayName = displayName.replace(ch, '-') displayName = displayName.replace('.', '_') displayName = displayName.replace("’", "'") - return actor + '/item/' + displayName + return actor + '/shareditems/' + displayName def removeSharedItem(baseDir: str, nickname: str, domain: str, From 12011bfdd47a1267822b9782fcc41db5e1912c87 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 22:01:48 +0100 Subject: [PATCH 145/459] Set shared item federation domains on edit profile screen --- daemon.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ shares.py | 38 ++++++++++++++++++++++++++++++++++++-- tests.py | 17 +++++++++++++++++ translations/ar.json | 3 ++- translations/ca.json | 3 ++- translations/cy.json | 3 ++- translations/de.json | 3 ++- translations/en.json | 3 ++- translations/es.json | 3 ++- translations/fr.json | 3 ++- translations/ga.json | 3 ++- translations/hi.json | 3 ++- translations/it.json | 3 ++- translations/ja.json | 3 ++- translations/ku.json | 3 ++- translations/oc.json | 3 ++- translations/pt.json | 3 ++- translations/ru.json | 3 ++- translations/sw.json | 3 ++- translations/zh.json | 3 ++- webapp_profile.py | 26 ++++++++++++++++++++++++++ 21 files changed, 157 insertions(+), 19 deletions(-) diff --git a/daemon.py b/daemon.py index 7b0cd85dd..2630295b1 100644 --- a/daemon.py +++ b/daemon.py @@ -203,6 +203,7 @@ from webapp_welcome import htmlWelcomeScreen from webapp_welcome import isWelcomeScreenComplete from webapp_welcome_profile import htmlWelcomeProfile from webapp_welcome_final import htmlWelcomeFinal +from shares import mergeSharedItemTokens from shares import runFederatedSharesDaemon from shares import runFederatedSharesWatchdog from shares import updateSharedItemFederationToken @@ -4825,6 +4826,49 @@ class PubServer(BaseHTTPRequestHandler): brochMode) setConfigParam(baseDir, "brochMode", brochMode) + # shared item federation domains + siDomainUpdated = False + sharedItemsFederatedDomainsStr = \ + getConfigParam(baseDir, + "sharedItemsFederatedDomains") + sharedItemsFormStr = '' + if sharedItemsFederatedDomainsStr and \ + fields.get('shareDomainList'): + sharedItemsList = \ + sharedItemsFederatedDomainsStr.split(',') + for sharedFederatedDomain in sharedItemsList: + sharedItemsFormStr += \ + sharedFederatedDomain.strip() + '\n' + + if fields.get('shareDomainList') != \ + sharedItemsFormStr: + sharedItemsFormStr2 = \ + sharedItemsFormStr.replace('\n', ',') + sharedItemsField = \ + "sharedItemsFederatedDomains" + setConfigParam(baseDir, sharedItemsField, + sharedItemsFormStr2) + siDomainUpdated = True + else: + if not fields.get('shareDomainList') and \ + sharedItemsFederatedDomainsStr: + sharedItemsField = \ + "sharedItemsFederatedDomains" + setConfigParam(baseDir, sharedItemsField, + '') + siDomainUpdated = True + if siDomainUpdated: + siDomains = sharedItemsFormStr.split('\n') + siTokens = \ + self.server.sharedItemFederationTokens + self.server.sharedItemsFederatedDomains = \ + siDomains + self.server.sharedItemFederationTokens = \ + mergeSharedItemTokens(self.server.baseDir, + self.server.domain, + siDomains, + siTokens) + # change moderators list if fields.get('moderators'): if path.startswith('/users/' + diff --git a/shares.py b/shares.py index b11929cbf..0c8b82fbb 100644 --- a/shares.py +++ b/shares.py @@ -979,7 +979,7 @@ def generateSharedItemFederationTokens(sharedItemsFederatedDomains: [], def updateSharedItemFederationToken(baseDir: str, tokenDomain: str, newToken: str, tokensJson: {} = None) -> {}: - """Updates a token for shared item federation + """Updates an individual token for shared item federation """ if not tokensJson: tokensJson = {} @@ -1003,10 +1003,44 @@ def updateSharedItemFederationToken(baseDir: str, return tokensJson +def mergeSharedItemTokens(baseDir: str, domain: str, + newSharedItemsFederatedDomains: [], + tokensJson: {}) -> {}: + """When the shared item federation domains list has changed, update + the tokens dict accordingly + """ + removals = [] + changed = False + print('Test 46237') + for tokenDomain, tok in tokensJson.items(): + print('tokenDomain: ' + tokenDomain) + if domain: + if tokenDomain.startswith(domain): + continue + if tokenDomain not in newSharedItemsFederatedDomains: + removals.append(tokenDomain) + print('remove ' + tokenDomain) + # remove domains no longer in the federation list + for tokenDomain in removals: + del tokensJson[tokenDomain] + print('removing ' + tokenDomain) + changed = True + # add new domains from the federation list + for tokenDomain in newSharedItemsFederatedDomains: + if tokenDomain not in tokensJson: + tokensJson[tokenDomain] = '' + changed = True + if baseDir and changed: + tokensFilename = \ + baseDir + '/accounts/sharedItemsFederationTokens.json' + saveJson(tokensJson, tokensFilename) + return tokensJson + + def createSharedItemFederationToken(baseDir: str, tokenDomain: str, tokensJson: {} = None) -> {}: - """Updates a token for shared item federation + """Updates an individual token for shared item federation """ if not tokensJson: tokensJson = {} diff --git a/tests.py b/tests.py index c73ed639e..25ac98858 100644 --- a/tests.py +++ b/tests.py @@ -132,6 +132,7 @@ from shares import authorizeSharedItems from shares import generateSharedItemFederationTokens from shares import createSharedItemFederationToken from shares import updateSharedItemFederationToken +from shares import mergeSharedItemTokens testServerAliceRunning = False testServerBobRunning = False @@ -3104,6 +3105,8 @@ def _testFunctions(): for sourceFile in files: if not sourceFile.endswith('.py'): continue + if sourceFile.startswith('.#'): + continue modName = sourceFile.replace('.py', '') modules[modName] = { 'functions': [] @@ -4300,6 +4303,20 @@ def _testAuthorizeSharedItems(): 'dog.domain', 'testToken', tokensJson) assert tokensJson['dog.domain'] == 'testToken' + # the shared item federation list changes + sharedItemsFederatedDomains = \ + ['possum.domain', 'cat.domain', 'birb.domain'] + tokensJson = mergeSharedItemTokens(None, '', + sharedItemsFederatedDomains, + tokensJson) + assert 'dog.domain' not in tokensJson + assert 'cat.domain' in tokensJson + assert len(tokensJson['cat.domain']) >= 64 + assert 'birb.domain' in tokensJson + assert 'possum.domain' in tokensJson + assert len(tokensJson['birb.domain']) == 0 + assert len(tokensJson['possum.domain']) == 0 + def runAllTests(): print('Running tests...') diff --git a/translations/ar.json b/translations/ar.json index fb361feb6..2c4af68d0 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -456,5 +456,6 @@ "Quantity": "كمية", "food": "غذاء", "Price": "السعر", - "Currency": "عملة" + "Currency": "عملة", + "List of domains which can access the shared items catalog": "قائمة المجالات التي يمكن الوصول إلى كتالوج البنود المشتركة" } diff --git a/translations/ca.json b/translations/ca.json index 083bbab60..3d57c4d21 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -456,5 +456,6 @@ "Quantity": "Quantitat", "food": "menjar", "Price": "Preu", - "Currency": "Moneda" + "Currency": "Moneda", + "List of domains which can access the shared items catalog": "Llista de dominis que poden accedir al catàleg d'articles compartits" } diff --git a/translations/cy.json b/translations/cy.json index 001716b71..c379bee01 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -456,5 +456,6 @@ "Quantity": "Symiau", "food": "bwyd", "Price": "Prisia", - "Currency": "Harian" + "Currency": "Harian", + "List of domains which can access the shared items catalog": "Rhestr o barthau a all gael mynediad i'r catalog eitemau a rennir" } diff --git a/translations/de.json b/translations/de.json index ff9b3ad46..9ea05fd44 100644 --- a/translations/de.json +++ b/translations/de.json @@ -456,5 +456,6 @@ "Quantity": "Menge", "food": "lebensmittel", "Price": "Preis", - "Currency": "Währung" + "Currency": "Währung", + "List of domains which can access the shared items catalog": "Liste der Domains, die auf den gemeinsam genutzten Artikelkatalog zugreifen können" } diff --git a/translations/en.json b/translations/en.json index d211aee2c..7386bf508 100644 --- a/translations/en.json +++ b/translations/en.json @@ -456,5 +456,6 @@ "Quantity": "Quantity", "food": "food", "Price": "Price", - "Currency": "Currency" + "Currency": "Currency", + "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog" } diff --git a/translations/es.json b/translations/es.json index bf3ec917c..229cf6758 100644 --- a/translations/es.json +++ b/translations/es.json @@ -456,5 +456,6 @@ "Quantity": "Cantidad", "food": "comida", "Price": "Precio", - "Currency": "Divisa" + "Currency": "Divisa", + "List of domains which can access the shared items catalog": "Lista de dominios que pueden acceder al catálogo de artículos compartidos" } diff --git a/translations/fr.json b/translations/fr.json index 8481d0ecd..2bbfe4511 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -456,5 +456,6 @@ "Quantity": "Quantité", "food": "aliments", "Price": "Prix", - "Currency": "Devise" + "Currency": "Devise", + "List of domains which can access the shared items catalog": "Liste des domaines pouvant accéder au catalogue d'éléments partagés" } diff --git a/translations/ga.json b/translations/ga.json index 7055f288d..3b14b37a6 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -456,5 +456,6 @@ "Quantity": "Cainníocht", "food": "bia", "Price": "Praghas a chur ar", - "Currency": "Airgeadra" + "Currency": "Airgeadra", + "List of domains which can access the shared items catalog": "Liosta na bhfearann a fhéadann rochtain a fháil ar chatalóg na míreanna comhroinnte" } diff --git a/translations/hi.json b/translations/hi.json index 6c46aa3fd..ffa373c19 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -456,5 +456,6 @@ "Quantity": "मात्रा", "food": "खाना", "Price": "कीमत", - "Currency": "मुद्रा" + "Currency": "मुद्रा", + "List of domains which can access the shared items catalog": "डोमेन की सूची जो साझा आइटम कैटलॉग तक पहुंच सकती है" } diff --git a/translations/it.json b/translations/it.json index dcc91184a..5753261da 100644 --- a/translations/it.json +++ b/translations/it.json @@ -456,5 +456,6 @@ "Quantity": "Quantità", "food": "cibo", "Price": "Prezzo", - "Currency": "Moneta" + "Currency": "Moneta", + "List of domains which can access the shared items catalog": "Elenco dei domini che possono accedere al catalogo articoli condivisi" } diff --git a/translations/ja.json b/translations/ja.json index 1bf47aef8..b2dbc0b83 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -456,5 +456,6 @@ "Quantity": "量", "food": "食物", "Price": "価格", - "Currency": "通貨" + "Currency": "通貨", + "List of domains which can access the shared items catalog": "共有項目カタログにアクセスできるドメインのリスト" } diff --git a/translations/ku.json b/translations/ku.json index 17b8bca0a..d96d7030c 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -456,5 +456,6 @@ "Quantity": "Jimarî", "food": "xûrek", "Price": "Biha", - "Currency": "Diravcins" + "Currency": "Diravcins", + "List of domains which can access the shared items catalog": "Navnîşa domên ku dikarin bigihîjin kataloga tiştên parvekirî" } diff --git a/translations/oc.json b/translations/oc.json index 5eb5c0953..fd933af58 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -452,5 +452,6 @@ "Quantity": "Quantity", "food": "food", "Price": "Price", - "Currency": "Currency" + "Currency": "Currency", + "List of domains which can access the shared items catalog": "List of domains which can access the shared items catalog" } diff --git a/translations/pt.json b/translations/pt.json index 74de6dca5..a933ab00d 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -456,5 +456,6 @@ "Quantity": "Quantidade", "food": "comida", "Price": "Preço", - "Currency": "Moeda" + "Currency": "Moeda", + "List of domains which can access the shared items catalog": "Lista de domínios que podem acessar o catálogo de itens compartilhados" } diff --git a/translations/ru.json b/translations/ru.json index 3d01c70d6..8e2a8682d 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -456,5 +456,6 @@ "Quantity": "Количество", "food": "еда", "Price": "Цена", - "Currency": "Валюта" + "Currency": "Валюта", + "List of domains which can access the shared items catalog": "Список доменов, которые могут получить доступ к каталогу общих пунктов" } diff --git a/translations/sw.json b/translations/sw.json index 7ff101685..0b7f70bed 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -456,5 +456,6 @@ "Quantity": "Wingi", "food": "chakula", "Price": "Bei", - "Currency": "Fedha" + "Currency": "Fedha", + "List of domains which can access the shared items catalog": "Orodha ya Domains ambayo inaweza kufikia orodha ya vitu vya pamoja" } diff --git a/translations/zh.json b/translations/zh.json index f8cb6d8a9..92e635694 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -456,5 +456,6 @@ "Quantity": "数量", "food": "食物", "Price": "价钱", - "Currency": "货币" + "Currency": "货币", + "List of domains which can access the shared items catalog": "可以访问共享项目目录的域名列表" } diff --git a/webapp_profile.py b/webapp_profile.py index 759456cd1..9e15447d1 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1340,6 +1340,28 @@ def _htmlEditProfileGitProjects(baseDir: str, nickname: str, domain: str, return editProfileForm +def _htmlEditProfileSharedItems(baseDir: str, nickname: str, domain: str, + translate: {}) -> str: + """shared items section of edit profile screen + """ + sharedItemsStr = '' + sharedItemsFederatedDomainsStr = \ + getConfigParam(baseDir, 'sharedItemsFederatedDomains') + if sharedItemsFederatedDomainsStr: + sharedItemsFederatedDomainsList = \ + sharedItemsFederatedDomainsStr.split(',') + for sharedFederatedDomain in sharedItemsFederatedDomainsList: + sharedItemsStr += sharedFederatedDomain.strip() + '\n' + + editProfileForm = beginEditSection(translate['Shares']) + idx = 'List of domains which can access the shared items catalog' + editProfileForm += \ + editTextArea(translate[idx], 'shareDomainList', sharedItemsStr, + 200, '', False) + editProfileForm += endEditSection() + return editProfileForm + + def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, userAgentsBlocked: str, translate: {}) -> str: """Filtering and blocking section of edit profile screen @@ -1957,6 +1979,10 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str, editProfileForm += \ _htmlEditProfileGitProjects(baseDir, nickname, domain, translate) + # shared items section + editProfileForm += \ + _htmlEditProfileSharedItems(baseDir, nickname, domain, translate) + # Skills section editProfileForm += \ _htmlEditProfileSkills(baseDir, nickname, domain, translate) From 8c3c22aec39ad9e0d6c5eccf4c797dafd03f11b5 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 26 Jul 2021 22:08:39 +0100 Subject: [PATCH 146/459] Only admin can set shared item federation domains --- webapp_profile.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/webapp_profile.py b/webapp_profile.py index 9e15447d1..d6548cb57 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1895,6 +1895,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str, peertubeStr = '' libretranslateStr = '' graphicsStr = '' + sharesFederationStr = '' adminNickname = getConfigParam(baseDir, 'admin') @@ -1904,6 +1905,10 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str, if adminNickname: if path.startswith('/users/' + adminNickname + '/'): + # shared items section + sharesFederationStr = \ + _htmlEditProfileSharedItems(baseDir, nickname, + domain, translate) instanceStr, roleAssignStr, peertubeStr, libretranslateStr = \ _htmlEditProfileInstance(baseDir, translate, peertubeInstances, @@ -1979,15 +1984,12 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str, editProfileForm += \ _htmlEditProfileGitProjects(baseDir, nickname, domain, translate) - # shared items section - editProfileForm += \ - _htmlEditProfileSharedItems(baseDir, nickname, domain, translate) - # Skills section editProfileForm += \ _htmlEditProfileSkills(baseDir, nickname, domain, translate) - editProfileForm += roleAssignStr + peertubeStr + graphicsStr + instanceStr + editProfileForm += roleAssignStr + peertubeStr + graphicsStr + editProfileForm += sharesFederationStr + instanceStr # danger zone section editProfileForm += _htmlEditProfileDangerZone(translate) From 3a19ec4d2e2ee4ec0d94c7ccda2f0e1c1dc4c4fc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 10:09:53 +0100 Subject: [PATCH 147/459] Function to match shared items during search --- webapp_search.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index a031a4333..d9d822ea5 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -102,6 +102,23 @@ def htmlSearchEmoji(cssCache: {}, translate: {}, return emojiForm +def _matchSharedItem(searchStrLowerList: [], + sharedItem: {}) -> bool: + """Returns true if the shared item matches search criteria + """ + for searchSubstr in searchStrLowerList: + searchSubstr = searchSubstr.strip() + if searchSubstr in sharedItem['location'].lower(): + return True + elif searchSubstr in sharedItem['summary'].lower(): + return True + elif searchSubstr in sharedItem['displayName'].lower(): + return True + elif searchSubstr in sharedItem['category'].lower(): + return True + return False + + def htmlSearchSharedItems(cssCache: {}, translate: {}, baseDir: str, searchStr: str, pageNumber: int, @@ -133,7 +150,7 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, resultsExist = False for subdir, dirs, files in os.walk(baseDir + '/accounts'): for handle in dirs: - if '@' not in handle: + if not isAccountDir(handle): continue contactNickname = handle.split('@')[0] sharesFilename = baseDir + '/accounts/' + handle + \ @@ -146,22 +163,7 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, continue for name, sharedItem in sharesJson.items(): - matched = True - for searchSubstr in searchStrLowerList: - subStrMatched = False - searchSubstr = searchSubstr.strip() - if searchSubstr in sharedItem['location'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['summary'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['displayName'].lower(): - subStrMatched = True - elif searchSubstr in sharedItem['category'].lower(): - subStrMatched = True - if not subStrMatched: - matched = False - break - if matched: + if _matchSharedItem(searchStrLowerList, sharedItem): if currPage == pageNumber: sharedItemsForm += '
\n' sharedItemsForm += \ From ec3b083543b50f738323558f8eb5aac66a12f749 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 10:43:36 +0100 Subject: [PATCH 148/459] Tidying of individual shared item search result --- webapp_search.py | 105 ++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index d9d822ea5..f7b87c1d8 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -119,6 +119,56 @@ def _matchSharedItem(searchStrLowerList: [], return False +def _htmlSearchResultShare(sharedItem: {}, translate: {}, + httpPrefix: str, domainFull: str, + contactNickname: str, name: str, + actor: str) -> str: + """Returns the html for an individual shared item + """ + sharedItemsForm = '
\n' + sharedItemsForm += \ + '\n' + if sharedItem.get('imageUrl'): + sharedItemsForm += \ + '\n' + sharedItemsForm += \ + 'Item image\n' + sharedItemsForm += '

' + sharedItem['summary'] + '

\n

' + if sharedItem.get('itemQty'): + sharedItemsForm += \ + '' + translate['Quantity'] + \ + ': ' + str(sharedItem['itemQty']) + ' ' + sharedItemsForm += \ + '' + translate['Type'] + ': ' + sharedItem['itemType'] + ' ' + sharedItemsForm += \ + '' + translate['Category'] + ': ' + sharedItem['category'] + ' ' + sharedItemsForm += \ + '' + translate['Location'] + ': ' + sharedItem['location'] + if sharedItem.get('itemPrice') and \ + sharedItem.get('itemCurrency'): + if isfloat(sharedItem['itemPrice']): + if float(sharedItem['itemPrice']) > 0: + sharedItemsForm += \ + ' ' + translate['Price'] + \ + ': ' + sharedItem['itemPrice'] + \ + ' ' + sharedItem['itemCurrency'] + sharedItemsForm += '

\n' + contactActor = \ + httpPrefix + '://' + domainFull + '/users/' + contactNickname + sharedItemsForm += \ + '

\n' + if actor.endswith('/users/' + contactNickname): + sharedItemsForm += \ + ' \n' + sharedItemsForm += '

\n' + return sharedItemsForm + + def htmlSearchSharedItems(cssCache: {}, translate: {}, baseDir: str, searchStr: str, pageNumber: int, @@ -165,58 +215,11 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, for name, sharedItem in sharesJson.items(): if _matchSharedItem(searchStrLowerList, sharedItem): if currPage == pageNumber: - sharedItemsForm += '
\n' sharedItemsForm += \ - '\n' - if sharedItem.get('imageUrl'): - sharedItemsForm += \ - '\n' - sharedItemsForm += \ - 'Item image\n' - sharedItemsForm += \ - '

' + sharedItem['summary'] + '

\n

' - if sharedItem.get('itemQty'): - sharedItemsForm += \ - '' + translate['Quantity'] + \ - ': ' + str(sharedItem['itemQty']) + ' ' - sharedItemsForm += \ - '' + translate['Type'] + \ - ': ' + sharedItem['itemType'] + ' ' - sharedItemsForm += \ - '' + translate['Category'] + \ - ': ' + sharedItem['category'] + ' ' - sharedItemsForm += \ - '' + translate['Location'] + \ - ': ' + sharedItem['location'] - if sharedItem.get('itemPrice') and \ - sharedItem.get('itemCurrency'): - if isfloat(sharedItem['itemPrice']): - if float(sharedItem['itemPrice']) > 0: - sharedItemsForm += \ - ' ' + translate['Price'] + \ - ': ' + sharedItem['itemPrice'] + \ - ' ' + sharedItem['itemCurrency'] - sharedItemsForm += '

\n' - contactActor = \ - httpPrefix + '://' + domainFull + \ - '/users/' + contactNickname - sharedItemsForm += \ - '

\n' - if actor.endswith('/users/' + contactNickname): - sharedItemsForm += \ - ' \n' - sharedItemsForm += '

\n' + _htmlSearchResultShare(sharedItem, translate, + httpPrefix, domainFull, + contactNickname, + name, actor) if not resultsExist and currPage > 1: postActor = \ getAltPath(actor, domainFull, From af4bff1f72ead8ef53feb55eaca5e2b1d54aa447 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 10:57:52 +0100 Subject: [PATCH 149/459] Previous button on shared items search --- webapp_search.py | 62 ++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index f7b87c1d8..061f4c6d1 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -169,6 +169,33 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, return sharedItemsForm +def _htmlSearchResultSharePrev(actor: str, domainFull: str, + callingDomain: str, pageNumber: int, + searchStrLower: str, translate: {}) -> str: + """Returns the html for the previous button on shared items search results + """ + postActor = getAltPath(actor, domainFull, callingDomain) + # previous page link, needs to be a POST + sharedItemsForm = \ + '
\n' + sharedItemsForm += \ + ' \n' + sharedItemsForm += \ + '
\n' + sharedItemsForm += \ + '
\n' + ' \n' + sharedItemsForm += \ + ' ' + translate['Page up'] + '\n' + sharedItemsForm += '
\n' + sharedItemsForm += '
\n' + return sharedItemsForm + + def htmlSearchSharedItems(cssCache: {}, translate: {}, baseDir: str, searchStr: str, pageNumber: int, @@ -215,41 +242,20 @@ def htmlSearchSharedItems(cssCache: {}, translate: {}, for name, sharedItem in sharesJson.items(): if _matchSharedItem(searchStrLowerList, sharedItem): if currPage == pageNumber: + # show individual search result sharedItemsForm += \ _htmlSearchResultShare(sharedItem, translate, httpPrefix, domainFull, contactNickname, name, actor) if not resultsExist and currPage > 1: - postActor = \ - getAltPath(actor, domainFull, - callingDomain) - # previous page link, needs to be a POST + # show the previous button sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - ' \n' - sharedItemsForm += \ - '
\n' - sharedItemsForm += \ - '
\n' + \ - ' \n' - sharedItemsForm += \ - ' ' + translate['Page up'] + \
-                                '\n' - sharedItemsForm += '
\n' - sharedItemsForm += '
\n' + _htmlSearchResultSharePrev(actor, domainFull, + callingDomain, + pageNumber, + searchStrLower, + translate) resultsExist = True ctr += 1 if ctr >= resultsPerPage: From ae917d3bba66b9d71a86935770ddc6fabe3693b6 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 27 Jul 2021 11:05:57 +0100 Subject: [PATCH 150/459] Page up and down buttons --- webapp_search.py | 59 +++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/webapp_search.py b/webapp_search.py index 061f4c6d1..08e29cd64 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -169,16 +169,25 @@ def _htmlSearchResultShare(sharedItem: {}, translate: {}, return sharedItemsForm -def _htmlSearchResultSharePrev(actor: str, domainFull: str, +def _htmlSearchResultSharePage(actor: str, domainFull: str, callingDomain: str, pageNumber: int, - searchStrLower: str, translate: {}) -> str: + searchStrLower: str, translate: {}, + previous: bool) -> str: """Returns the html for the previous button on shared items search results """ postActor = getAltPath(actor, domainFull, callingDomain) # previous page link, needs to be a POST + if previous: + pageNumber -= 1 + titleStr = translate['Page up'] + imageUrl = 'pageup.png' + else: + pageNumber += 1 + titleStr = translate['Page down'] + imageUrl = 'pagedown.png' sharedItemsForm = \ '
\n' + str(pageNumber) + '">\n' sharedItemsForm += \ ' \n' sharedItemsForm += \ @@ -189,8 +198,8 @@ def _htmlSearchResultSharePrev(actor: str, domainFull: str, '" type="submit" name="submitSearch">\n' sharedItemsForm += \ ' ' + translate['Page up'] + '\n' + '/' + imageUrl + '" title="' + titleStr + \ + '" alt="' + titleStr + '"/>\n' sharedItemsForm += '