From edfcc247ed7cc552fced207532673cc4e75ace47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E8=8B=B1=E8=B1=AA?= <1029214205@qq.com> Date: Thu, 30 May 2024 14:50:27 +0800 Subject: [PATCH] first commit --- DjangoBlogs/.idea/DjangoBlogs.iml | 2 +- DjangoBlogs/.idea/misc.xml | 2 +- .../__pycache__/settings.cpython-312.pyc | Bin 2971 -> 3004 bytes DjangoBlogs/DjangoBlogs/settings.py | 1 + .../auth1/__pycache__/forms.cpython-312.pyc | Bin 0 -> 2736 bytes .../auth1/__pycache__/urls.cpython-312.pyc | Bin 515 -> 591 bytes .../auth1/__pycache__/views.cpython-312.pyc | Bin 1581 -> 4046 bytes DjangoBlogs/auth1/forms.py | 38 ++++++++++++ DjangoBlogs/auth1/urls.py | 3 +- DjangoBlogs/auth1/views.py | 55 ++++++++++++++++-- .../blog/__pycache__/models.cpython-312.pyc | Bin 179 -> 1985 bytes .../blog/__pycache__/views.cpython-312.pyc | Bin 657 -> 819 bytes DjangoBlogs/blog/migrations/0001_initial.py | 45 ++++++++++++++ .../__pycache__/0001_initial.cpython-312.pyc | Bin 0 -> 2824 bytes DjangoBlogs/blog/models.py | 25 +++++++- DjangoBlogs/blog/views.py | 4 +- DjangoBlogs/my.cnf | 2 +- DjangoBlogs/templates/base.html | 26 +++++++-- DjangoBlogs/templates/login.html | 1 + DjangoBlogs/templates/register.html | 3 +- 20 files changed, 190 insertions(+), 17 deletions(-) create mode 100644 DjangoBlogs/auth1/__pycache__/forms.cpython-312.pyc create mode 100644 DjangoBlogs/auth1/forms.py create mode 100644 DjangoBlogs/blog/migrations/0001_initial.py create mode 100644 DjangoBlogs/blog/migrations/__pycache__/0001_initial.cpython-312.pyc diff --git a/DjangoBlogs/.idea/DjangoBlogs.iml b/DjangoBlogs/.idea/DjangoBlogs.iml index 700baa8..c47af1e 100644 --- a/DjangoBlogs/.idea/DjangoBlogs.iml +++ b/DjangoBlogs/.idea/DjangoBlogs.iml @@ -16,7 +16,7 @@ - + diff --git a/DjangoBlogs/.idea/misc.xml b/DjangoBlogs/.idea/misc.xml index f0e0ef6..f30b12e 100644 --- a/DjangoBlogs/.idea/misc.xml +++ b/DjangoBlogs/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/DjangoBlogs/DjangoBlogs/__pycache__/settings.cpython-312.pyc b/DjangoBlogs/DjangoBlogs/__pycache__/settings.cpython-312.pyc index d7d4dfad70f99f9a02e53f3b52a03841dfa9e463..6ea2a4e9c21bd18359af7d6accfe540dd7b9607e 100644 GIT binary patch delta 108 zcmbO&zDJz*G%qg~0}w<%4@+~M$ScXHv{7B2kt0PpO1@G>Q+2Z!qZqqD6}Nt3X-S5D zPJVi3o}Z?|=46f;j9j-kef-@${o+G|Cja7eLlt aT&6&kj6huMwKE2E!8H@@)6bKYx9w=hv|(_hcK z_uP9<&-tBm-!IkGCIaKPUk?mCrzhkOd?*cFQCN5ngd`D&z=TMc@h}X^nvf>UdRUNI zK^xMBbsilTcqC{`}cLZ;r*Eg_ZI|;iDh(-+z(6nCdv*v1#tc`-S9ppyOs`j*3U(0V)czDa;@B zhQx?JHVAScQk2~+#Td|P0NEl^8ih=VlEnK(>4Brtdg0{N`Kw>Uu@9Omr{|_e=ccdR z{qnQRWQLhHiCfzz>%=f02)P;Rgl%yIHCFlS-^~AT?%t=9cfU>LPfuXpIN9tAiF`y9 zyaFHNWfqD=*1iQ*A{(NIf}$@b$@+mn2>2rF#iKwRiDEot>j4Zk9}7eyJ(x;hfL0Pi z1By-B04fsdH8wUr+_0mof8WqP8pY%KcLe!}Ke{~>^-KMHJT}8j3V3g!+HCbCz+SZh{wWMt=m;4!9TS9l+UYm4XaAobyX?t^O^NhVY zV{gyex2Ek|Gxn~8{#KR!wxd4l*pPN?n0bDC#?d`v?#@|j66OUsbtywIw+cfVfHz6< zbPV|)yUM&6(L4pOG{7vj)Fb|tS?n+?J&9S`3TClEJH}?|DDjj5Mg#M7eX_x)HdBQK zZjDNY7T6U>Mij10+`Be5e`zfL!Gy}lx#_d{i5rDe*k{~Ex*C+|8U(B)>Oxox08KnV zrI^AzitR|&L$elRqFYNFQ3*{b#^mav2Wpd-Fn_?oMv9#T7Ln`TqRO!~kD*v`4Ly;L z0szSzby>&yv}3)>n3OT&=tvkIPrW&3bJp3KcD81nooQ$1bj#26GtSP8^Npw z<9s_&eaq^qpkHssv3th6JIB>#Iaiu<&2kMnYhA)Z*Mspsw^`=A-UuHSyO(OpZkAC zaJqWc6*TUHZ+Vp8HBQ6Sf4z|5dMy*#v+`A}6ZBJtQt1@0VO!a9SrE7>W2r1W#vzlt zN;d9^`U4TXO{o?1DDI+IEp!zE_J3N3fH&+;=*rmf``jk_0#0s3Xhpy_LtjF`j!j=g z*aRS(cSmBPA1-V)w^R&^;X`nLwxb5pL`_;JN(eY5y$%3LGrLyEM`bpZ=;y3WiFnr9 zl(sgdHqTny$9mPX@kH=+OUyRtTkD$yj)3Y0Y8H?S|v0J zr4$#Jy8V!QRVDRnv6_ literal 0 HcmV?d00001 diff --git a/DjangoBlogs/auth1/__pycache__/urls.cpython-312.pyc b/DjangoBlogs/auth1/__pycache__/urls.cpython-312.pyc index 61489362d18a8faa6f137367decc8a676baf436e..bc018646c50221cd1ba090f09f1314136a819624 100644 GIT binary patch delta 207 zcmZo>InSbgnwOW00SLC<2}?6)WMFs<;=lk0l=1nV<`5X{Dx7MLwch00`n^@+LINTl)2ei Mc$pfxi-dsE03jq87XSbN diff --git a/DjangoBlogs/auth1/__pycache__/views.cpython-312.pyc b/DjangoBlogs/auth1/__pycache__/views.cpython-312.pyc index feaea0ad500ddc5eee217983aaa9d9d8d22f68f3..8bdc82b2ccc8c5154ffaff9ca66ad4ba39f0b722 100644 GIT binary patch literal 4046 zcmd5F0C{El~fz8l>*U#6pRL`&Y7;0>ZA1xVv$&TrLwJPJv?~?m*5^?uP|k$ z;5m=X`H%d<$@CK01u=Ogo>dcr@mFO*Ov7^!i$ghZ6dP34Y)lf> zL0M3ER(Br}2a<{^;sY|4^qOvYAekW|Y{5GqsYwKazJNn7<9bKYsp8)F|DM!c&BuL6@Z zPMVRCK~;k66iNE)j%-{}-jK0CDn}BflOpCB90Y4jvg$FjH5Z0)=?B;DUHkRj(f1c; ze{^s9+|qj&@6XQu^|$99yW%-@u=NG%n6K4svZtG8`p}cf!e-bOI?oAAeiBmCSmzV@yw=1HeB;}0m zRG{R@Y3qWTq8EEh2KU%3q)xL3ixlx8FSX z=2YFE>}%(JbrSmUqu;{w6Tg>bK4LTjxXTF5723#q}M<&<@SNR8Ma$5O#GCv- zia$1|1nJA5<{}aSHB9gwH;}H8bOgpQ!FS!2z+zk#DHovW zOz{XmL*{HE#xhoTD@|~`ku0KUYi>ay=}G~qocMstWyXQG!33!<w>Rgs_Rns#qOey|L?+$+dtkt)?IQp72HiT?Vs-&e zbGI$HS4}icwp?hLbFZayTGRGoV|y{sp}9N$wLl35Q}j6S9||;Oq6b)4S`|kKqfxw7 z{v)u0Mc_U%S!N&b*_7Q_R~=Q81+Bz)RPqX=EoHI|Lj%mmX1w!_AVd z66?q_f<4bBnX3-N8>D)*WNHLwp8XJg3;-~|2(GK{D_q$rSdmPjP4Ecb<)`I+Jv6V9 z6L6(D0<8CEKfC|?cNc&7N%>t4T$W1!Pu1-byiDT*qK=k`XWgDCGhx7El9;+f5c}h~ zw5sGbz~kNDyubL<$;Hn;S@`^e#mk>A{_5xVuYIxj@rO&leCPh`IiLnPa_oiQP2c{0& zNDvmsFD#9myE{6u_~T2wo6?zb*^%7Qxs*(pHjZZmS<-DvT*{_J7*${c1G-H}4kT3t zlTeo{)St@&{}5v`j+JW_lhl9>H4zh9j~yT>B;}wK%Y|4=KN{%1VwF3>d%@r(VmS#N zl%zW_erW8_c<)&6R9Dg6G_wComw(>ZFx{uMA1E|-6@3Ra$H6-u-}v#d7S zjMYRAE`%Jf3Q&5mVn9B5BDnIogvkYCaz?xxI@3!+#$tp}NP2ZZ{Kz*?`HuC*ilZ`N z5=5|#tAKid!)g|j?+T!Iz{nI#(Uinj4^-A9WZ-YflMw>Jn0#(f%15bzzGelRNacqq z{1QS&2?E7R8l7JtdVU(h$Bx}#olA=^;X_~vm4TGg&^@p)409WG6i~-4^xU732mWrM z=38j}E!24%J$oB<7EtFc)bf?Ru4oU9a1Wh`t)IwE^-st}ruiXT$LxG$LF}dnMEct{ YmRbGKvE0JL?5J1}X|0oanW6jo2mQ`-c>n+a delta 764 zcmbVJziSjh6rQ>LdHW-|904IsRz#D{nT=p!6BPutOQT3wgmc}U++CQxJ>Sd~if|Sd zmf{w%v<+hApI|9UAl>R!R(9fbR=!#86kCUxZ@zi&&Ae~k`@H_nn|$soHuEko-HLk}x+B9y&2JSjHb1SycXJjU~^Jd&c zZapWlli7%F)#tKh0@4u4dIB$Kfb3Z=RP#PbS6d}1U{adxWqETGxF74dAad=_tf1y z(KlykBL6GG2i6-1$>6UJP(`j41hDNPuk1w-wD71*|LC}W>UK`_4lKR@;2cn^GFYAr zco}(DX}jd!1G-v%@~(K5cKhbtO6xHRM%nVW?%V2Zt9W?GQYos|c$6iQbqnaGz>tB7 z{!jxKH)tz3gCJC?f*^oeSzBNqx^=#8nv7)HPvATTESInRHy1T!8vQ-JH5Ib?RdR zDlh80MaVCE7bC}l!gi}&P<2ip!G|? E0Rqsef&c&j diff --git a/DjangoBlogs/auth1/forms.py b/DjangoBlogs/auth1/forms.py new file mode 100644 index 0000000..c603672 --- /dev/null +++ b/DjangoBlogs/auth1/forms.py @@ -0,0 +1,38 @@ +from django import forms +from django.contrib.auth import get_user_model +from .models import CaptchaModel + +User = get_user_model() + + +class RegisterForm(forms.Form): + username = forms.CharField(max_length=20, min_length=2, error_messages={ + 'required': '请传入用户名!', + "max_length":'用户名长度在2~20之间!', + "min_length": '用户名长度在2~20之间!' + }) + email = forms.EmailField(error_messages={"required": '请传入邮箱!', 'invalid': '请传入一个正确的邮箱!'}) + captcha = forms.CharField(max_length=4, min_length=4) + password = forms.CharField(max_length=20, min_length=6) + + def clean_email(self): + email = self.cleaned_data.get('email') + exists = User.objects.filter(email=email).exists() + if exists: + raise forms.ValidationError('邮箱已经被注册!') + return email + + def clean_captcha(self): + captcha = self.cleaned_data.get('captcha') + email = self.cleaned_data.get('email') + + captcha_model = CaptchaModel.objects.filter(email=email, captcha=captcha).first() + if not captcha_model: + raise forms.ValidationError("验证码和邮箱不匹配!") + captcha_model.delete() + return captcha + +class LoginForm(forms.Form): + email = forms.EmailField(error_messages={"required": '请传入邮箱!', 'invalid': '请传入一个正确的邮箱!'}) + password = forms.CharField(max_length=20, min_length=6) + remember = forms.IntegerField(required=False) \ No newline at end of file diff --git a/DjangoBlogs/auth1/urls.py b/DjangoBlogs/auth1/urls.py index 06cc34f..677919b 100644 --- a/DjangoBlogs/auth1/urls.py +++ b/DjangoBlogs/auth1/urls.py @@ -4,7 +4,8 @@ from .import views app_name = 'auth1' urlpatterns=[ - path('login',views.login, name='login'), + path('login',views.login1, name='login'), + path('logout',views.logout1, name='logout'), path('register',views.register, name='register'), path('captcha', views.send_email_captcha, name='email_captcha') ] \ No newline at end of file diff --git a/DjangoBlogs/auth1/views.py b/DjangoBlogs/auth1/views.py index c1b0e1d..752562f 100644 --- a/DjangoBlogs/auth1/views.py +++ b/DjangoBlogs/auth1/views.py @@ -1,16 +1,63 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect, reverse from django.http.response import JsonResponse import string import random from django.core.mail import send_mail from .models import CaptchaModel +from django.views.decorators.http import require_http_methods +from .forms import RegisterForm, LoginForm +from django.contrib.auth import get_user_model, login, logout +from django.contrib.auth.models import User + +User = get_user_model() + + # Create your views here. -def login(request): - return render(request, 'login.html') +@require_http_methods(['GET', 'POST']) +def login1(request): + if request.method == 'GET': + return render(request, 'login.html') + else: + form = LoginForm(request.POST) + if form.is_valid(): + email = form.cleaned_data.get('email') + password = form.cleaned_data.get('password') + remember = form.cleaned_data.get('remember') + user = User.objects.filter(email=email).first() + if user and user.check_password(password): + # 登录 + login(request, user) + # 判断是否需要记住我 + if not remember: + # 如果没有点击记住我,那么就要设置过期时间为0,即浏览器关闭后就会过期 + request.session.set_expiry(0) + # 如果点击了,那么就什么都不做,使用默认的2周的过期时间 + return redirect('/') + else: + print('邮箱或密码错误!') + return redirect(reverse('auth1:login')) + + +def logout1(request): + logout(request) + return redirect('/') +@require_http_methods(['GET', 'POST']) def register(request): - return render(request, 'register.html') + if request.method == 'GET': + return render(request, 'register.html') + else: + form = RegisterForm(request.POST) + if form.is_valid(): + email = form.cleaned_data.get('email') + username = form.cleaned_data.get('username') + password = form.cleaned_data.get('password') + User.objects.create_user(email=email, username=username, password=password) + return redirect(reverse('auth1:login')) + else: + print(form.errors) + return redirect(reverse('auth1:register')) def send_email_captcha(request): diff --git a/DjangoBlogs/blog/__pycache__/models.cpython-312.pyc b/DjangoBlogs/blog/__pycache__/models.cpython-312.pyc index e92a6ce197f08170e34fb7fc821bba9c4864385a..31b03fb6eef4fd7fbff8ecadc8c84dc29d403e99 100644 GIT binary patch literal 1985 zcmb7_O=ufO6vua^)#t9HG@qpwsvFZZD#5+9h89yur8o|yA`nFfVZ2D%i7X>&lT0ba0PUfaOgSLoF5vRk+peU_w*sqkzfpVo0%g{dUyl$nds%Oz@uXf9|Jj)GhlI`U9utSt{U<)P)mYM>=g zUJKK=wgY$NYX^#dq5J>mi{}-Sv!`iN!?q%t&Nv)6WdFdP5O~$>nxo;|$-axn9ey>A z$T0rxSN)o+;obqzGjOzbz-alU(Y}%r^e_oiOdas0BBpbF`P<&zC5qf^;d}Syjqbfi zb&=vlh;DHx?ksj!zge&T+^hZsP$-F_p{sfQYc-*1Jd96dq67UdJ+A`A#2~zr&-&Ld z-~U~{5eXikGE7TxR_Z}PK&Tgl*^G>lCCn8iR}2#tM@r8dByTWJ7(k6?a>VpIFBg-l z0k6zGp;MAc=f5N-B+L;yeIax@e1d{{Vj{;9)(=DG0U7kQC*7gLNp1_h(o4z(gr=K26QFWLggIM+$y15_B<_#tY`CmNcvEq5eBw8k$LtBJdPX zitm8gzQVs2vy}2J^5yT|(hKLfS;s%8AHhJZZH03n|5!y)d?xsx38Q~Fj{NR8R+cs; tQ9LIuj8(@M$2SGEH=Q;yCRWDojNbwe+M5R@(1jD#Q;Vmz1hhHp{{i>Xu{r<% delta 141 zcmX@eznL-aG%qg~0}$BQ`=r?b>Bk@r3@||%p9O%7=?p0hDU3M`xr|Yaj0}}bn#?aj z(wdC7*mCnzQge#^G?{L3rer1NrRVFVBo#3OB_|(ZG*JD;VFOf9nv-f*!~qmz1ma>4 R;{!7zBjY^=nIbkI2LN0!92x)s diff --git a/DjangoBlogs/blog/__pycache__/views.cpython-312.pyc b/DjangoBlogs/blog/__pycache__/views.cpython-312.pyc index cdcde863f9873a57ccc6efcfc7a28e7977df668b..4e63bfcac0e36070db712b13ff1d2adef627ed15 100644 GIT binary patch delta 497 zcmX|6Jxe4(5bc_;9i3g>z2HCt5e#Jf7&B22Lqm^C3?^A-w_6>V+0p75VNEpf2RMjm zBpAwt_cJb8*lf7X4h}K#BQvtPQ46|w_3G8D>eBx0H2yRizsuSw<*^QChT9zhk`rj>=TD>zvjCSioS=+LDAy^ zeW>avjs;55PNI`O)e}j!7x&sIoTDN}!7uM%t-Shado*a5kLlJJfvVOaSv%&^plWs& z)#a6^NiOmzPH>VKm!51=I%9vm*2Vl_dTHn_53FUZU*5uxQ{4yZe;BxjMW?MyutlWW zD3#3_`=_RFR)*ffz*@jMaohe_)#L|JZ!ZhveujCs$R%3T`JxLAL?$_ z%Q5Z*QPI!$11`E5MtO!39a5oMs&CgP+%vsQfA7uRNHbm}ViUiUNS!TLC_R|~Fk*8f g=8xF=iv{4nH~_)3!;Iz-TF(Z+;;YGwZyIUCcjESYkN^Mx delta 335 zcmdnYHj$P0G%qg~0}!0O>60ckkyp}O3CNkwkiw9{n8T3E7{$oQkiyi$5XF?j3?!LT zSb!u;C95XeOOURWjJMc|Qu9($izdHkw3zrfkd+fC$}-uKG1rY9$hyUzl#`zxpP5q3 z1mr0w6mftAxxs=dsU?Y-IYqoc5s=bi0U*)9aEDuPg8Bt1oy*+17g%(QxF)}0^si?F z$uj~8r~w=WrAhH1LyGu;f*|)7gBVcVGxRTWYh7T`DgsIPX|moDNXbgfOV8IU&d4t+ zNiHoZE@A=76oH%ywjONCN(QicunT{2*yQG?l;)(`6$t?qse-I776%d^m>C%v?=mQV RVq;{~1QB|lfrK_#CjcusOt=65 diff --git a/DjangoBlogs/blog/migrations/0001_initial.py b/DjangoBlogs/blog/migrations/0001_initial.py new file mode 100644 index 0000000..a3e0d0b --- /dev/null +++ b/DjangoBlogs/blog/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 5.0.6 on 2024-05-29 08:39 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BlogCategory', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200, verbose_name='分类名称')), + ], + ), + migrations.CreateModel( + name='Blog', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200, verbose_name='标题')), + ('content', models.TextField(verbose_name='内容')), + ('pub_time', models.DateTimeField(auto_now_add=True, verbose_name='发布时间')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.blogcategory', verbose_name='分类')), + ], + ), + migrations.CreateModel( + name='BlogComment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField(verbose_name='内容')), + ('pub_time', models.DateTimeField(auto_now_add=True, verbose_name='发布时间')), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), + ('blog', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='blog.blog', verbose_name='所属博客')), + ], + ), + ] diff --git a/DjangoBlogs/blog/migrations/__pycache__/0001_initial.cpython-312.pyc b/DjangoBlogs/blog/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c24827000cea75ce2402715ed89b31a7f3464471 GIT binary patch literal 2824 zcmds3O>7%Q6rNq%>$T&gc1YvevC|)gHnxJ*03i)RsA>M_PtuU4p|uhg+cR<3*j}?< zr*#hyL|e+CBAgl_gitSlVpWNRXsQaqu@aYE5ox255S73!xmilN^u)}r-K1)u^wI-+ zn4S0b{eADfnYTaJ*E<1uZmmsB&+q{JPKnxMt8UiE$mS}505WM1WyqUNvl&a&!cd+i zZOz!CwhR~L7{G!d0Ie4Qv>{unp=M~~y5T)~p4FdqUtHk=Gl}3e(h@vcHq81h)Ys$d0SpfjVZ5az=N+p>nZ)i3? zszoWtLTc5SYTn`-0~u(Awi35MBB9ST3+;yV4wK%zLLNFw&IQ)wO|wb1R9B<^(Yxy@ zz1RPX_lC{h;Wk6kzd1s458WltLNzl4Pop6h*g}qh&y|{L2#Dl%L$dX8N#suLd7PVS z+Je`%1#j=;yshgAn@iqWf01~0JiBu$8WBw@GUkTx2OCWNTY00IV00cG{nl=$P07RB1*$Nw$f0rO%ateDsbe>-LF1gyEc#Q2`Q_ftYYxW zmoApC-wZK^1)%CBFf{sKBLaVbS}wsK$5-?lf9$^25D`uqwh>iu5QQCImO)8_mj}aN%l?B zIVj0UOlFUg7U5tjmQ6~Sg>lRq$t9By+xvP?^!4@+V5gKr)oJ)GSSS9#g9H7qPL38w zWhsRc%4ENxPLrFTq*E~9BZ(a55yyMxfdc$)wx;LzO!^_s& zZE93dv+{~Juez00k5~1D=9_h2Nb`jjPyF=$if=-7zg4kWni}rgIES;+@Q6L{lkDwK z{o(n&x<9P>!;6XA{EGig)jhWA@vDK3`9VF@a&DfWQb#2+wiWFdd<$BU&)B-29{OrcduVto0n$d&ab$F})|M^+Z)cCgO+K zBUHEbsG)m|t<>8ocqakh{0R8gCxL(FAW8MYw0dF97hasWaeC>r-Zh|g4d`7XTGxo) zHKBD)s7O+k4+zfV>=ec6f7`$*u$4NF;&c$4`+|+!heza>YE2j7vCnMw2#iO}Kf6e+ zO2c^n-NHH?#M967U(U!-;o<1drl1O7Gh