diff --git a/article/templatetags/__pycache__/timetonow.cpython-36.pyc b/article/templatetags/__pycache__/timetonow.cpython-36.pyc index 593a633..0a68eb1 100644 Binary files a/article/templatetags/__pycache__/timetonow.cpython-36.pyc and b/article/templatetags/__pycache__/timetonow.cpython-36.pyc differ diff --git a/article/templatetags/timetonow.py b/article/templatetags/timetonow.py index 6b4f233..3a9b93b 100644 --- a/article/templatetags/timetonow.py +++ b/article/templatetags/timetonow.py @@ -13,13 +13,14 @@ def timetonow(value): now = timezone.now() now = now.replace(tzinfo=None) delta = now-valuetime - s = delta.seconds + + s = delta.total_seconds() if s>86400: return '%d天前' % (int(s/86400)) elif s>3600: return '%d小时前' % (int(s/3600)) elif s>120: - return '%d分钟前' % (int(s/120)) + return '%d分钟前' % (int(s/60)) else: return '刚刚' diff --git a/db.sqlite3 b/db.sqlite3 index d6fba15..ed0d3e9 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/edconline/__pycache__/settings.cpython-36.pyc b/edconline/__pycache__/settings.cpython-36.pyc index 3001fda..abc3031 100644 Binary files a/edconline/__pycache__/settings.cpython-36.pyc and b/edconline/__pycache__/settings.cpython-36.pyc differ diff --git a/edconline/__pycache__/urls.cpython-36.pyc b/edconline/__pycache__/urls.cpython-36.pyc index 27e4383..a0736df 100644 Binary files a/edconline/__pycache__/urls.cpython-36.pyc and b/edconline/__pycache__/urls.cpython-36.pyc differ diff --git a/edconline/__pycache__/views.cpython-36.pyc b/edconline/__pycache__/views.cpython-36.pyc index 03e0260..6bed60e 100644 Binary files a/edconline/__pycache__/views.cpython-36.pyc and b/edconline/__pycache__/views.cpython-36.pyc differ diff --git a/edconline/urls.py b/edconline/urls.py index 0834bf3..203d844 100644 --- a/edconline/urls.py +++ b/edconline/urls.py @@ -35,4 +35,5 @@ path('tinymce/', include('tinymce.urls')), path('article/', include('article.urls')), path('userpage/', include('userpage.urls')), + path('utils/', include('utils.urls')), ] diff --git a/edconline/views.py b/edconline/views.py index 1144c72..b697538 100644 --- a/edconline/views.py +++ b/edconline/views.py @@ -8,6 +8,7 @@ import userpage from . import globalarg + # Create your views here. def homepage(request): @@ -33,3 +34,10 @@ def homepage(request): return render(request,'index.html',{'cover':cover,'arts':arts}) + + + + + + + diff --git a/static/fonts/Monaco.ttf b/static/fonts/Monaco.ttf new file mode 100644 index 0000000..57217b3 Binary files /dev/null and b/static/fonts/Monaco.ttf differ diff --git a/static/upload/church_JNMUCQr.jpg b/static/upload/church_JNMUCQr.jpg new file mode 100644 index 0000000..510f991 Binary files /dev/null and b/static/upload/church_JNMUCQr.jpg differ diff --git a/templates/index.html b/templates/index.html index f37882e..064de59 100644 --- a/templates/index.html +++ b/templates/index.html @@ -34,7 +34,7 @@

