From 1e1a36ad5afb700d745de781a80f5845d658ebb9 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Tue, 8 Sep 2026 15:03:31 +0200 Subject: [PATCH] Visibilite de section et logo des e-mails transactionnels Endpoint dedie `PUT Section/{id}/visibility` : passer par `Update` aurait reconstruit le sous-type via SectionFactory, donc efface le contenu specifique de la section. Le logo des e-mails est servi par le service lui-meme (wwwroot + UseStaticFiles) plutot que par le manager deploye en face, dont il ne doit pas dependre. --- .../Controllers/SectionController.cs | 40 +++++++ ManagerService/EmailTemplates/EmailLayout.cs | 108 +++++++++++++++--- ManagerService/Startup.cs | 9 ++ ManagerService/appsettings.Development.json | 3 +- ManagerService/appsettings.json | 3 +- ManagerService/wwwroot/email-logo.png | Bin 0 -> 10263 bytes 6 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 ManagerService/wwwroot/email-logo.png diff --git a/ManagerService/Controllers/SectionController.cs b/ManagerService/Controllers/SectionController.cs index e4b5ecd..d21f734 100644 --- a/ManagerService/Controllers/SectionController.cs +++ b/ManagerService/Controllers/SectionController.cs @@ -758,6 +758,46 @@ namespace ManagerService.Controllers } } + /// + /// Show or hide a section in the visitor apps + /// + /// Section id + /// true = visible, false = hidden + /// + /// Endpoint dédié plutôt que : celui-ci reconstruit le + /// sous-type via SectionFactory, donc un SectionDTO nu effacerait tout le + /// contenu spécifique de la section. + /// + [ProducesResponseType(typeof(object), 200)] + [ProducesResponseType(typeof(string), 404)] + [ProducesResponseType(typeof(string), 500)] + [HttpPut("{id}/visibility")] + public ObjectResult SetVisibility(string id, [FromQuery] bool isActive) + { + try + { + Section section = _myInfoMateDbContext.Sections.FirstOrDefault(s => s.Id == id); + + if (section == null) + throw new KeyNotFoundException("Section does not exist"); + + section.IsActive = isActive; + _myInfoMateDbContext.SaveChanges(); + + MqttClientService.PublishMessage($"config/{section.ConfigurationId}", JsonConvert.SerializeObject(new PlayerMessageDTO() { configChanged = true })); + + return new OkObjectResult(SectionFactory.ToDTO(section)); + } + catch (KeyNotFoundException ex) + { + return new NotFoundObjectResult(ex.Message) { }; + } + catch (Exception ex) + { + return new ObjectResult(ex.Message) { StatusCode = 500 }; + } + } + /// /// Update sections order /// diff --git a/ManagerService/EmailTemplates/EmailLayout.cs b/ManagerService/EmailTemplates/EmailLayout.cs index 9f89f22..3124190 100644 --- a/ManagerService/EmailTemplates/EmailLayout.cs +++ b/ManagerService/EmailTemplates/EmailLayout.cs @@ -3,57 +3,127 @@ using System; namespace ManagerService.EmailTemplates { /// - /// Shared HTML shell for all transactional emails — MyInfoMate branding - /// (cyan #0df2df header, dark logo band, white content card). + /// Shared HTML shell for all transactional emails. Palette taken from the + /// MyInfoMate platform deck: navy #0a1222, cyan #0df2df, teal #0e8f8a. /// public static class EmailLayout { + /// Absolute URL of the logo shown in the header band. Empty = wordmark only. + public static string LogoUrl { get; private set; } = ""; + + /// Public site linked from the footer. Empty = no link. + public static string LandingUrl { get; private set; } = ""; + + /// Called once at startup from configuration (see Startup.cs). + public static void Configure(string logoUrl, string landingUrl) + { + // Une URL relative afficherait une image cassée dans la boîte de + // réception : mieux vaut le bandeau sans logo que ça. + LogoUrl = logoUrl != null && logoUrl.StartsWith("http") ? logoUrl : ""; + LandingUrl = landingUrl ?? ""; + } + public static string Render(string title, string bodyHtml, string ctaText = null, string ctaUrl = null) { + var hasCta = !string.IsNullOrEmpty(ctaText) && !string.IsNullOrEmpty(ctaUrl); + + // Le texte de pré-en-tête est ce que la boîte de réception affiche à + // côté de l'objet : sans lui elle recopie le début du HTML. + var preheader = StripTags(bodyHtml); + if (preheader.Length > 140) preheader = preheader.Substring(0, 140) + "…"; + + var logoHtml = string.IsNullOrEmpty(LogoUrl) + ? "" + : $@""; + var ctaHtml = ""; - if (!string.IsNullOrEmpty(ctaText) && !string.IsNullOrEmpty(ctaUrl)) + if (hasCta) { ctaHtml = $@" - +
- -
- {ctaText} + + {ctaText}
"; + +

