From 4eca093b0e3d71d25f98fa70ef84afcf12c7e925 Mon Sep 17 00:00:00 2001
From: Paul <paul.cancel.etu@univ-lille.fr>
Date: Wed, 22 Jan 2025 15:10:38 +0100
Subject: [PATCH] Fin de l implementation
---
MCD.JPG | Bin 0 -> 29614 bytes
.../infoetu/MeetingPlannr/config/Config.java | 6 +-
.../controller/AdminController.java | 55 +++++++------
.../controller/PublicController.java | 65 ++++++++++++++++
.../controller/UserController.java | 32 +++++++-
.../but/infoetu/MeetingPlannr/pojo/User.java | 3 +
.../repository/UserRepository.java | 2 +
.../MeetingPlannr/service/RequestService.java | 1 -
.../MeetingPlannr/service/UserService.java | 73 ++++++++++++++++++
src/main/resources/application.properties | 8 +-
src/main/resources/import.sql | 62 +++++++++++----
src/main/resources/messages.properties | 11 ++-
src/main/resources/messages_en.properties | 11 ++-
src/main/resources/messages_es.properties | 13 +++-
src/main/resources/messages_fr.properties | 11 ++-
src/main/resources/static/styles/main.css | 45 +++++++++++
src/main/resources/static/uploads/logo.jpg | Bin 0 -> 9853 bytes
.../webapp/WEB-INF/jsp/admin/meetings.jsp | 2 +-
.../webapp/WEB-INF/jsp/admin/requests.jsp | 9 ++-
src/main/webapp/WEB-INF/jsp/admin/users.jsp | 2 +-
src/main/webapp/WEB-INF/jsp/common/header.jsp | 4 +-
src/main/webapp/WEB-INF/jsp/public/login.jsp | 4 +
.../WEB-INF/jsp/public/passwordChange.jsp | 50 ++++++++++++
src/main/webapp/WEB-INF/jsp/user/calendar.jsp | 42 +++++-----
.../webapp/WEB-INF/jsp/user/newMeeting.jsp | 6 +-
25 files changed, 441 insertions(+), 76 deletions(-)
create mode 100644 MCD.JPG
create mode 100644 src/main/resources/static/uploads/logo.jpg
create mode 100644 src/main/webapp/WEB-INF/jsp/public/passwordChange.jsp
diff --git a/MCD.JPG b/MCD.JPG
new file mode 100644
index 0000000000000000000000000000000000000000..499a22166dc5f09eaeedf1d9105a2ce28b494cb7
GIT binary patch
literal 29614
zcmex=<NpH&0WUXCHwH#VMur521O|rx51HP%R%E6zF!=g1XfZG_a4@hiS~D;)Ffi~i
zFfe+xXELxbFfcGOFffMAnG2#BBp4W&&b^XhU}s=p<Y8c7uw!6gV32tQQ3Ya;bR3Xa
znxo*Hn3tTI!@$5gff*c{A`A>nOD3?u**hjp1hbiqjTsmwO+v5{A<H-e6q2JM3kn4Y
zHZm|WwK6cYGBQ#yG_f)<w=y(jz$V7P5WtYgP|A?QpuphFkjRk7kj#+Eki!5<?<{f|
zB_#z``ucgrdWk9dNvV3t`MLTPi3R$GdItIoHue<-iOJciB??KY>6v-9>hE{&S69eP
zu~iQ@^)>J<a8Aw0^r$LI4X*MFNv_IHwoFmCx8voqsj#ZZEyztRNmQuF&B-gas<2f8
zn{Jg?Yy}e5SF*DyN=dT{a&d#IC`w7QRf4*uyu4hm+*mKaC|%#s($Z4jz)0W7NVg~@
zO}Dr*uOzWTH?LS3YJf{>adJ^+K}lwQo&w0g#H9Sv5?iIx(##aAw8S*iq?8m>-K0e0
z6kS8ZltkUc6k|i(6l3EQb5lbLqcp=LC8+f&$#B~XN{e#9cBdrkr{<*QrskCt>l^AB
z>MQ9(98E1dD{>3qmSPQlB|Dpn+yX1-qSVBaRF}k(R69`g>KYj88X1MaqFm3|*w{uN
zO`VNCC@_#5N?n%|6<T&SDalsJAh+iiL97o>ErbR)nX#RdX@^vtKs=d~X$N5hrxx1r
za&dBU+UO%&j2RMOksud0I~#p?I<@2FQlJAE%$6YXdTL&Zt$Mk-z1{!Y49*PftgLLT
zEbMHoY#i+D99)9jT%4R-qWl6pf)b)qk`kg4;?i>Jiqf*Gvf>g-dP=Gqn%X+rQVROU
z`dUWnTH0D5Lm1gPIJh{uM7X&{v}7b?v`7a34=@OFFd8xoGBYYMFbOg;3o`yc!XVGU
zz{tu729P3=fq{{UnT3^&or9B$`~MM!tpW^8jLghTEX=H|EG!HRjJ1qR%nU4otU`*0
zj%>n#iR?;+B1Vl97jh^&Z9FI%bn%0VaZ*teCzqJGgrt<Jn!1LjmWipExrL>bvx}>n
zyN9P&a7buactm7Wa!P7idPZheaY<=ec|~Pab4zPmdq-#2q{&mJPMbbs=B!1Fmn>bj
ze8tLDn>KIRx^4T8ox2VlK63Qf@e?OcUAlbb>b2`PZr*zM=<$=M&z`?{`Re1R&tJZN
z`~KtSFOa_&8JNMo0ud0Oq4`UYfr*icg@u`g9po=YrgD(S1zA`X4cUYo1KAS`g_VpN
zIYgW$F5GyKQ`tD^gJ@FGMJ_QFlZUDwL0$v<j5v=qk>xYE#}NLy#lXYN$iO7XEXZKb
z@XM`BV*%UA@9w`3|Gd0%Q|-lnAO4lRV$fLB#gO@*A!`4-`7iyJ&HuXk-^+hfAi{n6
zUnke^v;QI(6jE5fX7=BY{}~o^O={rMSkxdb|7*#AhLYfw8~-z`^8fB?)xecAzodST
z{amZr>UZVe^FMdsThyh&05=e31w?qoqyG$%=D#2RXK)l%br=72a{qn#UmOcKL5|Uc
z895tbM*#DRMO_SNru!{m0{I5!T$t$)VGgXOuhd{z)TP0QW_s`nhGl4B!thFCQ3Ds;
zNSGHO!dSzEVN#dI0w$<?;bFpH1#%*q;gFEUFdQO`)$kz@;B0U0@8@6sqj9T#-~Hu3
zf>$iyxa)WE^Y*}(ul*oGfsi!ew*XV+srd#qZ!i3+4&FBZ{q~ptMC4&9Fz@o;Z-4C%
z^)CPY_E&wYRTo2>s$I%`>)QVe`R}?WXa4(LH~*n;_urKJ@B9C<giK+$3sETT|NZut
z{|sEEB8)OwHCun)D6rMM4HZ-V&k!>IT~{fCIaGV8wfnid?#tJD`c8h=|GoY50%nMW
z8^rFpu5guFH3vU$cYgUcDA@nk)&C47{~5d(%pfk#1$$oeN&s^iSU^<9tLEgsvfuyi
z-xW1QByM(yIz%8r!$Nc8%1_syCm#5+5|;8AV19sF@JfTpJa|jZ+bdO7m!YBL3CYGQ
z7<WUxhEQs>{EWPHiQT*_Zy^D~{x|DC!>;<JRt?fy{xj6>_y4dM;z_?n4T_!qGtJN4
zufP3!(Go~Z1t%fpOqlNx!Qn17U$?GIhTp3U5<(sS8Lq1TUNqN%7pgrL8u3BFDZdy0
zefZ}E14JSaYWMO$xKb}@%!f?5{BLXhUHeN7ybu?|yk9B;i&S@^`9}G3_x)$s{@v9#
z@aVry`QMNKYtmqZMJ(L+@bJii#=VwTPW`LNe=j<)K^%-oPQ6#&%3GJp&D#PECm~pg
zh>`GmR(@tbcb9+pR%=MWIl_udEXg@3_?hqL?f1X^d$AJgs2lsQ`TqOypW%Y5C(P?e
zfgyNo`RVw%?aY_vLIY@#{a4R_FP6g+lgM>w%wL&u)9%H8hKhPCT<K2a5;W$os+#;~
zSR4P{{|nCa9C&2;nc2_t_rLvnF&L?EXMjd4B9x$kF&h@|lc1$s03+1Bh{U7}%T3T=
za)DL|Skj#`EIUEVhk*KP>who*6^uyFnpXlCT}1+zA*tx)6`1Es8AJkI8DRcfj>wG=
z33zr}sbzr_?yMq#4!r$;gX(u_Ubz8FvaDwQyX^1DpPLKI{Op0QA`D0-!faUpmOu)o
zNsD3SP5{f4KvxD4n3=v{J2V)t1iCVynZ6w2A9(3e3Nsxd!Gcu7!c7mE!T@p-s_B|n
z7@&Cz5h@I&B7qL9a1#-JfJk5s6$VdNkpM=Bi{YWtAWL$fM_R4#GKm#>W?dT|*mf(D
z?Ww!E#W|zG3)Qn2F2RB+WU2^{?9vp)sWSu|Pbg=s%oy}lasIUVcj8a=>)P+wKh>&>
zq4%-FVvfD;`Bmj$@lI$~EM+i1?f*{ysXJJ7t}BCEvcEx1_l0l2g7<9s_vl{;yf#BK
zuTA6fnb}X`_rLsiVx<jOB}y^z>GJRHpMg)8e;5C>JkUY#xRk_ojlJwy5X%u|E?8M`
zoC%jf_m#5mD>b2wI)=*3zeoS9fXJ>`!1fcwUAfHUGy93X{P{~geeGX||GoTIg8{4p
zZVfM(8&c>tFXg_{-~SB$-%!fhdr|d;^;!_ezS3aa2j=D!F6=GpzEFm!yX3&)Fl9{U
zy-YnyUuE~cglJa*R|+q$Fx&@+EJSgs2%}`L&B0Il&bMFsfgOP;4<OdSjgwgHZ<If=
z?mxrw-%3lsN<*#J1|9Yi@?LpAI`Y`1#JESPJ;GZGC%K0FXJ|QoSpJLQOZy+P;{O>$
z9i;vtoEAFqKLclfoBfOAOZ6YD^#5t~|M00X{LkPlH~Bw9<Hh=qA&jeKGLmB!eUV)W
zarv?SV|J2)>uOw=e3Ae37i6FxE*r9sHM$8__1*hA70r@a{|rBz4E)a^DmM8_{pl}p
z``-t!od+i!NJ2}gKa~8i|Chv<`G5Gj|1)$g{wFNYQ-9(3lK%`3O5*>7GGwDgWh$D6
zW%DI|q@RiZv0}@-_J8q<zWry&YA~w-r!R1tSgnj^qWp&$Ka&5o*{c6%U|#f};n51_
zcyNLaJStT=xu*O3U;CS)Gtqpp7!(ph-tz@BZT8E*y!fA?q<*V|bR9{7yB?gjm%$SD
z^5jRn(!ujjTrRDaK3O<l@7`0DoT>*>d!ao5MzHF|V3+wVYEZ7Ypm9h)@b<mAVDUxJ
zV%!0efZ^#`aK_~`@=9N9_f2^ZPHf5>+<Bg8wW>@iVE1bN9qj*~VgBp<-+%T$b`S%n
zuh6G*CO4N?Y(COk&AsVWl}SNFYsa>qj0!6bJb2Re<v+t)U;BUcYxlqZ^q-+u{?@Ge
zKmQq`>+e7P&mhHc^uyji40^35Mclu+J$R3O`RQn=F8Qu_*5|WX2bbAd?aS1gRBQK7
zeAA~VsocBM3fKeZ{4V`!`bzB5i;G#)r>`u|(o5IqSDYgh8j?`@pr|VJKC)d5Ru?#P
z7EE#tIxn?ZcJWG4?;}-9;^aTv{kG}?XWePef(HH>-{gHBt^LoCmHD6H^WWV64E59h
zWY7H3yCTy2!|O<obq~t3Ygi*gCZG7ej4iG4!)ND@{Ou-h<Btm7$+|W5>y~ok82?hO
z2fNyCty&`*lFq<*A*}t6mOV@T1>ye;YR~>NJbImd@ct*I7xv$HxBq9j=pAhm!)N?Y
zRKCgn1?PW;$;I^_OX?3T{^0(X(f(6j{y)9lRvNpRd;T-Dx<Bau#Z>>~&iy~J@*h_G
z5&X~K;Qwd#?Eehi>&|uQ9e6bVp?zbL)2EDAT~?dpd>H;S9DgbO(7)l5zTuaah+pr_
zCo}$hzt&&=^8?;^`_G4eb*+A!u>ZrFe4{(uA;<T7nF_|#sD7~4&|cKUaYE@`Uj4^k
z@jpW9IQ}y%Q2*z9_CLeXtFit^q~`W5HW7Zs?7p?{o^htmG`EN}>q>ov-+MlG`MMvS
z{&uZr%C4q}<N3M5TefY>Em50zy<DbX#vi}TS5jjC86F1yQ2y62|EIb6e+F@P)KGb)
z@%3$aNdMsr(PyTKOSP2S)%51ik(lX_u=c#N3jeoO$Kpq}akoQvZQp-2>9R^!q3o`h
zoAOEzEu?!EFU+f(HzkYpJgRdq{%7FoKPdl&<v+v8o%?_6dNbu+K*f{v=84{3Z_*{v
zB<)e07CToYw(%HBEcBxqem&h)JfZU1(ginuFh^X_`(vWMZQK4Uk1xM_6ZP;(SLg&8
zJDIs_mbxoTO0uu5k_VScJKlHP|E%?&;l|1I{|uY2r=#UC4~{dL&-f=+$=&yY)J)Bg
zcKlqJI=@8?T$=k5zU2RYa`73z+kb|Az6`KtcJCcg8S}tD>euGNWURXKXV^;r`)N|y
zd~yH#$qtOLX7-BH{RRbh`9I9hDu*awc!h4N8Dgly(BiY&!+rl5zWaZ7^~7a13%FC@
zx8iYua-8&nf5LvB*%#N}S8I@kwgnfTk?Z}>P%|rghn7X&;vfEN89ZIhp<*@%U)=wG
zasfM{l?4%5HeD{^;V$`yc2^ixMeZ9^ng9Ki0W%ifXVJU|4vs5@NUmh?#WXt^(p80;
zeC&bS9&^V(Y7HujW<$Ls2R7#dBeZ9|;_kt~!=>_vYBM1gTwq?YB7Jw$wY$%EO-<}%
zi}SP#4SePKGV(vehbb9<*L|{e@vC3TkOga>9Jf=yWOJbIN|pT2lz;cGy#L|(rT^zI
z`|nTxGaPC#U9o09vOqsFr@`Vq%Z-=&O!r>j<GlXmw(r~RZ|P?;-Bp;G`><CyYe8b;
zX(110mFuzp892(@>|exOs{g>I|4(gae6x=H;i$v<TDQ(Tk^jL{`l94UPyP?*J%;}o
zoSseo&(L64|IwuWknF?$U*ab7|1fs{XXsozOOxl=e})$8!}4GBGVOmbi~na34$U`_
zXUqKLFnyO^H*<{LiRPOw=Gzzitoj@)TUalk<F~YYQIz75NvAD(+du4R?V5kE!Q@D0
z{EzyByHa<|54dQOUD3^Y?^___<lFClhc-U=&%k5UWMjSLK~Mh|iT?~gW<9j=Uw!gX
ztdgX7k!tIN9XsPa@9x>Vzx{mpyREvxK_A&xvc%uE^PT9S5NkNmNw#tOzQ@x}+FBL*
z9Vp%Zp}ivcU+Y=*{|pRC{}~=-S=wkmwD96Ak(K1$cI9rmz@1ulnORR$uYP^Kd#~Q1
zf3}{fu@*70uFKx`xlgY2Ju>;wpPdK(eOUFKb9L<Y_QzM(>A$`6>O4!Xvet>@Pd8T2
zNy(Q~w!6~sI_Hn=gWunFMEkOPPPpxrIQ^gM3$q=sEaU1HcWp)wcJuvLmKa65uDIiN
z{*iO~v$mhp>~(j@JD;=7oV9KB$`|^Nw7FxwUIhw8{uGX}C~rBd(c7U?cFrwhgKYgD
z#d{L}85S0M{%1HKvH!;``-glVj{oJJ<^PZ2=zoSI*KI3GeYKBmvkL87TWnLDxjlGM
z><Q`bhckkXGb_()Z#-*!`1|$=S+{be_CDK~U2e5$Pk3W$)60hQCte@=)t`5T@e*2W
zFa9U+o~Qo8PEhNIC;pFb&P9(ja{`XIc2<P(=#S$E@5%Qc{I&G%w2A4-)v4L4XSZIR
z!_BkdwA8e)umFxH{A+W6IEPP;{ZKb^<+(=H9oxeSmiOM}{Nr&}&hS6OMDe))3>$Kf
z|Le+nUMKsb*=OsI%EivM2P1{<^Vlr6c=C&@mZiKT%>R$dFZVxx<$r(r&+yP4nd6bR
z1lsl8c>l;R$M_$=Y~BBCs*hi4{ZFV?`CncC-=Fm#7Vu_it0VCjBe557ykGn1N{xSh
zX0_DWT;A1wiiK0t^%e+O$uJbokNL{9#eUP5{|rArZu`%$U;Cfv++@T3A6a&_6#7hC
zoukmXv~$9#^1LLGp11NE>@uHkv%L8UDNsfKGa%~|sZV|RpW*Z4-2V)9;eWJ_L&{cY
z;Vbg!(i@wT`!D3~`_J&_WBj}RtjWLiK}{y<{NJDUKMZntd}emr@Ba*U?eB`JLv%96
zBddrxe<auLlzw~E>7(gPR=1xR9a+%lzO@umI$dM$TiEyhfYg(se1?eA3I~*@1|8nM
zLga#&jqHC0CB5eV3<<aE|B7tg99Q_TzblS!(}%wP?9w$kv$!RaC)S-dymeZFB}L7u
z>&kzI*6k1afARijc(UaFA6K!2vXXGg{|wyu2j#y=|7SSqx&KF({lnZJ%Kw_{e`=cl
zXApFqAv;&}SpA{fAKd@4{b%?Tl>bku|3@^q)o}lxm+yauBY|n`;VU9;zpASF7T&p!
zyT$Otz6IA!|1)ryY`%Rkb;axJ+h^tQtl8GrVa{q9aE8r8l>d+JewO+R`TrSI)&4U)
z(m->SRL8|DHu}Y-ruT1}?w%c8ee|~O&fb&&!~QAT@BRyw_>gro#@()UrT?-W+q}~c
z=`^`GwyKvGz7E-s;&68~ORkuhC46|~Sh~b7Uw!766VDtj%{{9>gU!=E@UPaqg%72B
zUGsI9Tzls>zfyBjbh6a;;7JS5E7a><_5LSzzsdfE`G1B<lj}c<poT=Rv-8GYmcTXH
z-$PjC9sSlB<5S#iIG2c^Om{7u|6v=b?dQ1v&y?B!89ElD8XDP9<My9nX86Bj-u8dz
z?Y8=~U$o-=;dv_iKJ1;=z2fh(hc%9wn*K^IKOGbNA|2joU)ck#fCGIY)oVYvuFYhC
zRc1XqKC|;)_|L%kZ8<bG^TDf45%4g+G_=-)4gIeGk4G$rjg-!U4M%~5nP*<D$zGmu
zYwfXl({8mbubZ7R>AKIB?DkHt9`WPcbIL7O)qmLd_&>u!@1yg7Xykuf{P5vt^^Ym%
zKYQI%+UfLsWhlog;};AE)}5LmlmADj;y=TSppWVQxTODN{AXzGm;cKZr}LlTpmTk1
zSx5l8*nGjjK*tZ$yN%fFp9uVCc<hI+D71O?kGWs&W}2LiReK+}BfEY}(wzl|-f91F
zDHma!{-1$c&i<Fkp2+_UO>O@fq_S&Voh&{~@3w0G&!Eu$_1Cp~{f|7hM!#I6wt44<
zeXT6L`9%wa+8YiuIZCe8&|j;!BxT#KwsikJ8&62Azrw}v)c#@e@&61?)4T0I1l)h@
zj^h686}f%~RVwa%yBlZR9<0^0e7Uks$o}l@H<c?t=5|a}yl2pU;a32=D!Q98e{{`0
z_@AN0`{)dY{+}%N+xM1EdL>q%Wb?hiWfRY0n+0rgH*GF2+p}$3XUdKpT`KvzE?ETB
z3p>cnN3k9~_)5&HzRgW7%;fp5IeVLu84uSNb(QCh2cO?sTf6J>Tl@6iA$IkDK7D3;
ze|jxL(R`FBTc&=*C49ns&Wg>?a{7Nvv0os!@9M3K4n)M4BL9(I<8!W8P0r@KwYhU_
zx0*Na!{Hrs7~&`Fv^~eZ*Zzo|#ubydX{X&T>gvAJSKBh_ZSU!+p}hwDeotmy__!t0
zyrJ~VROgkK^NcwDGc3!EDeGPII_g>D!}Eff*UQeXP@AbbXMfJ!Ha81)Evp{>wG8EP
z($~U|*hz1?8f32#>%L=I@s{T-bqUwX)<51W?|LO~P2SPV=QD-Q8XabwoMm8<XI)TK
zU8iii$98$vJlVaMN+fQdyY%+Ur?e?f1!;HcySEizUN16zpU~bNAN4y-HJ)!5d^Iaz
zV`s#i#SM41%i87Lo6_)d?YoEjHZ;Avdib!>l8(bWER@!S#WgQGz4%AE|K)X(a?u%+
z%u>`dm1T{irZ7%kr7=6|lHwG${<D)}moH3p-ttfK{1xU~TUTrSXZYu)tNibF{R^h8
z8H_lx+W9)Z`V+g?*5AkiO%3+#<=V6O;6Jtg!^S@*`!yfl_4ZHZ`dE?4{+mB94*b-x
zySr54uWQNt4;%Nt@&je5{|vvia@@h?4YH8^!{quab03}mBl@4=TS!EDddPZ{-PNyO
zYw!I2bDH0IvCEB<42oYL%bxk4p|$rvLr{6Q{fF@X3^xP+GjL1)Tk>A4{=xeH44W44
zwteWEb}jtdLY0WGoO@nw+irQTz?Q8#u==4l_vw>*)1r4rF6(hOjyjk8c}mIMzWq_h
z@f)}}P+DA0=(3u(z54mhlg`=eN#>?%yxM!Ly65xxs+4)Wm*)F6ovphlKhbiB=cOdo
z&-|?2r7F)h?MLxW{h?$38CF^!o&Q7oKf{|9S4_;5KFpiVcXwZ*c8&f){r?QQ4#%oW
z4!#V`-Y4Dn?t0jcEsc8m`D?EkiymoB&YiR3#8dzMYtzt7L3j5GftPh6J9mE+*mrf!
z^QL>+nz<gk_x|?T{Bs76k(CDbaodQG(bM7tHf-9veW&rgJ6qS)o!f5C=Gv_+!~FKn
zHUB?4`M)NEi=_SkPWAtYxc^nXV)K6n*80Cwr5bNuj7i6%KunU50_N;v_Menq-~YzD
z<3Gbi-^l+Ay!KCKy_o;??O)%7AM=mj&-&1}G5<dU$NVnE&pB!SX1V5BYRSqv<pu0P
z*{eR-2;Tm|?C24mHP=r3Sn+9v<X@BB9n3QpbvaOH06)yUr~ZlS_5E+83;#1Lnzb!k
z8pXK|l2&Cq|2BVkeb}hmQ(tB0wyoRyG;V0>TzFDc@lWc~hU=x)yI(xq`Qho)IX-on
zPo|lLKECK_dMYbds>b+3v~N+aGJoo>f&-PwC+yAqnMKzxy!TQ$yv6yk=T=X7t!bP5
zdSoWPSRm}<?ltS!OqJQnS8^r4y}z}2rFCu9m*q!S-1N`6ZV(rJnyL0>P^i}aAU^Fg
zZ??P^`_SH;QX#PA`iV{Z?$10J&LVs#fwA9DD>re$<##o{99y<$XXfWDzNr?b(XFp{
z(BnZt;f0F{J71`WZJq76bLZte-SsvdH#bb@-mW0TnCJB6RaL00@e%zM6-~9KYcJVr
zos*vXTJ1y4xdVNC29>he&DARuui7{)`*p0|+G(cfn@57~+Ux2<C9ONySNxm0_n|`l
zL*pG5kAI7uo`2wCg7qqmMH3Ez=G)OQpYbE}Kd1J8Gm8Ar@aF5y>yjU(|E0$Nmel#r
z@U802%Z5N#kp>*|?iwq;`~PRKdHkPYfBPSeyY_!R_y7AR|6u_;beeUQc;>dA<MM%j
z(yzbtLr8Pt)CX&cv~0PT@b^wF|Ht|HS!FN(Gd!PD|NX;%hE@hoSNC1@KcCD0{bT>o
z4OyZ{p0EB{)_;bLlRp1vNLhDo$N6^2<6YZ%<bQa)H*Ws-w5;R?t4N>+(~i4A_dY#;
zl#{tUo?EkiQ&EaRr;*L&WBd=#Z!HTsucmK*{?p~Nx3{0UWuAQ5ZVKbGqT@9>d&2Vd
z)Mp<ljQZ)_!*K1dvm1k~qviWqb=}$g-_AAa&Re)>Va$}iV`4(ya(9gOaeh5-dpzn(
zb3Wq^aW;uF2NFED<S}2i&9czZdAl@IcdbI3+wb!-iJreE`aRnCB>(T^{|sE`9dCb&
ze_LFi`)%&*n)k+Una|wstNR}F{rJ|LyYaVfy|A;~x@(@vj!Ca_Cv0YTC&_k)Z`rQD
z)rY>#y1JszP;|+%XPWm?TEuGm<XJw>nHV)G^INcA+N^V&s@hvlT+~dmHSU}8PR!=S
zk+hG>+&fElT>q_i`^3L1PA8IYetz?W=lS}&sU~J&`%^ENWlNPz7r1>UvF{(-Z<C8#
z+9mE@Xl$^W!|xo|Z#FlzP)g-n*{3DR9P5Q9EWWMW75OGtZ(+o`wTZJ#f3H+YZ1~0O
z^)UJL+a;Hl%|1N&&dsPOhwaw`&Un9X{kGCF=uYlQ6~4I{`5Q7n&hA)pxwJa`%J!Uu
zq<wCgR_X6$gfI88R#o4KJk8{&x`bb-XyTTHRe#yD{NKFR-&(u+P1%2jjB_>bHOv21
zoE7I5%UIOq3|dWNxF;FZ?b!?J_VoW1xqAMO&AuzjKdKimv2vNL(EN0QUvs(oM$2{0
zC`Cu)eBPe@AJ%?}UC~lIzh|fH`K@+Mud@F$WW1XHg?aja2Bzfy3{v)6O8tK^p8n6!
zX!xI@H{;#>f0f7oGt~3{2!y9UkF?}hR?9B_XArWi>i=2UfBm)Jzl&GxU$E={V>$lM
zwDwl+V#DKC>LtJa3f_*CO_;52-TU@AwCi_3n{~$9$0=>BldFYpoHjhh&dk8`Ro%92
zEsi8^@gFidy*xy9*=P25k6+aH{H{hZn#oJc_VRy*^A`UZ-aq`$z-5kX?Svo2{~3(K
z|MpCu|68@J^jM9>`9Go2ZPNdQtoQVM`91aK<phyHR|QbG;=*X*ePtf+e};*|j-X!u
z{(oIjHYpAt)b{u@WNw$V|8{caJbvST<_zc4ekQN$);a!X2-|n%7vpXm8CXFU+O=2_
zyX=;+Os)IJ`SDAwOa3!_K4<^?NByA&l|_r;CCFkVaT#czW0lb5%I5EdU+j<7?p=9%
z`G1Cah5s4uAO0u8s46OlR8mMINin!WS43!et(W$F=H24I|NOc7UAC)ykwcMT(nhp&
zwi=!WBJLSv?tjR3y;vrpKJ89AU;XpR)d5UD{3MotkZam}Y?8+1wI}!%^!)UG9kn_+
zqeoIVF)Vn3OM*bQIoiYkqxj7Pj?O3U+l;43vx)rb_X^wAxaaJJV>S1`&UF0zw>Ha5
zgNc8`t=!PxI@TeH?$a!O1z)<wvE;LIn@R2NaxI3!m-GGDp9TdwfTrxRVVU$J`9G)D
zze(BrpW)`~&Fl0Z*_HhW&sYB^<ZYAkpJ9J&8&V({;0Wyre2^(n&8wW5dk+5I@k{=(
z-MuSsQA4Y%9}zuW;3Zxpt)BaIo?Bj``#*!oe}+RQv!tWy4>^|ac*Sopxh~wkK(HtI
z>$>*xBlmwkeQ5vtckzFQBiG8?)TdoP9A~>@d$G*MY^m<WeX9;KvVUH6U24iVImy^C
zd8yNS3lxNOr?hD@pX~Wr?Qr*cf7C+h=zVJc8P4l-{%5#<d;UMQ-uuG!&!@NiXL$ei
z{C@`F@FyR2U)^_H9-XOMT6N9v?yjGQOk<BWvP?Y2^>5LF_Tz&0qFyfc-7x9V?*Jju
zEk-P#j->NN_Dirc_eVK|-~DL*ud>4b-@iNm89tii{h0lq!Df&8e}?+J`X96IUif-&
zP3=0(+_+2cPQ`0gNH*tMoKWan(D!w<!#Q&luU*{zDF4r=5A*;2-T9y4QTC=q4HkD%
zo$2<zbN}blhw8t7d;XuHbKR*fhR5YKzW*8K#R>ms*uUNWkFPYUH+O4kF!$nh-<uT+
z_$YVA=lR0hkHr6QDed2O^kHM}H_1Qm)x3Lu+ic#Q7cP0MaMo)Z>HiE$YOoQjE&B{n
z1`z$({xh8DT=1XahS~l946eGe47V0_X)?f9v%uPz;N;E=O_W!l>!Id?MP)#PIoGeP
zFSY0S(e&7P=kC(qK^#JlB)b+cezU)ulOV)#j?LBaKLf}4-v10Q7Sul#lmEDGAACL}
zfL#(*Zt*|C`O^OxE;{^YIHhC%ICiecJ9e>s(jRwg*niky)$^a>gqZxtrT-aP(vSUT
z_!40Mshj`D^<>CQZ19R5GU<Pm?aco(ENcAEFeRe?*u*{Nk4q*0Gqg6|;s42W_@C)5
z=<1XgJ7mgHmG=K|tI7Y*;L7};L1V*zhTgK0zygc&XoBj0l<Umzu>aBFTQDDL9(>J=
z9=j|dzr^qvqx#R+pReNX%;SG__;=m-&oDv9<^%uwy=7Zv;$N%%XQ-)vq14@YqW+Na
z<N9A3@;_bML8CG#K2LWQXYQJOQ0m@l$t@q<d-6(kO;3DY^I%ig^>Yl*v#p+Po7cjV
zQoin#C?`u+gVw4B1__3w`OsB0B#$*QAuU>hPZ?=>F=(T98lp{mAItv~EPel*?N0rR
zWefPiKHZBx-?oo;Yg1a2=g~zvT?L`Nn-l)Avwv8rc5Ug^`iHXK8=u_2_3CwgwScXR
za`6`%!KhtwQ~$1f(9f}b?$g>nY47zrch}u0+`i)Yz1MHUOzk<2);{XiO_QFS_rYVm
ziK98^sppwCJpvLvpRcWO3z?^TZT8{s`;@k~9mt%vX~!p(@Xv83xod83Pmtq0rWJi(
z__coFTC=IL9osG|_7&tQ7hTeJk<aHbsQUcnb<oKNtDBd_W(vG|WUKZnti0iNnrm3@
zna1xW>VIeLx+h`1^`hvZB~@!Qh1BkUjZV7x`utic#-PJTtTw;4atZ$!#qArXsnVGg
zCgi%+y!Xf?KKBKpSFiEd$yU7EA{q5C`nQ^RZ`=K^vlri-`0{o1l{Q>$0gLk;_mSr|
zV^HgrWsI&OK{$pf7#7V&UHs<20v+#PjJ5>Mfe%9|VpbD<<txK0O|;dVj0b)MU5kFt
z|KruAJIuPrwytn#n8SN!75l4fxA%wiYRs2!o6941>uS@Ii6<5?DmS);DX*~U>VJ6N
zU$p!`LsuW92Qj0^JDU6Qqi0w3ltQ{<jWi_K&)sC{IZ|tvxApAHI?=0Nl9#>;`}W~{
zWm;bHw42Lr$yvsiITsljSY5Ao6_;#&xL51jx#z)GN*!g&t){$IY%{x~6dJqAyxjJ6
zRhK4seHv(u8X9IqNuWzU<p1fI#s9ZC_dmmzpi4iF|8t40{&#S@{4dRw`}l(YGjQlN
zf8Ow)p)+^?@;{%VY8h^$2b%{Ibdg8kuDOe!=PkZ`4Ypo#0ehe?>Jrlh4DbeKs{ahT
zvKO_sD`5*o8A?Ua)(JD@z)ON9D==4YVnzys`@fa;cST<Hnc7ADd1E0r1-gEe0k-lT
zx>|PDtFr#NyZZg37$$W=S5ZQjRWevX2bf{&K$lImOZa(v;g@d<I5n1`tfdZMUWF(U
zpzA7;)^0ioV|boD7&>(fTYh@wXZGg}52^xKrUXOQCyPKAA~QhNJTDK)Tb}Xr_KGiG
zmqR5Oub``hR!mnVgP-xw-6ijz1zjiFAnOWUN)7dNgEVY0|4J>1DL*IIy#4-n|1K?G
z$iiL*EA$rVdT2&klP|IIKSPUn+|S$p9JkBO|9o=q3dVbUpt=;^EWf$V-v8Q@3;$$)
zL_XLeUUWD5R<uf5y#BOyh3D+Qcl^nAx_@N5=h~>3yRJ?8RWrSD-+85Tn-))%)Ske|
zE_da?3yn1^_V_RIl{qiBJm(}!l4o?qA<Y+_-`~FWYd$i|^lH!dX}_lKnfC2J!|NsC
zkL-(Idhl9lTb(~5<+rfrTUk_#aLfYLMd3f&tUmg-ADLzHtN(`LmACU=7shLN7<s(?
zx`5Y8TPt4p#e~?}S-C+s)jsn?OF8g8C~$v%)HiWm=By>34lj>)ELO39EoOc)ieW!!
z?Ijv!M;)<v-SeaW@}a%^A1wj5bT>j0c`$n9Xd<RP(3UA96+Vl|Sl8*wP;ifJci*AI
z$HFH+Kehe(`=zyu)w5dF<)>!K%e~CxnD^*<(DrQ#@>{&q4jJ$l9p1jT_!s-1E_;Ff
zZ**7Izt|X%rMuqs!CE`**QY}+<~{1;?o8O0tRiUCz<E0Pg7EFN0sTI`*CMWY+%PiW
zS!3NCaJKx(z1z2c3fs@${e8uPCDm?q$#Z(XJ-E)kCORsvqAgnE?b>xpmvfYP)fSh8
zi8$E>b*=I%urv+#-0}I%AJuQ&$9mSzTOgN~vn7j-S>_kx{Yz7uo-YrKi(hug#Copw
znJrD;-3=$RSG@eLGJE<BkI%`8ljqclvV?uxSYvm|bO(pjpX~xO)gw8lKFKtAQTg&p
z@-~ePF<+~+Pp|tj+j_3}vF)FaKa06_&+md|^6yCu3hNi1ulka!dCyns<P*!X?L8a6
zs=8%J=5=W-0WDib#%!nqfRhjI|D==wTS?`OT#us`lb2txT@?xRAgRwWG&hiFTYWzw
z<u<!kbnd^!x%q`=X09p?V&_E|%ixVz<h9ogT*p54|LNHE{cm%g{g>ddv&j`m^~nn6
zDSNs+_sLc$S)^vjF4@Qw!Z5|o@j1Kl=XKLgA3CNRCF>q?Rd1_xcflck3#~Pk4<@W)
zxBF_|y#I;oh59$r`TrRfP1}~)@#$g$`=Px|6_2!(clCL%WU))uVme$H_t5Whf0nPO
z;+{)?Tr0Cz$@=-fSuA{#cS4uhX{N;mHX9B;mVJHY#iEDFZC7%xn(A|{ZOInt%`EEP
zko+(9$KPzh^esC+nDs7-T>i)J;m!sdCL@*JtDoLfylkj^KkKWP#*T>3x=tMZEj97#
z(|G3Jx>R%Hd3#Lq=XHJ8SlS=iFmYTnk<#YTIPjk#YR12<R|8!SXg~*$kOw*_YuJES
zR6<r-B3d=5>n%YwE_mrJWThoib(>}aU1DqvUaAQyfRN3TNk9Bz@jQ94qeeOtPd?H<
zT2eUSz*`o<JK=HP$}UCDcHiE)wkyYA3ai<#+?V}XPxi_5@QX73XK<E%{_kY$Tr;-P
z<Lz6jcXy=kxSX3ZiIrQ}#Q6#H0=dJN=H@TV44WjWyXAGpoy}%XI|`!PjxVczyf%K(
z1j%W3(igYfK3tx-EZVWaQ7*}iQSx=aSHvFc#fMk?-KT4IQ*XQ2XP0ElufMKM6=973
zZFTz8j_x;Q|6)(-T2*>?dcKNCes+GZU&qE{E2UapB|-xm*q7>xU~Be}(&XvoZ!D2n
z^q=8`+5PQ*jiPE9<{v5JUGZYFlV0j}SJo$=W~{bq@VnFJU{kWDzCB!HK3_@L&Y7W0
zofe$9vs*mR@|ACXR+ZlTHMh8Py>6zp7}W06KJiQNbtL<NDiiLUU8$QQJMtBG<>b}g
zi+j5ub;s}Hvm63`tj<)~$8;s{@$Pwy>3N?x9u@7r-LPchd6_v?*Zo0GVfO#eus-8I
z!xzEOt^0XjN_`1jwmxOLwxnXnX2!N-Rf2OK?~S<h#n;+xtJ$sGO^3NHi+1XsWJsS>
z%gPY9Xf|>@BbvBq>jOc}1#rrMtd~b=EMP3`4q#rf47}VKvXDK15!wY^W%1Zq>Z$mG
zx6q~H%&@(T;1$j**GsWVxW2NkzQ8gi7-{A63PxD38nV>d<k-A~pW+MOegQSP-t_-o
z{WEZr{k!!~9r%#;CW^eB_@AM)UaL#R-za}7WYu|t`l8ujm2M~gGwj+wmB9+SBL<@1
zw^!iU&d?Xu;O3V5->ZLC+?4+w{%HZC{i+by8n<}+Uh9<lyYZ($Ap&OfLTqB-hwUya
zbqlmP_-Q?8Nj$^^_VE56&pZ^a?Mclq-x3#pGg@z!q|qe<i9^+G>*K&mR@}RG%Tx3o
z$IR>RHad9xl43Z2;r+tt=PsXFx;|%;U`eaTjVM*+Ne0Eum-d_wzhWBkw#IYC(Y0BI
zr<cDz&ATVt`e!2dx{!4RZWdqU=T@!V+PcK5_3f2Hjad&Tc-&N13}HO(c;(CLYUO{g
z{)I5SLMeoUmtM<#GIhbF-2Ge9gSYNk!?D=@?THGx2dR5kOCPNYJ$t;_baijk`(pRy
z=U(P!&v@jdoMcq@dHzbS`rSUf_3ZU0x)DPpIoDN}yqJ}?@xy0_+@Mq1i`k}gcAtrP
ztEPI+UNK^xbhGD&_lGV1gqXaT_3PH9NSmuM#uFkK>Kz^WoqnfozcuUjTfy3e!Dlkc
zdmq;C-M-HDMbo=`ukODU`+Qu-=HEvN(Z}mepU?8!U!1u*>fJ71$E|6%Yq$GP66Tq0
z7bLrDg>nY_m88u%Td(yyT(YrC7n}Bd!X4k@!Y4LoH>TVY(E2s`q)kHdUNP=@>+_3Q
z@9sLEX;OUXKSP<v9HZ@d9E&O+Zfjk(bE;xz-RBbrLux(OvB`_RD#{GFbz_USRo2EE
z?giJ{ES`2=m|7_LB%w#Zepg{&#r%M)iI;1f7iJ5)SFBEyy}S34i?XWE>`1PrD!~aA
zuU(s-FK&`_44zQQQod-%wrg8+xx=<~2k&Tmb?@1+>_(Y_H=lgGq%$lnB_7Y}vMrC_
zd}+(vynnCmWoQ3<<f$vhyD#YEp@*L*xw%K_h+EInZrpo)kE2}wn%4&R4ot|Le%VF$
z)*WFjmDzRQDjv)C`EOPI&(Nc<XWIQkx0K~_(iX0bt0Qd>&T?AV=heq>LT*chUip`>
z{PwMyruF<c@5eYlIcrim$^P923)|YGnL!droj}H8sEc|Y+bMst(VqVcIvoeenFmTm
zkoSQ&z_%(cHe5c#&h<rgttMpi1p`bW8OwLh{V~^>P?3E6vcp@MiLa%)l~*1N5)g3w
zy1M@6t-0G<l&*bwTORN`Dyn;0*RyytO}@KFJ!~3(T(J|}JUQUPpW;-`&`HmuuP8bF
zWlRk)etR}{vg1eBd+lP~rq||XrYJ?IPP?=F)Co2YNd_kOs)fuI_Z~fbn{Zh-m*;G%
z=$*Hf#ZMOf?4BBGVQKR8<EQgtv$kz3tX=f|ERSAZi{X)vbJy(tHfPS4$4kw=ADra6
zrgpRM*30>d*%5Eg?pq+i^68l9lvdjvN=xc_Url^@?b=JR-b#V6->yq8R2GV>w*`qz
zJT7aw*x|~Se;RvSHa5Iu$ui$`V&d)D6Q90yoK|nZ{OOb7vC0*Bd*bWUzj>rzNbx>B
zd)rozSvAT6OWMM3pPG|wA@Mro%^vxcFQ<Jo7c-9RK5f17sdrKQiHN5R5?41wFg7-K
zZMi3RT|a-z?X!O0Za&$%-&|KH@1{&O*Bj@a$>)j+y&bNs)$i=<EnI&;Hgk=h?yaM0
zoqFDFMiZLzB_!_ND1I9pU$H%O^Q33TK6^D?`f<~{JS*(|+3*nMGQWdu49{hnUq}8k
zyOgK9<-9=ro{1gn-<4bxyX38R@^SFQo>H6AwVHp-SH1eR`~GX8$U_%je!h8o>1t!1
z(>=TMm@gFATU9a6OkRIZ|4a7WBEjd|rkh@zW!cB2)Z`h!aPscUV-~NYrv1~oGX1yN
z>yS%}wq5>eDQ1xp-FsfLF1X@=%x|fcxewm@m0i9bI_+%I*2~K;-+uGvOk`}Ra}m3Z
zoZZwUd%o=H1({}2d#=eRT~6(DR$)Ce`DZd0hXU){dA?GN(f@>d{eNraM*mh5UViJ&
z7UBK#od0afQFXJF<+u2<?$M6_3_o1*_djI4QsQrQ=X`0g%8B2z6L>XhgnORLUA+;+
zu&7Iu2ejQCgi*@UpbGPUF23yl4sQF;@JlnZ26bw1=0jb!bNQ?pllI(dNWc6bNjPXn
zD9f?E3<V6|WP)F?{o7ioyR5msFz>c%+bvJQ4SW)s3J-o?-O-s5FmI`&>ACEG@|*h)
zSIu5?t5G{sQ+lg*likBt+z+nasIg8-^uM1KUs8W2==tS;y8_s@K*zK;$sgVp?f!nN
z-r|2r)9v<uy)^ef1IOFxf4*JW{~@#ZKf`fN#?`K=^@pre|1;Qb)&C<E^Pl14Df@@A
zoBlJ*yEXkEuU`F+rwo~}t@<bIWH)^l|9)$_^M3|I@A>(^GC}*O%1-~Q&WitGvhzQ~
z<0-_LmjT-ZtmSpA|L`_9&;7M43;r{F5!w0g#earnQ`P?{Z;StYJLo^d!KLT_GsrC9
zjndZsCwN=`=i8O_4=z3b&(If$<_`^KKgizOFqb;nO`ooRzct+v?8bkVSeyvmD6r=I
z5BHq?&$li8&(Q2W|DUCAJ>Sh-`F&S*_5bXO`SqXSx);OsMVt1chKBryzMJ*ubG`pF
zuuZrBQ`BIZE%ZxbYrWvQ`9&<(rd_*NazTFAyR$8P)(xV4myavVYg+c7p#?dJrskbH
zblYDpbV;=6cH>Fj#@pqM4NoS&^E6=$|0h_>{I8)Z{~we4Kau)F`U~nW@caH}IN<T0
z;m}r%1w8wIxPIaP#rSgn4^H_XA%#DZ9gTeF3j03%@t?t^|M%is+mJTSGNk`!KsBKs
zS$Eh{@QOo@eU1Ma9Bj@1GcYRuXW+`IleoZ>{hvX|)_lQ#hUs8?VaG%;q!Bc5(m#m{
zX8k`VzTE5nXP5iyFU!&Vuw@B!3n6H+BbwFr55+IozhGbXpW%T@{llm!0c@Thjx|2h
z|KsuHUi-gT<!|3GOzVfYw-{0hx~*Tn%|uw`=0%fx^8H^t{xj^dUFDAwAT5Zo#07j&
z@l2UvmrQQiw#}DR*Uc@?+T?WK&s_Zv%Y*uoYyO>Uzi7vEW((f`QFYpT&u^Z8-hXHE
z<o%0IK0Spe-Inpzy{q@H>G!nNaIa7Q*SKB&m+s0t&NV9Y(i==%&Y5RU;Q3YeGBan7
zVRrt5j}LDz&z>DDeB_PWrBg@dzxMl3{-<LXY_#>-S!M$r8|(U9)g602vR=KrcT1YZ
z!%x=?{xI;g*8O96d0<sRg+!$Da?iw-p8565I|KDx9Gj0To>o?T(DUHA!pGQHkr+Pn
zxC{Bm<}W><zdrw6e0X1G-nNtj2LBn1R!D@#^uG?%vk}r?aOHl$l_fj&T}kvV+xe-f
z<B{7P(EyoJ^*Pr=UBv}^s~_H2ztvlM=QGvZ+^5?!wtZAOvD@GB@zV6$6^4)dj$L~n
zHtV(Dh6$dmt6f$V?JqIzi{N^dvBY@Z>_eI6?O$^>?r!k)dggh{ch2#>%>rzdzb>bI
z*z)22o!E?Ddv;aFE?RqPjn>1PZp=M@FZZu4DGA;o5E)(DcS^h1Wb=&cY?1SecYak{
zzNpIqw0Rtc3+D^&e-;W_YIFWSLpo@w4eE@54^x-M5@z`16WWGt<{I!SWY|WbQV~XJ
z*j^!P=td#LW>@e6W$0$0xvmUuF_q12FX~%<2Sb-K!v?GaFikyHV`(X;^`BuQbU1%U
zJWHJW@|X9F_s%eO&s`ty^^nbzOJq*<$EfDxW`(zpmewxIDhr#`5caG><QU@%&#)Z+
zVjp*3S4Y*aOEmgT&ls+05Ybm)Y~Pl(_ukgz<jU;aLZda)BR`#UGhi$7Pkir@`b$f=
zJZC%8-E~UBaucUZ_dmS041C%O=)@It%#$zipW#IJ`uZDYkN-2cnwlRx$KF^XC;iWH
z+kb{b_0K0=MqP4%zSN?Y;o1aO6MvD5KPT>*nv$YoVCyLHB)EBn1Y^U0hLX!GmaybK
zSn+UI|3kYi?;(K*9kj+0Qj&$w_@o#96MpLlSr(4ijRxUEmx*5qV8OOU46%a@i_tQ9
zEZK)k<PSkNS6zg5MOQFFHn&}lJqX!+mE{FJr-nfeygduH>&>c*0lK&C(&IBS=FWfA
zZ!LkJD+5`h?mh#&_YJhO8mtv%hZypi7mytkm?q!2!*RXsg?-DFDbVu*8u%e02Jt;8
z*}dcHlV0&p_%$?ukan>{Q!He=8EDHnd@ma`*r783R~S$>w#{{Aa2D=2%9DQoA9N&2
zNyt2~+b^RYA_6u1*vIbX2eH*{{~0Fa9{(`^=aZ}3BkrM2mopsMdM)oueS4g!RgJ(|
z+dF2{PN^lWGIyTyq*m)so${46rrzHAf-g7TUi)TVh`8CMpA%>DyIL~zToXM#W1nn=
zR<X>w3AR0xDg!r9Sj0TJfphitTiq4g!!NDfvRixW*3EmocXzMukWyvjb6WRC$a9%;
z>7VF@$7^!s1A4qv1m&l0Yk#?ApIP&dD+WJy`%jPAdwEH|_M7IJ@d`qW`nP+Mug|-B
zAS)>D?hEq|XYzx-zt#DadF`8{qHDOkrJ`Mu1G~bLS^HO2&RTlCYi&-#j>=N+jJOLD
z5u8)Sb&T8?AN0sozVtg-yyNm7|HZGaJKZ|;HFf5cT?^e}U%xV9bKkMrYTo+f#kbeo
zntGsBcAGkDxA_7Fp?a=IB^8qcKeA0$y8r5XfA&nSvuPVux1a7^Q?`Dcf${CGTOSjD
z%k6#mHtNy6$Te%8PfgEQWF+Ce>}t_sb;-vHg|ot#e65du+g?8F%;Y=K$CurxRI~gL
zGWV2s-uVJI!^Wtp^bV7~NtsHQ*R^aqws4+NPY>gZf&((u*XL)IeXlw)PkHyI%{x8p
zZl@MBxHz8{V%|A5qNu)G!gg<V{^1Qvzjr6^_jM1s;=6aX;=E@!lX}>cpV(_nDq?)!
zSDSf*Z+_tQEoaqA+XHq9oO+QGa_h*ZCUyySn~6u=Z2T9?s{i(hSHEd<Ui-|goA)p0
z-AT8*aW?f;fcTHOi@lO`I#Vs~Z&!?%dfc|3{p-52=Wok0OSh>vUGo?8x^!1}{j6^<
zH<s;U;xjrd-LN#l>h^k(NZ$qb;xo;!-Ci3Rdh3~`*)ea%&l-$7*}LX_J{qMrzg%_e
z-CyoHE}U%Flx+SpR5l#?r?%(W=d&3$Zu4J$SNp}Cwtd<oCb@u3MSdcTUtafwt=p}2
z<5kq*my=$Jm2b9toUFTiKI`iQhQrMb2dch3o7?m}x%$ywVgDT&dS5v%UUas*n#RHJ
zbcBiF*QNUvi`_0S@p$)sYV2d7R+n^Zem85!1K&-LybAg|>Crb2-fvUubqwEcHmj@M
zdz*!Ufk9bz<HSo#cJAEw@A^dju-85l-{c-KxDnZuC$r#A>$i!mc?JaxOA~h9y7Qyo
zbIbPav#PHM-gQf9og(&GBv~av{=NQlPx;Q0N|R|<uKlv#r2R9^iR18d=>oUnw>{UT
zA4YV0KKSuu`xMhD;uFrObWT4#`FjeV`*P=hqEXS>GfKOAeHYDKyQaqNy0F{CX}XgA
zc^}vVB%Xg*bg+1rzwnDonVaL+Efm<^*yMF~s>bmJT5d(PhCwf9y_>zpq<eWRXPi#H
z)0&>0FT6Ie?U*lcV%OW%_g_ZEo)NhHO2E_fO~JoUrJ)KNdfU%#nNWG&>+**+fp;oq
zJ^E+;);mBuL&@Ac?6r{025a|IOD6XF6lH8+KT^lu3R~K>5mIhp>_I*T+06zkFEtqn
zmHS<#knvhl7Tw4dE~r1T_#^kfL${{?>lQ6~e<aGqo-cW0_KG~cXB#4vxAZojco!^P
z)?a^W*^jDDojRSq&~W#UqK`IhIHB<7gwn1ZCVTB}IjZjM-Qs6uQm|^`k47K&Ip?`}
zovfv=TwXNa_mb}8S+V{`Y^REpeHtv;Rf>GWFY(RS_)wh1cK1kjvB+A(;O&`7t31EF
z(Y~T~`OUg?&vm*A(?Whv@)X*ve_`TLw_9$rUWI=79TxQagn=gO-m22?%L_I>HT!n#
zwbPzSpPqYc+naq|C?-aG_KByT?Sc%7U-nsruXo)4=Ht(H{SA3Gp9<dx?3<aWdSO?!
zcG?bERYT4C^OCDWW~1~=L48vgCcck~S;v#S=1CCIyF*NA1%PJNn4z=VrDl-6S^y)o
zBeop2><E1LjtFcCl`o{?f*i>sDuZaBBM<X3s)CNsftm=LKL;P3!wYS0Kn;I+1w8VP
zX1FF;lo7k(qN)s{D2pN1&O!;w0LBOJe+rh!|7N@WpW(&QwX+U>SbIinAM=ObF=u78
zZq;sDIq^_a&C<v7zHYTWVs>F>UaCj3Y@8bx`=bmdFHO;@7mNf>9ad<b;Ht|Y5*Vm^
zclSNpRWUBt4HuNRSm>>)658uvVe>nHWeez-A}pA)Mlee?(kj$E_}b!-DFG~cmvbfi
z>=yqxzy8u(_%c&JXmAGL(THPFu;y{t?z{%&yY|o(`bZ0Cmn>?qKwm3g`aDjytpCt{
z0XZ|R)2m;(M*4ABvzwfCKRVI;aOK+M?N`N~Wd0CmyX3I2DZT7Z)#q7df-gnFx%GQe
zH&46nvu(cbwhSG`X^~qcz1q{_L>+AA@JzmKTJQ0(`_*?TC3hY#{e71<Kir<1QDEnM
zN2uyoV8YI;7h_`oGxTib&fl&hHtmyV{%ZF`scjGU7(EdxK6sL8cKu^sr{f|2PVboQ
zwsm(CqwbOIMw^bEIr7%yc<`qNPg{)*F>hC_na}fj^&?#_>kr-1?<<ys=AM#`Sb3|u
z&xn6%Hp&<)I4uUcilELst@tBzx&9FMf=4FGzs>GT7XN3knSN`@N2DoJv=OE+%+ZL2
zuD!@>U?+7sIK4HyIOFjv`<`EaHIY{%zl5KFgngB_rz?X~RK7voJG=HD=eKXQM%XY1
zy3lzA6RtJhT3#AVdveWZ>IvHYXRx>Y_u|`1<Q3rXL#3uL;#d$Zsw%?BlWRV0N1t8u
zkMlo}*OqHugdJFkNJYzb`)*0I_|^Z|Zok(o*zuLTI1(9>wrR7^$nWl7_|N$5+hD|D
z5Da-p3y_gkX@eYv>|S@2mD)$)XLK!KMuZhwY%n0LNS-b2`*fXh|AK$U|3pn8Mb`p0
zY|HzD0v!a9Jczsv#R|Hp%0Up9MkFsHHoh!}ZErz#54uzFEJRmAK4K7&e6X!S4||I?
zyFYpV7W4lMJbz>})^1%M9dWh&!{nR;QcEALP4lfk`0ZOu;-gKop8a(Ey;eX&<Uhl0
zzxoHJ*D9)g+JipWw?>ss^qsqP?U`y0$<!7GK6n2aS3Xr(`%Ek?{QdObb>GQT6F=!&
zavqnNRC!)))gxn_cUyC#BWL?;ywW$9wRP&wGX;yc^u3%{-rT^xG&wTMYI;ZMc|M!#
zXIXydJua5X-m)j1wL$quyWHK?>*pWZ_P$E*t^WEo>IOPOwwo7x4y}9l_LQ6ZilYwy
zxHtFNnq+<tH}hYfFL+X#>74xgcPiyyUwd%cMAug4<}QzXxm`YWe(-*8|1B&$NkQx0
zoZ^=}Bw##muXf+Az573$@7&{Akhkw{Y5vkEmEf}ycaAPiWo>#O#9hX0vF<-Z)WwC@
z-YU0cF5g~}7Wr`2-G+)g=`1ONcfPW17C!K+u==NdUVi@4E4tI}J@W1p*AbbtBsYMO
z<=f-NqZ~YUxudV9d=y=}X1n<JZQD5>rR~1Fr(gBZJJtvd2@7TRD`C?+^G!{zZ?U!Z
z-uqi(U*)v$Pg8${@E&_m`MUqQY1!e!htB=dURmnv^l@R&aj83AyF=vmX!XovS2vyi
zs8;QH$Va~BZ^br-hb~Xw*}O(Q;Z{+_E5qh%4Myus|3>?CM_yjHHKp>+JKamtH+35)
zUJE^Up~K+(LFS%f-!0c`9Ii(lZCx_me7e95-J>h^ZuLmH7tv(az$jA~?S3$9(ycRk
z>+{ui&wHtN?|V_F%@GM7o?He#!=9o{`GaDU?)ny+EnVqpdc@A>U0Og&%#(C3!{(mc
z1CQrizF=x(^0zu{ns;|~u2=M2hbZf{ZHrSBCZAz$YIR^?KKT0D%;i7keY@wLeb?`i
z=flmjD%a@0anXqsE`N8^frW{$(kAT0hnV6sX;-&KzjbnLUD$bdDv#G)4=r{ZzAW#2
zj#c+u?N+Y5`znd!o<{PHTcV=ZvuiIHPdsjtAvNdS#7D7t*}Ym}JNNB24_b8Rz^}9u
zf$_a36#@!&va8H#jO&aLixX-)x>nTG*2}xvjF)xGWWA%u844D5NWSn~`wKioWw5AA
zgSq=h|J;3|w@?0OXqj|g(*FJRM0lke(|!bb$HX$6b#D+8uDvIZ|A7zaoQqOWKsRZS
z(nLD&!|v=>S@Gjy9tWkK$Lw!0l+jELoPTYX_F1;*^-TL()Lf^&Il6t8<1M~BX*<_i
zM{NHfw)qivynu7L=-h8hzJI&^G3um=|Fc!kjGwk?opOBL(<629KSS&4hy1@dV*WEc
z*!KKCL+|A)E|V&Pe%<<K{njX3ZRT6$?K`^ODZTLG(OzfCKG|>fVW}yZOYYo^ziqZK
zA@$y;gWD`w8rU9IC1)Iun{&nf!`2$b{|t`Y^Z$r}c0KH}f0*^d`CpT`{SRHx<U~o9
zl;HZN_czlr-zQWqyP9}KwrPT$>%jo)ls;Q~zWi5pGJEm`yjN!$-TXOSU`_DhYQa+L
z;;qwV`AviWiLGz3f1z0XpP}jY{D0=|e{|zH>MuAw|Ifg>-TqHrue0-_i)&Obm+W}^
zPCfFO%t@tV0)}=CWpB^hguZ(Gp8?Gs^@py0X#dNiSN|cp_&>w37gy2~T?_v+5ORKG
z<Bh<WdNiy1e?;vQ_|LGQ_545Xy!sz^#S+Ry!sY)naEBj~|01ySKf}S?^Zyy77o+*W
zb%t!K=<)vyXfFBB(7N`)e}-QicVIDcF^!!_$E|-Xk671^x*qPj>AD}UeLl*!m-5OV
zx)`4|tM}{f+pa=4&PpvT)!uveVrWRZ%H-I-&g8$-AO5xWzyGXu(lrh9$I{yt#QdAW
zSOpr(9qEUOhSs_r)Aje>wPEMoCwHAboZRx@ZK}fK52i_73=hBPdLP**doj19*7?x&
zsLqX@x{vl=ey8~9opLB+#G1Y}Ya5I#pIcb0mH*-Owf>io>H6Qlv;Q+3U%eE3<f-PB
z1JCRqYG3`&@WSKT{`YU!|7Va}VyAQUcs$GN`0wAY|7YkA4}|vAmav=tXW)!m|DVCh
zZR>xA`rP^-U*$hcef6K=i%K?3vA-)rF}h$-{h`R!&*eX~ypH_Oux{J_KUKTBU^{vp
zWUl{Ft=j*WQ|kKPzgzz^JibzYD17yQh6@wd*5AJ!|IaqoR)p~knwW=8^lBl84}M#x
zTG;OeX}Wp^X{8~<b95gq{wK6I{$ERP_P>9({xf`h_4366zGO5p$46&l8r+*o*U10M
z1_#iJWyiq_#u?9|+3t?&;H#G}I>@+Qk-Fdgb+5|(7n5Gx3Y%Ur{mk{?W0zyR7PPoV
z+-zE*JIV8TgZ1@%Xh=eij>SDifwJCP^GW~<`buwDcMN)RHFRY-NF~O~dBnmb%u}JA
zk&b8fgs$jbhUmgGpe}#6>S91&J1+=pl*5jwUIts{zXW<j%LUByhT&>qtLIm0!j6?-
zfE^(XT?GG11AZ`cAJQq}vlhep5&_KH=7WzP&qX`TTjU0G{d@>~SRLb(YDVdmx9rMZ
z+1h5_hMvX^pZP)CY)~q~h`xj#F-rki;0@aousqN~0C7lj5Y$~8k<Q_V`5);7=3f74
z`>pxdmqK?6xIj+1TL3$0d>wS5KjNfVh_RSwk}rjBoLJ=r&tMn_XhUOlaVPu?@ny(^
zDh$wl5wNI0oaSu?-8nH;720pXICb3(ws!)$&j4wr0&)a5bh`m)QX}MN^Ygs+%eUYg
zLJ*T_80p_o^RwE|+wXtZmxRoQMitWb4_G)Mf`j96@YD6@4mZApo>LtNpZJ08)X9cM
zJ!0ku=3mUDmkHZFF$uc=BLH`zUhxQVkT}v75C&+>BK!~Cf8h&@dQV?)fvkaf?z$i>
zPr*V8ad0X|(i4Q`DcGJ6$S|7b71)XFBCjCVDL{`Leg&QPvI5@@AqDN?uY~1Ch_Es&
zw}nha8Y=_OEwVw%$XAfV+Wi(VLA&98u<Q@M1OlGFVH;>5cS9hpSn!1Jn$uwN0$t+(
zH4s_`Q*A>YXjBx{=vNG|<CkG!0lxo%2^!VG@UVag!%H6IuxL;L-<g3D7T^mr7>9W<
zGol^yY)<P_qtUK8Kw6YM*OkE?_2g&7tr1{lzOdEsh^h%`@w4Be25B&N8SM1osYs{s
zgAITzeg@qw0p=nfwQPzQ1!4y;XNIhO4qgE}#CzkhHq%r3j<=S;5;UqRj(gCvz?VbB
z;q^OYy(L(1<&^-IG}MLqNEaF)t+)o=<B`;Bb8?^S?|<{(bWH}k7Wu{v<h7fi>o%Zg
zfkW*E7ekt`lfF%12dGbi=4=Oc@Da0+)!3l<aWHol?EG}Zr4an!!<%6zg+mVxcNfWg
z=KD$i{+Is@DIr%8rAdP<IPf7G7!Yv*J7;<lbggy(BdRhoPn2ed4@pDf9B!>*m&s?f
zpW^R-)o;@B2fH6-dBGOY(eUudD1^o-^fYVeyaqEQ%C19m!Yjne0>nYvA)wU)U~VSt
z{B-2|No-K;ffyJn6S#b)`HB7Ym;X*gx%mNdf-hQh%!cNKRj^w$&?1dW$Kqqm(1}3i
zdJt=GAOb8%mn3vS>L1v3K3y6N(5m91E2M;l7QBH_F1)}EbVa`TKm%=S9OU2$_)dHX
z7il_51EsnG-TDJL@52?cG8ukt5R4C>zH${oo@H9pg)+SaotQC!HC>>5cxkW_RFxrJ
tyR>2v{Jtd-NF4~T8liloI#C4ml8P?KTo~-+0awW6C~Qg#!e#t_69BA|KY#!L
literal 0
HcmV?d00001
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/config/Config.java b/src/main/java/fr/but/infoetu/MeetingPlannr/config/Config.java
index f67aab4..0612dec 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/config/Config.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/config/Config.java
@@ -6,11 +6,11 @@ import java.util.Arrays;
public class Config {
public static final int MAX_GENERATIONS = 1;
- public static final ArrayList<String> WEEK_DAYS = new ArrayList<>(Arrays.asList("MONDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"));
- public static final int SLOT_DURATION = 11;
+ public static final ArrayList<String> WEEK_DAYS = new ArrayList<>(Arrays.asList("WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"));
+ public static final int SLOT_DURATION = 60;
public static final double DAY_START = 8;
public static final double DAY_END = 17;
- public static final int MAX_PEOPLE = 3000;
+ public static final int MAX_PEOPLE = 30;
public static ArrayList<Time> createTimeSlots() {
ArrayList<Time> timeSlots = new ArrayList<>();
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/AdminController.java b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/AdminController.java
index 091a81e..e0309e4 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/AdminController.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/AdminController.java
@@ -58,10 +58,6 @@ public class AdminController {
@RequestMapping(value = "admin/dashboard", method = RequestMethod.GET)
private String adminDashboard(@AuthenticationPrincipal UserDetails details, Model model) {
- if (details == null) {
- return "redirect:/public/login";
- }
-
User user = ur.findByUsername(details.getUsername()).get();
model.addAttribute("currentUser", user);
@@ -73,12 +69,9 @@ public class AdminController {
@RequestParam(defaultValue = "1") int page,
@RequestParam(required = false) String search,
Model model) {
- if (details == null || !details.getAuthorities().stream()
- .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
- return "redirect:/public/login";
- }
+
- int pageSize = 10;
+ int pageSize = 5;
List<User> allUsers = search != null && !search.trim().isEmpty()
? ur.findByNameContainingOrSurnameContainingOrUsernameContaining(search, search, search)
: ur.findAll();
@@ -105,17 +98,20 @@ public class AdminController {
private String requestsList(@AuthenticationPrincipal UserDetails details,
@RequestParam(defaultValue = "1") int page,
@RequestParam(required = false) String search,
+ @RequestParam(required = false) Boolean filter,
Model model) {
- if (details == null || !details.getAuthorities().stream()
- .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
- return "redirect:/public/login";
- }
- int pageSize = 10;
+ int pageSize = 5;
List<Request> allRequests = search != null && !search.trim().isEmpty()
? rr.findByReasonContainingOrDescriptionContaining(search, search)
: rr.findAll();
+ if (filter != null && filter) {
+ allRequests = allRequests.stream()
+ .filter(req -> !rs.hasMeeting(req))
+ .collect(Collectors.toList());
+ }
+
int start = (page - 1) * pageSize;
int end = Math.min(start + pageSize, allRequests.size());
List<Request> requests = allRequests.subList(start, end);
@@ -128,29 +124,40 @@ public class AdminController {
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", (int) Math.ceil(allRequests.size() / (double) pageSize));
model.addAttribute("currentUser", ur.findByUsername(details.getUsername()).get());
+ model.addAttribute("filter", filter);
return "admin/requests";
}
@RequestMapping(value = "admin/requests/validate/{rno}", method = RequestMethod.POST)
- private String validateRequest(@PathVariable int rno, @AuthenticationPrincipal UserDetails details) {
- if (details == null || !details.getAuthorities().stream()
- .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
- return "redirect:/public/login";
- }
+ private String validateRequest(@PathVariable int rno, @AuthenticationPrincipal UserDetails details,
+ @RequestParam(defaultValue = "1") int page,
+ @RequestParam(required = false) String search,
+ @RequestParam(required = false) Boolean filter) {
+
Optional<Request> requestOpt = rr.findById(rno);
if (requestOpt.isPresent()) {
Request request = requestOpt.get();
-
+ User user = request.getUser();
Meeting meeting = new Meeting();
meeting.setRequest(request);
- meeting.setUser(request.getUser());
-
+ meeting.setUser(user);
+ if(user.getUsername().endsWith("@univ-lille.fr")) {
+ us.sendValidationMeeting(user, meeting);
+ }
mr.save(meeting);
}
- return "redirect:/admin/requests";
+ String redirectUrl = String.format("redirect:/admin/requests?page=%d", page);
+ if (search != null) {
+ redirectUrl += "&search=" + search;
+ }
+ if (filter != null) {
+ redirectUrl += "&filter=" + filter;
+ }
+
+ return redirectUrl;
}
@RequestMapping(value = "admin/meetings", method = RequestMethod.GET)
@@ -163,7 +170,7 @@ public class AdminController {
return "redirect:/public/login";
}
- int pageSize = 10;
+ int pageSize = 5;
List<Meeting> allMeetings = search != null && !search.trim().isEmpty()
? mr.findByRequestReasonContainingOrRequestDescriptionContaining(search, search)
: mr.findAll();
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/PublicController.java b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/PublicController.java
index f24108f..ba5859f 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/PublicController.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/PublicController.java
@@ -5,11 +5,15 @@ import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
import fr.but.infoetu.meetingplannr.pojo.User;
import fr.but.infoetu.meetingplannr.repository.UserRepository;
@@ -17,6 +21,7 @@ import fr.but.infoetu.meetingplannr.service.UserService;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
+import java.time.LocalDateTime;
import java.util.Optional;
@Controller
@@ -28,6 +33,12 @@ public class PublicController {
@Autowired
private UserService userService;
+ @Autowired
+ private JavaMailSender mailSender;
+
+ @Autowired
+ private PasswordEncoder passwordEncoder;
+
@RequestMapping(value = "public/login", method = RequestMethod.GET)
public String loginForm() {
if (isAuthenticated()) {
@@ -66,6 +77,60 @@ public class PublicController {
return "redirect:/user/listeAction";
}
+ @RequestMapping(value = "public/password_change", method = RequestMethod.GET)
+ public String passwordChangeForm() {
+ return "public/passwordChange";
+ }
+
+ @RequestMapping(value = "public/perform_password_change", method = RequestMethod.POST)
+ public String performPasswordChange(@RequestParam("email") String email, @RequestParam("newPassword") String newPassword, Model model) {
+ Optional<User> userOpt = ur.findByUsername(email);
+ if (!userOpt.isPresent()) {
+ model.addAttribute("errorMessage", "Email not found.");
+ return "public/passwordChange";
+ }
+
+ User user = userOpt.get();
+
+ if(!user.getUsername().endsWith("@univ-lille.fr")) {
+ model.addAttribute("errorMessage", "Le mot de passe ne peut pas être modifié car l'adresse email n'appartient pas à l'université de Lille");
+ return "public/login";
+ }
+ userService.sendPasswordVerificationEmail(user, newPassword);
+
+ model.addAttribute("successMessage", "Un email de vérification a été envoyé à " + email + ".");
+ return "public/passwordChange";
+ }
+
+
+ @RequestMapping(value = "public/verify_password_change", method = RequestMethod.GET)
+ public String verifyPasswordChange(@RequestParam("token") String token, @RequestParam("password") String password, Model model) {
+ Optional<User> userOpt = ur.findByVerificationToken(token);
+
+ if (!userOpt.isPresent()) {
+ model.addAttribute("errorMessage", "Token invalide ou expiré.");
+ return "public/passwordChange";
+ }
+
+ User user = userOpt.get();
+
+ if (user.getTokenExpiration().isBefore(LocalDateTime.now())) {
+ model.addAttribute("errorMessage", "Le token a expiré.");
+ return "public/passwordChange";
+ }
+
+ System.out.println("Password: " + password);
+ user.setPassword(passwordEncoder.encode(password));
+ user.setVerificationToken(null);
+ user.setTokenExpiration(null);
+ ur.save(user);
+
+ return "redirect:/public/login";
+ }
+
+
+
+
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null &&
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/UserController.java b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/UserController.java
index a08a937..5ad54b0 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/controller/UserController.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/controller/UserController.java
@@ -3,7 +3,9 @@ package fr.but.infoetu.meetingplannr.controller;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -214,12 +216,40 @@ public class UserController {
}
@RequestMapping(value = "user/calendar", method = RequestMethod.GET)
- private String calendar(@AuthenticationPrincipal UserDetails details, Model model) {
+ private String calendar(@AuthenticationPrincipal UserDetails details, Model model, HttpServletRequest request) {
if (details == null) {
return "redirect:/public/login";
}
User user = ur.findByUsername(details.getUsername()).get();
model.addAttribute("currentUser", user);
+
+ String monthParam = request.getParameter("month");
+ String yearParam = request.getParameter("year");
+ LocalDate today = LocalDate.now();
+ LocalDate currentDate;
+
+ if (monthParam != null && yearParam != null) {
+ currentDate = LocalDate.of(Integer.parseInt(yearParam), Integer.parseInt(monthParam), 1);
+ } else {
+ currentDate = LocalDate.now().withDayOfMonth(1);
+ }
+
+ LocalDate date = currentDate;
+ int month = date.getMonthValue();
+ Map<LocalDate, List<Meeting>> meetingsByDate = new HashMap<>();
+
+ while (date.getMonthValue() == month) {
+ List<Meeting> meetingsForDay = mr.findByRequestDate(date);
+ meetingsByDate.put(date, meetingsForDay);
+ date = date.plusDays(1);
+ }
+
+ model.addAttribute("meetingsByDate", meetingsByDate);
+ model.addAttribute("currentDate", currentDate);
+ model.addAttribute("previousMonth", currentDate.minusMonths(1));
+ model.addAttribute("nextMonth", currentDate.plusMonths(1));
+ model.addAttribute("today", today);
+
return "user/calendar";
}
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/pojo/User.java b/src/main/java/fr/but/infoetu/MeetingPlannr/pojo/User.java
index 8c621f4..403f8fa 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/pojo/User.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/pojo/User.java
@@ -1,6 +1,7 @@
package fr.but.infoetu.meetingplannr.pojo;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -36,6 +37,8 @@ import java.util.Collections;
@Table(name = "users")
public class User implements UserDetails {
private static final String REQUIRED = "est obligatoire";
+ private String verificationToken;
+ private LocalDateTime tokenExpiration;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "users_uno_seq")
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/repository/UserRepository.java b/src/main/java/fr/but/infoetu/MeetingPlannr/repository/UserRepository.java
index f3329fb..7aa3e46 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/repository/UserRepository.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/repository/UserRepository.java
@@ -13,4 +13,6 @@ public interface UserRepository extends JpaRepository<User, Integer>{
List<User> findByNameContainingOrSurnameContainingOrUsernameContaining(String search, String search2,
String search3);
+
+ Optional<User> findByVerificationToken(String token);
}
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/service/RequestService.java b/src/main/java/fr/but/infoetu/MeetingPlannr/service/RequestService.java
index 52539c4..a98d42d 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/service/RequestService.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/service/RequestService.java
@@ -20,7 +20,6 @@ public class RequestService {
}
public boolean hasMeeting(Request request){
- System.err.println(mr.existsByRequest(request));
return mr.existsByRequest(request);
}
}
diff --git a/src/main/java/fr/but/infoetu/MeetingPlannr/service/UserService.java b/src/main/java/fr/but/infoetu/MeetingPlannr/service/UserService.java
index cc613c9..f847c54 100644
--- a/src/main/java/fr/but/infoetu/MeetingPlannr/service/UserService.java
+++ b/src/main/java/fr/but/infoetu/MeetingPlannr/service/UserService.java
@@ -1,16 +1,21 @@
package fr.but.infoetu.meetingplannr.service;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.LocalDateTime;
import java.util.UUID;
+import fr.but.infoetu.meetingplannr.pojo.Meeting;
import fr.but.infoetu.meetingplannr.pojo.User;
import fr.but.infoetu.meetingplannr.repository.UserRepository;
+import jakarta.mail.internet.MimeMessage;
@Service
public class UserService {
@@ -18,6 +23,8 @@ public class UserService {
private PasswordEncoder passwordEncoder;
@Autowired
private UserRepository userRepository;
+ @Autowired
+ JavaMailSender sender;
private final String UPLOAD_DIR = "src/main/resources/static/uploads/";
@@ -30,6 +37,25 @@ public class UserService {
public void banUser(Integer uno) {
User user = userRepository.findById(uno)
.orElseThrow(() -> new RuntimeException("Utilisateur non trouvé"));
+
+ if (user.getUsername().endsWith("@univ-lille.fr")) {
+ MimeMessage message = sender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(message);
+
+ try {
+ helper.setFrom("paul.cancel.etu@univ-lille.fr");
+ helper.setTo(user.getUsername());
+ helper.setSubject("Compte banni");
+ helper.setText("Votre compte : " + user.getUsername() + " a été banni le " + java.time.LocalDate.now());
+ sender.send(message);
+ } catch (jakarta.mail.MessagingException e) {
+ throw new RuntimeException("Erreur lors de l'envoi de l'email", e);
+ }
+ }
+
+ if ("ROLE_ADMIN".equals(user.getAuthority())) {
+ throw new RuntimeException("Impossible de bannir un administrateur");
+ }
user.setEnabled(false);
userRepository.save(user);
}
@@ -43,4 +69,51 @@ public class UserService {
return fileName;
}
+
+ public void sendPasswordVerificationEmail(User user, String newPassword) {
+ String token = UUID.randomUUID().toString();
+ user.setVerificationToken(token);
+ user.setTokenExpiration(LocalDateTime.now().plusHours(1));
+ userRepository.save(user);
+
+ String verificationUrl = "http://localhost:8080/meetingplannr/public/verify_password_change?token=" + token + "&password=" + newPassword;
+ String emailContent = "Bonjour,\n\nCliquez sur le lien suivant pour confirmer votre changement de mot de passe :\n"
+ + verificationUrl;
+
+ MimeMessage message = sender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message);
+ helper.setFrom("paul.cancel.etu@univ-lille.fr");
+ helper.setTo(user.getUsername());
+ helper.setSubject("Vérification du changement de mot de passe");
+ helper.setText(emailContent);
+ sender.send(message);
+ } catch (jakarta.mail.MessagingException e) {
+ throw new RuntimeException("Erreur lors de l'envoi de l'email", e);
+ }
+ }
+
+ public void sendValidationMeeting(User user, Meeting meeting){
+ String emailContent = "Bonjour " + user.getName() + " ,\n\nVotre demande de rendez-vous a été validée.\n\n"
+ + "Date : " + meeting.getRequest().getDate() + "\n"
+ + "Heure : " + meeting.getRequest().getTime() + "\n"
+ + "Nombre de personnes : " + meeting.getRequest().getNumberOfPeople() + "\n"
+ + "Raison : " + meeting.getRequest().getReason() + "\n"
+ + "Description : " + meeting.getRequest().getDescription() + "\n\n"
+ + "Cordialement,\n\n"
+ + "L'équipe MeetingPlannr";
+
+ MimeMessage message = sender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message);
+ helper.setFrom("paul.cancel.etu@univ-lille.fr");
+ helper.setTo(user.getUsername());
+ helper.setSubject("Rendez-vous validé");
+ helper.setText(emailContent);
+ sender.send(message);
+ } catch (jakarta.mail.MessagingException e) {
+ throw new RuntimeException("Erreur lors de l'envoi de l'email", e);
+ }
+ }
+
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 03e0917..e884b3e 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -26,8 +26,12 @@ logging.level.org.springframework.jdbc=TRACE
spring.security.filter.order=10
server.servlet.session.timeout=30m
+logging.level.org.springframework.mail=DEBUG
+
spring.mail.host=smtp.univ-lille.fr
spring.mail.port=587
-spring.mail.username=paul.cancel.etu
-spring.mail.password=meetingplannr
+spring.mail.username=paul.cancel.etu@univ-lille.fr
+# mettre : export MAIL_PASSWORD="password" pour linux ou $env:MAIL_PASSWORD="password"
+spring.mail.password=${MAIL_PASSWORD}
+
spring.mail.properties.mail.smtp.starttls.enable=true
\ No newline at end of file
diff --git a/src/main/resources/import.sql b/src/main/resources/import.sql
index 3937dba..445c81f 100644
--- a/src/main/resources/import.sql
+++ b/src/main/resources/import.sql
@@ -2,7 +2,7 @@ DROP SEQUENCE IF EXISTS users_uno_seq;
CREATE SEQUENCE users_uno_seq START WITH 1;
-- password: password123
-INSERT INTO users (uno, username, name, surname, phone_number, birthdate, password, authority, enabled) VALUES (nextval('users_uno_seq'), 'john.doe@example.com', 'John', 'Doe', '0612345678', '1990-05-15', '$2a$12$o0C1lgpgzoxPrE64DHda6O0DEDqQznVxqXb5y6gzWne3BP4nZMWrC', 'ROLE_USER', true);
+INSERT INTO users (uno, username, name, surname, phone_number, birthdate, password, authority, enabled) VALUES (nextval('users_uno_seq'), 'paul.cancel.etu@univ-lille.fr', 'John', 'Doe', '0612345678', '1990-05-15', '$2a$12$o0C1lgpgzoxPrE64DHda6O0DEDqQznVxqXb5y6gzWne3BP4nZMWrC', 'ROLE_USER', true);
-- password: securepwd
INSERT INTO users (uno, username, name, surname, phone_number, birthdate, password, authority, enabled) VALUES (nextval('users_uno_seq'), 'jane.smith@example.com', 'Jane', 'Smith', '0698765432', '1985-08-22', '$2a$12$9DmwZZv31epkRx6kUOCeueYRCrlAUyV1J0iB6eienUWRKT3ozpLGu', 'ROLE_USER', true);
@@ -22,15 +22,51 @@ INSERT INTO users (uno, username, name, surname, phone_number, birthdate, passwo
-- password: noemiegoat
INSERT INTO users (uno, username, name, surname, phone_number, birthdate, password, authority, enabled) VALUES (nextval('users_uno_seq'), 'theo.vienne@net.fr', 'Theo', 'Vienne', '0619515793', '2000-08-31', '$2a$12$XWqr1MGt9oSxRm2YfEMQle4QWq9r9QcpSXDcOgsEzEQTqtLRdBgcC', 'ROLE_ADMIN', true);
--- Modifications des INSERT de requests pour inclure l'heure
-INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Rendez-vous pour discussion projet', 'Discussion des objectifs du projet', 6, '09:00', '2024-01-15', 3);
-INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Point d''avancement mensuel', 'Revue mensuelle des progrès', 6, '14:30', '2024-02-20', 5);
-INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de planification', 'Planification des prochaines étapes', 6, '10:00', '2024-03-10', 4);
-INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Bilan trimestriel', 'Evaluation des résultats du trimestre', 6, '15:00', '2024-04-05', 6);
-INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Session de développement', 'Session de codage en équipe', 6, '11:30', '2024-05-18', 2);
-
-INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Rendez-vous pour discussion projet'));
-INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Point d''avancement mensuel'));
-INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Réunion de planification'));
-INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Bilan trimestriel'));
-INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Session de développement'));
\ No newline at end of file
+
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Rendez-vous pour discussion projet', 'Discussion des objectifs du projet', 6, '09:00', '2025-01-25', 3);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Point d''avancement mensuel', 'Revue mensuelle des progrès', 6, '14:00', '2025-01-24', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de planification', 'Planification des prochaines étapes', 6, '10:00', '2025-01-25', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Bilan trimestriel', 'Evaluation des résultats du trimestre', 6, '15:00', '2025-01-29', 6);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Session de développement', 'Session de codage en équipe', 6, '11:00', '2025-01-24', 2);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de brainstorming', 'Session de brainstorming pour nouvelles idées', 3, '09:00', '2025-01-25', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Atelier de formation', 'Formation sur les nouvelles technologies', 4, '16:00', '2025-01-25', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de coordination', 'Coordination des équipes de projet', 2, '11:00', '2025-01-25', 3);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Déjeuner d équipe', 'Déjeuner pour renforcer l esprit d équipe', 7, '12:00', '2025-01-25', 6);
+
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de lancement', 'Lancement du nouveau projet', 2, '09:00', '2025-01-23', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de suivi', 'Suivi des tâches en cours', 3, '10:00', '2025-01-23', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de clôture', 'Clôture du projet en cours', 4, '11:00', '2025-01-23', 6);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de feedback', 'Retour d''expérience sur le projet', 5, '14:00', '2025-01-23', 3);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de coordination', 'Coordination des équipes', 6, '15:00', '2025-01-23', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion', 'Planification des tâches', 7, '16:00', '2025-01-23', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de brainstorming', 'Brainstorming pour nouvelles idées', 2, '09:00', '2025-01-26', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de formation', 'Formation sur les nouvelles technologies', 3, '10:00', '2025-01-26', 6);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de développement', 'Développement de nouvelles fonctionnalités', 4, '11:00', '2025-01-26', 3);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de revue', 'Revue des progrès', 5, '14:00', '2025-01-26', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de stratégie', 'Stratégie pour le prochain trimestre', 1, '15:00', '2025-01-26', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de bilan', 'Bilan des résultats', 1, '16:00', '2025-01-26', 6);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de lancement', 'Lancement du nouveau produit', 2, '09:00', '2025-01-27', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de suivi', 'Suivi des ventes', 3, '10:00', '2025-01-27', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de clôture', 'Clôture du trimestre', 4, '11:00', '2025-01-27', 6);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de feedback', 'Retour d''expérience des clients', 5, '14:00', '2025-01-27', 3);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de coordination', 'Coordination des équipes de vente', 6, '15:00', '2025-01-27', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de planification', 'Planification des ventes', 7, '16:00', '2025-01-27', 5);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de brainstorming', 'Brainstorming pour nouvelles stratégies', 2, '09:00', '2025-01-28', 4);
+INSERT INTO requests (reason, description, uno, time, date, number_of_people) VALUES ('Réunion de formation', 'Formation sur les techniques de vente', 3, '10:00', '2025-01-28', 6);
+
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Rendez-vous pour discussion projet' AND date = '2025-01-25'));
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Point d''avancement mensuel' AND date = '2025-01-24'));
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Réunion de planification' AND date = '2025-01-25'));
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Bilan trimestriel' AND date = '2025-01-29'));
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Session de développement' AND date = '2025-01-24'));
+
+INSERT INTO meetings (uno, rno) VALUES (2, (SELECT rno FROM requests WHERE reason = 'Réunion de lancement' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (3, (SELECT rno FROM requests WHERE reason = 'Réunion de suivi' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (4, (SELECT rno FROM requests WHERE reason = 'Réunion de clôture' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (5, (SELECT rno FROM requests WHERE reason = 'Réunion de feedback' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (6, (SELECT rno FROM requests WHERE reason = 'Réunion de coordination' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (7, (SELECT rno FROM requests WHERE reason = 'Réunion' AND date = '2025-01-23'));
+INSERT INTO meetings (uno, rno) VALUES (2, (SELECT rno FROM requests WHERE reason = 'Réunion de brainstorming' AND date = '2025-01-26'));
+INSERT INTO meetings (uno, rno) VALUES (3, (SELECT rno FROM requests WHERE reason = 'Réunion de formation' AND date = '2025-01-26'));
+INSERT INTO meetings (uno, rno) VALUES (4, (SELECT rno FROM requests WHERE reason = 'Réunion de développement' AND date = '2025-01-26'));
+INSERT INTO meetings (uno, rno) VALUES (5, (SELECT rno FROM requests WHERE reason = 'Réunion de revue' AND date = '2025-01-26'));
\ No newline at end of file
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 73b8482..f2f57a1 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -1,4 +1,4 @@
-header.title=Meeting Planner
+header.title=MeetingPlannr
header.subtitle=Organize your meetings easily
footer.copyright=© 2023 Meeting Planner. All rights reserved.
@@ -48,6 +48,7 @@ meeting.new.description=Description
meeting.new.numberOfPeople=Number of People
meeting.new.submit=Submit
meeting.new.return=Return to Calendar
+meeting.new.occupied=Occupied
actions.page.title=Actions
actions.greeting=Hello
@@ -110,6 +111,8 @@ admin.requests.validated=Validated
admin.requests.validate=Validate
admin.requests.no.found=No requests found
admin.requests.return=Return to Dashboard
+admin.requests.filter.button=Filter
+admin.requests.filter.active=Filtered
admin.meetings.page.title=Meeting Management
admin.meetings.title=Manage Meetings
admin.meetings.search.placeholder=Search meetings...
@@ -130,3 +133,9 @@ admin.dashboard.users=Manage Users
admin.dashboard.requests=Manage Requests
admin.dashboard.meetings=Manage Meetings
admin.dashboard.return=Return to User Actions
+
+password.change.title=Forgot Password
+password.change.email=Email
+password.change.submit=Submit
+password.change.error=Error processing your request. Please try again.
+password.change.newPassword=New Password
diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties
index 73b8482..f2f57a1 100644
--- a/src/main/resources/messages_en.properties
+++ b/src/main/resources/messages_en.properties
@@ -1,4 +1,4 @@
-header.title=Meeting Planner
+header.title=MeetingPlannr
header.subtitle=Organize your meetings easily
footer.copyright=© 2023 Meeting Planner. All rights reserved.
@@ -48,6 +48,7 @@ meeting.new.description=Description
meeting.new.numberOfPeople=Number of People
meeting.new.submit=Submit
meeting.new.return=Return to Calendar
+meeting.new.occupied=Occupied
actions.page.title=Actions
actions.greeting=Hello
@@ -110,6 +111,8 @@ admin.requests.validated=Validated
admin.requests.validate=Validate
admin.requests.no.found=No requests found
admin.requests.return=Return to Dashboard
+admin.requests.filter.button=Filter
+admin.requests.filter.active=Filtered
admin.meetings.page.title=Meeting Management
admin.meetings.title=Manage Meetings
admin.meetings.search.placeholder=Search meetings...
@@ -130,3 +133,9 @@ admin.dashboard.users=Manage Users
admin.dashboard.requests=Manage Requests
admin.dashboard.meetings=Manage Meetings
admin.dashboard.return=Return to User Actions
+
+password.change.title=Forgot Password
+password.change.email=Email
+password.change.submit=Submit
+password.change.error=Error processing your request. Please try again.
+password.change.newPassword=New Password
diff --git a/src/main/resources/messages_es.properties b/src/main/resources/messages_es.properties
index c1c31df..59cdb24 100644
--- a/src/main/resources/messages_es.properties
+++ b/src/main/resources/messages_es.properties
@@ -1,4 +1,4 @@
-header.title=Planificador de Reuniones
+header.title=MeetingPlannr
header.subtitle=Organiza tus reuniones fácilmente
footer.copyright=© 2023 Planificador de Reuniones. Todos los derechos reservados.
@@ -10,7 +10,7 @@ register.phone=Número de teléfono
register.birthdate=Fecha de nacimiento
register.password=Contraseña
register.submit=Enviar
-register.login=¿Ya tienes una cuenta? Inicia sesión aquí
+register.login=Iniciar sesión
login.title=Iniciar sesión
login.email=Correo electrónico
login.password=Contraseña
@@ -18,6 +18,12 @@ login.submit=Iniciar sesión
login.register=Regístrate aquí
login.error=Nombre de usuario o contraseña inválidos
+password.change.title=Olvidé mi contraseña
+password.change.email=Correo electrónico
+password.change.submit=Enviar
+password.change.error=Error al procesar tu solicitud. Por favor, inténtalo de nuevo.
+password.change.newPassword=Nueva contraseña
+
profile.page.title=Perfil
profile.title=Tu Perfil
profile.picture=Foto de Perfil
@@ -48,6 +54,7 @@ meeting.new.description=Descripción
meeting.new.numberOfPeople=Número de Personas
meeting.new.submit=Enviar
meeting.new.return=Volver al Calendario
+meeting.new.occupied=Ocupado
actions.page.title=Acciones
actions.greeting=Hola
@@ -108,6 +115,8 @@ admin.requests.date.time=Fecha y Hora
admin.requests.requester=Solicitante
admin.requests.validated=Validada
admin.requests.validate=Validar
+admin.requests.filter.button=Filtrar
+admin.requests.filter.active=Filtrado
admin.requests.no.found=No se encontraron solicitudes
admin.requests.return=Volver al Panel de Control
admin.meetings.page.title=Gestión de Reuniones
diff --git a/src/main/resources/messages_fr.properties b/src/main/resources/messages_fr.properties
index 48267a4..8b22b6b 100644
--- a/src/main/resources/messages_fr.properties
+++ b/src/main/resources/messages_fr.properties
@@ -1,4 +1,4 @@
-header.title=Planificateur de Réunions
+header.title=MeetingPlannr
header.subtitle=Organisez vos réunions facilement
footer.copyright=© 2023 Planificateur de Réunions. Tous droits réservés.
@@ -48,6 +48,7 @@ meeting.new.description=Description
meeting.new.numberOfPeople=Nombre de Personnes
meeting.new.submit=Soumettre
meeting.new.return=Retour au Calendrier
+meeting.new.occupied=Occupé
actions.page.title=Actions
actions.greeting=Bonjour
@@ -110,6 +111,8 @@ admin.requests.validated=Validée
admin.requests.validate=Valider
admin.requests.no.found=Aucune demande trouvée
admin.requests.return=Retour au Tableau de Bord
+admin.requests.filter.button=Filtrer
+admin.requests.filter.active=Filtré
admin.meetings.page.title=Gestion des Réunions
admin.meetings.title=Gérer les Réunions
admin.meetings.search.placeholder=Rechercher des réunions...
@@ -130,3 +133,9 @@ admin.dashboard.users=Gérer les Utilisateurs
admin.dashboard.requests=Gérer les Demandes
admin.dashboard.meetings=Gérer les Réunions
admin.dashboard.return=Retour aux Actions Utilisateur
+
+password.change.title=Mot de passe oublié
+password.change.email=Email
+password.change.submit=Soumettre
+password.change.error=Erreur lors du traitement de votre demande. Veuillez réessayer.
+password.change.newPassword=Nouveau mot de passe
diff --git a/src/main/resources/static/styles/main.css b/src/main/resources/static/styles/main.css
index c85eee2..691c506 100644
--- a/src/main/resources/static/styles/main.css
+++ b/src/main/resources/static/styles/main.css
@@ -60,6 +60,7 @@ h1, h2 {
text-align: center;
border: 1px solid #ddd;
min-height: 80px;
+ transition: background-color 0.3s ease;
}
.weekend {
@@ -154,6 +155,21 @@ button.back:hover {
background-color: #0056b3;
}
+button.filter {
+ background-color: #ffc107;
+}
+
+button.filter:hover {
+ background-color: #e0a800;
+}
+
+button.filter.active {
+ background-color: #28a745;
+}
+
+button.filter.active:hover {
+ background-color: #218838;
+}
/* Formulaires */
.form-container, .container {
@@ -609,3 +625,32 @@ button.back:hover {
.language-switcher span {
color: rgba(255, 255, 255, 0.5);
}
+
+/* Styles pour la pagination */
+.pagination {
+ margin: 20px 0;
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+}
+
+.pagination-link {
+ padding: 10px 15px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ background: white;
+ color: #007bff;
+ text-decoration: none;
+ transition: background-color 0.3s, color 0.3s;
+}
+
+.pagination-link:hover {
+ background-color: #007bff;
+ color: white;
+}
+
+.pagination-link.active {
+ background-color: #007bff;
+ color: white;
+ font-weight: bold;
+}
diff --git a/src/main/resources/static/uploads/logo.jpg b/src/main/resources/static/uploads/logo.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..80505741dcf815b0304f1f7fdd35d7678b786d73
GIT binary patch
literal 9853
zcmex=<NpH&0WUXCHwH#VMur521O|rx51D4UR%E6zF!=g1XfZG_a4@hiS~D;)Ffi~k
zFfe+xXELxbFfcGOFfc~UnG2#Bq!<{ODqhJjurn|)@-Q$k*fB6LFvz@ur~<J^I(GC_
zNKeg6ElMm&O#y`pBCHs*Kw&c)R-jOTVMhi}1_g$6hE#?;hE#?khD3%EhExVn!2N&7
zoSIQmQedU8pI5Axn3A8As+XLft6z~=pl_&WpwD1qUr~^loSj;tkd&I9nP;p1e)oQL
zh0GLN^>9;P1K$GY)Qn7zs-o23D!-8As_bOT6m@$$UM`yotBTx$+|-gpg^Jvqyke^g
zTP3jRR(Zu%AYpwaJDZ}EG^-#NH>irDlr&o<s9Va*%k|2Q_413-^$jg8E%gnI^o@*k
zi&D~bi!1X=5-W7`ij|=TxTF>*7iAWdWaj57fDBAb$}cUkRVppbOtDH!OfyYNNio$;
zN;FQ<H8e~~)J;q=Hq=cqHcl}&HMB5FGfYx~TAz{(x4odWC<knJO0s@xPHJvyUP-aO
zp`M|>l0L-IbhXpYCMDS_xhOTUB)<q^eQ;_a)X`*ySx%;%BT@#k(FX-wPNp4%5u93R
z$IHdZ$!VjHY%^F9lFy;4gIwI~Z1myLZO6-{KnF0GEkR_N)Vvg1^>TH4yZ^TtoEeyz
zm_Ue`nVE%|nT3^um6e5sm5ZI7jf01ahliVso12$UNPw45kdK>NKukbTSVUA*l!sqj
zLQF(LNJLZwWC$ZO3kxd?D<>-}rwA`MuL#ND{{aR;4u%B`3z!*|7?=bZnFSgDA7PMZ
zU|?ir1Or%^!N|nS!pg?Z!O6w_{|LiY0R|>UW@aW9W{|5H7#M3AnV1<^1X+a?4ISBp
z0~6Vm3Pp?>CobercG`GQH0a_772~9$CQdFfaS2H&RW)@DO)V2sGjj_|D`yv1H+K(D
zui%i-u<(e;sN|H?wDgS3tm2Z=vhs?`s^*r~w)T$Bu1S-pOr17;#>`oZ7B5-4Z25|n
zt2S-kvUS_`9Xod&I(+2lvEwIBp1O4T%GGPvZ`{1~@X_NZPoF)1@$%KjPoKYh{r3IG
z&tD*aF)}cNeFY*QK11`DAOjO46AKG73p*<d3j-rlIRhgTvmgtrq9L1*V<3BCp|Fxs
zBZr97#DyCVaw;1KeGpA5y2vG_V)9V+BgkuDpAqM=CbE16_ZY%ow-|Vs85x)anFSf_
z8UC^-Y3?asdOjy8*@R)wKanYGzwceC!LX=HgHfuLZTd#_1<!i^g)jtU8y~6DxU@%p
z@ts$<jxEez_VU}QIPcjros<}_Jh^`EKLc-l{`&U$Vs*xjAATQRU0v{9TVdPPB|SRA
zCl;46ykcjcpT+Pip(#Z0{zUJ$_jb&%GyNg;`?&Z${_dkv6(4nHx6by<42|_<Zs6bf
zGIq7q^YwmT8lnul`~&9{yVvbp!gR@0x_zy3Rr#`0tV^?87yb%T@zShso3w&+rN;AX
zT?`tFCaz#Cc5^kGTKQP=ylmmU25Fs6De;L@l>>Lq>|o$2w_W|@{w?c&T)GeVn>7oU
zy$D+0vnN>M)`qAPzI$|Esx<fTJH~nbXJFO((f-lt^-(MN!~YpLqF!x$5q_=7Gv%^!
z?e|TS+>WKbVb5P$B){4JkI?<Vk4(3-?~A^Z_uQ1E;>&e+v&~|KM{fTFDj%<9sQLY<
zzr$X7e_p@jvbj9ryVh=>{d=ylf3WP-73!<Y9@|EIUw@(g#`SO9m;N)P&wTLFzf&*y
zT20PH5r37mdnXJSo9Zh+&Yyj6-?R_nhxhaR(Y=s#agX|<$QRcHdn&^&HpJh(y;{B~
zyW`{Yw}Bt_Kb+s4^T+y;y?DgUtjpV^WZr$+eEE#_cY7&kJBh!$|1%`ZOJ~*#ncnZ3
z$KCfh{PI@r{%x_xQ~Yn`EW2fHz&vTEj9|onhRyDOM0OvM=a0L8I9_<+{Md^Rl}xT&
zN&0lPR;FG;=J)kiebd)OWnC+XyLrd8GTrE%=fi>*uiPb+SFE4;pCMEKhWvqexwY#|
zdF}4$bN$%3DeTud8&~b3`l{yU`JvCdoNX^Ze1GITPfgH=zx%d7%uTx<arv3#-Mj}=
z)Hmw~WUsjYLi?lJ<A?tlj{Rr2QT|9r{Mxkd+dlq0oO*Od=9=}_?MoXTKRY1v$vA$~
zJ<+S{O3(AWv}%32bMvlUTe>Q9KhM{^$(UmA<Y%{Rh1;=vUuvr^&G-Ac#J*~>M4+pv
z!l5p=rTM!wCwag3vN&n0X;m0f`nKBt!?y0~)vvA>Mr3Xalw7BmX5n$6ROKpT=}POa
zr#0LkgAd7z=$yUZTH~0w&du&W!_73F(=R7p+ck?JKIYf6sci2okFPkY_9mq|sdqV}
zyTZp`T|VrUhI<<SY`M4RNza3C%3V+Psn>*lSbiv$FE;sT)!TLbX*(64)!(yZUZfso
z@AYZ^?c{I$SMAgOGqh}cJxjHveAn5(_iwD7Rp1kl8uPRFbwj&+)Ss0f_Dz5M{<iz0
zD`ip3yOeidT_>^q*4F5WGKFgHg6sW^zlRhD8NWDxOZXpG<|FwIVc&=St!Bb6&BEDF
zNOd}0pDuag@2&ow^UrrZ_{0C7f#t@J{U4nVZ@;I0Exp<G$~xh<V&8rpS(BGH<MFh2
zJO49e?fd(8=6{AHdB%U|&Axqqoj2=0Ls#L<(&{Vh$@P~-Uo2LB9q%fp*dBX&a^w52
z|F($K>Ho3)@$%#1Z=xT&E6QUw?y|D-oVmr;UyVJcf0p=jxf_r6d+kr3f1Ce+{Fcz3
zz?V|(-|{=m{{3h0*rzS?s`|}yKK2#sU4AV5F!@ltq>Z&+@1wV3_ro{ea<X2h+Vbnz
z-H=bUt3S_vT>eK`{+K-fSLa9mEx*>>S|eaSS9G%H)3}3u1#2ww?5!C7y6oD}^pnS2
z;ApWti^|EpMV9O=g~6gXCF7hLKl888zqH@JK284i_ka)crmy}{{CMSTuPY`I=FzXh
z4W`a}&Z0l*<vo8VYvwr9->&}|4toEI{HW!*g}*)Qaz(b=WW(7D%<tV{o)^9;{X(&i
z&tI{R*WVg`IQ?+mR=$ak>?Ja{N%fo&fAjmhN#)Dkvi4G6W_zE|Uz@aJ`_#0i6n>di
zMeCL@ao9U)f71R|{GXwzs^a_OxV7w^HU;11<UZVar#dbB?b*tT<&s)!&nwKIas72!
z@1k2jid{Ec$`Lr8nwT%UM(ga3j*Ct!)UPvtUFKO@Ykl$hQuEAT`c<F#JMM3JeptTM
zYU{q~kNQP#+{h9(h~Kz6zvR5{jo<rwmHGo0$b4DxaA()Id(s^#k&9=FxlHwQUd^*o
zzER}kr#YcQ-hV=m{>l20%ziAsb)V9Q*t1V&)l}`>KKa{>U&jtMYaU+~l78Ft)~}#<
zw{CBoyM^(`;*ZSVDt}!6EnTttcfF7KhMSIhN!Px#r8l{pbC|$)rz9~xqVDYe55aH6
zkGh?A__2_uif_e@o@{}Yzjc;3&Sd|yS^d-cee2gG?Ato+)+>&8Z|8cQWH})jBYbMk
zxAh(SGw1(M%WvDK$bGi>;gg9!;$w3!+0DBXdG22G@0O3RG??Go>W6K<u9LLy^16k4
zH_t3z^1(ySOtx#{t+jg6`!D=6{ju~xe9wOd$^Q(A^QF!%d{uUR!pn~yHgEZZc&=^M
zbBj{GP%Zj({iX9a?|-}Tqx!dF^uf&RI+J+e1WuFc?_59MKi%#w`DKam%|Gc6dbuCT
zH5V_c==R!v^~>(wQ{TP^w#iyG-}v42=i1+8_77%DeQ$WL_A&iX<hoL63I2H{8x_UN
z_J2LuexLdC`YZMZUlyNPV*F|Ox4QofO_MA3Kde1tC-q})a^d>SY3IL{?VM!0{n{x5
z28F*L*lK^QzvSN*FTO|pWB1{gYkmYrHHU4f5f`*SFZrd^W~JQMXa5<txW7sK@cUcm
zmT8XzH%Xaye0$p+wb5bIb9T9;FNJfeKgQ4AFJQ<1C+BZ_P3+&@OJeTw8pa!DN5wtc
zKGArN*IYJ-=jG2G&fjdCW)X7Y+{PB^?h^$fd`q6~a{_r~@jad&&a&@%AAFYYuJPNi
zXt4j6>gLbi@6>+cvk?8T{|NuB$B*ORS{G(NtmaIQco&%T@AQSFRCC@7)0LkcJo(sS
z{o?&;`9D+-9lEwp;m2mX=O-rG*ygGgUU*b4-_PyAKW&fkhr^HA-yZ#_^gcR6?TX5-
z8Pap(o>YcSJA6SVnL)03ZQZ1QXX`iAH~eEhp1U&cuzXid<khXxSN`7dvHW`{eVO5v
z&+#r9a<Z5HC@(&|EMr#fzM0Ez_fEVW?II{rc0e@Gy-(s+=G>${o6R1_84K^t==^iu
zfQRRoOx@IT6`vpRx7SHk)IE;=Xtp+YN#=xg&t^Sx*!g%--;$3P!cW_8S^h_)`QUy|
z<((J*$RvME?>rW|XZn8zm+g7(XCA+wc${gU<fR(bkEg$V`M6>4&g`{r7gKKKtuyAE
zo3!MM<K_KVE_lA(zoq<-$nD6FCEM-fe}w1iO;1;UH#=gcaj{v&8Sg)m=dX7?`p=La
z|3m#hL*GC1*AsrMKHgur@JHRF#6pu_jk?uUMx2M{71a7W#^=<Xy}!Y|v5w_%_R1^)
z_ebl+^v~qKdn^~jd#q;5%PXa(Uv9+SxP8KgvCARs(wp2bbM3Ajx@50)?V3oN$T<(!
zfCcF{_^+)sRoakXU~$0m*C)-WnJ%xJ4(+M_w(UpfqxG%(nM(gN2>j5y{Wj|9Za0yi
zY5r<P2W)0nKd86*`gKK~qIbtVOA(tKgHxv@489zE@XzF=z1@F?^!y)Mx!YEj<}dsc
zx~1<>?(OiHEl$cR^J<R#liORn`h7p&kJ(-y)28RIda?c6uXXb+&Xk*NVh>~g8Gds9
zmhwNs(+|drO?w~uVb${H8ehe9CV{%+)?ZI+{GRz$^uyhCt3O`;#`UqWEoIl%%aNaQ
zpWfA*Te0&M$Afg)4VjF;-hXs|@SmY&pK(R$mw54v3t87@9P)VleZz-J_gBm3FRe@c
zcTxU>?|g|G)gAMX+~-}FXXCu+_I%5PmZ|%;9=Az;pL`|!`u@%Je?*uM?3YR2cG)yC
z_|QM?<c(|I-Qif4$Nuil*Vh+5#|OFnOOuVZwAW@nX6v|M@u&IU3jZ@SO{-Y^@az#e
zi609y3)f{D2=d>aA-Z=fvy6d#4PWiA@R#|A*LTTD{#g6)Md^O7*J8hpwk$5Jh{%gi
zk6Yzh$^KUMH}4PTZ!K-v_Z4l-A1#l+`|6rukJk<R*TFyb?X6gU&R*n??vMMwIe(n~
z9d|5yb*znDaM0zG(@wo~{Le70`|_NUe6L0Rx2Du2?$>Jjb?kP?#%br?alCuGBxsUj
z!6J_vatl_zvR(aX{>|n8xMCm1ce-77{`kuMVZNXxhko(D&Z0X!PyQZ0U-die+$ZkS
z70<WNRNp=!O|7?CGd|$(TBURG2kO~t1V0?ho@t}~aIU@Uw5L6j&gP1#Bvt)6J|}&<
z=#BNK&xgHz|E~5m_pvyO3pI=j?!2~BysEpU*XS<y-lLaWCFEwls+;=r^l$I_gMs>A
zm+$=W{g8E~Zc@#WUw@BO&hoq2%J1_{eU_8m^&j7V2mN6GR&(iIz0j-IYD-S<;?=s+
zzGKEOR#|pg-@24Paevp_>HfPOr}UqpCHzoW_^h)2l4llMVwDRgg{t>kIbL6H%bNX%
z<!@7+^1q9_HdU-YzI}U@SZB$-oawhFrtjXx^3~O*p{UYA^jG<Z^l$Ya%n!t}mfYuf
zaq;H9qmk>EGOkOvy?I~q54*@a&yW3YuK#CX*}D1iwbL;_))wfkE0W0&TmMP(b!Uof
zU3dI~D)#m-`#E0<yo}PDpTG3wwFfF?*EX`a%Ca&1+p4yHb;Jv+obR`$-Z3*XSXrqy
z_mGmtyNK%t?YC_IBg*ad;r*>G{H+(<*Jde5+>hSD{^rFEn`8Tr)$)6Z<gbuVtcm&Y
z^uhVYdGeFPK9+6fn=?D&X#U*G=iEOm;`3a8_I!8zhamrs{|q8Ep<)w1oVymwcI1@U
zEsMpU*Xv9FD6e9Eq22N$_P61mq`#YO0&Kh=X`fzEvNJ}qbZ>Imh2>i`svciR+V8nP
z{r-*j58uD}p3k$(ZvNku>;E&Hj?MeQHrw_T^C3R#x{{v`U;UZ=*ThulJIy~LxW?+q
zGy~f&o)^;$SJwZjHr=_~r03=Xy-M|hm%lzcU0|Kqwx(X<$6?nW)emp4tv_PbB9%5V
zgK^fg2M^i&7|vh0wx?G6-2ROG=Cb|r`vhOdaqZq+%~-YSW6X;4)3sz4D(^TS_)nza
zWsUOVeEuK5J)__Ay%wuFJGuDztuyPy?RKoT)B51o`FO5Su6+Du-O^s8(i`8l2fmM~
z`h4N9_Q%Ob<d}av{LS&>O#E%rr@f0#&UnApShx31!jnAar_bj!pIE=$KJESw&Hk2s
z>blE1A0L!&t#P^Gzh&cp2A3I=*qrM*?b;^DdH?pUKWLD@<)r#Y^+T2^yPAu=Zt<M&
zTzuSqXWftE%*}DpALPG%`_cW|>Q(o{c-fnGU;k2F-SK4O6?4_63s{&0Z%<-hc(!i;
z?N@^LZyztrHz_{!K;miV_I1{xw!D@6$NO)FKYad{HzoMUt)Qc;-YChYonhJ8-?yZH
zpXG}?7Xs@aoPR6%z`kF~>Rv@~bl;;Q_SwmroTZFkG_Usu#<j10an0)8?83yAH*dcZ
zGw0qE9o=!*RpC4P&+CuBAB(@S{n7q6tJj%#?NP2QUG-9I?X#n6GM-M$aIloi;&W%7
zclGUmhRx-FM71o%t{=WHrn{%pQE=V<Z26P7XD)tI>+xtt@BLfR|2WH+{pg5$J)dh?
zzt0uU^vRC9^6#ATx9IG@`%CI){KNNebw4iu=6r449>corO80IT*|o1*Y?D!ZO{tRk
zMPdA=d*UC<dLR3W-44I-l{+`se{Q*JVYb4=!msjA!u3A}KZtMqXBu%l{V^}s^^i{;
zpPnaf%lLSXrFo9rjPENYf668=`IGl|@16pilX+XULniaQ^QkKB6285i;cxaAV{j>3
zSHb=uCv4WdwLEn?A44jVt1Bf8Zf}=ymv=t#D*MHLjtYC{4`QwPOMcY*Y`bJ~Lxg4f
zwjKrM1?Q~h7oRt6|Cs#Py?Gz!kJ_*O;@M%pQn)xhX1tpf#O~sF(uDEWgB1+2OTIm3
zsH*?E`g!~<<8Kdt)c&@;eLB<jQuaU9TRv5`-S1Ou{xhtp>pY(v&tG?8{)ZrW>6)0`
zlMmPPzSXNSU*cCYb(TQfuh;6#62E7Dk-0Yi&HeujEayM!AJJU&QU9p*>R!G>Wy1C8
z8}C;a@$tpX3%2?CZ0_0>a{iC~yYEZw&s#5{*PqCG_h|PCmMFhDlDY{okB#QFOTN0d
z^;`d&%OImZ>_0qp%XK@2J$33InI2yEKbQ0NM#6*t4CkHfwSFCcxc$(2u0Kj2-b9(V
zY>BRwJIbymBA0Uf_qpfK)L%#4Q~PK(@xflP$6?n^rE`sawg<hpz0zdjVD+i$@-vma
z`Psa4_mqA6uypSp?QIs1V^?)qCvPo`v-@%Pfqm;*p&#py{Hp8wG(k+dvti<<0=u}a
zAGW{M{LjGZRJmm5hyM(RqHB-*d;9hEj9>HIlKOv&t(W^D{#)>m-{0kXT;^$iJbU%s
z^aXeJ&oo=PYP0FtQ_n3rrRT`UF)U$!eYbC6?y8%I%QLO+M#s2qI%<=y@#3(S`+8BU
zFKhZaf5aU=SSNO=#(wGIWwV#wI-Z`l?)L1{j$4ar3)r7Wt?s$Xn3#F`dwX8hbBPtl
z@2=PXyTVSXqWfX`f%`%~W*?fyy&^j-aIvejVnM?Wo~P9fmJ9gZukW!>e57mi*#D}|
z+ON{m4MEw_E&S3Sdj#tCy4JA&_N+gsXL7bSZ|nZn>72{5czAjj#cY44YBR^}_`A=2
z{LdrzGuS^^dFa<KUF}C}TgpxvGwgacSCFgS;>^U92i5&vf0;k*@7_```O@syzUCFT
zvv!r9de>DQE~Ly;)&HbYgz?AY59hzl_;LKVM8(oSmXEltuU~pFLFbm-y6B9UPl?6f
zs|x$Hza0Kn`**z!<8P-3H<Mb|lD{SE-*2hDBvfkq@n&0f*ZX~XHG&_~kH_=vn+WOv
z2X&|^@4UUOx{i;_KIY%lpN+qr>JQr1)PE2U&KLb-y=(XNFW$k=Rj&C2{W>pe#IDY~
z>q_IV$4^%;Uc92>oU=BA<F{w~JpL}Ve=z4C$Bz@+XKil_(`Wy&$RWOj^Ivzte}*p?
z+3%iT^)K}Ec~B~3wY##%Sap9}oz89V`fO>A$uXUVh5Y&VmVact(^s+nl0VCj&yGJj
z7u?RalfL?PYp>nIa@C`o+nH~CKYx8Gt9)<kKI?x6_8H$)*me2M%)`04mtS7*o_FK%
zoU((W-<Q96{hxtV=mY=J-_h0lf-&mNf4uhBNzY_x3bLB<)TWMY-|~;;&0peWGs>;Z
z_AZNh^(*&K;@iB%_jcGGtCBB_pR<39{U4!CAF?LBy3e&<Y1*|&ZoAHUjj(R@A2J`p
zZ?88Is?rCiTC)$|n}nXb{>Xp&qx*<NO0H+)Z`sW~&D;M~$uAfEq<(mRL%n2uZol|{
zhC_~PV(!|_dB=0>*~*DLUe!*^o7@-mudR*zZB~EKuq2;t@4oFHtunUW_4Jr%QXw%r
zqMs!{Z(ZG-;`q|cZ(_Sbd(6trF5J9%Xkk(l3rEM^hQLb`<m7+b{yX-cf&V{4`fiJ~
z%Q@naAI0pe*Tyf%EU({SrSPjb?#nu}Nf8rvp028TwC6v=qyG%Y?b&MhKm306Pxsow
z%gMKV!hZ7FoUv9aRoid)pCLp3hlYLsKie$hiui|Lmp*G;6f<8@dGqP`IOYR)PM>Oi
zVY@=(clNg_fA`z4eo(TWYjmjOqkGq(hb6x?&E2`{FNeRdYo7Jz-PJwLANPOz^6}($
z*Iid~Yc6j)ci-^kH}45X{(i<+o?PEicQ*cmk8JpnPq!=DAFaK!D|Swc*De#?f0H#{
z|33Cl=AYVrk3YO0{xck&&s!p463_ioY?s|^hC>Il<?hUXZ}?C7ov)Gq%=)zbKh&2u
z+wmUHUiD*|MNRml6CWNv?~%?<d);w)vEZw#Ul$$Upuqf8hQWa8H-FaCf2uX1ALbwY
z&%l3nUB%|8try=2<nbE+`*=Q~%QC5U_2<ivrvDKQK2mkPYtdyp$%;IQH!X)Oo?b~d
zI$t`uhb6d`{i6xvuZ$%{)oC;CN1gewZ*BRyig=GJ`ftv*zJ0nRTsUewn|cY)>Ao8(
z{fD-^YS?&frb=d^@KG(DiB@k@3OEv9Wd3;XVfWTUUg_V(_z!;m%r#Mc??XRq=s$Q*
zb)t9u;^n_&4}bl6q4v(N^-truZr!aZ`rG&?C(L7APEJbBjFdCCN<aN-zK|}v!L{zv
zV};L+uOt3xedz0cyq52=$49p5*{ffE=icU>z2f`A?~*TSj~dt&|D98xf1jzQu<f?*
z)%19-A8QXC%Xv3n@XXym+ie8r-OYX#GQsG%GDCue<)7NupY6ZR_|MRkbU)*B%#Z$u
zCYR!(H8bZs&iv0%D_?)t{A2Ql2meB!-DjwOut3iK?|f6$58M0K=9k{(yZUFQ!d}+=
zHOHzn^Y^gvvrUlCtGgt>LBGMy*41w5^4rHB`imRrZ2wl+D08;?-!{X8=U04Le_nd6
z@Z;rgm>>6Vas4(s*7S+)bJtzDRzGY18uus1ubIzLXJ4WGcwVF2%7?aD()a8uGtP)T
zV*YzA_=M@ritfWz;XNkZbJZg+ul?$4R(jiX?Wz3U9bat!9u1J!`**hfLtsAhpLB0q
z^&@@zgg!dAI`f=k-ShVH_q<yF<g>N8GOKkmE>6{QXZ3UZV!8gD{w?=^oOzo~uXof5
zol~A7Z@S06QjJ|w#pORkOoG(M{FeG{$B*v686WYVp(Xg(Ht|{ere@V_yIh(O^h|)w
zP^P8i*YmXub$egUYF*c#ZhQCbk+5kWR%WWrJhUdET;<#P#{HS+e`ubY`LMZkL5+Tw
zZFW#V()6e)@|%y``OmOA{_%5}FY9OQQ?9A`ar$?D#lafO1-D||mz-2PVd86U7qqT$
z=KLSKKJUJ^Z^z$R_77&xllj20^}3ysO`XzpM!s25{~3<Y(LH$U{(a?7E193<x6a?J
ze~kaO^HKLB^AvXP>Gz&_aDMF7(<}`0T(qkGG41!UPrrXt{DA!CZL`{4E>91*?(p~B
z){2G8bdPKPJ@{pP_U4V-FTYLPvbD)NJ0d)%;Lz?+@!zYgxjx?SzrVG-!9IPi!O2Im
zp?Rfgz85mXa^%WwH!b*JdzMx1sLIEv+ow&BtmM(+HrXT~YIsuJrmpm3{#(QU46Ftp
z<{uW@eX)jR<B#=6ytr&n&HtiZ_N3_kG577FAI~4-zjgW1`?sx+93R&6M&7-d@!cq=
z-T9Ka*hJ3*#ul%y-w407f3yA{q4$0l!Y;@CSZ#5olvV4o<M!gG?)QZ@{62moSmkH&
z!Tp^7%<JynGs(>knD^@0$@xNAx7w07ZHs#?^~FYx>B;pM=ij{i&GX0R?|kL$`@|j}
ziBo5Oc6I&Y^Hb_yPN^0DdHq6_-{j<nf7R~$Ue<YVd5!M$6tis`J081!;j$1ljJ=ca
z+;SQ7$)utR`6x}xKbo10$v-m989lB{IZ*!C+c&0s>D^lm;`hIPSugqH=Wn$d`@aj<
z%*g(gbSqo2kZ)t!!aQXQi<2)N-`u%A^{hsYfeGV}!yn7P1^(FmTkgl~e?s~EcVq8l
z2H3qYeJit#;Yp(rpSz!<#G`#vL4)ECmQRy<f78bLqo2i>wVoZFrYAzWE2|Pt*f-3N
zoBE5>#4s+}#@ukt1&^gFeXcxfmTzQlju)xB^q=8_&wcJc9>3$ZfB5B5qrGm2k=?xd
z$LGpkAOG`N<?)pO_M5xRb|$aS+>j-ed&m6R<lc*CSsEWF-e`UqQ-1V(gB{C{r_28K
zACuzUx9nlr(aPTGg8vzIJ)VDVzlx3dNA1V`Z}cCtUDi+BvuTqMv)<$tY}MLR+1KP=
zX1|*KCHq_6-`zIsA77o-dA7%&uVSH_%4Pi}+&ezb*ZpPmtiiVXVgAAW?0+OLC|<Hr
zUljea>_FlZm%nfJzqOY0&8;|m%u2rDi}l?2BT{qMO<v}-^vS$e0bPbSi~qEnehz=D
z|DS=C>x2D)-_>)Cou}E0Rcy{<UAnrhiS0kbvi_e1mg~>YSARVJ?f1u%%3`y`vNdhe
zbghnhGQT`ldp?Tc`~F+m|F~MK*X+IC_D|Sdb?NSZFTb9g{C(56zn99+CuN`g&yao}
zG-h_h|CV;Bzvz!ouU<P|SU3CZq|X7T@7^sxrTK*Uyn5Gf|8JT98Ja39gdb$xdM&5a
zr!QWy$#BQnLb=x&(ajwfz8<sMS{tr%Uum5svxLL}*@oj;44>yW+cWIp{UGe@YkkPd
zB&;WdbA5{Rx^(5;`@cx6cf1qc^K4?V_bdjVIS(%H@0z?^w)VvW)+{fL*A7A#U9}#s
zWl$BFf7IK7RV4jF;P=V}OjG^s?w&9FvgGb8O~$i-9Dd|K?C;tqQ!9LQmg%~U36C=(
zF9^%;l3P`2tNl6rTOX)s|IvQPeADasTu*1mT#h{5Yvz8tJa^tH_xlo$`>MiUl`8(6
z(&bQ>QFo^PgOBvu$ORwSkG4%Zc4NaNTQ+vNoIU>;PN)Bq`@{8D^274h5`U%_1sVHf
zFY9ipHM*1QogHyVU-taU@20EP6!ts%#>ahO*r~qO_{YjG=O3+Nczs`F=i4r?C<cus
d556zSE9{%X@KnLv<ZABDUl;CXYB2x52>=;d&k6tl
literal 0
HcmV?d00001
diff --git a/src/main/webapp/WEB-INF/jsp/admin/meetings.jsp b/src/main/webapp/WEB-INF/jsp/admin/meetings.jsp
index b546775..1e97791 100644
--- a/src/main/webapp/WEB-INF/jsp/admin/meetings.jsp
+++ b/src/main/webapp/WEB-INF/jsp/admin/meetings.jsp
@@ -62,7 +62,7 @@
for (int i = 1; i <= totalPages; i++) {
%>
<a href="?page=<%= i %>&search=${param.search}"
- class="<%= i == currentPage ? "active" : "" %>">
+ class="pagination-link <%= i == currentPage ? "active" : "" %>">
<%= i %>
</a>
<%
diff --git a/src/main/webapp/WEB-INF/jsp/admin/requests.jsp b/src/main/webapp/WEB-INF/jsp/admin/requests.jsp
index 811b96d..e042243 100644
--- a/src/main/webapp/WEB-INF/jsp/admin/requests.jsp
+++ b/src/main/webapp/WEB-INF/jsp/admin/requests.jsp
@@ -18,6 +18,9 @@
<input type="text" name="search" placeholder="<spring:message code="admin.requests.search.placeholder"/>"
value="${param.search}">
<button type="submit" class="back"><spring:message code="admin.requests.search.button"/></button>
+ <button type="submit" name="filter" value="${param.filter == 'true' ? 'false' : 'true'}" class="filter ${param.filter == 'true' ? 'active' : ''}">
+ <spring:message code="${param.filter == 'true' ? 'admin.requests.filter.active' : 'admin.requests.filter.button'}"/>
+ </button>
</form>
<div class="users-list">
@@ -37,7 +40,7 @@
%>
<span class="banned-status"><spring:message code="admin.requests.validated"/></span>
<% } else { %>
- <form action="${pageContext.request.contextPath}/admin/requests/validate/<%= req.getRno() %>" method="post" style="display: inline;">
+ <form action="${pageContext.request.contextPath}/admin/requests/validate/<%= req.getRno() %>?page=${param.page}&search=${param.search}&filter=${param.filter}" method="post" style="display: inline;">
<button type="submit" class="submit"><spring:message code="admin.requests.validate"/></button>
</form>
<% } %>
@@ -59,8 +62,8 @@
int currentPage = (Integer) request.getAttribute("currentPage");
for (int i = 1; i <= totalPages; i++) {
%>
- <a href="?page=<%= i %>&search=${param.search}"
- class="<%= i == currentPage ? "active" : "" %>">
+ <a href="?page=<%= i %>&search=${param.search}&filter=${param.filter}"
+ class="pagination-link <%= i == currentPage ? "active" : "" %>">
<%= i %>
</a>
<%
diff --git a/src/main/webapp/WEB-INF/jsp/admin/users.jsp b/src/main/webapp/WEB-INF/jsp/admin/users.jsp
index 982f36a..6b88938 100644
--- a/src/main/webapp/WEB-INF/jsp/admin/users.jsp
+++ b/src/main/webapp/WEB-INF/jsp/admin/users.jsp
@@ -63,7 +63,7 @@
for (int i = 1; i <= totalPages; i++) {
%>
<a href="?page=<%= i %>&search=${param.search}"
- class="<%= i == currentPage ? "active" : "" %>">
+ class="pagination-link <%= i == currentPage ? "active" : "" %>">
<%= i %>
</a>
<%
diff --git a/src/main/webapp/WEB-INF/jsp/common/header.jsp b/src/main/webapp/WEB-INF/jsp/common/header.jsp
index 2653ba9..ab5d84f 100644
--- a/src/main/webapp/WEB-INF/jsp/common/header.jsp
+++ b/src/main/webapp/WEB-INF/jsp/common/header.jsp
@@ -11,7 +11,9 @@
</div>
<div class="logo">
<a href="${pageContext.request.contextPath}/user/listeAction">
- <span class="logo-icon">📅</span>
+ <span class="logo-icon">
+ <img src="${pageContext.request.contextPath}/uploads/logo.jpg" alt="Logo" style="width: 50px; height: 50px;">
+ </span>
<h1><spring:message code="header.title"/></h1>
</a>
</div>
diff --git a/src/main/webapp/WEB-INF/jsp/public/login.jsp b/src/main/webapp/WEB-INF/jsp/public/login.jsp
index e435d4b..dfc1ccf 100644
--- a/src/main/webapp/WEB-INF/jsp/public/login.jsp
+++ b/src/main/webapp/WEB-INF/jsp/public/login.jsp
@@ -20,9 +20,13 @@
<input type="password" id="password" name="password">
</div>
<button type="submit" class="submit"><spring:message code="login.submit"/></button>
+ <br><br>
<a href="register">
<spring:message code="login.register"/>
</a>
+ <a href="password_change">
+ <spring:message code="password.change.title"/>
+ </a>
</form>
<%
String errorMessage = (String) request.getAttribute("errorMessage");
diff --git a/src/main/webapp/WEB-INF/jsp/public/passwordChange.jsp b/src/main/webapp/WEB-INF/jsp/public/passwordChange.jsp
new file mode 100644
index 0000000..964e264
--- /dev/null
+++ b/src/main/webapp/WEB-INF/jsp/public/passwordChange.jsp
@@ -0,0 +1,50 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
+<!DOCTYPE html>
+<html>
+<head>
+ <title><spring:message code="password.change.title"/></title>
+ <link rel="stylesheet" href="${pageContext.request.contextPath}/styles/main.css">
+</head>
+<body>
+ <%@ include file="../common/header.jsp" %>
+ <div class="container">
+ <h2><spring:message code="password.change.title"/></h2>
+ <form action="${pageContext.request.contextPath}/public/perform_password_change" method="POST">
+ <div class="form-group">
+ <label for="email"><spring:message code="password.change.email"/></label>
+ <input type="email" id="email" name="email" required>
+ </div>
+ <div class="form-group">
+ <label for="newPassword"><spring:message code="password.change.newPassword"/></label>
+ <input type="password" id="newPassword" name="newPassword" required>
+ </div>
+ <button type="submit" class="submit"><spring:message code="password.change.submit"/></button>
+ </form>
+
+ <%-- Affichage du message d'erreur s'il existe --%>
+ <%
+ String errorMessage = (String) request.getAttribute("errorMessage");
+ if (errorMessage != null) {
+ %>
+ <div class="error"><%= errorMessage %></div>
+ <%
+ }
+ %>
+
+ <%-- Affichage du message de succès s'il existe --%>
+ <%
+ String successMessage = (String) request.getAttribute("successMessage");
+ if (successMessage != null) {
+ %>
+ <div class="success"><%= successMessage %></div>
+ <%
+ }
+ %>
+ <a href="login">
+ <spring:message code="register.login"/>
+ </a>
+ </div>
+ <%@ include file="../common/footer.jsp" %>
+</body>
+</html>
diff --git a/src/main/webapp/WEB-INF/jsp/user/calendar.jsp b/src/main/webapp/WEB-INF/jsp/user/calendar.jsp
index 56b1599..dc48507 100644
--- a/src/main/webapp/WEB-INF/jsp/user/calendar.jsp
+++ b/src/main/webapp/WEB-INF/jsp/user/calendar.jsp
@@ -7,6 +7,11 @@
<%@ page import="java.util.Locale" %>
<%@ page import="fr.but.infoetu.meetingplannr.config.Config" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
+<%@ page import="java.util.List" %>
+<%@ page import="fr.but.infoetu.meetingplannr.pojo.Meeting" %>
+<%@ page import="java.util.Map" %>
+<%@ page import="java.util.Collections" %>
+
<!DOCTYPE html>
<html>
<head>
@@ -19,19 +24,12 @@
<h1><spring:message code="calendar.title"/></h1>
<%
- String monthParam = request.getParameter("month");
- String yearParam = request.getParameter("year");
- LocalDate today = LocalDate.now();
- LocalDate currentDate;
-
- if (monthParam != null && yearParam != null) {
- currentDate = LocalDate.of(Integer.parseInt(yearParam), Integer.parseInt(monthParam), 1);
- } else {
- currentDate = LocalDate.now().withDayOfMonth(1);
- }
-
- LocalDate previousMonth = currentDate.minusMonths(1);
- LocalDate nextMonth = currentDate.plusMonths(1);
+ LocalDate currentDate = (LocalDate) request.getAttribute("currentDate");
+ LocalDate previousMonth = (LocalDate) request.getAttribute("previousMonth");
+ LocalDate nextMonth = (LocalDate) request.getAttribute("nextMonth");
+ LocalDate today = (LocalDate) request.getAttribute("today");
+ Map<LocalDate, List<Meeting>> meetingsByDate = (Map<LocalDate, List<Meeting>>) request.getAttribute("meetingsByDate");
+ int totalSlots = Config.createTimeSlots().size();
%>
<div class="calendar-navigation">
@@ -56,27 +54,31 @@
<div class="calendar-header"><spring:message code="calendar.wednesday"/></div>
<div class="calendar-header"><spring:message code="calendar.thursday"/></div>
<div class="calendar-header"><spring:message code="calendar.friday"/></div>
- <div class="calendar-header weekend"><spring:message code="calendar.saturday"/></div>
- <div class="calendar-header weekend"><spring:message code="calendar.sunday"/></div>
+ <div class="calendar-header"><spring:message code="calendar.saturday"/></div>
+ <div class="calendar-header"><spring:message code="calendar.sunday"/></div>
<%
LocalDate date = currentDate;
int month = date.getMonthValue();
-
int firstDayOfWeek = date.getDayOfWeek().getValue();
-
+ %>
+
+ <%
for (int i = 1; i < firstDayOfWeek; i++) {
%><div class="calendar-day"></div><%
}
while (date.getMonthValue() == month) {
DayOfWeek dow = date.getDayOfWeek();
- boolean isWeekend = dow == DayOfWeek.SATURDAY || dow == DayOfWeek.SUNDAY;
boolean isPast = date.isBefore(today);
boolean isConfiguredDay = Config.WEEK_DAYS.contains(dow.name());
- String dayClass = isWeekend ? "weekend" : isPast ? "past-day" : "active-day";
+ List<Meeting> meetingsForDay = meetingsByDate.getOrDefault(date, Collections.emptyList());
+ int bookedSlots = meetingsForDay.size();
+ double availabilityRatio = (double) bookedSlots / totalSlots;
+ String color = isConfiguredDay ? String.format("rgb(255, %d, %d)", (int) (255 * (1 - availabilityRatio)), (int) (255 * (1 - availabilityRatio))) : "";
+ String dayClass = !isConfiguredDay ? "weekend" : isPast ? "past-day" : "active-day";
%>
- <div class="calendar-day <%= dayClass %>">
+ <div class="calendar-day <%= dayClass %>" style="<%= isConfiguredDay ? "background-color: " + color : "" %>;">
<%= date.getDayOfMonth() %>
<% if (!isPast && isConfiguredDay) { %>
<form action="${pageContext.request.contextPath}/user/meetings/new" method="get">
diff --git a/src/main/webapp/WEB-INF/jsp/user/newMeeting.jsp b/src/main/webapp/WEB-INF/jsp/user/newMeeting.jsp
index 1967c11..f1d7ae1 100644
--- a/src/main/webapp/WEB-INF/jsp/user/newMeeting.jsp
+++ b/src/main/webapp/WEB-INF/jsp/user/newMeeting.jsp
@@ -30,10 +30,14 @@
for (java.sql.Time timeSlot : Config.createTimeSlots()) {
LocalTime time = timeSlot.toLocalTime();
+ String formattedTime = String.format("%02d:%02d", time.getHour(), time.getMinute());
if (isTimeAvailable.apply(time)) {
- String formattedTime = String.format("%02d:%02d", time.getHour(), time.getMinute());
%>
<option value="<%= formattedTime %>"><%= formattedTime %></option>
+ <%
+ } else {
+ %>
+ <option value="<%= formattedTime %>" disabled style="color: gray;"> <%= formattedTime %> <spring:message code="meeting.new.occupied"/></option>
<%
}
}
--
GitLab