From aadf3fd06ec00ad197f39b44152b39bab25153cb Mon Sep 17 00:00:00 2001
From: Milan
wk7z)&i1YFZLk=m9o<2djr`8qyuO zKSuuDvRXh|#zkGlL3m$a{vyHVM^&@Tqz^04%+T}WbUhsq%IlP#K0%}Bfs&i}-^Xi~ zI6Fw(v&?$8WcTb#%+uw?9*V3rX*$T5+32RE@Feur{^&QydUT8YOkeZLRKB>+s*(8f z{>j7zK1~ZbvNS{$<9`XszCSH_P^@76_15~+e%IG--Okbfre&+Vs?XoDocO-J?fr*V zTt3LAGeO`+NUQ?ybgO;rz0(-}vP{}OQMm0PL%)8u&8+8J76_)Ngl#W&x>S}pL$~A2 zIis1L@1nnLbuG{8`XAx_Zqv4hlN3xBt66TGBzIDrb)I*X1KVniofq@k6;7~xnK54` z?Y48^2aZ=R0oNs!T3FBDm}qi?A@6n?qtCW^mUptwb@NoneeHLgx8xb;!7B$fE%xa> zTF%Fv_vK-{xGZ&UG2Hu{~Y)nn|-Z+&5ilSY?4bH72Vv{ ztP!8Wv`RE;ZM;o}pnbd0%tI`b=WOsiqn@r4wj-e4kAISis0l-~9QW$YKlN?BQiUH! zzH-Sh`H)i{qEhUV5w04#G~s}$RE0o3ORDeUjms62XBRRsm^c2~z2KEqU(xPw2Gc{j ztwQHM6Ln}<(&%Dn5U)33MT+GLW5KZY<&FDKJC*WfK3x4mZH|N9r;3PUdmga*Y${N< zICS)t&d!y4tk>L}nU`VwswVx@i77vlLQV)zJhbJx3hRRIBV8{SPpV3eeEQ_k!PK8; z`n{LMPW*CnqMm}eNnT)x!EM -fhqRJss|#JZS92oRrC9o!ja~__7xN#Eneple zZkP8wDVwuus*YOfo(sG+lKb?RurYO+wH=thw9xc-@EnI_4QcB$+1Om|>`yLBHt(7D zyhUpF<1f1ei|$4?=-m>^m)~?rFSBy *f#+xxeXG!q^&G9pAm|zniPQ%fQ2n>#%2@c=Gu! z%W}??Z~cr{N}CyY*{*%aoYvadu25MVyRz0f8UnpZ@=>SF?i-g=Q2Otn}j6->~K6 z!l?5 ud$S62n}e9&izG%`uz-1ckfNo|kBnUy+Mr!86L6D}Y(X>Etbq7z&@ zp6 @q{g u5#qxD)(gvmsFf}QEg2! zTeDyHS)jOCLzU^&q}kWQ{Eo~P2yNWVK1Z@gNyaL0(T?~n|9?&~Z8cr1cxgtY?a?DI z)`h+{-uCsu!2@~H20X 78MPW)s6EC*57r%<;4LxiwUtRF9CH?d7>h zQ*qHE2{ygm>=U*uZaQI{$iaHsIAX 8#HPbcPVLm^(#VG8uP}h-9iw?~0=v%$Yvhw}Rn)5T|Ia6*vOPuL+ zIh N|$5_jjhX)+1eW@yZM{3U0?QINS()}0% zkExd(I3K^?J$ ZQ#_%u&(nQ+2=_r-G$8a4Zv0xBKmtmTpqdKt3XV#llf6(7nvI`ZBq zeYaj3VNriQ|FF_Txn~;9whJ|HKJ{P2TE13L@cLHC{l6 UHWSTTzJhtR(pV5`PX?k4e-{+cEKGJx&VBs0JoGE7nxhl_w@Bdd~ z_##`cB!)NLW9Pr)|3&9n{yvy=`9#Tk ;YVnKch3Co<-h^*lTkbC1W8 z^|{aH!-rZw?Z4BVt!yYW)ul~+M&p$`wSMxG^wlTZzx-&% m z)ts9<9e0|3(`(7vY0~8I?uh0Av5q5Nw;3hbd$$Vi_LF$xb6l#i=KudShZ&|{tzhfi zcw4r~kg<5Wsp5AXjuS-zPaOBuGD~hQZJzii=HAw03k~%TM*ID;TPWcC@8PjW@hY=x z&q>Nod~;}bBX>gn;f$-Ich0F>tyra _!Kp|s D+Yg3$ TIdxCP z71#Nxc4(?Tt*(zu{FgbsNwg*;YV#(IkGl_u@*Mebi|_auvqQ$aFG;2TV}ICjRsmOy F{95keR^e8@#2EA&|BIQ!Y0%_nOo2~?ev_S#i5Ft zhOI@)m9YsAIYXx3oFaBQ#gMn-`0pdC3M@;xu6s{h6!ZG<#yvY8+-En~ao1#~Sj>B; z8N4iVp03j@`^p6;ui@BL$)B@y+Bb&FYa%?ktV~aKGDV;GrB!j&XmRBnpO0(q b4F zeEyWf`R^XP&v0;)@7+4ZIZwnv(DYF9jm}*PU!U`epXj_h`^2q>b^(7MPJ9-WCbmjf zU|+)i_K6l>D@$gyWqQqh+h7}dN8U-=Ou#2HW``uBiN15;uG3R2FSKzzSKcsl&xIuZ zgMk|N^iMf!+`3|~=igR4QSG3x?yB`G?w()1f7i2Rxi%5kl&8#*z2jxNA^2%%TNh8a zcLvYI|3{DQp4j4VSRlw>7-4#eKQZUF;m*#SwR`0ACOz!Cnqwn3 &C?06C$TXzUGhE^JMAU57ohQicci3`?KVa!1t-f+jUn|yV%%$=w)Md znE9C_=6-{kNJy&~-%zSQ`gP_AsK_2kM%mv%NsVb=33wZ2Be4LkU}V#Jz-F5NgX zO|y8Ov7CUhSwr&lL${9nVK8_;d8*{j$?Rs$nu}(My|LT>d=uyXz9ZcVQLm1jVi5j! zb@S1P)ht&9C#24f rda-Yc zsMwCV_q1el+V+dO6mvUl?%=rk a2MenV&qr@oe6+p4LAK*VilvN#3>m zaP&>#Z3k7AoWx>3Ot|4x6;$?uMeP1Jd6j9ln&%B~omq5QO ~dEgHsko2be7YmA|-XznSFU& zQ(pva%&a~X^myjyd9$k6 8 F&wBKva#wQzR z-b$PCY`L4r)*Mv_gY&a@B{K7_i)#28@`Pow&V>}6)hllP ybEeO*d1oD zu+^S M7@{-_J8pk^a(~5C zjmVQx`~Drz6Lb+35mbnh3pBRiHWB>fp?zRcA&XVM$rQ~{qsqu7)!VjcGXLi)5t=q* z-gy?A)2g2stFKr)^NQaW&@D86dNt{?$)T$ON=qinKS-6iWqQw_O)Z?=RW4Z~X7;|g z*cUM~CVq>IcUf+nS|IuGT>5JbW_tl|FYb?A^KNe1_s8E=LPC~laYp#UOA=?5d#>O3 zvU625hvGRV+ZB$2mN%CcZtXCM@RxR7t;c$J->Qf!e-+Ap#`s)ml9 }aVLNumz_4}}>+%;zd?G7 SD{2x z`~*v _HLGZFieC?@KdAJ*JE3(4!zG<>l0TXJ*PiZCH9In=&3+%>Bdx8o zzrAx*O!)MuPK?9*+w+`H=~@gO_exGhO7aSe>2ZHa@%YzJlKAyPdu6ZNWHyD#8Xv^$ zG?uHyylFbVVX2e1?#aI*=R!B0ey2CXA-T9dW&Md86XX{~e*U9kZSaTJGsso{g>i+U zHt&>TiN^NF7bloaa}@Y)SHRJ+Vzm$B;X~(d-g2(z `^FX5TbI6j^?i|{)1 ztiPyFDtcv6Ou_BttHo>`8e~pzuk)O#$TdaA_ia1#(Q`8ot`5|l!FKV$DfW<8X1^C$ z9ZlW3PW;u%0}n3FINR#gaHP`k`72J2(_4fT`;RWqVVwB)f>EBd!DIstxrFVqENwZR z%xCXQEsgLw%sYQ$lG&`rE#Ybs&C?f)?5Ip?Osw@ibYREKrLugC3-4R6YsoZe?7VjT zn#o3yy}gR3|LyO6ze?Hm!v?#5Yl1WSl&<~UtRa5tMQ7pn1yOS6KYd@`msU7I?beFf z&;Fh+xWB xvj!1HZt<1Ej$D|El!YyW$3qP$+`?DQuSJ+t~B zM-|N0W`E9S!E{1Tv;6J7FA6hv{Y+1n&({w=@qBf3_0=kF2J_gqmRXZ8`i7X^oXm5e zvbd;C<#v{G&atMKzXUE7eaTiin9ZWt!+Xrs_2AnTA6jyM{W6PFx8^+E_nlWXx2iN? zliHl(r<(hEW@_+NPpgPxJhP(7xOk%Fh3^p^oHM+S#WgCPKKc04^1=mc((8{hR#crh z{99}5lB>&nIXW&){k-^RU7eOBhtWr#SO=|rOiV@(X4F-L8v8j|PUKrYX 4G$8DY;Zxy93~LgSkIeoVa4-Lf z_of|I^A~j7OL5%#pzm_9e6g8$|9Nc*8}sg >T^uvzmaY@>vA8_ z8LPJ(j_6|U=~!CG)2O~!pt+!1|Hvkhy6-9B3Ly#CPIY}a;C@1N$`#@4c5&S^jtL=; za=cBhrG9$dGLz|eFk55(k@&{){~6crz19`3tJuoNB)<9EO5KMSm+$^3bn=nGme)*E zE&ZM95Qt$ZehSxr}rc4LoAI(?cU^}CAP zr85mL804PBer-J&S3bM{M%gLhJ2S4H$-H6l!+4+O;e|&nCmHA3=y!*$v+0>8>ic$% zvd59>GXL+a=aYJ{)z>6yA>$^mm5t6`MeJ*CF|BhIOA1Pkxb0}SMBzf$oXkg7yM%g@ zmL|Gye)L#q*}^RizH9i+nf(ulZ%b9E>pKzaBK33XEw%T{Ol~+mzLd{#cdq0^<+h$L zo*DBUOCoA7vgggRu61Es;3A~CKuubF_XqKgA3WE;s^vQQ=;-)&E_Gd0 {d_CxNUvb8=&Cud(jp24T`BiXWkeF@t; zw;~&{j$7%wg*!Igebzj2yNvVV8jb^V! PyVCD4>@5Gy?QvP9TSFr0U2C4V z%z@T8tCg9%_jUyfU0tjo-Nv%xi=Tz${>{36wb%X_`FyDnGo9!u#1n%9h39=m z!%P!BukZI5cTfAIU|D~P%dX4BN3KfxkkP$UdLnJg=HbsI&$w{9@@_fd)M@m!@mrhN ze&zi8UEiPC)H!@?F}||kS=4m?h4Vh1Xtyx__FzTC_vdzJxD4ECeyV!^S37y)!}8zr z Wlodz^W#mer=P07^UY$}mJsVjr>o-Md@PGd^}m>< zHD5f{#Cpw@r*Rp}YdEvNU%h$mkZ;wUD1TGqIT2oy&6jl_GhM6jCoj!rLHFcFNiE~9 z^S^thv~5^xe%g4C+LnD<>yCe2GJmOi>)i{Nx-Pl8EOJhXa_E@1`0lQVaJ8~J4f91e z8Dynz@d^6r&Rn_D{>_Qmi_?2Vi{<~{*jwhQ@BMMDo6w!Bee2zKORvb87NYc3C`Pir z&2v^rtxeK$8E;P}<3o)m6SGYE3pB+AX7oGkoF*FL 56vP z^|9!k?zU9({mgvg2alY&$(PxZT+RII-&OaN5XJK2jypH4-l?GfH%4y7vp}P5E_QCx z@{!Bu85Bz1nwoUHkiopA`$+B|lN}P3bN{BgpSWw KCdroO>3N zVBGX `8kOzx!5P}X$ZTlt^Q`(BW)3pW6bNcCM6tHme~9%W%6A)K5ho7Q~L5{ zv0lgRUrd#*$nbZr+OhVIhx><38LPxQRptgQv6|Ch?sES0+#OT@9jVzTlj@uGT;bZ< z{{id8ny(1xM^0QJtZxwK=M*(Nd+WZ#aVLJtnr&h7WmdI~=oIu(^n86yYgc-KoadrT z0d=o {bMep3nnXgeRUiCcU@WXl1 zi{m A-$ukDR8+Vk_V>KEyZlZQ+fOO4)^9ou5tM;#|3D*{On>8 8pe0w{2ROO(#uD z-fgqf+BL@M<8+7BTvM0|Ryi(X%~!t1wtvZLjuS^s!%yk5q;f?ry0l^96vwF+f)nRl zPPwg7psf5wO8J}M#4aoUwmDxrPW(Cg;lJgFo`!i_tCsw$zLPpP$%yT9_>{eKPxDPE zOx~u>+WMtPx?QGlj#x?bp@h|k*2XZDiyVBkCik|RZ{Re&OSWr{y_xN9(wDX5aJ89Z zmgKSX+bTDg_V;)_C|344yH3pirr6B(dz*I7k7nj6-_V!hnf$}!)xp(^&aCX>dwihN zR7YYL)AMP0@j-v5EsL7;e41LE*6VEBl@k^|n!x3v`DUR}dzQ k?;X-W#y) zfPna!%N5~E#QbK&FH!usgDLUt>INmIM++Xvsz;yq_~N>f;irq@tu+m46EAi-@Nr+{ zt+-;r%ru*$sYSA9qMDt@ 8t H;^_xsIX{ezGCUg8vc_vWwjzqlR0jJWP?b}cfk=iz7- zGH6J0{Uzdb{JV-1 vj67-_ z_wY?G0gdx{RC!SyNeoiFA6ruu*I!j8KmU$D@OUm0-? LSxRa&RL=_<2pIIeHL;j#Q)9_)7*aey#KG% zR0WOp&@U3odzxEcEm {EX|NrMUym$SxJ-cd}cSUumgv^@42PdXV z|4QB HXDMJ|I=Ak3k c`Q$ep!kgRbP+x0m+Z z*4W!58liC1>AL#+IWcWlcTE3LBHCnGe(nVCftvxcMG4w9lh^9DGnj`TyO4d8!z8hG zX;Zi9>4hhS7D@e0-}RwMc-^T2bv^&eg6+?~1ehiqb{2iTc&!6-#d4|cnRo7PVaeK2 z{9+4R$u&hC-WVg>L`J6^ E7IVY(9xr3%NI~5nSjp{csawoYOw8C{YO=+YIhs zyb*pAerhlI7O* OT5;u zWRcADE*X(jo W ziz+FG0{@t{n#vxJio9fV d2x~eKHn=a-=_;`wG zSGBm@D#%xLwTxuA7?_lC``q<)GJWd8pC?tgFK6-7U^y7|D3XtP(#aIF*ONF46lL|M zAK9UKR9fhxuERPBe%C_Yc_O;!7 ;apr`T#a_#9 zHErV-JQNaEQ=fWYxVUkd*lP~!Z7DgGF_+>d&YAi9R-f4|qn>w#CNCcNYHexyBDHf_ z=%p!}`#0UWc{r_d+s`@K4A HHThJjym~ofsFgYf_mSpJAQP%$914&-r(@2`#A4Y&p}&u}JgfJRL`L6}9@it{IMz&a+O<0=N&5_MM%pGv_XRbL zR?10kf_|!(EGBblTXO2Z;yJT;$%->;Kl>MIdCV~>`52-X(=0fdO~yjwgh#&5t#vz1 zA{O5cS)095ykxDX%#v# z+hP@BKCHV#kr5G>3<`iZ0#C zp8Iyvk2U&dozov?e>48Ilf7Hr%P8#5j| )q^;P}?x|_E)(M-UqwObA#DpCAH7oFI@OSL;v<( z-{7$61us5!eel|3r`Gd}#qM* vh3{t >nu8jiueC`{PM{}Lzc;1uU`cmy2I-FVqS&AlHGq+b!#dxmvm+KFYR<`R5+Qx z>+vfVi)jT_Y%}Cb`Z?$Fvg}Dt%5dtK7$>TteOkBS;lovptg>FpIZRw$rroC}3LMtR z@nMsBP?Wg3C-c_pW6CAA9V<+B$XdxZv}}0XF#FKytHn!9mllTARA#bOW^0MHFDl)$ zbMlT*lSPFQUykgy?lw}H-qTXnUAbzF?T#|`4JV@&PHO2sUw8DKtM!MMZuhQzUB-Q; zZ{@5-cO68}?W)rbUT3glq18G?wnN!Z`y= _4FxtGm9cW&?7%D@h12C6c6J( z{tK)5_uhN*p)Gm;_k*)i1jB uQ$>mtI zQ|GNC%T+xg*Y6WpJnlX}CGug*gQ%Yq?#mTFmzL!0mh}`7G&0gZWgEg7bl+Wlg64@; zTvHbxQ1m#mgd_VI^U;baGhDe7&+irIbABSs`A@D@;@aGF>8L5QdJEH*{@*Mu;P~n5 z?e>{6B0hJE6J!E{y{r`#mdXe?zI|orrB}pcdo=O1fC-n~35|){!ew%q9G3GvIJ%}* zCSb{qWtVj)pBKtm*%@^GPua0o`-EK&>qSpUykQl2?Ph6qJ gj^9>M|cAOutX0*bjggdG=d^!IZYxZY;bCdjDnC+oVZ)y$?Us-y84?RtIUYP}yKrsf>ay)TELE2CP56=y z*DPUj^yK~|Azj$}@)qCULVlTq)VrT%*{B-kPY=%Ii3*tV
*dWSYga2w zH-0i(P3&6}+ttUM-?lmVNl%}6x?SQ}Yee=!gG&+zxu-PG(Cg8!$dwl8{B&CG^7MUY zn67hj?^6v(ELs0&RZ%F5s1!?9cA3h-hi80NdHS5}JSEqa(lDd3$k$Dz`Zi;ZTxRz) zi-+HO>kdCq`{oq?+5M^xcQc!k!wDC`Hy+A0Uv95Zza()~XrA+?WiwLC*WFxqY7*zN zGdCM}Iu4w)7ixUUY*4r1!v2Zc30x;Wb{!Y^^m?hsaf7^*`pXl#u9>*5vip5^uEu(= z!)q*yUkNg`uUWw3ZLlrk$z8$tIVn79Yvl8fF*op;X-Vxm;ZWKblBRx9S#O6m@4~78 z`DKZ=GBJ&rEL#0}rOUQOnR2(Ck
d L{lTOsk_ZN2ts zOG%9ld(!qTPLfQCT)9RtZ{E%Po2QpeSk7v3u;ETv)Pf2AUC&sUnERZsKAu>*JagUI zc7xZ=f)S4@B4;m4JjPO*(if?B_TakbXLbuM^ecG6Jn6Tb+?KslMHb{lA1)S{SQNO- z{j|iQ%S)8xEoD+$Lni!VURM9Ot$*UYaM8u(vYJePciSF+m0k37LG>r?OD^YRPZ(5| zL_M3l>@xRT3+evc)Kt@r!N!02O;7QP7cshJ`5&D#>7by~Tt!V)2R^Gq%~#^|w5#9c zBrCq?2>dknVqMJX hP zj1xjw_4yCG3M7V=U*0vhf5mG3jdoK^r_GLP+7a@&vSGP%eSn`v pvZp?EW5kiuX_e+PYZiTI`r+AgDJnC0zs{XWG7Rf>8TK)AJ hdjU0y_Hd@QRa}sfWdeMPpPO+NQ z?Dw4hkxK;x8?-pQZ)`ks@2kk3n@jfp(-%6@HLtAymT-yZCq*}z7cJFFU7D**jyN6K z^YiAxn%2uPuCw{1-PBg>Yl|pfDzAMw@{#qDrH#@P<&CNZoF*zPZ`jzyY>_eX+gj$m zHvcat->yCWZL7s$?YQ*gA^h7n9h|CKuK4o}n-8ZUn`^)!33HtmiOG@{lN&;P3zT?u zB=kjgzdqbIvn6h(s^?$dN~7?d^EPm)d-v?uHC;G?rJb?iS>*lRmOb;Ht {_Q8u4IG__gCEk~OI> zp8gYm;53{4asQPqYb*{g+x5`J%b|kpiVfq_5O1bnuE)zZPf>7Ch?ve0b!B0)yY$|w z2Wq*iCe^O6?_)afuqj|e?}U}}_h?yhSQ&2kFTaz2amOFmS=Zkmf4`>k&(6h{!fVAl zOeb^hJNsGk;LOQuCa<`&kLgW#%~eL0dGhrFpEx%-yw$vF#? a@-B8A)g4w?( zLsFTiu5I(1H;cZQ|J~jA(5iCn0;>b*>|#<+4xW0w&_=4CWtp5tT;3E9pTyJk2lrfx zd3fy{o5V(* @~zuiYkgDCmGY0D zJPbNZS5KQLlxlLg@8uH7y}Vy0l!zW>hzjXjsO8Jd>)yX%!d1Q-D>?7o6jQJ}?r)*m zG-H346{}MV>+GY94~<@Y+^Jr+u1KsZWQA6fn`n~Kk_jHGb)FpKwK%gbGE r`J%^|4WpWP3a|ZN)}Rx+_Ljz?N!!;h+?nw%WiyNH>?3V+lRuhG zy4e>Jbu&+Q*)uMk9sdtScHTJpvBgC7%0W@~!x1}zr?0$u=8y}6kV)9>LOZsNC9atj zx^)M5 67!CTTRXER>=g+YX?OdSaQ4nFeV06Me#`0eb?Z+)D9gECli 5It>LCHQpr*0arLD z6zz}pZ8Xp+5|Inzd{(%wREBAWV(Klu=Qa{~zJ}hD5_q*Z?j+p0C|GrHcgHp}GYOlH zN9?Dxj`=7zJ0~!$a7>!`c186X2?3pLQaQ`L9TZni_SUsJbU3qpm7(E>|NFn44ezi~ z7BKWK(v@ksEjZzZov4FYVZh0To6bGe=le{)hPAXzI_ta7c!n6q<@ -pK0Qf)>Bw!N;4x@FhxGS1<$pXI% C2;;fY#7T0`r z{suBiG=y4ePst4D@ibo0>(4xi^Pq2&=Z!s{TV}J&{QpEaPBH80qsLFegl!$!*Ra`2 zK3uG5Q$20vN8t~8d(-rV4^6b(wWZoC>fDrPpYQA3E(~3wW41jrESrU?s9}Y}ftTMH zTGhC-%spmZTD?Ks=}wW@SN$}Pxs{OxeJd&_to+)=cy(s}EP-u-Rh7XkSL&qhWGEfG za%H!2RD*84Lc@!zH|F H)PMkLVS!r?bZlV09p ZQI1r!y0xd#na}wdQ{xK}#uC<74FH1IfD!-z literal 0 HcmV?d00001 diff --git a/website/agenda/fonts/open-sans-v15-latin_latin-ext-regular.svg b/website/agenda/fonts/open-sans-v15-latin_latin-ext-regular.svg new file mode 100644 index 0000000..78eb653 --- /dev/null +++ b/website/agenda/fonts/open-sans-v15-latin_latin-ext-regular.svg @@ -0,0 +1,336 @@ + + + diff --git a/website/agenda/fonts/open-sans-v15-latin_latin-ext-regular.ttf b/website/agenda/fonts/open-sans-v15-latin_latin-ext-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..65a04f13496937ee74352af93a2cbe2eb623a7e1 GIT binary patch literal 38424 zcmZQzWME(rWMp7qVGwY4adqQ};_PH#nDT{zfx*f>z(1Hll7WYTVX6TG1A~BjaHx}t zs@gXOhN%t=3=G%&gY}K-OdakqFx0CsFfb$}=Oz}Iq~sYeFx0m&FfisNmz5~+`${Zf zVCdY!z`(F5tsp(OE^(V614F|X1_t&m>50V!45AFm3=C7AfXq+NsZ3+&zI2#@fl-Em zp~NO5H8F*0S8@RZL&X#Z1_rZ?jMPNlGwiVp4D}2Q3=Aq6xg`}BX3TbDV5m65z`$mb zlb@Uz#aSxPz))Jjz`!h%n^;l6tj5H}z)%{&z`&r8mzbNXX61N|fuSpcfr0T>L4I+` z`57;+FfjC(FfeeeEhtJYaNa&`2?ImN3I+y7P6j51gA5ETT&&v|m>Jj^M7Ogrvof(T zvokR08|fP}>Kp4D8yW~J3M!f_3Mw9C_WZLwgQ@awJL|SzUKuRU7?>Dl{C~{Y3D(WW zpzR>S%*n~b!on}W$jHOQ!{#5!BgVwcU~jK2D8yhXp)V-JXenrHENE L_b!OkG>z{|kQ z&ce*X!O6zN&dkhUZ+zAWWUi$k#9U@&W<_RYW=4CBOid &bwvWRdB^6+p9@$+%Ba0v^Gh{?*av#^P?FfvH8NU*Z9iLh}g zGE0dwOK?djC~~my@bdUY$}sZEGYAL@_(e*J3i|Uh`U^7h3i1lFiVHJyu`=iz+Zzj6 zTI%b^#tIr6YYQ@f{0yaq7%eRY#f=4pEI}?761NltVPg;azcr2jf*99 !;tJ|JBGZw+Gz12aSO|4tSjHX#ND1~mp mHW5}a zRt9~2uz!UtjX=H?1p5_A3Nea LF`A6c z0 ~Vjo@U@=5M@wg&}A@WaCJ~&5MbpJ(~wb8($nBF5MVPhX6BXG=GEra zHZWu}kdRcBWEW; sRq^uaHnYl5KXQZ((zq&4;0joZ< zq#Co9mV&;J0;gA`0t -(^q4{YBy42P$j2lqBBp9$ zX0FEs@wBK28#|+!nYl3&JEO8X8#tTK^9XNDO%IFApZ#(EKkxh)o!S3(c5ex_RXMVL z(XNiUjMFWg#aFm=GHO5FS@vbx^|vgOU27w}>w|q06MoN~#kj&HGR?8H^H )b&dGfzUQ~urElofM@fq@ZJVldi+OAL|i49v_dtW2Pi;w-3~02e;6 z0s>MtfOG`?+ry;Dx{ZOCLB@fHoq>ymfrXEsTOFj$9;6MNgbfXZ1%%9v*_cE{gv6EA zn1W_cS~Owel H&uYaU#?v3ZFrGWO{+}TO zBSRQdJqsV32m>F3frB(ND+?n#FBc~_C%*s-7b7nd4-YRFI|~~J9|ME^5hF<61C=X` z;+CL-MOB UxHe;XEzvdrnHZ0ii>0i4nW4kt+$iLG3ZSwzi#W806 zn-<6DCciD8u^Z%@42H)nnk ^I9XYFc$wKb*aIRN_&FFk81!Qy86T;b zR}@tgR2Eb;RTMO4>SdhsFQxlm8sn^PX7R3nVT`M~7}qc`GT8t7!I;bNj)8+ga4RbV zCl@ONgFZN}EDa4*#o54NW6l`IFO_6kEA7bpPCVt`_o56&qZkH8h7iV^O!iEL49pCI zTbUSGSr`}@^o_w0W@sR6swf)5_?q$Nj2R4!keZ6&69YGcgabDl0|O5)C$oPfrx*h` zWm|&Mg*iC0LTaQLww6|oPL{^bS *Opb!vYU|_lpPFKR)nHX4DnVFe{U{Psk zz{se~$aMSf{bit(1kQ#`3~B%0u;_wwp*VwvgNP6V4;urUqy(p^cO)k}GrxBvvm_*+ zX@eaN%~}FX%1TUv0z$?{LX2Qi5G=x?`~KUTZ|}am`~K(Qv?a@?PF=QS8q?i>t^c|h z>lup}>llmv)%~0F@8G|?jOvWmjMj{*|L!v|GVEbsU^>mZje(0n#X*3Rfsuoen}>yy zot=@BjZuODRF>%rSwgKhHZ)LF7Bn$uV`EomG-f)zh(pxmN(5tb4~tM!iKyP1B1UZn zMurpy1{PJ;(+rXf+799pjO@Y;!hDP@Qqlrk-jM?Aj1oSPAcH~02gqBHG-zm`sA$S) z#{?+_+0{YiqNoTHJEN!~i|QYH#>Nc=7JYTmi&G*_y*u}6&b5CBm_GC~HgB3SA-J^D z#=mgI BoP&XZks |BE0kz9yS1~o*$?Gbp%ZD_y p_P(?pn#&Xsj(5XfMZuT z1G$&^-P{QqR{gv8?`z4y==kdijMe|*CQMp=Vsc%=s@#yM*G*U7u*CIjlH(BDGU?(& zWu1BYhK!nwT>aDAva3wo3fuz^gW4e43=Aw;tos0sl5I225nW`$v=US^t&<`@dzN67v5W7FpJ93{nhM4)XlWk|G=; z;>;{E(hP#$kqiuMqM{N$k)mvDJl>IP7(oZE1`G`tjaWoQ7?hR3)wq$FseqyqySgx_ z>0-vD`<#*U-~E5zo7^s>ZaDm}Gk$)UIn(vO+f|F1Yo8u}{O_Ot0$t-}a~X|f&6(EB z{O2hS3YpUXZ&>tMPcw)ys4%!VC`$=)D=IQDun97&sq%31$@)fe^YMu=i->ziibyhZ z_(n3b!!r*kS}nni4@89wDz6z7jl@OOl$Ds6jX^OeD#E0!#J~!RS2j@*F|krce@2f& zN8jYvKX`cZ-W`4X^UC9Y-xy!_&7CwcYFcE#WTryK^^B{9`z8O~{H6yE9Mz{bme3 zv3cdhWnN8g>65+kfMVoaS0~>?9g8(BdD+3cVI|nn9Pb4!N11Jt*`M}UXkx|i9 zQIwHUmMQMfW9GAeSFy^@Z2fhfff>{$VbKKpUWq}E!Ph}koL@~uhgp`BlbKCWgr7~{ zfK5 SBg9Ow`%f7?nVwAr7w7nLJLsYMZ=i+P}N6{xBMKRlF`=K7H!4IftgUF #0hd?(Q&~IlUq!rp3voD<-uZ6t`Lb z-+;>uaRyZeM+YToK_xCO1_l;EW;Jy_Ip0Wb5k5X95fKURND+1>cHc-Q)MO`Q2@64x zj|>eM1r$Na&e%vuSQ*^3Ff|b}HWCwpc*58S> SVaPE<0983f+ogBXthdNwG&d^!o0h^ z9aP0If$PFnmJ|jK1|bF|2LVQ2URF*{5n*lt|442DaK(sX3%q&+^`#(IASz1H@@gkb zD_cZ;$@Cs%jT@-^%wYJ$qRDcTfsH}bfs=)Sm63&=gPDOrAJj(!H561CO&LWQ8Cf*{ z*fSse^NE@76XUWZ#`{zM_5Rxd3X^iigDjHFkHNi0EeBC{CKd)J22KtZ)__PBMuvb$ zenv({2K`uV1_^yo#DP0Ipspk+>6 Vsl&aw+J9Q2_1<&)uc-Ufr8;hc_g~FQr-u}m1f}Jx8n!3zS?!shoEKIU zulVmev{TE(Q2F1DZ3pWX22%!m23H3qWj!-KYjzQ71_n(LK0Q_kM+plH7F!NMZb5EA zZFwVRHEl$vQpnN>Tmit!Ib!5CP`{by#))m3HFHq9Wirj7ddViG@j2fI-p7T$D{& zfk99}NJY_zMTk+v*hp9y)GuM%5wRd6tc;sOV`|#8m2VEXtZ|iWjV_w<@BPO6|F*4T zbYj%M^yt8se^dYEUt{cHWVp`gzy0^O!{>$gJVKi$Gu`Za)07byo^*D@ng5bv|Fpz5 zU0<`6QDE}Ue=8sUJO6J-SZfGlKVupr3*)r=TmNnTx8xtAB_pfI76vBJKnJS;>mCMn z20jK$2RSA_ZVpCPP|9LvVP_HG=VS7W {!iS%Lb_h6akD zem|ozBO|k#IvYDPOTxCl+qSG>a_VQY|ChQ-QCUoL4dcas`mB3?xiDoh9u2EaDE?>7 zz|3%xfq`Wo>ox{H25|-j22TeyVHQ~~eo09_E*5q^7DXivehGdFuSkA=J_bHXuSh;2 z263-Q24<9s2b_}N?FmSH2rDYEfVx1UEP^5|%8J5@kUB?MP!P&G$vBr$nUQ5;H>2;r z@9+LGtzwkeGH=6yUyOzuH*ep~x^3N_rj?Rhmj7-axx?&I*jAnUcjDi>os&8mK)I#% z{~MO`;JQN1L4=u4gp-e1TtbM!Gg63!o6R$lTNv&>aBXd9z^KF^D8OJ0i+&R!6=NfD zb~R?}kN;jW^8TE8WXAn}d;cw5$!LG$*&2^!tj7QL|9koG;lJbNQ!N=g7&9L+hU^HL z (}8*0574DE! zuc#~t>hKv0Dhe92%CGwKZ`CShrd3QE{sypa`#Xs#kAZ>lf5N|d#td+;Sk{4;n}dUo zpOcw^)iaVo2@-3dt_Y~j!VIb5%}fQ21x46+;>_HAlhaon_*XAIQKX`fb=$9qeHTF? z3d+GR!7kQv5CyrGm5G}R pt7K`AfquO%gYmgU(Z~%ifQ`E zzgw7&cKkiey6vwv)78JNknjMf1y%-m2R>#71{OAUMrH U4D1YoLfp*k{2a`T>>!K5
15_%!tGc^(CZC098c{iV6(K{an_4|K|QX0xFIe6Bu0>tr?Sk z7yteE`N#K9pZ_x5{kQO69%DCS24gB?=f8q~3;tdIcb3tJQHxQX(eU3{1}0GMWdWrx zP6i imc6K4JNOopW$b%YFu)GB+sX!sXD8ix$?pi4@ zf@@DTWo4F(e;5BfTeFI>nn~p^_smD9_MP0v!gKHa--E2%{z^|;II#~L3jda{f p5j`=SECi zOpJx;--{3b{`5p$$yl| dFw z9||gIK;vJcicHgpAN1GcKv_DD#^NyL6AY!K~R8^i=B~OSV)kAnT6Lg zk_DPwjNzHn(11};1e8<2#i1A|gauh8|6Tg` tVIhM7#XA)7}$MSw=qb9Mw2ADq@_gp_*gk51i6?Q zIUz-gF?4Q&(b5v+dqy=-gA8Q4FvxVMCBnw6M_2s2DQdzfX7KOPihtExo*Rfsn=-O( zVH7q{6gGXfh554MF_D(Je~ekT{i)nHYx{cU%0Kn95BHs6ZUw~x2Ll7kG;pb}>>$L# z#>&mbz{bnN#K8h8@wbhsKO7(+>VC|5vbu@!3D?J&f6md0YQk zGd2IMW%|Rk>+fDB=f6H6^BEWzSgOIT90dn{7FH%!c4kg423AIPW>D=1DmbAHKG49Q zF(~;n8Z+54w(a;QvF4xTE~eW|xBoQ$J;|ic+|Izr5X8X10xFF;K_g}yjLhr|>@18d zT->ZIo{_A~j2tL!7I2ddR1kqi%)p}siY)ejgqb=13NY{g^MhHgm1V}v*58>7j10^E z?P1CXr$q$^J~l?s_$LP^D-$ytqdKUijFNm5A?*^T{D0<*C;#1NRQ$Jx?N{$FMUZbn z!!LKCBb&^O=p&oLjLM9=n3R|OeE>-eARWg4_Avf|+04wq$i~3N!O6g`&ddUul(R>+ zSzHm60|XU~85RHCXFU1Od>iZi-k+d$6@wv@I*T&vK?XJk2?tIFW_Au%CI%KJ27P@* z$`l4CO-4h;P05URXZ-8=w~0xedDfpark8)k85sZn`FE6g)_)K12r#H0%g6*80S1kU z85)3wT9{}3Nn)P&?`SgvBV#AaO=clBMFvg=;cW~o++1u8j12lxXTf<4G$*O1rfzC1 zCMM1-l;^lf TTL05Kx2-~XIY{^DWAdJK~;%^TTV_=Qiy|DRgIB} zTh2X_iJO~2RFvN>QdEFd$}N&rj6oDM`3V^r097-X15}Ez{+fWQIe26gI?^aAsA^ DRKlYu3Eg4NqAR&Rp(mcz=MfL_Wt9Wy>Z#G^|^D>Jy*
QK}{$Tc4ISf zZ~+S{l}yx_*4=pj@mlsq8y@BIWgHwOXI4#{wQA hG$(9UE48{N7u$Z%nFbFZoGdMXY%Cd 1xj|X{twuBupko>6;%`y z19u47n3dSr6;0Hb>OcQ`&d7S_+=sustUFh1TpKZWPRm?ACfgnnMh!-GMovbHe;@B< z9z5YSMNN_U>6#gHmxE$WmVtqpk4>II1T^jRm3MvaKo0^*nD1y4Bpz2ywk!jw)m&=YEVN47!)lP7U zi((XKKK{p&`M9@@E#nmBR{7d4chF3y`acmCLzWl@Ee3N2M}|BHJ8ek|RUUa;RzqP% zVOAzBB^h~DNiHX68GciKQ@2PCYxhVlB@Qiq4t@?XEiESZNG*O1d-q5U0Wm%INHJ+> z>W8E=21`o`(4d&5r8YP}K+AkkJ^*1u14dEUpsG4(_|(`KG|&o~MgR>Yf!iPEVA9w~ z95D;5ti%SMC^j`wR$^l@JT0Sh=YXMhmRHoFP5b}d`S;@b%Z3syO9$7`>>J0zT>lA9 z>$!L`Z^rS$x~Qg-&)-VxSUfT%lndP#9^kMD)zzNXzit15Nhy=0gab@%qO_D( wN}h27U%*2SL!tFC(LXATPT|Brg*` zBPeB@10_>Pk^t4$g2v29({YSp%a(1i(9*E7($KPC@nF=nG&QrZFgIslVERAtp9t7& z9tH^p9S1RCZf;%<4oN970gp&A2TpzlR<}q7Xsk-1SR#zn!4yEYO*`J&)!EMzVdFm$ z$##*@aF##61^(?}kA+zd2~$}%5e5MUGY4q_J}xe9ZVomURt^?HAsz+>0RavUW_E5- zW_|{HP>Vs|QqUMwn2K8pf+v(g3t`O6L9G&HXm3=6oiRi^f-&jnKNrT^C;!#fE?L6C zWN4erSpHA z&!Yj$^7AY?&Ak5FnAOuPW z#`gB$>?$Z^DWPu+s?i~19H2B|XrPEl5Qvl^Zfs^|tN x zc4-d%)me;;{v3w8%6IK#I@#R3Y3JWLAbR)JzXw@7rUgXp3QO6436x?WA?v~t!yv+7 z=pex$0!pqToMNIpoB{$YZjk~4T>Q8aD~i*M;cfw?)HMr0&M@9lc m zY$>EBNMea$0Hq`i2N7WgUUmj{DM>Cdw@5AlW&xDKAGsH+tfa # EdNfs{g+HyWGXdw3d;FQTyMyf4?u?xqbQKO;9Pn z;hzXgDcIMF4E7ERlH4rp>>Tn!970OUEZhwI{Gx7=`~sZ(9MTLjh+-bPzzWnAf^}j+ zIhIvPO&v0X01X6Ctqm&bMM2dhXl9dX9jKD#S=KJj;jk+2*4vNoR!n1>wW@F7LJ|L< zgs^|MY$ns90{&h5_Zd`aGe6#STJ`zyXQ$5I2GvLZL|DwhVFhVb2*`>tOG!xzNGd2w zNsIH#%E^iG3JbHlMG6b>GPp(Z;;S~n6_cuw8KemWDo4ek^WWme=FqVZHYUGiGg#-W zoHTo8?Ui>Qu05>elxSYY&0Ac)>9XpxQ;*M|zuC>G#mLRb%c!?%+RrnLS5w@UL()C7 zI!g?L0E3o;2rnlm7Z;PD5H~+B1D7bcWPrAIge*ZB6B2x&7LGVOXiyw9{sIm>rX`+M z;w~nwdzW We`Dd_Ei4|Vvr8a7SLR}t7zPyvGY1(+&y`ge-g9+}6cb=$ z=7gsoeMkWdiZDoy0#89BCQ?n*)S;_rpw18x19e@Qi=UjkRpq}mq^T!=;mk(+Tl+Sy zv|id)R-v2Hces;Ld*(9NSsHr5p$^e@mZ3S`ZL>YvUA!E1>?};(ConKF$p3%Cw1U-* zK@8Np77!8P sYB6q5O#m7h0k!`@B@QG;K=w0&TX*2m zKxJ5~S&-4Es%g#qWt|e7##>8H9B1+PYuVPa=PHxK?;Vo^!}eWdIt#KH)R$vo@c^&= z;9}(B;brAy 6uOc5?DO`mOXJX zW%Ok-_zmh$i!(5=DYJMm$S~+Qh;T~F%885d@iMdUvT#TVaq=^8fQn9gV`E4!7_{sS zwB`>qL7}Fu%*M{F%xEkm1S*xxg&|g&2^%xA=${le;n0vff8gKMby@iw9NdONC$}82 z5aD1^-oNJGMW$x!i|gb6*0Xs0Oa2$&ZMoHysq}C6no^Z%Om}|o0Qpg!fq{hs>_;UB zL3UO)21ZuU5*J1eZc!FSeo(Crb`faS2-0JPR6C5yj641{?`2eERM`EmnQ`Kte`o)l z+QVeXB>pdkamnA8f6p`S`RBsG$iVbZgoTa8gFzHD>cPp+z$_-t!pp!b &= Id1n*UkKQ zE-{*^H!|bj#hI&_dYEF;7&VJ3L_o2U!4$xv$sEEU&Y H$t=Xr#>Nm3 z$;P0s%^)CgME?jVN XpP*+o93^J`ZOPSzO80nOxZ`NRz z*5^^@;T^?v(%Ec!u8f+Dw1efeJVgZs2~g{C#=maHPL? 9i0R{$U z9`Gt-0cIsoM;l|6v9PhQvapD<5@>;`v60wq&?4hX8ykB|V>hNXzwfj9w7WaldpIyK zGVJ^RhWR#lR>sajPK<+>nVE})K|p|ogIPval9frAi Wa1VRW|foY zk&u+|i{zA)6lM?>@QD =`1L2 zsu|f>#90 7at!xKbw#cGlQ@wzp%V86TdLOFb|_B8xLsB z4ye4;*N+txvNQs%%*HzD&S=aC3P(^?2wvuBs%WanXw2lnSiSCF0wdeM!;H574lnt4 zgwf{T5k`i8shgPYGhP4luYOhizb}kD^{eWcnHZRmN(0cCvH%ks8#6mQ2PX?N3o|>U zSOBdihcxTKZ6)x85VV8 s=`u`8+X>76#h77h0&JJou48m+$au$-3 zay)Dd%4~M_qRiH2GP-PvoQmr1k&2?60`8HV4A360pb!Ii5(<)@VG(8q>%W4MGPq47 z0?Nyvqyt$Dfjr=@$7C)dCN2(&MRhi|?l3>Ekn>05<|mpawfmHmRL|NqEy!!;t1Gt} z{0;@R^%~?9_q91qYFTdBIeDi`D6?i*r>aJNXl1vodb7H;wS!HFxy$tIs9w#$Nj)N@sxa(P2X`4h9CHZFB3J2r|7g!3XFfcKivhp!!uwG{1W)NqPbKqfP;1OaFl91$Q z7Ug8nH%8qcfYhl}MPf6WnyRXqo2#jsGS-@^s+yats+zJEn&|177#SKGL21xvL(2a* zY%Y+QcLp;CHwSffab|vfd3g~|esNZF3pO4GA$}ft9wr7M0U;(HAs!)VZDnC&X%<)w z4A~U`U6Bh4WN #Ijq(o)$P!QyZal;taAQLKfxGx3WN{+TXf<4zeR^L*D>m@S;wfge%-(88`k~1 zww^_K-K-UhbZbOpKHj+a&8E!w-wu%a6aN;SI{I(fh4YM2$JYM4ymlR<<_4%XP(F8K z7GWx6J Yl6YW5K78s6lY{)XH Oo5MW^76Xp}*5at(>7h+-%77%6<5*8B1nZhjfL8U&Z$p=YjAQCjy16_*3D#FGt ztjr9a>NFA~l-QUq=L95%#bz;X3iW-r;(YrQM%HDsf8Pfu9mcp*IFlRGuV4pn$Buw% zxqqdMN&n_CS2z7T3f6@u!7={-#U{eS#x@z}xIPQp?=LJoY$EjxObqqRF--4RPlNmI ziVl3d48ovQ)=~mo2gH~`8Bgkr5vcG4kGX&!eq#ifA( z#f3q^#VnJXwry<==?~e`98^>o98y>WioXm7M$k%lCN%~=1_lOp#v}%Yzo1^AA(IzV zFY8GL3D6i9yRbMTBcCt>i `jtfotkH~Opctqj&TQ*SH`ojL?1I3S2d%E{HB6{>0L`17#S3qEWvgvGgvw(@w2ir zFbm0Xv8kvE#R}&NGx-QJatlifGYJcGNGZzmawviwA!Ml^8ygGDoZ!wUJG?MAHZnC) z#$5Z%R2<~8VaJLYC6lW!KQAp$PY!T)DzI?1?}_V}%kn%RU0i=rL%&6}%c6 |r*zgi )7cI)M^CXn0KkG$jEV zgI5O)%frS)7{mUpp1-U^iYx!=v 9|19uu |I|#9`GBPl*ax)7Ea&WLR^RqI-HhMwUOhWS(C^IPwf*QG|0%pdFED1$di|s;g zoWAvfN%!9$whcc6n2m*g{bpqO2ilXs 90jVk9JtsR7 p2%bS!aNuKNU}omx;pXIIR$yb01NHkXK_LL@sDlC=GF-#P#;(p7)ZfY`X4dY^ zy6w*tCg 9qy0&PEIV~~*KhtwaUoS^#SNGx)WWdu1I zRBsp?iLoLz6Pfm{o;GRais>_!{{0u7kr@+{nI6NEzhwKag^RatTU1q5RaIS9#=yi7 zz);G(0$M{TI|y=fvM@0)vompVvSF(qK;2_?Mn-c+Rz@(zyyD-AQbzfI4@(&%{*@xw zOg#TG-5KK<<6Qq`N+X#6|1)?qFtA=^-NvBK7|Ybmz`&r!Q1zdIk)1J _> z{A2)|eV+khYA)1NZIDv%c)Rhxa^?`0I0jh;4bW<94iPzJMj3t?Wf2xlEe2Ue9%flt zK{Yi6|420fL2l1TL1jc6&{9G_J}wqsPJvP(BdE0p-rxo5!ij@Zw}~B-xtW=%DN_H6 z$=?(qLVWHl&zF_RjD3dna1PX7-v1~G7KW)fgkW6(CmqN0?c6{11{ zhYCgq24#o}SyUDJFk2Yi8CW1H(9BSUsbI)tFlP*6;M&5>qyjbs;Sx588U}VoR|ZRl zMh2$;{}>q98d #CT4AgL&2u!pD+#i4@H;s0-l3Mo_-piqU_lF1+h4pk;b zMX(akJi9&v1FHvkeUdmsxPy_Ps3->uF9U-x3mc1sqzD^3n`fjTKf68qdiL||_t{yw znc3M{I5`EpA~~5^L{V0sf+j!X V~U{C-5&w#F?l#vUf0un5kDi|FYwm?* @9H54an4F>{ucBupzYqs2 z3$p+(3$vtWBpb6ZbjjQ?A#u=N5YXH^tX6|80B00nP=t&m2!htRgGU&c1=JbEz`N+g z8P$}Pm{`Fp3tvx3t;pE~T3dK=Zem5&fh~D|6N)!{{_#gWm1%C)3fLmUlwSW?#~5=# z>kQ-C1E!q%H O>D!~ZudbJ*k=3_vxLrWUKLGJ`k|4}+|r7OSC=kdT(1o|;>v9)p%Bc(w~V9RV3! zM%k?aPKhFrB{I+<3D^{hsR?xPAE=5lQBzg`P2_-^QY@1F5f;+!em!Z2Ht)%{pW|^o zIIljz)!o~nqyFEUWq0nKd&u&sz1YoJQ9;YnIDT&Gg4M3G)b+P{XS)SehuCMCT1J@$ zgx>${!?J19?z#W}GsOLW!+e%akx7lgnSp_kjlrpp0aRP52|{Ztboo+ku)H1)c}9mD zV0mK{dDgoy^BLXug5^>5vtEYDGh{wu0M}AhU;{v@0^)*Jm `S7*qreEF>ja3@jxi jb=7s51(p9p9*5|bK(E2O4mbOYCvp!GsR;23v@ z#Q0WljL3k4^Z$SFdLm@`Qa`Y~3J!Tjhwor{O%!=>j6%%M Q6U6V z@rHqcNgnJDh%K4_wljcS!o(;ERsyQ43;(}iNn%rE0PVW+a?n;}#WU8 ztgMYqY+*E0Z&%m97dE!`R+bL-Okbc324;rh|9e=HSlXfWy|;s=xV(&r2$u|6WiROl zuj~bw72P7)Kx^!dfhP8_R`%d&B~SuU2TdD*cQt`i2CE3Tl4oNz0ayBF;Pn@?3POtF z8X0-s%_<2gO|1X3*Oc+1chTZyOw*13>AM#$UI|%^;acj~JcE$|v>?N$+^2QM&( zZKl_*EJ^uWYkV7&GM0d5ufZ`N4vG0ApqOD4gT)-Wd}$$A9-ea0 ^ zF+Wog6thf>T3`jBavVGZ3wA*?*af8!6Xd}P7?>EqYe&HAyObF09OM<)q(uY-1la^t zl!X`srFq0SnK|X$B00sF#oZ#Aq01h?i`p!amw1Ac03*0O1~2kdR~83vLopT 0U{r_G0_Z76vWucO< z{XbI CxZ(Q6|NkNL$zcCNe3Q8q;$LmBcR>DSU|?VsVA}%eT`{sV zIzjszOpw;he`wvyngp-eoW6p>lnGL?!F!74a1)#vw88BmCVp^x2yVhtxCzcrz$U1I z9RV`opA)Mf>mG;+4D5_<%nQJEK-@nk76z6mh(3@wQ$JW7ye|*j0#gH*%%zMRU>h0X z?KtonS8y!4LSoScWFw;j%tka57#$eaKul1Dxf#s_Mh9>i#0YmY!~{b~508 HjoO&07%)7(-n>k2O(xCA-iHB!XJ!|pTYismvrD=g5Z!)2ZaQqBSQq( z1SWVNy!ihc)zk155crmZ0GdaBD>Zv;+uL2^kB5mTedt8lbG@RWk+6?wJ~k8jC99S<}n?&w=s4 zvN?0GF6<5VY;D}Jqw%*YVr}n#XwMp)(!srAMkjEum}4^|Je`7kE(A{L;5GrH6Bj77 znc$@hr1uU==@1i~83e$g&B%ikUo1kbpp*_V!TC4X1Zh~hgO~t{4TuRYjQNbF3>=#o z;k|o^8$hWBWCA;*3u6d4)iA+ZHYfi%f%eKVsWE7PVv^B~sTiCqivK0D2(iR4sWG@i z#nTw%Vg3fE5s00RFjtGCxEh?wKz6b*Izn6xZ(W1>&9-0@w7{v%f#D^@-&!yez^jA7 zCU}DK8>7Q1kiVG&VE%@f017#X3C@fR5EEojOaO-*#02MaU=y5BO#tOLhzTxCDG(Fj zITq{&aDD@s017#IaL6%x!_D|-4cZmTq{g5H4xw~Lb#Tambt1Vw16*1#yTSBI;^Iz)gPe zu76`uMOeN8?+;ZrWi$nCm1JgGb>akQm*`2B(m(%L_Wk(r<45oB^Pv5rEQY=Rx?a9y zU}O*ht%6_^VUS~pbTASV5#Zq9 ~_47BmZqTk3 cRt2-NK-X7;X23vgYtZg%(5he-kN^J}7X1@p&SAaJpavSVW?*OVVPXTN zJJ23orc`jc(t)I!f6&oDSgruAx@PhRrA)?DsEUOUA0SGUf7UE7S+_BPPL>1BlBF|r zg3M;v@Xwk>7F_Q8g58}CO1cc7bSll_#=y&;y#`&Cbrj%*e#b!OF+a#LLRY!ok4F z&jMXq2RbniHueLaasZvY1li3i!p^LJ{$CQ~?vwwfw{P0S!{YW&?(ZDNABF!aS=|0T zT~>%v>ga)7&QSV48Iot V?kAM zRzXG4lbOzIRxxTi|C_?p%J|2+D3w|6uiLuH%l~X ^&Ie=S z<=|mp;$-GvV`F4zWM^dMVqgU=@r2t2+NdQ2TlLRqE)I%Du$7EqogNpL<+*KH#+(pW z(EHb(Da50&oLK=B&Y)7y9-KPCBd!dkj7*RiL@o6|sgnWRk7FpU1eMv0i2CxMHE4G< zlNtl)+)sAKbcVCwm;$9u7jR7lY7wz9riWp%kI|7~8N@zVZUDstiwnX&Mn`$Deem`a z==f348C>cN0SxWndMM-tIEH1wDFC!z`(HiCMGTS*77j83yu5rIA|m4AOnl5z(n4H( zqWsK!LdcusK|N>C+Eq|l3z~WaFS@!u 8)%RMz+_@XYH&G z)SZ0ySDk~=D{f(U#EjpHGnOx&IeY1{sVwgG6M2{vT0~x#lrtJKaxii->Xa81RQ~(^ zDDz*rvicNt1t!bOx36Bjd-cYRCG+Mjfz%u<=fUxB0;v~Z^EmS0=!cZY% 4y%Xx5}XacD>YC+{o!ar-!9t9>f22hKdoiY92dvIxVl7Ru)K1N3d zNKcLlKBLpbz<^{QJEP -AILrUD z2JL`gQey~%h&F*l!Tb5a{xbuG5ToP&I}qF8^EdMU-!NSV*Un*}5MoSA0+-!V;M$pi zkpXN1c)r&jT-!46uyFG5F|i5=a`Ex=c}24G^E2}Zu`w|-fsRuEojA#03_e;F+#ZEa zwS((hMnTYiZ^%w<(1KFNhyTE51~8iaJGJWHab|tS57Vdr6X^XL$F!)I0kNk;hQY@{ zQ(RbDnumi)RFZ>>k4;vNPfS?MJyME`o69{?oSU0X&_S4=jRCyP7ra_V>a38ZKG+3F zc@Na}H8fBKofE{StRM(p7p!b*4&CknTI(vpBpzy`kfrtSL~rk0=#~#g-G7%$Me6wk z7-Rl5GBSX-ef(!={Bjo*zL423(7Ysg=8DmsAs-UH@RA!mHU@4lgU7xY-N7T`h}s%# z0=VplnBc@P5n=+oZwfI1G!g_c!ATOF9wfkpE_? ODc;8lNv)f zC_gc}F?|7xgU@UL+iD3;;SQifghB0D4Oo5x?`Z*-0g(_DpmV`NDj>sgpph`pc?e)L zz;la?4*wrOLJ;0pM;o7HbXW_H1Ni6wXsKg0JE+?UHi04Y|3|3b<-pDW`3!tc0@xNC zkUJPk8Jr-t!1E$_R|wdpF`&H2Q2GLFi!dynASQr%<6sjQT^L=knE>uLflOd$bYZv; zDKpW`0IflRnBn;UEyN6XdP8#sqhlM`6_C^dYCkbBFx_U8XW(Iwb>L%QWaMSzW8rj* zWPxnZ)D~m}?{5O{b4BcUW(-@#n4XZL9qySJ$uftrS+!NJx(d{%JN!?CMV$3Cg93w# zgNl@#91n+(3 7Dp*$VC_WEMlSGS z&Eo8k?QWoMub>DUqlc@5n{B-By?cwcg;t1(rr9I~FiQP hB3GHa$b{xRRNo`D6taz})H6@vhS1Ow;<%R~ngK~@$CHW79fW_Au`22E}bZcZf; zCI(q%P6K^4J{=}b2{zA2P7yu{B|ZrWJ|$)j85R!E86x_S{yON8bWqU*TFVFP?}3^) zAaOzPEPx0*Blx^`Mm4ZE#DqbIzp=413&WQc3Pbj$f)|Z3XJjymtlRK+-m-N}B3V_* zDYep@B=z|WI5*BPOcOt?u)-<-vXil~lZ~aR1M9R_i{I*u6aVG2TsP@REh_zE#<;<1 zqCvaFzYvyI>#14-dJA>`eYLc)u{5`}Wncx(p)#LkU&X-5pu(WfV9nspknUivE97Km zW1uN2%B`ZpX24)3E5z>M$;ruLZ|Q0*$ju|F=^n`=D$1{*An6vVAi%Hc7RfKhCCb8} zZx1e^p%Dl^HbTPQ_=u4pc&Qzz%?}C_Fb17jXl7=v2wFtR2-_D2J52&~gdupbEAnv? z$gAntk%EWux?YwTBa7&g21X`D=z$ZGTgqD#mb;sWTu8lm5_aMQ -yUVo1rr*b2_I%&xGU4_;jYHe)JOg(TPvX2{|e z@a)|l&}=1>8pBpd*#VxNW7dV4koC_Rshz^cn9c^Ozq0;WBehc47}Ixx4~1a4$-ItD zk%13%atS+w06#ZN10QI+ffRJVfuRBT^m8#WaZzQ^2{_7(JoRbf%fxbP%OhGlf{R%` zbTnvcwKj>HH@AVBlZ-sbCbBUI@Ut>DFmp5L8zY%$3_3!`Sd^WOU0F>{osp-wD6=WL z+-ZW|GV!!}Hbt-ECTmHTDdC!R9UwRAvfN~DWle&tp=M+O9jXYP>I3H;MNvgYU8ab? zD_D~{85kK=S#B~ ULXu%0=b8El%K zp{WU68C=U*Zgw^sT1YuK*Y$zQtVOI$;9dFL48q&M$89rnGU$VkS^;%ck=L~@ve1I; z>t`*5ZLDWtV$5f`$*jxP3SK2D=fESx#=`(!CCVwv%%E=!brYyG054yL@9{!T(9F8k zX{i;JDXBF%kZf(@$hs-1yfg_p@qt#*Ix!Y9Ffi~lNH}mYaI)}nG7AVYfi^Ee3r#}< zaS`xA8OlnaQyq-i*fzy!nLFB?5ASaeWuZn=7=(t=-fq7Zm0kEH{}yu-*mlkK^YQ zm5>x {QiNwYID51kH*(XSvC|4RWdtXfFaEt1u^vh$t^h zgD@gFg3feB6pE_w;_ Q^50z;Fb(SCc}B?ss$r(HV3JJv_wGlJESGz z^nVJ(EnHw@V6&6pRz@_qEOGju2rg|H#lePvPIO|t$-IsUa`HR_6ALQ?XqO3SjMWly z@_Z0;4D{srAjX^QcvZ5)R7x@*WOM W<@Y N~0yqy7b;Scm41JGbBX!W}?BUAsM%6?|>ML(?D7#JCn7#LWT z!DrQZJ81BM2Y0yzm{{3ag@i#jK=?&6aPSHGL~`(P@Ue1n@%lt^v9W^oP=Z@9mJ<5< zpmrPx3ql%lkZvY;88hhIA7jX3W>H1P9gHUb&Me!ofm!hHR3?|ddl %$QtN$G8Nvh5la*cpvSbb0WI(ffd*iOgWsQp9t+WQ8l^d5e zo(JuvWoDT5{|$KGfEa@!gBC-AgSnE7jI5ftfQSe$CzBATIJYdDwvM!tccg%@UnH|M zw*ViX0Jk(VtE`-ymUpBao2IgKs44#D36tEQ2`6U;6s`+ORP~P}Kv*pk6+7Kej2T zBVZ1h*cK6kE$~!S5Q8u8Wc5=D l#iuWuA__be1Plz%Z>lseS?^%KYj%|BJ=X!cM}&)1h)(9|G#1BWIfHG#!%>B zFDouDCMLzr#Lh0IBFMreCC;Xleu+$1f)@r!U9M$t)o!At$V) z#NZRD#4gO|6Df>*L_ElwIHM2LOaNstMg`EtO6;QIitw|ql$98ml}(M+P0f`}jfFt# z&DhnLrT-oDXZ-i~3lkIL`G1b8r&rCHIcfFVfA{s4&0u1hu|%CwLDqn1uUBO@!LN@8~y10zEQQ!jXrAT#Kcb m zK&3y(*TV4bp0S_+>}VtKP&@2IBT&1gljSTE=#)6n*?9Q(X$h-yt>BK(k79XhzC2LK zc#j(cBV*w|Yo`4F&ES*uK)N{_Sg`N%DwHv1U%{PZ9`!H8VV9|p@j)jBM#e^#n@shP z7R)v*x
PVp!@$KL!eHYdFT%>r z&dkio#J~hPgN%`tm6KmYUW7?Rgqe?Bo>>8U5>qVFc5YB%1PUldWl?2QMN?2IsAy_p z&d1Eo&a|a=;=eq`8Qr!2KC!V%$jNc9U=}%bDwWCk@7cp`-aP8Mx`N;S{|ChdQ#h!- z0G$nW{I?R)UVse!K*o;Q3fW7U)EM-^Es9dcIgBBo5f=D*m< FMtLIYI$#QnVrI++l@8Y!RQIJhP+{d<+k^nWvY8(Nf^AJj4=stwI# zTAP+yQIVQjmj`J>n>zekmsnAnlvG-l2 *xEwUF56``c?NX`52g*^T};q4%$f^J z!)%AK$oqlh8JIx(AJ|gZM8LOk**M6HvGEHE$}lj9aI>)}DT{D3b8^aiM)Grl_X2>n z0)QqzjzKCHM2!NDLjlkksh}MI3gUK5rpDrIZ0uqvhYRodH}~H$@DaL+jLwX%n~kib zxADm~XC(YC{q^PZx37>xhUfh&gw23l=W$NU6q~(PeZ#+h|E~Qz$7sl?%?LVd_#&wF z4O+z{$?}0gj6n&M`o!4 Dl6x<>OxCEG`5NnTcxg36u6vFAq z+ugtm8X*Tt-Rv~BlG?&AUzz6qXYahV>*vm2zjh8}DI@G)sa}4^ (#n;UcP<% z@}(O$7#Kl25!gWIj(}H&va+)?a*GIZ3(CrHG4e^X3NiDEdq(m>kDfgSI{yGvhJd%t zLPn=WVL=KGU wPAe2QzP{(JcE$G }fGJ~7CG&ZcCP;pOKS7GY=S5|$B`lNS}^5)xw; z;pLF!mt=8^loS&I%^hnqNZ1>LR)&H%(SsUK;Q9 n6)^Zi*fIVg1BMW)jX42*v{Sa$p? zX6t7V1f4Cw#LCLc%fY}P%q_qI+FdCKDlMRcd5j|L?8@rU+auUb&5RZP6))q; ;z22i#xPi||TU(PYg^Pjlul2tL|Cs*QGH^2pZUt=sg)}=sW*Qo>BCWyw zYi$kbBwK&7G&8d_H#Z06dxn1tKxfo4sWDiCY7EA5mO0?u1|Eq8j{`?T#98P4{|g_( zeF#y{$j(^CvI^4gf$#AG>tyR^Qey!3dl<`DvLIy;d Xde2^JzjP70Fo*-nMDrgku-%*y!;MtUOAlETCGHe06T9$!<^$4_#*an)P0o8-x zxf~Isxg2(eN=AFIdij4MNV7TY43!K=!ENo63=E9L;MT3+HU<{ZDkacS$DovKXaGtn zCs%<^u?2-6D+2>#A85pgu?bw_I{mAGga<3E9|&3{Z-b Wste}-VpmA@gEt&r=K*zly+rL07`d2eBFii&SA%m!4bpN*)VhY@z&kPJq z-k>=Us0ycl#~>=$p>F;EoPmKU7&N~PRpI;(vU8G=1*(D}?4J|kBG9ZDNCjxc5O~$f zZUzR%dUiu5HIOK1H7EmQ+ze^w-@kh1#jM*Hz$zHn8FZLH>5Y+rfw7mN3v{{)11kd) zg8+j*1878@kKqRcGXv<>Re1&o3}h&0_|I6)_=YKqshMdV(@kbkW=H0D<`U*<% zR|FIV5(T~r8Ve>0&KJBZ_(jM^s7>g&u#9kl@M7VwBBmlmA~Qvvh>D2%idKs57QHMc zAr>SSB-Si;PFz7ePyCShbBQF08ImHBiIQ_9cS# VdR@w7+zp^giha zGD0$zGDR}0WDdw&m3b}eAsZ>1C0i}qBRfxamFy2W7CBKlH92!RPq~G1o8%75U6Fe# z_f4Kd-b{X>{3iLs@>k@a$$wX{R9K*}LE(VH1%(F+9~GGtdlhFYu2kHqcvA6};%mh} zO1w()O6^M1m6j`QS30hAUFn6gp7IpsCCXcrk0@VNeyU=l;;Ry;lBZIy(yuaGWx2`@ zl@lsARbHz6Qsq{aQPokkRrOO1Q$3`5S@p5%7c~|&Q8hI+BQ-lUf3G$nZ_oK!y1=0 z?rXf(RMm{q%+{>dY}cHmxma_v<`FG!tq`qLtx~Nvt!Y{-w03ICXiw8#uDxCRkoGz4 z``RCL7~y?zqII%$%5)laCh08Fxvy)e>!cf`o2*-`+oC%~cbV>X-IKbv zbf4*d(qq;W(Nonk)pOU2(96)P)a%llt+!h5lD?|`5(8(05Q9{MGJ`gQi3W2FjvBl( zWHHn+^f7ESTw(aoNZ3fl$i&FiD9k9ssLH6@XpYfFqhm&ojTwzOjV+8bjhl@Z8*etg zX8g-U*d)T_n5l*7I@6=37ftV)v6v~FIhiGzEjHU?cGT>e*(0-e=E~;A=Fa8|%)eVW zSWL9oZ*kA!mBlwpc1sCMO-oBlSIa=lSj$YyI?H~`1(q8v4_jWbd}{g4iqT5QN)DVm z7$t8f>o52k&u{aUfs+F?eZp|k)UX@GxT<~T>hD>9uW~NpNMc}M0Nv9I5@ujI3!ax_ zU;*7k2*Sz?Gnh>oT3NLjj9Df#G_y@($Y8zBP{??cp_M6)A&4oC!ImY9A&g}*Lk8nF z279K-3?WR43^SNMFo>{BW=Lb1%&>=LGD8Z>WQI7F$qd>olNnlBCNq>m`57#e8M46Y z6&dV7X0S|VC (>vM*a zOv?WgSY9%mWKm~0$!^JTl8uAmB%3uuEz3nH{fXfuOA134OD=;D>t%*rEQc8w*fSWU zS>`ctuv9ZJuuNnKVp_zojOhb|IMX5qW0o)mL*{A!|FFzs>|`lsh+}!quz~qJLov&9 zhGLdlhGM2|46-aq4C*W|88$GnF)U(Y`#+Jz7|h uy2Ih+l zSxlE1 P%_!2{%XCTO^`OlH`} zG8r5$)hv@4 C$Hn13+@u-P(rv)MA}vDq?6G3PTFgTovY z?r50p&;Ng*cmT%%C?24g^*Dn*EZjk1kBmX_4dR2s9vOqf{xQQzmR}4Tz~KxEYc$Mu zmmv-u_O+0(2ZcWfGqL@@#l*(2l8NpAW+t}(Z&;2p6vD$D6!yq?GD8M9K0slQj5!#X zKyeF-1CW0~Zor^<7-~WGgZzw@2BpX648 Ga1!Jn z*5?ciEH4>EnArYLfcW|U874LcQzkZs4J ED=P1B)ud22eg=Vq;hg zr6Zv EKcM&m*?~^87Beuy#X$M!Dnl_S{y<>}quG-fYS}v(3Ryle zoCKwBD4hVMn?U&ul;&~LxeP+!{D4M-(g!Gy5KDvdF*q-Q@-ZkrLG(n1AW)tHn!NBm>8tz7zTVXUP9p^I0 zg33uy+%Q=)oMf_QU}dsq*vw@8|2TyGe>Ic!|IbX;|DQ8iGlVf&Gwf!GV>k(_YdCuU zu3%u`T=RR@ziQ4!AQ5nN0woxG8JJnNF-~A$Wnf^P#cIsJz$g!)nJ+S=F@EG=V&Z0F zV`gPxVq#gq02;pl?RAD|HgpVjW^iCoP!eL3|0lxG!qLkVp}@d6A0*2HI>mv3L4ko8 z+%AW-33(V;K<6bf_%Jvyurly4crbV})G?f9IM1lgXvCPvn9NwuIEir~;|9hLjQ^RW znar7vG2LRiBgZIbFV`yfS?;HTh=P=YoPv^qnu3*ri$aOQ5{2aoYm|h*{Tl@aPX;fB z1q^2xE--2^8Z#y_rZ7%oT)?=V@dM*8CJDG{403jI4RW94z9 wpd}g z!fKFd{~t42{(toUAj9_mdl?x1Z)cGEzy1Hx|CjzB{a^Lpj)CF7-oL7U+5fU1=RVeb zbn;Q`qn3wu53L_sJv4u4@=)oa
diff --git a/lib/calcms/calendar.pm b/lib/calcms/calendar.pm index 7038713..3a3a60f 100644 --- a/lib/calcms/calendar.pm +++ b/lib/calcms/calendar.pm @@ -184,7 +184,10 @@ sub get{ my $dbh=db::connect($config, $request); - my $used_days=events::get_by_date_range($dbh, $config, $start_date, $end_date); + my $used_days=events::get_by_date_range($dbh, $config, $start_date, $end_date,{ + exclude_projects => 1, + exclude_locations => 1, + }); my $used_day={}; for my $day(@$used_days){ $used_day->{$day->{start_date}}=1; diff --git a/lib/calcms/comments.pm b/lib/calcms/comments.pm index d1078e1..2f3927c 100644 --- a/lib/calcms/comments.pm +++ b/lib/calcms/comments.pm @@ -112,18 +112,18 @@ sub get_query{ my @conditions=(); my $bind_values=[]; - #exclude comments from config filter/exclude_locations + #exclude comments from config filter/locations_to_exclude if ( (defined $config->{filter}) - && (defined $config->{filter}->{exclude_locations}) + && (defined $config->{filter}->{locations_to_exclude}) ){ - my @exclude_locations=split(/[,\s]+/,$config->{filter}->{exclude_locations}); - my $exclude_locations=join(', ',map {'?'} @exclude_locations); + my @locations_to_exclude=split(/[,\s]+/,$config->{filter}->{locations_to_exclude}); + my $locations_to_exclude=join(', ',map {'?'} @locations_to_exclude); $from.=',calcms_events e'; push @conditions,'e.id=c.event_id'; - push @conditions,'e.location not in ('.$exclude_locations.')'; - for my $location (@exclude_locations){ + push @conditions,'e.location not in ('.$locations_to_exclude.')'; + for my $location (@locations_to_exclude){ push @$bind_values, $location; } } diff --git a/lib/calcms/eventOps.pm b/lib/calcms/eventOps.pm index 2039d6d..12ae8dc 100644 --- a/lib/calcms/eventOps.pm +++ b/lib/calcms/eventOps.pm @@ -2,9 +2,14 @@ package eventOps; use warnings "all"; use strict; +use uac; +use events; use series; use series_dates; use time; +use studios; +use series_events; +use user_stats; require Exporter; our @ISA = qw(Exporter); @@ -52,10 +57,11 @@ sub setAttributesFromSeriesTemplate{ } sub setAttributesFromSchedule{ - my $config=shift; - my $params=shift; - my $event=shift; + my $config = shift; + my $params = shift; + my $event = shift; + #print 'setAttributesFromSchedule:'.Dumper($params); #set attributes from schedule my $schedules=series_dates::get( $config, { @@ -134,3 +140,189 @@ sub getRecurrenceBaseId{ return $event->{event_id}; } +# get a new event for given series +sub getNewEvent{ + my $config = shift; + my $params = shift; + my $action = shift; + + + # check for missing parameters + my $required_fields = [ 'project_id', 'studio_id', 'series_id' ]; + push @$required_fields, 'start_date' if ( $action eq 'show_new_event_from_schedule' ); + + my $event = {}; + for my $attr (@$required_fields) { + unless ( defined $params->{$attr} ) { + uac::print_error( "missing " . $attr ); + return undef; + } + $event->{$attr} = $params->{$attr}; + } + + my $serie = eventOps::setAttributesFromSeriesTemplate( $config, $params, $event ); + + #print Dumper($params); + if ( $action eq 'show_new_event_from_schedule' ) { + eventOps::setAttributesFromSchedule( $config, $params, $event ); + } else { + eventOps::setAttributesForCurrentTime( $serie, $event ); + } + + if ( defined $params->{source_event_id} ) { + + #overwrite by existing event (rerun) + eventOps::setAttributesFromOtherEvent( $config, $params, $event ); + } + + $event = events::calc_dates( $config, $event ); + + if ( $serie->{has_single_events} eq '1' ) { + $event->{has_single_events} = 1; + $event->{series_name} = undef; + $event->{episode} = undef; + } + + #get next episode + $event->{episode} = series::get_next_episode( + $config, + { + project_id => $params->{project_id}, + studio_id => $params->{studio_id}, + series_id => $params->{series_id}, + } + ); + delete $event->{episode} if $event->{episode} == 0; + + $event->{disable_event_sync} = 1; + $event->{published} = 1; + $event->{new_event} = 1; + + return $event; +} + +# add user, action +sub createEvent{ + my $request = shift; + my $event = shift; + my $action = shift; + + my $config = $request->{config}; + my $permissions = $request->{permissions}; + my $user = $request->{user}; + + my $checklist = [ 'studio', 'user', 'create_events', 'studio_timeslots' ]; + if ( $action eq 'create_event_from_schedule' ) { + push @$checklist, 'schedule' if $action eq 'create_event_from_schedule'; + } + #use Data::Dumper; + #print Dumper($checklist); + #print Dumper($request); + #print Dumper($event); + + my $start = $event->{start_date}, + my $end = time::add_minutes_to_datetime( $event->{start_date}, $event->{duration} ); + #print Dumper($start); + #print Dumper($end); + + my $result = series_events::check_permission( + $request, + { + permission => 'create_event,create_event_of_series', + check_for => $checklist, + project_id => $event->{project_id}, + studio_id => $event->{studio_id}, + series_id => $event->{series_id}, + start_date => $event->{start_date}, + draft => $event->{draft}, + start => $start, + end => $end, + } + ); + + #print Dumper(" start_date => $event->{start_date}"); + unless ( $result eq '1' ) { + uac::print_error($result); + return undef; + } + + #get series name from series + my $series = series::get( + $config, + { + project_id => $event->{project_id}, + studio_id => $event->{studio_id}, + series_id => $event->{series_id}, + } + ); + if ( @$series != 1 ) { + uac::print_error("series not found"); + return undef; + } + my $serie = $series->[0]; + + #get studio location from studios + my $studios = studios::get( + $config, + { + project_id => $event->{project_id}, + studio_id => $event->{studio_id} + } + ); + unless ( defined $studios ) { + uac::print_error("studio not found"); + return undef; + } + unless ( @$studios == 1 ) { + uac::print_error("studio not found"); + return undef; + } + my $studio = $studios->[0]; + + $config->{access}->{write} = 1; + + #insert event content and save history + my $event_id = series_events::insert_event( + $config, + { + project_id => $event->{project_id}, + studio => $studio, + serie => $serie, + event => $event, + user => $user + } + ); + uac::print_error("could not insert event") if $event_id <= 0; + + #assign event to series + $result = series::assign_event( + $config, + { + project_id => $event->{project_id}, + studio_id => $event->{studio_id}, + series_id => $event->{series_id}, + event_id => $event_id + } + ); + uac::print_error("could not assign event") unless defined $result; + + #update recurrences + $event->{event_id} = $event_id; + series::update_recurring_events( $config, $event ); + + # update user stats + user_stats::increase( + $config, + 'create_events', + { + project_id => $event->{project_id}, + studio_id => $event->{studio_id}, + series_id => $event->{series_id}, + user => $user + } + ); + + return $event_id; +} + +return 1; diff --git a/lib/calcms/events.pm b/lib/calcms/events.pm index 9ebb864..2dc14e7 100644 --- a/lib/calcms/events.pm +++ b/lib/calcms/events.pm @@ -232,7 +232,7 @@ sub modify_results { $result->{rds_title} =~ s/\_{2,99}/\_/gi; $result->{rds_title} = substr( $result->{rds_title}, 0, 63 ); - # $result->{event_id}=$result->{id}; + #$result->{event_id}=$result->{id}; $result->{base_url} = $request->{base_url}; $result->{base_domain} = $config->{locations}->{base_domain}; @@ -246,20 +246,28 @@ sub modify_results { $result->{no_comment} = 1 if ( $result->{comment_count} == 0 ); #fix image url - - if ((defined $config->{permissions}->{hide_event_images}) && ($config->{permissions}->{hide_event_images} eq '1')){ - $result->{image} = $result->{series_image}; - $result->{image_label} = $result->{series_image_label}; + #$params->{exclude_event_images}=0 unless defined $params->{exclude_event_images}; + #if ($params->{exclude_event_images}==1){ + # if ( (defined $config->{permissions}->{hide_event_images}) && ($config->{permissions}->{hide_event_images} eq '1') ){ + # $result->{image} = $result->{series_image}; + # $result->{image_label} = $result->{series_image_label}; + # } + #} + + if ( defined $result->{image} ) { + my $url = $config->{locations}->{local_media_url}||''; + my $image = $result->{image}; + $result->{thumb_url} = $url.'/thumbs/'.$image; + $result->{icon_url} = $url.'/icons/'.$image; + $result->{image_url} = $url.'/images/'.$image; } - if ( defined $result->{image} ) { - my $url=$config->{locations}->{local_media_url}||''; - if (defined $result->{image}){ - my $image=$result->{image}; - $result->{thumb} = $url.'/thumbs/'.$image; - $result->{icon} = $url.'/icons/'.$image; - $result->{image} = $url.'/images/'.$image; - } + if ( defined $result->{series_image} ) { + my $url = $config->{locations}->{local_media_url}||''; + my $image = $result->{series_image}; + $result->{series_thumb_url} = $url.'/thumbs/'.$image; + $result->{series_icon_url} = $url.'/icons/'.$image; + $result->{series_image_url} = $url.'/images/'.$image; } $result->{location_css} = $result->{location} || ''; @@ -811,11 +819,11 @@ sub get_query { # exclude location my $exclude_location_cond = ''; - if ( $params->{no_exclude} ne '1' ) { - if ( $params->{exclude_locations} ne '' ) { - my @exclude_locations = split( /,/, $params->{exclude_locations} ); - $exclude_location_cond = 'location not in (' . join( ",", map { '?' } @exclude_locations ) . ')'; - for my $location (@exclude_locations) { + if ( $params->{exclude_locations} eq '1' ) { + if ( $params->{locations_to_exclude} ne '' ) { + my @locations_to_exclude = split( /,/, $params->{locations_to_exclude} ); + $exclude_location_cond = 'location not in (' . join( ",", map { '?' } @locations_to_exclude ) . ')'; + for my $location (@locations_to_exclude) { $location =~ s/^\s+//g; $location =~ s/\s+$//g; push @$bind_values, $location; @@ -825,11 +833,11 @@ sub get_query { # exclude project my $exclude_project_cond = ''; - if ( $params->{no_exclude} ne '1' ) { - if ( $params->{exclude_projects} ne '' ) { - my @exclude_projects = split( /,/, $params->{exclude_projects} ); - $exclude_project_cond = 'project not in (' . join( ",", map { '?' } @exclude_projects ) . ')'; - for my $project (@exclude_projects) { + if ( $params->{exclude_projects} eq '1' ) { + if ( $params->{projects_to_exclude} ne '' ) { + my @projects_to_exclude = split( /,/, $params->{projects_to_exclude} ); + $exclude_project_cond = 'project not in (' . join( ",", map { '?' } @projects_to_exclude ) . ')'; + for my $project (@projects_to_exclude) { $project =~ s/^\s+//g; $project =~ s/\s+$//g; push @$bind_values, $project; @@ -1202,10 +1210,11 @@ sub render { $template_parameters->{ 'project_' . $project->{name} } = 1 if ( $project->{name} ne '' ); - $template_parameters->{controllers} = $config->{controllers}, + $template_parameters->{controllers} = $config->{controllers}; + $template_parameters->{hide_event_images}=1 if $config->{permissions}->{hide_event_images} == 1; # use Data::Dumper;print STDERR Dumper($template_parameters)."\n"; - template::process( $_[0], $params->{template}, $template_parameters ); + template::process( $_[0], $params->{template}, $template_parameters ); return $_[0]; } @@ -1241,37 +1250,47 @@ sub setDefaultEventConditions { my $config = shift; my $conditions = $_[0]; my $bind_values = $_[1]; + my $options = $_[2]; + $options={} unless defined $options; # exclude projects - if ( ( defined $config->{filter} ) - && ( defined $config->{filter}->{exclude_projects} ) ) + if ( + ( defined $options->{exclude_projects} ) + && ( $options->{exclude_projects} == 1 ) + && ( defined $config->{filter} ) + && ( defined $config->{filter}->{projects_to_exclude} ) ) { - my @exclude_projects = - split( /,/, $config->{filter}->{exclude_projects} ); - push @$conditions, 'project not in (' . join( ",", map { '?' } @exclude_projects ) . ')'; - for my $project (@exclude_projects) { + my @projects_to_exclude = + split( /,/, $config->{filter}->{projects_to_exclude} ); + push @$conditions, 'project not in (' . join( ",", map { '?' } @projects_to_exclude ) . ')'; + for my $project (@projects_to_exclude) { push @$bind_values, $project; } } # exclude locations - if ( ( defined $config->{filter} ) - && ( defined $config->{filter}->{exclude_locations} ) ) + if ( + ( defined $options->{exclude_locations} ) + && ( $options->{exclude_locations} == 1 ) + && ( defined $config->{filter} ) + && ( defined $config->{filter}->{locations_to_exclude} ) ) { - my @exclude_locations = - split( /,/, $config->{filter}->{exclude_locations} ); - push @$conditions, 'location not in (' . join( ",", map { '?' } @exclude_locations ) . ')'; - for my $location (@exclude_locations) { + my @locations_to_exclude = + split( /,/, $config->{filter}->{locations_to_exclude} ); + push @$conditions, 'location not in (' . join( ",", map { '?' } @locations_to_exclude ) . ')'; + for my $location (@locations_to_exclude) { push @$bind_values, $location; } } } +# for local use only or add support for exclude_projects and exclude_locations sub getEventById { my $dbh = shift; my $config = shift; my $event_id = shift; + my $options = shift; $dbh = db::connect($config) unless defined $dbh; @@ -1281,7 +1300,7 @@ sub getEventById { push @$conditions, "id=?"; push @$bind_values, $event_id; - setDefaultEventConditions( $config, $conditions, $bind_values ); + setDefaultEventConditions( $config, $conditions, $bind_values, $options ); $conditions = join( ' and ', @$conditions ); my $query = qq{ @@ -1297,12 +1316,15 @@ sub getEventById { sub get_next_event_of_series { my $dbh = shift; my $config = shift; - my $event_id = shift; + my $options = shift; + + my $eventId = $options->{event_id}; + return undef unless defined $eventId; $dbh = db::connect($config) unless defined $dbh; - my $events = getEventById( $dbh, $config, $event_id ); - return undef unless @$events == 1; + my $events = getEventById( $dbh, $config, $eventId, $options ); + return undef unless scalar(@$events) == 1; my $event = $events->[0]; my $conditions = []; @@ -1314,7 +1336,7 @@ sub get_next_event_of_series { push @$conditions, "series_name=?"; push @$bind_values, $event->{series_name}; - setDefaultEventConditions( $config, $conditions, $bind_values ); + setDefaultEventConditions( $config, $conditions, $bind_values, $options ); $conditions = join( ' and ', @$conditions ); my $query = qq{ @@ -1334,12 +1356,14 @@ sub get_next_event_of_series { sub get_previous_event_of_series { my $dbh = shift; my $config = shift; - my $event_id = shift; + my $options = shift; + + my $eventId = $options->{event_id}; + return undef unless defined $eventId; $dbh = db::connect($config) unless defined $dbh; - - my $events = getEventById( $dbh, $config, $event_id ); - return undef unless @$events == 1; + my $events = getEventById( $dbh, $config, $eventId, $options ); + return undef unless scalar(@$events) == 1; my $event = $events->[0]; my $conditions = []; @@ -1351,7 +1375,7 @@ sub get_previous_event_of_series { push @$conditions, "series_name=?"; push @$bind_values, $event->{series_name}; - setDefaultEventConditions( $config, $conditions, $bind_values ); + setDefaultEventConditions( $config, $conditions, $bind_values, $options ); $conditions = join( ' and ', @$conditions ); my $query = qq{ @@ -1362,7 +1386,7 @@ sub get_previous_event_of_series { }; $events = db::get( $dbh, $query, $bind_values ); - return undef unless @$events == 1; + return undef unless scalar(@$events) == 1; return $events->[0]->{id}; } @@ -1371,12 +1395,13 @@ sub get_by_date_range { my $config = shift; my $start_date = shift; my $end_date = shift; + my $options = shift; my $conditions = []; push @$conditions, 'start_date between ? and ?'; my $bind_values = [ $start_date, $end_date ]; - setDefaultEventConditions( $config, $conditions, $bind_values ); + setDefaultEventConditions( $config, $conditions, $bind_values, $options ); $conditions = join( ' and ', @$conditions ); @@ -1605,28 +1630,33 @@ sub check_params { } #if no location is set, use exclude location filter from default config - my $exclude_locations = ''; + my $locations_to_exclude = ''; if ( ( $location eq '' ) && ( defined $config->{filter} ) - && ( defined $config->{filter}->{exclude_locations} ) ) + && ( defined $config->{filter}->{locations_to_exclude} ) ) { - $exclude_locations = $config->{filter}->{exclude_locations} || ''; - $exclude_locations =~ s/\s+/ /g; + $locations_to_exclude = $config->{filter}->{locations_to_exclude} || ''; + $locations_to_exclude =~ s/\s+/ /g; } - my $exclude_projects = ''; + my $projects_to_exclude = ''; if ( ( defined $config->{filter} ) - && ( defined $config->{filter}->{exclude_projects} ) ) + && ( defined $config->{filter}->{projects_to_exclude} ) ) { - $exclude_projects = $config->{filter}->{exclude_projects} || ''; - $exclude_projects =~ s/\s+/ /g; + $projects_to_exclude = $config->{filter}->{projects_to_exclude} || ''; + $projects_to_exclude =~ s/\s+/ /g; } - #disable exclude filter by 'no_exlude=1' - my $no_exclude = ''; - $no_exclude = '1' - if ( ( defined $params->{no_exclude} ) - && ( $params->{no_exclude} eq '1' ) ); + + #enable exclude locations filter + my $exclude_locations = 0; + $exclude_locations = 1 if ( defined $params->{exclude_locations} ) && ( $params->{exclude_locations} eq '1' ) ; + + my $exclude_projects = 0; + $exclude_projects = 1 if ( defined $params->{exclude_projects} ) && ( $params->{exclude_projects} eq '1' ) ; + + my $exclude_event_images = 0; + $exclude_event_images = 1 if ( defined $params->{exclude_event_images} ) && ( $params->{exclude_event_images} eq '1' ) ; #show future events by default my $archive = 'future'; @@ -1670,7 +1700,7 @@ sub check_params { #print STDERR $params->{template}."\n"; my $template = '.html'; - if ( $params->{template} eq 'no' ) { + if ( ( defined $params->{template} ) && ($params->{template} eq 'no') ) { $template = 'no'; } else { $template = template::check( $params->{template}, 'event_list.html' ); @@ -1690,7 +1720,7 @@ sub check_params { my $default_project = undef; my $projects = project::get( $config, { name => $project_name } ); log::error( $config, "no configuration found for project '$project_name'" ) - unless ( @$projects == 1 ); + unless ( scalar(@$projects) == 1 ); $default_project = $projects->[0]; # get project from parameter (by name) @@ -1702,7 +1732,7 @@ sub check_params { my $project_name = $params->{project}; my $projects = project::get( $config, { name => $project_name } ); log::error( $config, 'invalid project ' . $project_name ) - unless @$projects == 1; + unless scalar(@$projects) == 1; $project = $projects->[0]; } @@ -1759,12 +1789,14 @@ sub check_params { studio_id => $studio_id, json_callback => $json_callback, get => $get, - exclude_locations => $exclude_locations, - exclude_projects => $exclude_projects, - no_exclude => $no_exclude, - disable_event_sync => $disable_event_sync, - extern => $extern, - recordings => $recordings, + locations_to_exclude => $locations_to_exclude, + projects_to_exclude => $projects_to_exclude, + exclude_locations => $exclude_locations, + exclude_projects => $exclude_projects, + exclude_event_images => $exclude_event_images, + disable_event_sync => $disable_event_sync, + extern => $extern, + recordings => $recordings, }; #print STDERR Dumper($checked); diff --git a/lib/calcms/images.pm b/lib/calcms/images.pm index 8fb0c88..5d81a33 100644 --- a/lib/calcms/images.pm +++ b/lib/calcms/images.pm @@ -364,6 +364,13 @@ sub getInternalPath{ return $path; } +sub normalizeName{ + my $name = shift; + return undef unless defined $name; + $name =~s/.*\///g; + return $name; +} + sub readFile{ my $path=shift; my $content=''; diff --git a/lib/calcms/mail.pm b/lib/calcms/mail.pm new file mode 100644 index 0000000..fe44a04 --- /dev/null +++ b/lib/calcms/mail.pm @@ -0,0 +1,26 @@ +package mail; + +use MIME::Lite; + +sub send { + my $mail = shift; + + my $msg = MIME::Lite->new( + 'From' => $mail->{'From'}, + 'To' => $mail->{'To'}, + 'Cc' => $mail->{'Cc'}, + 'Reply-To' => $mail->{'Reply-To'}, + 'Subject' => $mail->{'Subject'}, + 'Data' => $mail->{'Data'}, + ); + + #print '
'; + $msg->print( \*STDERR ); + #print '
'; + + $msg->send; +} + + +# do not delete next line +return 1; diff --git a/lib/calcms/password_requests.pm b/lib/calcms/password_requests.pm new file mode 100644 index 0000000..7640287 --- /dev/null +++ b/lib/calcms/password_requests.pm @@ -0,0 +1,253 @@ +package password_requests; + +use warnings "all"; +use strict; + +use Data::Dumper; +use Session::Token; + +# table: calcms_password_requests +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(get insert delete get_columns); +our %EXPORT_TAGS = ( 'all' => [ @EXPORT_OK ] ); + +use mail; +use uac; +use db; +use auth; + +sub debug; + +sub get_columns{ + my $config=shift; + + my $dbh=db::connect($config); + my $cols=db::get_columns($dbh, 'calcms_password_requests'); + my $columns={}; + for my $col (@$cols){ + $columns->{$col}=1; + } + return $columns; +} + +sub get{ + my $config=shift; + my $condition=shift; + + my $dbh=db::connect($config); + + my @conditions=(); + my @bind_values=(); + + if (defined $condition->{user}){ + push @conditions, 'user=?'; + push @bind_values, $condition->{user}; + } + + if (defined $condition->{token}){ + push @conditions, 'token=?'; + push @bind_values, $condition->{token}; + } + + return undef if (scalar @conditions) == 0; + + my $conditions=" where ".join(" and ",@conditions); + my $query=qq{ + select * + from calcms_password_requests + $conditions + }; + #print $query."\n".Dumper(\@bind_values); + + my $entries=db::get($dbh, $query, \@bind_values); + return $entries->[0] || undef; +} + +sub update{ + my $config=shift; + my $entry=shift; + + return unless defined $entry->{user}; + + my $dbh=db::connect($config); + my $values = join(",", map {$_.'=?'} (keys %$entry)); + my @bind_values = map {$entry->{$_}} (keys %$entry); + push @bind_values, $entry->{token}; + + my $query=qq{ + update calcms_password_requests + set $values + where token=? + }; + print STDERR $query . Dumper(\@bind_values); + db::put($dbh, $query, \@bind_values); +} + + +sub insert{ + my $config=shift; + my $entry=shift; + + return undef unless defined $entry->{user}; + + my $dbh=db::connect($config); + print STDERR 'insert ' . Dumper($entry); + return db::insert($dbh, 'calcms_password_requests', $entry); +} + +sub delete{ + my $config=shift; + my $condition=shift; + + my @conditions=(); + my @bind_values=(); + + if ((defined $condition->{user}) && ($condition->{user} ne '')){ + push @conditions, 'user=?'; + push @bind_values, $condition->{user}; + } + + if ((defined $condition->{token}) && ($condition->{token} ne '')){ + push @conditions, 'token=?'; + push @bind_values, $condition->{token}; + } + + return if (scalar @conditions)==0; + my $conditions=" where ".join(" and ", @conditions); + + my $dbh=db::connect($config); + + my $query=qq{ + delete + from calcms_password_requests + $conditions + }; + + print STDERR "$query " . Dumper(\@bind_values); + db::put($dbh, $query, \@bind_values); +} + +sub sendToken{ + my $config=shift; + my $entry=shift; + + return undef unless defined $entry->{user}; + + my $user = uac::get_user($config, $entry->{user}); + return undef unless defined $user; + + # check age of existing entry + my $oldEntry = password_requests::get($config, { user => $entry->{user} } ); + if (defined $oldEntry){ + my $createdAt = $oldEntry->{created_at}; + print STDERR Dumper($oldEntry); + print STDERR "createdAt=$createdAt\n"; + my $age = time() - time::datetime_to_time($createdAt); + if ($age < 60) { + print STDERR "too many requests"; + return undef; + } + print STDERR "age=$age\n"; + } + password_requests::delete($config, $entry); + + $entry->{max_attempts} = 0; + $entry->{token} = Session::Token->new->get; + + my $baseUrl = $config->{locations}->{source_base_url} . $config->{locations}->{editor_base_url}; + my $url = $baseUrl."/requestPassword.cgi?token=" . $entry->{token}; + my $content = "Hi,$user->{full_name}\n\n"; + $content .= "Someone just tried to reset your password for $baseUrl.\n\n"; + $content .= "If you like to set a new password, please follow the link below\n"; + $content .= $url."\n\n"; + $content .= "If you do not like to set a new password, please ignore this mail.\n"; + + mail::send({ + "To" => $user->{email}, + "Subject" => "request to change password for $baseUrl", + "Data" => $content + }); + + password_requests::insert($config, $entry); +} + +sub changePassword { + my $config = shift; + my $request = shift; + my $userName = shift; + + my $params = $request->{params}->{checked}; + my $permissions = $request->{permissions}; + + unless ( ( defined $userName ) || ( $userName eq '' ) ) { + return { error => 'user not found' }; + } + + my $user = uac::get_user( $config, $userName ); + + unless ( ( defined $user ) && ( defined $user->{id} ) && ( $user->{id} ne '' ) ) { + return { error => 'user id not found' }; + } + + unless ( password_requests::checkPassword( $params->{user_password} ) ) { + return { error => 'password does not meet requirements' }; + } + + if ( $params->{user_password} ne $params->{user_password2} ) { + return { error => 'entered passwords do not match'}; + } + + #print STDERR "error at changing password:" . Dumper($errors); + + my $crypt = auth::crypt_password( $params->{user_password} ); + $user = { id => $user->{id} }; + $user->{salt} = $crypt->{salt}; + $user->{pass} = $crypt->{crypt}; + + #print '
'.Dumper($user).'
';
+ $config->{access}->{write} = 1;
+ print STDERR "update user".Dumper($user);
+ my $result = uac::update_user( $config, $user );
+ print STDERR "result:".Dumper($result);
+ $config->{access}->{write} = 0;
+ return { success => "password changed for $userName" };
+}
+
+sub checkPassword {
+ my $password = shift;
+ unless ( defined $password || $password eq '' ) {
+ error("password is empty");
+ return;
+ }
+ if ( length($password) < 8 ) {
+ error("password to short");
+ return 0;
+ }
+ unless ( $password =~ /[a-z]/ ) {
+ error("password should contains at least one small character");
+ return 0;
+ }
+ unless ( $password =~ /[A-Z]/ ) {
+ error("password should contains at least one big character");
+ return 0;
+ }
+ unless ( $password =~ /[0-9]/ ) {
+ error("password should contains at least one number");
+ return 0;
+ }
+ unless ( $password =~ /[^a-zA-Z0-9]/ ) {
+ error("password should contains at least one special character");
+ return 0;
+ }
+ return 1;
+}
+
+
+sub error{
+ my $msg=shift;
+ print "ERROR: $msg
\n";
+}
+
+#do not delete last line!
+1;
diff --git a/lib/calcms/project.pm b/lib/calcms/project.pm
index a6fb181..777eb06 100644
--- a/lib/calcms/project.pm
+++ b/lib/calcms/project.pm
@@ -1,14 +1,17 @@
#!/bin/perl
package project;
+
use warnings "all";
use strict;
use Data::Dumper;
use Date::Calc;
+
use config;
use log;
use template;
+use images;
require Exporter;
our @ISA = qw(Exporter);
@@ -109,23 +112,16 @@ sub insert{
my $columns=get_columns($config);
my $project={};
for my $column (keys %$columns){
- $project->{$column}=$entry->{$column} if defined $entry->{$column};
+ $project->{$column} = $entry->{$column} if defined $entry->{$column};
}
+ $project->{image} = images::normalizeName( $project->{image} ) if defined $project->{image};
+
my $dbh=db::connect($config);
my $id=db::insert($dbh, 'calcms_projects', $project);
return $id;
}
-# delete project
-sub delete{
- my $config=shift;
- my $entry=shift;
-
- my $dbh=db::connect($config);
- db::put($dbh, 'delete from calcms_projects where project_id=?', [$entry->{project_id}]);
-}
-
# update project
sub update{
my $config=shift;
@@ -137,6 +133,8 @@ sub update{
$entry->{$column}=$project->{$column} if defined $project->{$column};
}
+ $entry->{image} = images::normalizeName( $entry->{image} ) if defined $entry->{image};
+
my $values =join(",", map {$_.'=?'} (keys %$entry));
my @bind_values =map {$entry->{$_}} (keys %$entry);
push @bind_values,$entry->{project_id};
@@ -151,6 +149,15 @@ sub update{
db::put($dbh, $query, \@bind_values);
}
+# delete project
+sub delete{
+ my $config=shift;
+ my $entry=shift;
+
+ my $dbh=db::connect($config);
+ db::put($dbh, 'delete from calcms_projects where project_id=?', [$entry->{project_id}]);
+}
+
# get studios of a project
sub get_studios{
diff --git a/lib/calcms/series.pm b/lib/calcms/series.pm
index 97275c4..38eea9c 100644
--- a/lib/calcms/series.pm
+++ b/lib/calcms/series.pm
@@ -2,8 +2,12 @@ package series;
use warnings "all";
use strict;
+
use Data::Dumper;
+
use events;
+use images;
+
require Exporter;
our @ISA = qw(Exporter);
@@ -136,15 +140,16 @@ sub insert{
return undef unless defined $series->{project_id};
return undef unless defined $series->{studio_id};
- my $project_id=$series->{project_id};
- my $studio_id =$series->{studio_id};
+ my $project_id = $series->{project_id};
+ my $studio_id = $series->{studio_id};
my $columns=series::get_columns($config);
my $entry={};
for my $column (keys %$columns){
- $entry->{$column}=$series->{$column} if defined $series->{$column};
+ $entry->{$column} = $series->{$column} if defined $series->{$column};
}
+ $entry->{image} = images::normalizeName( $entry->{image} ) if defined $entry->{image};
$entry->{created_at} = time::time_to_datetime(time());
$entry->{modified_at}= time::time_to_datetime(time());
@@ -179,6 +184,7 @@ sub update{
for my $column (keys %$columns){
$entry->{$column}=$series->{$column} if defined $series->{$column};
}
+ $entry->{image} = images::normalizeName( $entry->{image} ) if defined $entry->{image};
$entry->{id} = $series->{series_id};
$entry->{modified_at}= time::time_to_datetime(time());
@@ -522,7 +528,18 @@ sub get_events{
my $dbh=db::connect($config);
my $results=db::get($dbh, $query, \@bind_values);
- $results=events::modify_results($dbh, $config, {base_url=>'', params=>{checked=>{template=>''}}}, $results);
+ #print STDERR Dumper($results);
+
+ $results=events::modify_results(
+ $dbh, $config, {
+ base_url => '',
+ params => {
+ checked => {
+ template => ''
+ }
+ }
+ } , $results
+ );
#add studio id to events
my $studios=studios::get($config, $options);
@@ -552,7 +569,7 @@ sub get_event{
my $event_id = $options->{event_id} ||'';
my $draft = $options->{draft} ||'';
- unless(defined($options->{allow_any})){
+ unless(defined $options->{allow_any} ){
if ($project_id eq''){
uac::print_error("missing project_id");
return undef;
@@ -580,18 +597,19 @@ sub get_event{
$queryOptions->{draft} = $draft if $draft ne '';
my $events=series::get_events($config, $queryOptions);
+
unless (defined $events){
uac::print_error("error on loading event");
return undef;
}
- if(@$events==0){
+ if(scalar(@$events)==0){
uac::print_error("event not found");
return undef;
}
- if(@$events>1){
+ if(scalar(@$events)>1){
print STDERR q{multiple assignments found for }
.q{project_id=}.$options->{project_id}
.q{, studio_id=}.$options->{studio_id}
diff --git a/lib/calcms/series_dates.pm b/lib/calcms/series_dates.pm
index e6f5353..3df09a9 100644
--- a/lib/calcms/series_dates.pm
+++ b/lib/calcms/series_dates.pm
@@ -540,11 +540,41 @@ sub delete{
from calcms_series_dates
where project_id=? and studio_id=? and series_id=?
};
- my $bind_values=[$entry->{project_id}, $entry->{studio_id}, $entry->{series_id}];
+ my $bind_values = [ $entry->{project_id}, $entry->{studio_id}, $entry->{series_id} ];
#print '
$query'.$query.Dumper($bind_values).'
'; db::put($dbh, $query, $bind_values); } +# get all series dates where no event has been created for +sub getDatesWithoutEvent{ + my $config = shift; + my $options = shift; + + return unless defined $options->{project_id}; + return unless defined $options->{studio_id}; + return unless defined $options->{from}; + return unless defined $options->{till}; + + my $dbh=db::connect($config); + + my $query=q{ + SELECT sd.* + FROM calcms_series_dates sd LEFT JOIN calcms_events e + on (sd.start = e.start) + where e.start is null + and sd.exclude != 1 + and sd.project_id = ? + and sd.studio_id = ? + and sd.start > ? + and sd.end < ? + order by sd.start + }; + + my $bind_values = [ $options->{project_id}, $options->{studio_id}, $options->{from}, $options->{till} ]; + my $entries = db::get($dbh, $query, $bind_values); + return $entries; + +} sub error{ my $msg=shift; diff --git a/lib/calcms/series_events.pm b/lib/calcms/series_events.pm index 7e9d9d2..328bf08 100644 --- a/lib/calcms/series_events.pm +++ b/lib/calcms/series_events.pm @@ -17,6 +17,8 @@ use series_dates; use studios; use studio_timeslot_dates; use event_history; +use images; + # check permissions, insert and update events related to series @@ -46,10 +48,15 @@ sub save_content{ return undef unless(defined $entry->{id}); for my $attr (keys %$entry){ + next unless defined $entry->{$attr}; $entry->{$attr}=~s/^\s+//g; $entry->{$attr}=~s/\s+$//g; } + for my $attr ('image', 'series_image'){ + $entry->{$attr} = images::normalizeName($entry->{$attr}) if defined $entry->{$attr}; + } + #print STDERR Dumper(\$entry->{content}); for my $attr ('content', 'topic'){ if (defined $entry->{$attr}){ @@ -60,7 +67,7 @@ sub save_content{ } } - #print STDERR Dumper(\$entry->{html_content}); + #print STDERR Dumper(\$entry->{series_image}); #print STDERR "ok2\n"; #return; $entry->{modified_at}= time::time_to_datetime(time()); @@ -93,8 +100,15 @@ sub save_content{ where id=? }; - #print STDERR $query.Dumper(\@bind_values); - db::put($dbh, $query, \@bind_values); + #print STDERR "update:".$query.Dumper(\@bind_values); + my $result=db::put($dbh, $query, \@bind_values); + unless (defined $result){ + print STDERR "error on updating event\n" ; + return undef; + } + + #print STDERR "result=$result\n"; + #print STDERR "entr after update".Dumper($entry); return $entry; } @@ -410,14 +424,14 @@ sub insert_event{ #get event content from series for my $attr ('program', 'series_name', 'title', 'excerpt', 'content', 'topic', 'image', 'episode', 'podcast_url', 'archive_url'){ - $event->{$attr}=$serie->{$attr} if defined $serie->{$attr}; + $event->{$attr} = $serie->{$attr} if defined $serie->{$attr}; } $event->{series_image} = $serie->{image} if defined $serie->{image}; $event->{series_image_label} = $serie->{licence} if defined $serie->{licence}; #overwrite series values from parameters for my $attr ('program', 'series_name', 'title', 'user_title', 'excerpt', 'user_except', 'content', 'topic', 'image', 'episode', 'podcast_url', 'archive_url'){ - $event->{$attr}=$params->{$attr} if defined $params->{$attr}; + $event->{$attr} = $params->{$attr} if defined $params->{$attr}; } $event->{'html_content'} = markup::creole_to_html($event->{'content'}) if defined $event->{'content'}; $event->{'html_topic'} = markup::creole_to_html($event->{'topic'}) if defined $event->{'topic'}; @@ -475,6 +489,8 @@ sub update_series_images{ return "missing studio_id" unless defined $options->{studio_id}; return "missing series_id" unless defined $options->{series_id}; return "missing series_image" unless defined $options->{series_image}; + + #print "save $options->{series_image}\n"; my $events=series::get_events( $config, { diff --git a/lib/calcms/studios.pm b/lib/calcms/studios.pm index 8b7edfa..99fa98e 100644 --- a/lib/calcms/studios.pm +++ b/lib/calcms/studios.pm @@ -1,16 +1,19 @@ #!/bin/perl -use CGI; +#use CGI; #use CGI::Carp qw(warningsToBrowser fatalsToBrowser); -use CGI::Session qw(-ip-match); -use CGI::Cookie; +#use CGI::Session qw(-ip-match); +#use CGI::Cookie; #$CGI::Session::IP_MATCH=1; package studios; use warnings "all"; use strict; + use Data::Dumper; +use images; + require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(get_columns get get_by_id insert update delete check check_studio); @@ -96,8 +99,9 @@ sub insert{ my $config=shift; my $entry=shift; - $entry->{created_at} = time::time_to_datetime(time()); - $entry->{modified_at}= time::time_to_datetime(time()); + $entry->{created_at} = time::time_to_datetime(time()); + $entry->{modified_at} = time::time_to_datetime(time()); + $entry->{image} = images::normalizeName( $entry->{image} ) if defined $entry->{image}; my $dbh=db::connect($config); my $id=db::insert($dbh, 'calcms_studios', $entry); @@ -116,6 +120,7 @@ sub update{ for my $column (keys %$columns){ $entry->{$column}=$studio->{$column} if defined $studio->{$column}; } + $entry->{image} = images::normalizeName( $entry->{image} ) if defined $entry->{image}; my $values =join(",", map {$_.'=?'} (keys %$entry)); my @bind_values =map {$entry->{$_}} (keys %$entry); diff --git a/website/agenda/planung/requestPassword.cgi b/website/agenda/planung/requestPassword.cgi new file mode 100644 index 0000000..3b99163 --- /dev/null +++ b/website/agenda/planung/requestPassword.cgi @@ -0,0 +1,165 @@ +#! /usr/bin/perl -w + +use warnings "all"; +use strict; + +use Data::Dumper; + +use params; +use config; +use db; +use auth; +use password_requests; + +binmode STDOUT, ":utf8"; + +my $r = shift; +( my $cgi, my $params, my $error ) = params::get($r); + +my $config = config::get('../config/config.cgi'); +my $debug = $config->{system}->{debug}; + +$params = check_params($params); + +print "Content-type:text/html\n\n"; +print qq{ + +
+ + +