+ Le bouton ne fonctionne pas ? Copiez ce lien dans votre navigateur :
+ {ctaUrl} +

"; } + var landingHtml = string.IsNullOrEmpty(LandingUrl) + ? "" + : $@" · myinfomate.be"; + return $@" - - - + + + + + {title} + + +
{preheader}
+ +
-
- +
+ + + - - + + + + + - +
- MyInfoMate + + + + + + +
{logoHtml}MyInfoMate
-

{title}

-
{bodyHtml}
+
 
+

{title}

+
{bodyHtml}
{ctaHtml}
- MyInfoMate — {DateTime.UtcNow.Year} + + MyInfoMate — la plateforme de contenu des lieux culturels.
+ © {DateTime.UtcNow.Year} Unov{landingHtml}
+
"; } + + private static string StripTags(string html) + { + if (string.IsNullOrEmpty(html)) return ""; + var sb = new System.Text.StringBuilder(html.Length); + var inTag = false; + foreach (var c in html) + { + if (c == '<') inTag = true; + else if (c == '>') inTag = false; + else if (!inTag) sb.Append(c); + } + return sb.ToString().Replace(" ", " ").Trim(); + } } } diff --git a/ManagerService/Startup.cs b/ManagerService/Startup.cs index 60c5f56..03a4f8b 100644 --- a/ManagerService/Startup.cs +++ b/ManagerService/Startup.cs @@ -121,6 +121,10 @@ namespace ManagerService services.Configure(Configuration.GetSection("Stripe")); services.Configure(Configuration.GetSection("Resend")); + ManagerService.EmailTemplates.EmailLayout.Configure( + Configuration["AppUrls:Logo"] ?? $"{Configuration["AppUrls:Api"]}/email-logo.png", + Configuration["AppUrls:Landing"]); + foreach (var policy in ManagerService.Service.Security.PoliciesConfiguration) services.AddAuthorization(options => { @@ -320,6 +324,11 @@ namespace ManagerService //app.UseHttpsRedirection(); + // Sert wwwroot/ — aujourd'hui le seul fichier est le logo des e-mails + // transactionnels, qui doit vivre sur le même service que l'envoi pour + // ne pas dépendre du build du manager déployé en face. + app.UseStaticFiles(); + app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); diff --git a/ManagerService/appsettings.Development.json b/ManagerService/appsettings.Development.json index b2255f1..fe80efc 100644 --- a/ManagerService/appsettings.Development.json +++ b/ManagerService/appsettings.Development.json @@ -26,7 +26,8 @@ }, "AppUrls": { "ManagerApp": "http://localhost:9090", - "Landing": "http://localhost:3000" + "Landing": "http://localhost:3000", + "Api": "http://localhost:5000" } } diff --git a/ManagerService/appsettings.json b/ManagerService/appsettings.json index 01772fa..259688e 100644 --- a/ManagerService/appsettings.json +++ b/ManagerService/appsettings.json @@ -47,7 +47,8 @@ }, "AppUrls": { "ManagerApp": "https://manager.myinfomate.be", - "Landing": "https://myinfomate.be" + "Landing": "https://myinfomate.be", + "Api": "https://api.myinfomate.be" }, "Stripe": { "SecretKey": "sk_test_51U14rjRLQgHvlM4X4ewATCOOIzdIfTZEJCwwZT9sfpWm9LkrISoSEHfgHbPhJTujdYdPdRyCnrDoo5Sf8NMyCrPT00RfYB8fSN", diff --git a/ManagerService/wwwroot/email-logo.png b/ManagerService/wwwroot/email-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd1fe18cf19c2485d141116f7d98d2e2a5e9664 GIT binary patch literal 10263 zcmV+yDCpOTP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>DC#*?CK~#8N?R|N0 zU)7bSYG(eKPNt_PX$TMkUbXKVlDxCn@+wP~tj&vdI|Q(0V{L#px^=ic{Jj*9xj=l`3}e~5~T`esyA)K$NOT^%x~?CV!WMO~w8&Q(!gOTIelYqwn; z6}9u~sHi>H+*Y{t>T}1_XTN?hA#dv0n8j1i#Vq;PgE32{4$fZkuLoySJ9RK-$%oI) zRu;45!>6Nfo_cQfl237bNYATlWV4ri_~PuvWY5lC{QkkY3#Yr{=e+i_8~?=r<^NxN z{{2-63-WHrTf1lGqV>yh%co{8E}NRQxNK_1 zqO$23i^`{GEiOZwnzg8Gnrva|)Qm-?(=!*APR(4X)-EikwKEr2OwCwaIX!Dp<@C%Y z8>VJ0*)aW$tm^4+WYkQrt16n>`_uW;?{p|{K^Ft<2$Egs+$ zGn)6;YLnmd+y2iy}2k~p~ zg!sZcA+F$i5Vz*LWU<(;mVL6-TW#Nrx5_ox57-vg>i$-UTh##3D;nUc`L{z+RT&)L zmjf^!4=^4Ja3T(%KOUej9-vnl_Vu_gE@+YF`(o8~W&GMe9KeZqfYC&Ni6kQB^S-$5 z4-Z9e@PYchf38bk`rTQ#l)n{Ucn6@xu4;hTmG#DAR@6fbw#s60tPK0HE9y=?L{Gf5UNnEK`aN;# znli6X-{V-UbIn|*IlLx+7Lk4?5nwzKKI@M=_Uosk=lQt(&Y%DIjc;Fn!-C>x;tK8n zw3w9*F3?^}M!*xG5^zI69mI0b!wiXVtQNj=OTCzTbDenpkz9bu+42|HfU8!5q`_xk za&jUVCqVb1hxHBkVWuK!JP|%0N)VsEl<;4D4FAoaKc1PIHS^|*Hxi1rL+r{e3P?_X zi~`vw4|hCYdc2&czDtfJNPB+({5qI%TOG_;To1i_vjEP{0bt&ZAW2{tz-lZ#SFH;J zkVgXpGi-e@Lc;Ee2N+5KIF|ySyd3|`&|l8_&pw`C>GLNuQWwp>ZQ}}>1JT4o4DS8|Xb z4ZO7o;H$Nv0EbAFBS#E;-8cE?Tnb$3i68n^NBlKDu3zf&^=qSJXWzPEI-&3`1*-OX z4Y;TO9DE}=?%0(l4|FC!2s|3*fp6w*g`Ib=5-?#s--63zhZ$WrGQlRX-H8B0u?!@i0N*$$VdJ3Le8P-_t^1DLaRT(e z$C5xqoKHqM+V2B=+0S)1t@uGg(KZ9F1yT}Vq()aarpjTSPAz&dTkCj#i2SUm11xQ+ z@>x+U{!7kQ_|f0oDy;4+$uQF6gBXbr3m#9b4zOW!yba7*!oG0EUPXrgrpaXZ;86S) zALr1|Rdcgb<}9uHE!N#qW~3>dS0$tcnN>hSgB?PU(tb{e&H=h2vWYl)c`eLZS_et_ zRq*R$$p8bf4nPKI28siI095APK5>`3SFa75BcQ$S)^ofHLx&kofZz1RzyInZGr#5I z9r#?IQ_z`MbeEM8BQHvR9Iw5S)M#z@K{wX|djP5bwQ$vft#JGH0)Vl(*88#k_PksZ zaaqs5l7-#RJ)K|o`f|PN@eX15Z^V1fC&9-@V*lF5+xh&b>tdtl+_vd++@X^9D$jS# zMuADfYHXsylEA8-05Li$91@TKl%T8fw!-5-T>x-qZV<39W!y8B1tKHFxYrxA@)!JG zHRnW$UT=RU?U;LG-~Z)v@!#-qH=pZsSKgOUc&CyTTkY5ii0QjBEisXzz?B?Bh-y*m zS%jFY47E};M6>jIvx%>gjvx+6LK-x5zb;0t}tSz|Yg zBy(qz;G>t~YJA+yXV#Lkp7^zQNH8J+?~`CEA_ZhXrmTlcGU^07hOl+Nksn$8=zsb}fVecJ?C_=%%l=KRUk_uD`h+R#lb>F%%<2Z>(@YbHJA8 zHG0g6;lH>8l;`QvbHf0=3_*O}>?=3{!30f*g2VCfo0sD*4E^-lKl1U9x-M;5E>%3M zxYw&FARxn%u=RQ_HC9$a&wSJz$&Vyrxo&Hb2yyL_1}LjqFT}}cfk1xI6DJH%#xOF1 z)seeKuVZ5ao;Sx@`dMMd)|)lXcX(04jGUuC9=_<0gO8q@o8sdib$!;V3OrJdK?1CI zt0M3&24EXb<<=Rg~<%^K{h zx?j$j;|G<9bp+(s3M8PSrUWDb!Cyws1tTEv3#;}EBL#ZBI%*E_)XSOtKKIpfN@Q zd$h=b=3}z3O2|r&O30XmiGX}XK=nFV1*u=o{z9&PYxyu<>=SW0aKq*_;bclmLhcDv zLEJ*;V?p4vW+dQ17y;P@wb8fLGJV%Mz#&9F(XyT`i7@xL4_pG??vHf|YW4zhA8_sB zdMK|h7H~pIkZ&Gvj)tr26cvH{Lgpfw^D~0`fG;>v5kdDY7GNwsJOWCNch%?Y`U{x3 zpgidH5_AuI=U73SLx3U(=Luv^3<6%OwV$r51S}*0brb=T01lcDpxuaZz-@^bK`J6x zDB1KKAy3TMnm{hlWBxscfCv&)dSj)36_8BA^`fGx zD6#|@L$CLvLKDKu&97=9Ab%+{_eH4AC<({IM`2YE5>Pog1|=gWpmcNyQnxfqIa{wD5A-3;&zkKTDmZ2B{HE``BB|%gLDevcIfM>g89TjE`1H}i*`7YNS!u{(J ziSfzSW9=L`#(h96z}a~C==l%=*1mM4q2k;atQ+iwl97{8I(7n5>vqA+`9uPFcJ#_R zAnEbR2xz|n+c#5&N@DCTSXMwaN`mT8f&hkM01$-Z9JE-kuZZAd1)K{eix%@8e)Q=3 zV^!|)BaF{$fFqfbA9f#=&EpB-1gz;gw6$Cjux_vqN=8q@`q4p1t=|Q+WF$!NwdZ3V za6DZDOI|Ocmuv5Lce}I(JsrJ&g_Iss0ht_72Nj#w2^gLW&>suXj|W+?0E5g1&|>I$ zfophOwMZa0oHu=c9lqW;7vS`4fRQ->BiPOc81-$IY{Rbkb7w;CduuLy+IwS=fK^8i zZNUgA8tj90Lw!&*)Ca|*!?6C$5G2>^gc+2WE!YNJrt?(GTM{hbYR-`%1ALHx>z34s z($Zq_`N4Sj?1@CUv`^Y+`x4-@1IqR%z@`1NB~$a4&~UxFerX>c%l1<}hxSAgT%w4KbGTP z|2P*u_}M&oUv?TCIG=N8tSKh@#sk?85x9>+MO_CemO;f23O0Hl^3b! znh53nw&KwdBafdk0%SRg<)=vQz|}b%Ydo7v*7(*SuoX<1yj2+VX7$; zrkXQgx+xQ;ajXpc)4MX{`0h-Y+NoM?JF{r+{T*2_g@)I6WWoE5*)Y|Z1=G!G@XNw{ z7{2Cq7@1KIBR4b{8~siLjNE9)Bj4EqBR8Pwv07us^>F%z2C`9QxE|XJv+sbT*K7IrG2m`4c@9)3M0`C zFrHkctUB0F+6?1K*j5WQCvg5O_EW0GS-DRJDhVUtNJII#aY+Ct z;Y2?a4fR6tnPFHzIs_>wN0^uy2~I>9DXO>fV&zCb3m^h$o=XrBPz%@HQU}EarSR$f z>2R?v3#RTt0u%`ZXqF4KWBeN4n@Ki}--Cd+VIM)yGT=|OWWfi!B>|`BZh^7nYB82n z4P!~wVk{ZkDlwi^ErC~r;57v@wYW}$PWLGA)jX{^n}h^x2`8Z7=%Fo`go_4ySp*>X zvZY8kGb|&adI!v0P${b-<@K)g@O+@=yl9c+k@RTOks!W(OC6M~DHEUU$$*Op7J}Nw zjDYp$B*>BgwFW`fbJ(^94cAN~0r#cDTjfh(1PMU!2}A@?g2+KN(U4*Rw;u14APGS5 zFG~PMKp92=^ZY{c{(cz+)Yl~RL(=9(DMwVIsz@->BfZ}e;`PnRCmlgp0#pPb0mW-d z;p6)=l;>LD9pm$i3|y~`&zWloIwzzh8!i$7%iwhM7Uj9FNlk1rCNazMQhT?J4sUEcUA8<2;69*6 z6Cgg`lPM*rElZ%slh-S-FZyIK5~REsz=Z*uljB4{HbuY%O+d=#5E2k@39O0^?2EA! z3qB`EwVDW%6D~u*iw))H$E@yO!4wH1X;UN2TBziR-syCuM;ar>OTy+1$VLE3kV621 zuf3Rq?Rb86pg+}?E$?w8C!2wv4by0f0E&RQG6}o#qqIDw8pcwXBq8`X=YsC^HSpRl zq-}v?b3+JN({;F>a)6Pb0atz;!)tv|q9P%=O3IP+c31x~@v7EVNh%^t>8j<(l_2rS z9-Tb1r5F34M<4)!mt$#kKL=cq;1ZCbB&Y$#31npiu-1DyRx%^|DOQRkLCQ4fX1$7l zw5{O;Py{$hSn0oxh?71ElBkj~B}bdK!_0+Bj*Q$WsZl##>HS{m$rG?FLy3T*)urO& z-I>x;3G8eJxbkdnULKG{C}Id`COHiFsTM{+h0Os(0-5o41*#;&%<%~D#!7;W{NUV$ z)Gg2xPQa0d^7A?gGoba@C%}}bl7y1eNWwOlIbV}N#|=vJc_OxCxK)sR8m1*^ogzTx z01J4l(sa*ooj+&p3sgNe5_A&Q1dIfdum+pSkOtQyA_4)_8VN!IW|ajASk-l?&PY%| za$J+ukzI$08%bC<+zW{tw#i6Pz;iirOeSFaAVybF`L*JDl%UlpL0E<=FSZ1jgz6A* zk0L=`yQuG>v5o)^yqvqplJHv<`EYvfR$J?n%up1N*6YYn^}Pg_fzQiQmY}u4cybNA zow_xg0G$I6Op=!n;4f=f9gUrW;*&j)P=1HZ2}X{%1Sz0R1*CzO5rBUaCL^E>Fb5Do zj=t-0HuHJ~W*9*Z@NC?|)|w=oZzV{9PkoU|x zP{bt6t~+wo4<%!#VBLuxNZN209c70`f&!gAiX`A&0?NfFyEEYfj{pv;_HqRNB7&-; zfdd`MNQUgX4|Yoev;=XFKN~`TBqxjvb)N@38twy-fY6QD$}S{8ao|&nh?t249;Yz($H~v*98hIj{(_eHj67mM@1>(OU^*^kx|e#;ZkGB=GUs zR7HT69ZQNlj7zDByb&V_IzMibuq!`a8LB*9uI0c7C5kGf$q^_yJphaU{wEMyxP@eC z=AsQSXIVAOU7^TeIpEq`>!DCfkcj{bqy!HE=Vb@WgjC1JG@TW6%mi-tgA)~BWZyxGVJ zB5HU5O3#gobtih^=6`-rq<^;!Vpi19gZ~-x$|zyuk;d#Lo8VjXs-Q5xNPO}AB)D{M z3VeKT3Vd=;s|#4le>!jvxYNuixb3FRLJwput``a0>@J+z5Ox4_Ff9VICPiXBZxY_2)*Q zbmBA=9zQCUJ^Gkf_=BH_oO}MeNWcAFNU6CS=2q;4wHt01lRNUo1X^RhINL}qpiN@C zolHz_UnVBE<-@t{%ZyEKUn(ZHFB22n^3h=8u6#I4{pDivu4Q8Ku6!|hXFj#(?#hSB zJC=)yZOg>u+5#AjZ&1mVgX;rI(IMPQD6(-$A_)qM0DT&2a=^&}1!SM_ft86CTXl`V zS3r};yGPlY0Ee^_{i5*HpeP=JG1svzcK3okY(-{J2Ur-9n>D6 zKH9F#C&aFdePUO}ezNbSKQA6h`!n%M>PC6GX-Z8ep(@~|thm=@l*oCtOI9r>gCZa- z2N(%bk0A|MCtdI?_%5IvXlXVVZ~~~WzSG#repsveYflb9!MQ=$@bF=1NqQ8T^EyOx zUMDo?c8KO&Y&)SvTMo6NHMbL5b2_0lcQ3T&?1k2waJ*Nv<~%OP(nOo;x82lfvjGQ9zbl4R1w^fTKrhi2!~6&*cXbuzwch zd9?*u0*?f^gmAzIl<9Q?uog`bv4#jZ0xe08Kob$r0nK^sqB)mZXvx*qVXQT`9a`;J zwB~k*HkwC6aympy4wBUYZEDf1*RIohgtsC+ z55NepM-d!of2@F3vSVb%j}%j)%KL+aaH6!yYilM4p%Mv5eiWMKbwcyJ4rxR{2ee2N z*lN&Q^EyPU9OJyXhGV*hZH^iv33#36+lh$A+ymnAjNRg2k}F|6rN$+IsvH+k-5*P> z7I-~Kma|ziNjPEztUB6NhY#h01caL}LpB05@cuPUfZxXmkYkE~$w8=ipk+iRUN3UWK3G4}vGo_-$^pr3R_Ab|BH@J8>k zB;~SY&yp-T5zEkmE|miiH0jkU`34CwG8F*1k0L>j8Br{eGjCTUV!IZN2pAA+CI+G6 zQ6fMzV+71^NAL{+VP=5GRs)auLCz`blw@dv@;U@YLR)S-?8x0O9?#qjuO*QLnPUbG zw5j!ZpGAU85G7=1<0dzDvI^=6BLI)1?K5E)+%RKAF#>`fPJ4vFb-YIrp{^_N$uI)S zDFPl9&GS1&^SpNQdTk!~Qf{ctdubk7;gHfUhSZxS+44UV& z91sNGAwWx!8B?+~UN7^7SxeOu$xmBOI}#v>faDDU(9Rl<=iVrF(1OMZP-87Y zE-9|Z8~M?uB7ky0-T|>UV>d4U27@yOADo4%=n z6akn6R0Oo7JPJ)-66O&QAqsRJK;sg`z2Aw9wj4=7N4g{+%*z87a*a+=z3cjDTR&fj zNPr_jVaL#6p6x|QaE+8QTIW?#mItTJu;_M})G7614irp;}FV1(yR&fRc6-$jswi z@b&%rbX0D_bYEU$5unb4oGM5W(3<)f?gJF~oj`|jxFKUXz>px#djCA_-dkhHVBhn0dW1jFSk?7H9X*od1R${bB-E?%^_l?41in1&v=8y+qslN+hyYWD z>Lfe_6%ThoOKLkb&)@42&h!ol%PGd55Q{0g2(&+c9R5D!8?(1 zfT@Ba5ujwl5Wt>-xzV5=%(GWm6iI*yPzU=O^(2+-P1{WR5AEqOim`8a~1=Og&o$0QtD1@RofNig27Kn%`XE!UF5 z=lNO#_{jmbGxj8CsGm+li2#xyB*1zZAqy57;Uaq>EGP=ikSKQ=NvY2uR)_=p09rfL^XWoq*nKNHF;zZL{=rn;&q;Oh%ES zh?rD%D84w`$3JSpKmF6SMZL#9T8AX?N=Tmfm>sb8+Msvy1nqjbM+A|fz1@XAKHjPW8PD+4_`}3sgwt2 zT1K?qb27*_V-BD^;OIXmAgu}}eIJaHv-SYfnNQ zPxifHyUJ+4D$%d?sx307;Ptz64!LkCGqR zH_}7%CJ(6cQ=bDF0>r`e7V&y=rNFwKgJ?mOz>lYq1QGZOU|HwWI+Q3y8WLn%_*Lds z7)Y($?c;7fOZV=LEgb6mExy0W0kxopo0cfHULXO^eIX>M2vFclE5ICZegN*Qc?w$7 zI&1`}MC{0rYY4JXJ7V>8OzA!y(goy4J&x=Y_vUuOj@$#{#k8IDS_*>3rN;GgQ}GDh zcQ$EOrgYR$dp@li_5bt3NyRZf?#^fR%SWCmdu1X3vIDrv4Q$SycrwDM4S5_%VAJt~ z?)O6hzLJQ$;8O#zuD2VS7VZ_zxqFrOcc`}`EZ{Z|sLDqXU{67H6zKhceMNhp?KHh=P(tZ2#N=8pW z(J31RveNO1P!*65yxs@MYr*4ZuKns&MDl#qu6$((s{h9er2kC|GyysS)P_vSkrx3h z0o&a$+mP8uk+Kw3?*!rdru)QS<~$(Ir)+=;rT+o&{Rl7#*p34vDLa=&CZ3I5pYG!w z`dNMS&_iXfpNF+37hFMx_(seJ9Eor?XHEi^)zFY6V9oFV6pZx4j#W=UYgVUx8YWLe zbp+@gvFV#=&;*!Ak&fi(cY@^GaHay14`Ukho`&}9KNoMNmYM`io~*!~2+EFAb_UFg zhWE0*3%yC@Kk;#n_*vTC{*9u+-Zx4oM`fK(z_~{hku78oV6|WGe$CS_{C?eE`7Z-d z^$*XB*2H$H4?l7oQB zaqxkB0DN;#P==)J^brZG#|B~Dv191f9489GQ z+X?s53t3Nz2QvQ_E~Ko7v#B-8`?beoYjxaIIs`Z)DI^*Y{5Lb|VKS}i<9+c3sXo46 z>a+5hX9|l)PJB@^ep>BV_0CtD-o^SPcm!w?7=hgL*NpVTnz3HkR=OWr(@^^<>1yfG zQ4lb-^Ht<1>2VM0NcUMqL~ll*?A)8%0Zn=P#QirtDkf7mi}NX))R{d3Elnm-PJkRs zN#Z6wUmrOW_?h)^F1`Ar=Msx=^Kt!3pZsT@TwQo-Xu9;3vqmx;5}20e;oKD+#@P8!~e8ZfUbhfsi1AA!fQo}+xg zUrkgb=+e_hfRrE9{MNj^us!df_-{A;T=b{k4sWEC(Qem*uXLZwP)HOQc}m*^=hE?r zp&s7JYJl~Y^e5u`@MeV|NTE-wdVNCjfI1KeMKh+zbHL_7BDi_pBsmg$um$g zF-lgf+LDPgP&|1Cit$;)>tj&($|!6+H~`!3I1Kk?AB6kz4#K@RAB1}sAB6kR7CjC3 zEy7mY)3AHdL9+W6O53e$&w_)nTN(EE;GCMjpXQ#1`xiV7yXQX*ZS!A%znK3dJel)% z@J>bzyqi`9zf9i(Z)P;WTN(A9)j4({qaH3$EA8!!dU!Xp0p7{1M-ncbN!j$$v82+5 zlB?psTBY;5epVkooUrEDOEpEk$Nr(9_t>**jveh;+kLdJ;P^}Z1;@Jj3c8Q>t?fD1 zS8%MWr?9K5XZ_D!=&rANqWA8&N4oEheWbfFuf4l5=h2?+Igj*iyXhCb+i&_s&s{hD zyk~p1vdo91Wk1;6n2r4hdmA$!=#lnN&yEZ^wUD!tUY3%I?9W4LxWllPY_LlPbGUC02G1CsuTy zNUZ1{PO9h`PCzT`8BQwiK9yMBJ)9`p6G;`_BgvKf`{PT0a6GBB>OlPJIX*6b`26AX dhtHS&{2ybva3QB5eL4UD002ovPDHLkV1g7(HGBX7 literal 0 HcmV?d00001