{% if title %}{{title}}{% endif %}

     {{i.readtime}}     -  {{i.tag}} +  {% if i.tag %}{{i.tag}}{% endif %} 阅读全文 diff --git a/userpage/__pycache__/forms.cpython-36.pyc b/userpage/__pycache__/forms.cpython-36.pyc new file mode 100644 index 0000000..4be9639 Binary files /dev/null and b/userpage/__pycache__/forms.cpython-36.pyc differ diff --git a/userpage/__pycache__/urls.cpython-36.pyc b/userpage/__pycache__/urls.cpython-36.pyc index 33b830c..69fdf70 100644 Binary files a/userpage/__pycache__/urls.cpython-36.pyc and b/userpage/__pycache__/urls.cpython-36.pyc differ diff --git a/userpage/__pycache__/views.cpython-36.pyc b/userpage/__pycache__/views.cpython-36.pyc index c9d7c82..1569d5d 100644 Binary files a/userpage/__pycache__/views.cpython-36.pyc and b/userpage/__pycache__/views.cpython-36.pyc differ diff --git a/userpage/forms.py b/userpage/forms.py new file mode 100644 index 0000000..3b89968 --- /dev/null +++ b/userpage/forms.py @@ -0,0 +1,80 @@ +from django.core.exceptions import ValidationError +from django import forms +from django.forms import fields +from django.forms import widgets +from django.core.validators import RegexValidator +from django.contrib.auth.models import User + + +class RegisterForm(forms.Form): + username = fields.CharField( + required=True, + widget=widgets.TextInput(attrs={'class': "form-control",'placeholder': '用户名为8-12个字符'}), + min_length=6, + max_length=12, + strip=True, + error_messages={'required': '标题不能为空', + 'min_length': '用户名最少为6个字符', + 'max_length': '用户名最不超过为20个字符'}, + ) + email = fields.EmailField( + required=True, + widget=widgets.TextInput(attrs={'class': "form-control",'placeholder': '请输入邮箱'}), + #strip=True, + error_messages={'required': '邮箱不能为空', + 'invalid':'请输入正确的邮箱格式'} + ) + pwd = fields.CharField( + widget=widgets.PasswordInput(attrs={'class': "form-control",'placeholder': '请输入密码,必须包含数字,字母,特殊字符'},render_value=True), + required=True, + min_length=6, + max_length=12, + strip=True, + validators=[ + # 下面的正则内容一目了然,我就不注释了 + RegexValidator(r'((?=.*\d))^.{6,12}$', '必须包含数字'), + RegexValidator(r'((?=.*[a-zA-Z]))^.{6,12}$', '必须包含字母'), + RegexValidator(r'((?=.*[^a-zA-Z0-9]))^.{6,12}$', '必须包含特殊字符'), + RegexValidator(r'^.(\S){6,10}$', '密码不能包含空白字符'), + ], #用于对密码的正则验证 + error_messages={'required': '密码不能为空!', + 'min_length': '密码最少为6个字符', + 'max_length': '密码最多不超过为12个字符!',}, + ) + pwd_again = fields.CharField( + #render_value会对于PasswordInput,错误是否清空密码输入框内容,默认为清除,我改为不清楚 + widget=widgets.PasswordInput(attrs={'class': "form-control",'placeholder': '请再次输入密码!'},render_value=True), + required=True, + strip=True, + error_messages={'required': '请再次输入密码!',} + + ) + + def clean_username(self): + # 对username的扩展验证,查找用户是否已经存在 + username = self.cleaned_data.get('username') + users = User.objects.filter(username=username).count() + if users: + raise ValidationError('用户已经存在!') + return username + + def clean_email(self): + # 对email的扩展验证,查找用户是否已经存在 + email = self.cleaned_data.get('email') + email_count = User.objects.filter(email=email).count() #从数据库中查找是否用户已经存在 + if email_count: + raise ValidationError('该邮箱已经注册!') + return email + + def _clean_new_password2(self): #查看两次密码是否一致 + password1 = self.cleaned_data.get('pwd') + password2 = self.cleaned_data.get('pwd_again') + if password1 and password2: + if password1 != password2: + # self.error_dict['pwd_again'] = '两次密码不匹配' + raise ValidationError('两次密码不匹配!') + + def clean(self): + #是基于form对象的验证,字段全部验证通过会调用clean函数进行验证 + self._clean_new_password2() #简单的调用而已 + diff --git a/userpage/templates/userpage/login.html b/userpage/templates/userpage/login.html new file mode 100644 index 0000000..c91c0ca --- /dev/null +++ b/userpage/templates/userpage/login.html @@ -0,0 +1,68 @@ +{% extends 'base.html' %} +{% load timetonow %} + +{% block header %} + 物志 › 登录 +{% endblock %} + +{% block banner %} +
+

+
+{% endblock %} + +{% block content %} +
+
{% csrf_token %} + 物志 › 登录 +
+

+ + +

+ +

+ + +

+ +

+ +

+

+ + +

+ +

+ + 一个月内自动登陆 + 忘记密码? +

+

+ +

+
+
+ + + {% if errors %} + {% for error in errors %} + + {% endfor %} + {% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/userpage/templates/userpage/profile.html b/userpage/templates/userpage/profile.html index 64ef98a..0ba5cb3 100644 --- a/userpage/templates/userpage/profile.html +++ b/userpage/templates/userpage/profile.html @@ -35,7 +35,6 @@ {% endfor %} {% endif %} - {% endblock %} {% block banner %} @@ -128,6 +127,7 @@ {% ifequal member.id user.id %}
{# 消息提醒 #} +

{% for i in notice %}

{{i.comment.user}}{{i.comment.article}} 的评论中提到了您 {{i.comment_time|timetonow}}

diff --git a/userpage/urls.py b/userpage/urls.py index 69c7369..e7b111b 100644 --- a/userpage/urls.py +++ b/userpage/urls.py @@ -4,5 +4,7 @@ urlpatterns = [ path('member//', views.profile, name='profile'), - #path('update/', views.update_profile, name='update_profile'), + path('login/', views.userlogin, name='login'), + path('logout/', views.logout, name='logout'), + path('register/', views.register, name='register'), ] diff --git a/userpage/views.py b/userpage/views.py index 2339607..2099955 100644 --- a/userpage/views.py +++ b/userpage/views.py @@ -8,6 +8,8 @@ from django.contrib import messages from article.models import * from notification.models import * +from . import forms +from django.contrib.auth import authenticate, login # Create your views here. @@ -57,6 +59,78 @@ def profile(request, username): +def userlogin(request): + + if request.method == 'POST': + # print(request.POST) + username = request.POST['username'] + password = request.POST['password'] + errors = [] + + post_check_code = request.POST.get('check_code') + session_check_code = request.session['check_code'] + user = authenticate(request, username=username, password=password) + if user is not None: + if post_check_code.lower() == session_check_code.lower() : + login(request, user) + if request.POST.get('auto_login'): + request.session.set_expiry(60 * 60 * 24 *30) + return redirect('/') + else: + errors.append('请输入正确的验证码!') + else: + errors.append('用户名不存在或密码错误!') + return render(request, 'userpage/login.html', {'errors':errors}) + else: + return render(request,'userpage/login.html') + +def logout(request): + try: + #删除is_login对应的value值 + del request.session['is_login'] + del request.session['user'] + except KeyError: + pass + #点击注销之后,直接重定向回登录页面 + return redirect('/') + +def register(request): + # username = models.CharField(max_length=16, verbose_name='用户名') + # password = models.CharField(max_length=16, verbose_name='密码') + # nickname = models.CharField(max_length=16,verbose_name='昵称') + # email = models.EmailField(max_length=16, verbose_name='邮箱') + # img = models.ImageField(verbose_name='头像',upload_to='static/img/user/',default='static/img/user/1.jpg') + # ctime = models.DateTimeField(auto_created=True,verbose_name='创建时间') + + if request.method == 'GET': + obj = forms.Register() + # return render(request,'register.html',{'form':obj}) + elif request.method == 'POST': + # print(request.POST) + obj = forms.Register(request.POST) + post_check_code = request.POST.get('check_code') + session_check_code = request.session['check_code'] + print(post_check_code,session_check_code) + if obj.is_valid(): + if post_check_code == session_check_code: + # values = obj.clean() + data = obj.cleaned_data + print(data) + # models.User.objects.create( + username= data.get('username') + password= data.get('pwd') + email= data.get('email') + nickname = data.get('username') + # ) + models.User.objects.create(username=username,nickname =nickname,password =password,email = email ) + request.session['is_login'] = 'true' + request.session['user'] = data.get('username') + return redirect('/') + else: + errors = obj.errors + print('hello') + + return render(request,'userpage/register.html',{'form':obj}) diff --git a/utils/Monaco.ttf b/utils/Monaco.ttf new file mode 100644 index 0000000..57217b3 Binary files /dev/null and b/utils/Monaco.ttf differ diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/__pycache__/__init__.cpython-36.pyc b/utils/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..f7719e0 Binary files /dev/null and b/utils/__pycache__/__init__.cpython-36.pyc differ diff --git a/utils/__pycache__/check_code.cpython-36.pyc b/utils/__pycache__/check_code.cpython-36.pyc new file mode 100644 index 0000000..d89c580 Binary files /dev/null and b/utils/__pycache__/check_code.cpython-36.pyc differ diff --git a/utils/__pycache__/urls.cpython-36.pyc b/utils/__pycache__/urls.cpython-36.pyc new file mode 100644 index 0000000..a6c33a8 Binary files /dev/null and b/utils/__pycache__/urls.cpython-36.pyc differ diff --git a/utils/__pycache__/views.cpython-36.pyc b/utils/__pycache__/views.cpython-36.pyc new file mode 100644 index 0000000..e877873 Binary files /dev/null and b/utils/__pycache__/views.cpython-36.pyc differ diff --git a/utils/admin.py b/utils/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/utils/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/utils/apps.py b/utils/apps.py new file mode 100644 index 0000000..7527884 --- /dev/null +++ b/utils/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UtilsConfig(AppConfig): + name = 'utils' diff --git a/utils/check_code.py b/utils/check_code.py new file mode 100644 index 0000000..1e52819 --- /dev/null +++ b/utils/check_code.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- + +import random +from PIL import Image, ImageDraw, ImageFont, ImageFilter + +_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z +_upper_cases = _letter_cases.upper() # 大写字母 +_numbers = ''.join(map(str, range(3, 10))) # 数字 +init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) + + +def create_validate_code(size=(120, 30), + chars=init_chars, + img_type="GIF", + mode="RGB", + bg_color=(255, 255, 255), + fg_color=(0, 0, 255), + font_size=18, + font_type="static/fonts/Monaco.ttf", + length=4, + draw_lines=True, + n_line=(1, 2), + draw_points=True, + point_chance=2): + """ + @todo: 生成验证码图片 + @param size: 图片的大小,格式(宽,高),默认为(120, 30) + @param chars: 允许的字符集合,格式字符串 + @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG + @param mode: 图片模式,默认为RGB + @param bg_color: 背景颜色,默认为白色 + @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF + @param font_size: 验证码字体大小 + @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf + @param length: 验证码字符个数 + @param draw_lines: 是否划干扰线 + @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 + @param draw_points: 是否画干扰点 + @param point_chance: 干扰点出现的概率,大小范围[0, 100] + @return: [0]: PIL Image实例 + @return: [1]: 验证码图片中的字符串 + """ + + width, height = size # 宽高 + # 创建图形 + img = Image.new(mode, size, bg_color) + draw = ImageDraw.Draw(img) # 创建画笔 + + def get_chars(): + """生成给定长度的字符串,返回列表格式""" + return random.sample(chars, length) + + def create_lines(): + """绘制干扰线""" + line_num = random.randint(*n_line) # 干扰线条数 + + for i in range(line_num): + # 起始点 + begin = (random.randint(0, size[0]), random.randint(0, size[1])) + # 结束点 + end = (random.randint(0, size[0]), random.randint(0, size[1])) + draw.line([begin, end], fill=(0, 0, 0)) + + def create_points(): + """绘制干扰点""" + chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] + + for w in range(width): + for h in range(height): + tmp = random.randint(0, 100) + if tmp > 100 - chance: + draw.point((w, h), fill=(0, 0, 0)) + + def create_strs(): + """绘制验证码字符""" + c_chars = get_chars() + strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 + + font = ImageFont.truetype(font_type, font_size) + font_width, font_height = font.getsize(strs) + + draw.text(((width - font_width) / 3, (height - font_height) / 3), + strs, font=font, fill=fg_color) + + return ''.join(c_chars) + + if draw_lines: + create_lines() + if draw_points: + create_points() + strs = create_strs() + + # 图形扭曲参数 + params = [1 - float(random.randint(1, 2)) / 100, + 0, + 0, + 0, + 1 - float(random.randint(1, 10)) / 100, + float(random.randint(1, 2)) / 500, + 0.001, + float(random.randint(1, 2)) / 500 + ] + img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 + + img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) + + return img, strs diff --git a/utils/migrations/__init__.py b/utils/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/models.py b/utils/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/utils/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/utils/tests.py b/utils/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/utils/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/utils/urls.py b/utils/urls.py new file mode 100644 index 0000000..a7390e6 --- /dev/null +++ b/utils/urls.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.urls import path +from . import views + +urlpatterns = [ + path('check_code/', views.create_code_img, name='create_code_img'), +] diff --git a/utils/views.py b/utils/views.py new file mode 100644 index 0000000..ce5ae41 --- /dev/null +++ b/utils/views.py @@ -0,0 +1,16 @@ +from django.shortcuts import render +from utils import check_code +from io import BytesIO +from django.http import HttpResponse,HttpResponseRedirect +# Create your views here. + +def create_code_img(request): + f = BytesIO() #直接在内存开辟一点空间存放临时生成的图片 + + img, code = check_code.create_validate_code() #调用check_code生成照片和验证码 + request.session['check_code'] = code #将验证码存在服务器的session中,用于校验 + img.save(f,'PNG') #生成的图片放置于开辟的内存中 + png = f.getvalue() + + return HttpResponse(f.getvalue(),content_type='image/png') #将内存的数据读取出来,并以HttpResponse返回 +