From f2f4a97c37c2a3107c657f0c852ae319433092e0 Mon Sep 17 00:00:00 2001 From: yangjian Date: Sat, 22 Feb 2020 14:22:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B3=A8=E5=86=8C=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E7=A0=81=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=83=A8=E5=88=86=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CHANGES.md | 5 + MrDoc/settings.py | 2 +- README.md | 60 +++---- app_admin/migrations/0006_registercode.py | 33 ++++ .../migrations/0007_auto_20200222_1106.py | 18 ++ app_admin/models.py | 22 +++ app_admin/urls.py | 1 + app_admin/views.py | 110 ++++++++++-- app_doc/views.py | 5 + template/app_admin/admin_base.html | 5 +- template/app_admin/admin_register_code.html | 157 ++++++++++++++++++ template/app_admin/admin_setting.html | 10 +- template/app_doc/manage_base.html | 1 + template/app_doc/manage_doc.html | 14 ++ template/app_doc/modify_doc.html | 9 +- template/register.html | 7 + 17 files changed, 416 insertions(+), 44 deletions(-) create mode 100644 app_admin/migrations/0006_registercode.py create mode 100644 app_admin/migrations/0007_auto_20200222_1106.py create mode 100644 template/app_admin/admin_register_code.html diff --git a/.gitignore b/.gitignore index 2abff53..23454ed 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ coverage.xml *.log local_settings.py db.sqlite3 +media # Flask stuff: instance/ diff --git a/CHANGES.md b/CHANGES.md index 92ef9a5..6c84bea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ ## 版本更新记录 +### v0.2.10 2020-02-22 + +- 优化修改文档页面的文档状态提示; +- 新增注册邀请码功能,可在后台配置管理; + ### v0.2.9 2020-02-17 - 优化文本编辑器排版 diff --git a/MrDoc/settings.py b/MrDoc/settings.py index 0b85f62..d233b37 100644 --- a/MrDoc/settings.py +++ b/MrDoc/settings.py @@ -25,7 +25,7 @@ SECRET_KEY = '5&71mt9@^58zdg*_!t(x6g14q*@84d%ptr%%s6e0l50zs0we3d' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False -VERSIONS = '0.2.9' +VERSIONS = '0.2.10' ALLOWED_HOSTS = ['*'] diff --git a/README.md b/README.md index eb22e09..bfee7ee 100644 --- a/README.md +++ b/README.md @@ -4,44 +4,46 @@ ![Mrdoc首页](./docs/mrdoc_2019080101.gif) -**手机端文档阅读界面:** - -![移动端](./docs/mrdoc-2019-12-15_204807.jpg) - ## 介绍 -基于Python的一个简单文档写作系统。 -当前版本为:**v0.2.9**,版本发布时间为**2020-02-17**,更新记录详见:[CHANGES.md](./CHANGES.md) +州的先生(zmister.com)自用并完全开源、基于Python编写的文档写作系统。 +当前版本为:**v0.2.10**,版本发布时间为**2020-02-22**,更新记录详见:[CHANGES.md](./CHANGES.md) MrDoc拥有以下特点: -- 基于Django自带的用户模型,实现简单高效的用户管理,支持用户注册、用户登录、管理员等控制等功能; -- 基于Editormd的Markdown编辑器,支持Markdown语法的文档写作,支持图片粘贴上传; -- 提供文档模板功能,支持文档模板的创建、修改; -- 仿GitBook文档阅读页面,支持文档阅读页面的字体缩放,字体类型修改; -- 支持三级目录层级显示; -- 支持文集导出为markdown文本格式.md文件; -- 支持基于文集的权限控制,提供公开、私密、指定用户可见、访问码可见4种权限模式; -- 使用方便、二次开发修改也方便; +- 站点与用户系统简洁 + - 基于Django自带的用户模型,实现简单高效的用户管理,支持用户注册、用户登录、管理员等控制等功能; + - 支持全站关闭注册; + - 支持注册邀请码配置; + - 支持广告位自定义配置; + - 支持统计代码自定义配置; + +- 文档系统清晰 + - 基于文集的文档撰写和阅读; + - 基于Editormd的Markdown编辑器,支持Markdown语法的文档写作,支持图片粘贴上传,支持从本地MD文件中插入内容; + - 提供文档模板功能,支持文档模板的创建、修改; + - 仿GitBook文档阅读页面,支持文档阅读页面的字体缩放,字体类型修改,页面社交分享,良好的移动端阅读体验; + - 支持三级目录层级显示; + - 支持文集导出为markdown文本格式.md文件; + - 基于文集进行权限控制,提供公开、私密、指定用户可见、访问码可见4种权限模式; + +- 二次开发方便 + - 使用Django传统的MTV开发模式,路由、视图函数、模型易于理解; + - 使用非前端工程化构建前端页面,主要使用Layui进行页面布局和展示,方便改动; -在开发过程中,参考和借鉴了GitBook、ShowDoc、Wordbook等应用的功能和样式。 -## 软件架构 +在开发过程中,参考和借鉴了GitBook、ShowDoc、Wordbook等应用和网站的功能与样式。 -后端基于Python Web框架Django +## 网站架构 -编程语言:Python 3 - -后端框架:Django 2.1 - -前端UI库:LayUI 2.5.4 - -MarkDown编辑器:Editormd - -页面社交分享:Share.js - -Markdown科学公式:Katex.js +- 编程语言:Python 3 +- 后端Web框架:Django 2.1 +- 前端UI库:Layui 2.5.4 +- JS库:Jquery +- MarkDown编辑器:Editormd +- 页面社交分享:Share.js +- Markdown科学公式:Katex.js ## 安装教程 @@ -51,7 +53,7 @@ pip install -r requirements.txt ``` ### 2、配置数据库信息 -默认情况下,MrDoc使用Django的SQLite数据库,如果你使用的是MrDoc源码附带的Sqlite数据库,则无需另外配置数据库。 +默认情况下,MrDoc使用Django的SQLite数据库,在旧版本MrDoc附带了一个Sqlit数据库,如果你使用的是MrDoc源码附带的Sqlite数据库,或使用Sqlite数据库,则无需另外配置数据库。 如果有配置其他数据库的需求,请在/MrDoc/MrDoc目录下打开settings.py文件,在约80行的位置,将如下代码: ``` DATABASES = { diff --git a/app_admin/migrations/0006_registercode.py b/app_admin/migrations/0006_registercode.py new file mode 100644 index 0000000..545e99c --- /dev/null +++ b/app_admin/migrations/0006_registercode.py @@ -0,0 +1,33 @@ +# Generated by Django 2.1 on 2020-02-21 20:06 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('app_admin', '0005_auto_20191125_2155'), + ] + + operations = [ + migrations.CreateModel( + name='RegisterCode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(max_length=10, unique=True, verbose_name='注册邀请码')), + ('all_cnt', models.IntegerField(default=1, verbose_name='有效注册数量')), + ('used_cnt', models.IntegerField(default=0, verbose_name='已使用数量')), + ('status', models.IntegerField(default=1, verbose_name='注册码状态')), + ('user_list', models.CharField(blank=True, max_length=500, null=True, verbose_name='使用此注册码的用户')), + ('create_time', models.DateTimeField(auto_now=True, verbose_name='创建时间')), + ('create_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': '注册邀请码', + 'verbose_name_plural': '注册邀请码', + }, + ), + ] diff --git a/app_admin/migrations/0007_auto_20200222_1106.py b/app_admin/migrations/0007_auto_20200222_1106.py new file mode 100644 index 0000000..e1ece42 --- /dev/null +++ b/app_admin/migrations/0007_auto_20200222_1106.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1 on 2020-02-22 11:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app_admin', '0006_registercode'), + ] + + operations = [ + migrations.AlterField( + model_name='registercode', + name='user_list', + field=models.CharField(blank=True, default='', max_length=500, null=True, verbose_name='使用此注册码的用户'), + ), + ] diff --git a/app_admin/models.py b/app_admin/models.py index 18c3f09..f4b8861 100644 --- a/app_admin/models.py +++ b/app_admin/models.py @@ -1,4 +1,6 @@ from django.db import models +from django.contrib.auth.models import User + # 系统设置项模型 class SysSetting(models.Model): @@ -27,4 +29,24 @@ class EmaiVerificationCode(models.Model): class Meta: verbose_name = '电子邮件验证码' + verbose_name_plural = verbose_name + +# 用户注册邀请码模型 +class RegisterCode(models.Model): + code = models.CharField(verbose_name="注册邀请码",max_length=10,unique=True) + # 注册码的有效注册数量,表示注册码最多能够被使用多少次,默认为1 + all_cnt = models.IntegerField(verbose_name="有效注册数量",default=1) + # 注册码的已使用数量,其值小于等于有效注册数量,默认为0 + used_cnt = models.IntegerField(verbose_name='已使用数量',default=0) + # 注册码状态:0表示数据已满,1表示有效,默认为1 + status = models.IntegerField(verbose_name="注册码状态",default=1) + user_list = models.CharField(verbose_name="使用此注册码的用户",default='',max_length=500,blank=True,null=True) + create_user = models.ForeignKey(User,on_delete=models.CASCADE) + create_time = models.DateTimeField(auto_now=True,verbose_name='创建时间') + + def __str__(self): + return self.code + + class Meta: + verbose_name = '注册邀请码' verbose_name_plural = verbose_name \ No newline at end of file diff --git a/app_admin/urls.py b/app_admin/urls.py index ed9f1b9..6d16f39 100644 --- a/app_admin/urls.py +++ b/app_admin/urls.py @@ -18,5 +18,6 @@ urlpatterns = [ path('check_code/',views.check_code,name='check_code'), # 注册验证码 path('forget_pwd/',views.forget_pwd,name='forget_pwd'), # 忘记密码 path('send_email_vcode/',views.send_email_vcode,name='send_email_vcode'), # 忘记密码发送邮件验证码 + path('admin_register_code/',views.admin_register_code,name='register_code_manage'), # 注册邀请码管理 ] \ No newline at end of file diff --git a/app_admin/views.py b/app_admin/views.py index 8d1ea76..54af43a 100644 --- a/app_admin/views.py +++ b/app_admin/views.py @@ -6,7 +6,8 @@ from django.contrib.auth.models import User # Django默认用户模型 from django.contrib.auth.decorators import login_required # 登录需求装饰器 from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage,InvalidPage # 后端分页 from app_admin.decorators import superuser_only,open_register -import json,datetime,hashlib +from django.core.exceptions import ObjectDoesNotExist +import json,datetime,hashlib,random from app_doc.models import * from app_admin.models import * from app_admin.utils import * @@ -68,20 +69,29 @@ def register(request): email = request.POST.get('email',None) password = request.POST.get('password',None) checkcode = request.POST.get("check_code",None) + register_code = request.POST.get("register_code",None) + is_register_code = SysSetting.objects.filter(types='basic', name='enable_register_code', value='on') + if is_register_code.count() > 0: # 开启了注册码设置 + try: + register_code_value = RegisterCode.objects.get(code=register_code,status=1) + except ObjectDoesNotExist: + errormsg = '注册码无效!' + return render(request, 'register.html', locals()) + # 判断是否输入了用户名、邮箱和密码 if username and email and password: if '@'in email: email_exit = User.objects.filter(email=email) username_exit = User.objects.filter(username=username) - if email_exit.count() > 0: + if email_exit.count() > 0: # 验证电子邮箱 errormsg = '此电子邮箱已被注册!' return render(request, 'register.html', locals()) - elif username_exit.count() > 0: + elif username_exit.count() > 0: # 验证用户名 errormsg = '用户名已被使用!' return render(request, 'register.html', locals()) - elif len(password) < 6: + elif len(password) < 6: # 验证密码长度 errormsg = '密码必须大于等于6位!' return render(request, 'register.html', locals()) - elif checkcode != request.session['CheckCode'].lower(): + elif checkcode != request.session['CheckCode'].lower(): # 验证验证码 errormsg = "验证码错误" return render(request, 'register.html', locals()) else: @@ -90,11 +100,27 @@ def register(request): user.save() # 登录用户 user = authenticate(username=username, password=password) + # 注册码数据更新 + if is_register_code.count() > 0: + r_all_cnt = register_code_value.all_cnt # 注册码的最大使用次数 + r_used_cnt = register_code_value.used_cnt + 1 # 更新注册码的已使用次数 + r_use_user = register_code_value.user_list # 注册码的使用用户 + if r_used_cnt >= r_all_cnt: # 如果注册码已使用次数大于等于注册码的最大使用次数,则注册码失效 + RegisterCode.objects.filter(code=register_code).update( + status=0,# 注册码状态设为失效 + used_cnt = r_used_cnt, # 更新注册码的已使用次数 + user_list = r_use_user + email + ',', + ) + else: + RegisterCode.objects.filter(code=register_code).update( + used_cnt=r_used_cnt, # 更新注册码的已使用次数 + user_list = r_use_user + email + ',', + ) if user.is_active: login(request, user) return redirect('/') else: - errormsg = '用户被禁用!' + errormsg = '用户被禁用,请联系管理员!' return render(request, 'register.html', locals()) else: errormsg = '请输入正确的电子邮箱格式!' @@ -388,6 +414,62 @@ def admin_doctemp(request): return render(request,'app_admin/admin_doctemp.html',locals()) +# 管理员后台 - 注册邀请码管理 +@superuser_only +def admin_register_code(request): + # 返回注册邀请码管理页面 + if request.method == 'GET': + register_codes = RegisterCode.objects.all() + paginator = Paginator(register_codes, 10) + page = request.GET.get('page', 1) + try: + codes = paginator.page(page) + except PageNotAnInteger: + codes = paginator.page(1) + except EmptyPage: + codes = paginator.page(paginator.num_pages) + return render(request,'app_admin/admin_register_code.html',locals()) + elif request.method == 'POST': + types = request.POST.get('types',None) + if types is None: + return JsonResponse({'status':False,'data':'参数错误'}) + # types表示注册码操作的类型,1表示新增、2表示删除 + if int(types) == 1: + try: + all_cnt = int(request.POST.get('all_cnt',1)) # 注册码的最大使用次数 + is_code = False + while is_code is False: + code_str = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' + random_code = ''.join(random.choices(code_str, k=10)) + random_code_used = RegisterCode.objects.filter(code=random_code).count() + if random_code_used > 0: # 已存在此注册码,继续生成一个注册码 + is_code = False + else:# 数据库中不存在此注册码,跳出循环 + is_code = True + # 创建一个注册码 + RegisterCode.objects.create( + code = random_code, + all_cnt = all_cnt, + create_user = request.user + ) + return JsonResponse({'status':True,'data':'新增成功'}) + except Exception as e: + return JsonResponse({'status': False,'data':'系统异常'}) + elif int(types) == 2: + code_id = request.POST.get('code_id',None) + try: + register_code = RegisterCode.objects.get(id=int(code_id)) + register_code.delete() + return JsonResponse({'status':True,'data':'删除成功'}) + except ObjectDoesNotExist: + return JsonResponse({'status':False,'data':'注册码不存在'}) + except: + return JsonResponse({'status':False,'data':'系统异常'}) + else: + return JsonResponse({'status':False,'data':'类型错误'}) + else: + return JsonResponse({'status': False,'data':'方法错误'}) + # 普通用户修改密码 @login_required() def change_pwd(request): @@ -429,11 +511,12 @@ def admin_setting(request): types = request.POST.get('type',None) # 基础设置 if types == 'basic': - close_register = request.POST.get('close_register',None) - static_code = request.POST.get('static_code',None) - ad_code = request.POST.get('ad_code',None) - beian_code = request.POST.get('beian_code',None) - enbale_email = request.POST.get("enable_email",None) + close_register = request.POST.get('close_register',None) # 禁止注册 + static_code = request.POST.get('static_code',None) # 统计代码 + ad_code = request.POST.get('ad_code',None) # 广告代码 + beian_code = request.POST.get('beian_code',None) # 备案号 + enbale_email = request.POST.get("enable_email",None) # 启用邮箱 + enable_register_code = request.POST.get('enable_register_code',None) # 注册邀请码 # 更新开放注册状态 SysSetting.objects.update_or_create( name='close_register', @@ -459,6 +542,11 @@ def admin_setting(request): name='enable_email', defaults={'value': enbale_email, 'types': 'basic'} ) + # 更新注册码启停状态 + SysSetting.objects.update_or_create( + name = 'enable_register_code', + defaults= {'value': enable_register_code, 'types':'basic'} + ) return render(request,'app_admin/admin_setting.html',locals()) elif types == 'email': diff --git a/app_doc/views.py b/app_doc/views.py index e656429..cbf0b17 100644 --- a/app_doc/views.py +++ b/app_doc/views.py @@ -398,6 +398,11 @@ def manage_doc(request): docs.kw = search_kw else: doc_list = Doc.objects.filter(create_user=request.user).order_by('-modify_time') + all_cnt = doc_list.count() # 所有文档数量 + published_doc_cnt = Doc.objects.filter(create_user=request.user,status=1).count() # 已发布文档数量 + draft_doc_cnt = Doc.objects.filter(create_user=request.user,status=0).count() # 草稿文档数据 + pro_list = Project.objects.filter(create_user=request.user) + paginator = Paginator(doc_list, 10) page = request.GET.get('page', 1) try: diff --git a/template/app_admin/admin_base.html b/template/app_admin/admin_base.html index 3849acb..586bfd9 100644 --- a/template/app_admin/admin_base.html +++ b/template/app_admin/admin_base.html @@ -55,7 +55,10 @@ 用户管理
  • - 应用设置 + 站点设置 +
  • +
  • + 注册码管理
  • {% endif %} diff --git a/template/app_admin/admin_register_code.html b/template/app_admin/admin_register_code.html new file mode 100644 index 0000000..0ffa1d3 --- /dev/null +++ b/template/app_admin/admin_register_code.html @@ -0,0 +1,157 @@ +{% extends 'app_admin/admin_base.html' %} +{% load staticfiles %} +{% block title %}注册码管理{% endblock %} +{% block content %} +
    +
    + 注册邀请码管理 + +
    +
    +
    +
    +
    + + + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + {% for code in codes %} + + + + + + + + + + + {% endfor %} + +
    注册码最大使用次数已使用次数状态使用的用户创建时间创建用户操作
    {{ code.code }}{{ code.all_cnt }}{{ code.used_cnt }} + {% if code.status == 1 %} + 有效 + {% elif code.status == 0 %} + 失效 + {% endif %} + + {% if code.user_list is None %} + 暂无注册用户使用 + {% else %} + {{ code.user_list }} + {% endif %} + {{ code.create_time }}{{ code.create_user }} + 删除 +
    +
    + +
    + +
    +{% endblock %} + +{% block custom_script %} + +{% endblock %} \ No newline at end of file diff --git a/template/app_admin/admin_setting.html b/template/app_admin/admin_setting.html index 7b9684d..9f8568b 100644 --- a/template/app_admin/admin_setting.html +++ b/template/app_admin/admin_setting.html @@ -26,7 +26,15 @@
    -
    开启此选项将全站禁止新用户注册
    +
    开启此选项将禁止新用户注册
    + + +
    + +
    + +
    +
    开启此选项,新用户注册将需要填写注册码
    diff --git a/template/app_doc/manage_base.html b/template/app_doc/manage_base.html index be622cc..199a140 100644 --- a/template/app_doc/manage_base.html +++ b/template/app_doc/manage_base.html @@ -73,6 +73,7 @@