diff --git a/app_doc/urls.py b/app_doc/urls.py index 47385fa..8a8e316 100644 --- a/app_doc/urls.py +++ b/app_doc/urls.py @@ -20,12 +20,15 @@ urlpatterns = [ path('manage_project_colla//',views.manage_project_collaborator,name="manage_pro_colla"), # 管理文集协作 path('manage_pro_colla_self/',views.manage_pro_colla_self,name="manage_pro_colla_self"), # 我协作的文集 path('manage_project_import/',views_import.import_project,name="import_project"), # 导入文集 + path('import_doc_to_project/', views_import.import_local_doc_to_project, name="import_doc_to_project"), # 导入本地文档到文集 path('manage_project_doc_sort/',views_import.project_doc_sort,name='project_doc_sort'), # 导入文集文档排序 path('manage_project_transfer//',views.manage_project_transfer,name='manage_pro_transfer'), # 文集转让 path('manage_pro_doc_sort//',views.manage_project_doc_sort,name='manage_pro_doc_sort'), # 文集排序 path('api/my_colla_list/', views.MyCollaList.as_view(), name="my_colla_list"), # 我的协作文集列表 + path('api/import_local_doc/', views_import.ImportLocalDoc.as_view(), name="import_local_doc_api"), # 导入本地文档API #################文档相关 path('project-/doc-/', views.doc, name='doc'), # 文档浏览页 + path('doc//', views.doc_id, name="doc_id"), # 文档浏览页(通过文档ID) path('create_doc/', views.create_doc, name="create_doc"), # 新建文档 path('modify_doc//', views.modify_doc, name="modify_doc"), # 修改文档 path('del_doc/',views.del_doc,name="del_doc"), # 删除文档 diff --git a/app_doc/views.py b/app_doc/views.py index 4753ff0..b71ac22 100644 --- a/app_doc/views.py +++ b/app_doc/views.py @@ -1071,6 +1071,95 @@ def doc(request,pro_id,doc_id): return render(request,'404.html') +# 文档浏览页,可通过文档ID 或文集ID+文档ID访问 +@require_http_methods(['GET']) +def doc_id(request,doc_id): + try: + # 获取文档内容 + try: + doc = Doc.objects.get(id=int(doc_id),status__in=[0,1]) # 文档信息 + doc_tags = DocTag.objects.filter(doc=doc) # 文档标签信息 + pro_id = doc.top_doc + if doc.status == 0 and doc.create_user != request.user: + raise ObjectDoesNotExist + elif doc.status == 0 and doc.create_user == request.user: + doc.name = _('【预览草稿】')+ doc.name + + except ObjectDoesNotExist: + return render(request, '404.html') + + # 获取文集信息 + project = Project.objects.get(id=int(pro_id)) + # 获取文集的文档目录 + toc_list,toc_cnt = get_pro_toc(pro_id) + # 获取文集的协作用户信息 + if request.user.is_authenticated: + colla_user = ProjectCollaborator.objects.filter(project=project,user=request.user) + if colla_user.exists(): + colla_user_role = colla_user[0].role + colla_user = colla_user.count() + else: + colla_user = colla_user.count() + else: + colla_user = 0 + + # 获取文集收藏状态 + if request.user.is_authenticated: + is_collect_pro = MyCollect.objects.filter(collect_type=2, collect_id=pro_id, + create_user=request.user).exists() + # 获取文档收藏状态 + is_collect_doc = MyCollect.objects.filter(collect_type=1, collect_id=doc_id, + create_user=request.user).exists() + else: + is_collect_pro,is_collect_doc = False,False + + # 私密文集且访问者非创建者、协作者 - 不能访问 + if (project.role == 1) and (request.user != project.create_user) and (colla_user == 0): + return render(request, '404.html') + # 指定用户可见文集 + elif project.role == 2: + user_list = project.role_value + if request.user.is_authenticated: # 认证用户判断是否在许可用户列表中 + if (request.user.username not in user_list) and \ + (request.user != project.create_user) and \ + (colla_user == 0): # 访问者不在指定用户之中,也不是协作者 + return render(request, '404.html') + else: # 游客直接返回404 + return render(request, '404.html') + # 访问码可见 + elif project.role == 3: + # 浏览用户不为创建者和协作者 - 需要访问码 + if (request.user != project.create_user) and (colla_user == 0): + viewcode = project.role_value + viewcode_name = 'viewcode-{}'.format(project.id) + r_viewcode = request.COOKIES[ + viewcode_name] if viewcode_name in request.COOKIES.keys() else 0 # 从cookie中获取访问码 + if viewcode != r_viewcode: # cookie中的访问码不等于文集访问码,跳转到访问码认证界面 + return redirect('/check_viewcode/?to={}'.format(request.path)) + + # 获取文档内容 + try: + doc = Doc.objects.get(id=int(doc_id),status__in=[0,1]) # 文档信息 + doc_tags = DocTag.objects.filter(doc=doc) # 文档标签信息 + if doc.status == 0 and doc.create_user != request.user: + raise ObjectDoesNotExist + elif doc.status == 0 and doc.create_user == request.user: + doc.name = _('【预览草稿】')+ doc.name + + except ObjectDoesNotExist: + return render(request, '404.html') + # 获取文档分享信息 + try: + doc_share = DocShare.objects.get(doc=doc) + is_share = True + except ObjectDoesNotExist: + is_share = False + return render(request,'app_doc/doc.html',locals()) + except Exception as e: + logger.exception(_("文集浏览出错")) + return render(request,'404.html') + + # 创建文档 @login_required() @require_http_methods(['GET',"POST"]) diff --git a/app_doc/views_import.py b/app_doc/views_import.py index 8405503..00c747b 100644 --- a/app_doc/views_import.py +++ b/app_doc/views_import.py @@ -18,10 +18,17 @@ from django.contrib.auth.models import User from django.db.models import Q from django.db import transaction from django.utils.translation import gettext_lazy as _ +from rest_framework.views import APIView # 视图 +from rest_framework.response import Response # 响应 +from rest_framework.pagination import PageNumberPagination # 分页 +from rest_framework.authentication import SessionAuthentication # 认证 +from rest_framework.permissions import IsAdminUser # 权限 from loguru import logger from app_doc.report_utils import * from app_admin.decorators import check_headers,allow_report_file from app_doc.import_utils import * +from app_doc.views import get_pro_toc,html_filter,jsonXssFilter +from app_api.auth_app import AppAuth,AppMustAuth # 自定义认证 import datetime import traceback import re @@ -133,6 +140,144 @@ def import_project(request): return JsonResponse({'status':False,'data':_('参数错误')}) +# 导入本地文档到文集 +@login_required() +@require_http_methods(['GET','POST']) +def import_local_doc_to_project(request): + if request.method == 'GET': + project_list = Project.objects.filter(create_user=request.user) # 自己创建的文集列表 + colla_project_list = ProjectCollaborator.objects.filter(user=request.user) # 协作的文集列表 + return render(request,'app_doc/manage/import_local_doc_to_project.html',locals()) + + +# 导入文档到文集API +class ImportLocalDoc(APIView): + authentication_classes = [SessionAuthentication, AppMustAuth] + + # 上传文件 + def post(self,request): + project = request.data.get("project",'') + editor_mode = request.data.get("editor_mode",0) + file = request.data.get("local_doc",None) + try: + project = int(project) + editor_mode = int(editor_mode) + except: + resp = { + 'code':5, + 'data':'必须选择文集' + } + return Response(resp) + if file is None: + resp = { + 'code':5, + 'data':'文件未选择' + } + file_name = file.name + # Markdown 文件和 TXT 文件 + if file_name.endswith('.md') or file_name.endswith(".txt"): + doc_content = file.read().decode('utf-8') + if editor_mode == 3: + doc_content_html = markdown.markdown(text=doc_content) + else: + doc_content_html = None + doc = Doc.objects.create( + name = html_filter(file_name[:-3]), + pre_content = doc_content, + content = doc_content_html, + top_doc = project, + editor_mode = 1 if editor_mode == 0 else editor_mode, + create_user = request.user, + status = 0 + ) + doc.save() + resp = { + 'code':0, + 'data':{ + 'doc_id':doc.id, + 'doc_name':doc.name + } + } + # Word 文件 + elif file_name.endswith('.docx'): + if os.path.exists(os.path.join(settings.MEDIA_ROOT, 'import_temp')) is False: + os.mkdir(os.path.join(settings.MEDIA_ROOT, 'import_temp')) + + temp_file_name = str(time.time()) + '.docx' + temp_file_path = os.path.join(settings.MEDIA_ROOT, 'import_temp/' + temp_file_name) + with open(temp_file_path, 'wb+') as docx_file: + for chunk in file: + docx_file.write(chunk) + if os.path.exists(temp_file_path): + docx_file_content = ImportDocxDoc( + docx_file_path=temp_file_path, + editor_mode=editor_mode, + create_user=request.user + ).run() + if docx_file_content['status']: + doc = Doc.objects.create( + name=html_filter(file_name[:-5]), + pre_content=docx_file_content['data'], + content=docx_file_content['data'], + top_doc=project, + editor_mode=1 if editor_mode == 0 else editor_mode, + create_user=request.user, + status=0 + ) + doc.save() + resp = { + 'code': 0, + 'data': { + 'doc_id': doc.id, + 'doc_name': doc.name + } + } + else: + resp = { + 'code':4, + 'data': '{}读取失败'.format(file_name) + } + else: + resp = { + 'code': 4, + 'data': '{}上传失败'.format(file_name) + } + else: + resp = { + 'code':5, + 'data':'文件格式不支持' + } + return Response(resp) + + # 发布文档 + def put(self,request): + sort_data = request.data.get('sort_data', '[]') # 文档排序列表 + try: + sort_data = json.loads(sort_data) + except Exception: + return JsonResponse({'code': 5, 'data': _('文档参数错误')}) + # 文档排序 + n = 10 + # 第一级文档 + for data in sort_data: + Doc.objects.filter(id=data['id']).update(sort=n, status=1) + n += 10 + # 存在第二级文档 + if 'children' in data.keys(): + n1 = 10 + for c1 in data['children']: + Doc.objects.filter(id=c1['id']).update(sort=n1, parent_doc=data['id'], status=1) + n1 += 10 + # 存在第三级文档 + if 'children' in c1.keys(): + n2 = 10 + for c2 in c1['children']: + Doc.objects.filter(id=c2['id']).update(sort=n2, parent_doc=c1['id'], status=1) + + return Response({'code':0,'data':'ok'}) + + + # 文集文档排序 @login_required() @require_http_methods(['POST']) diff --git a/static/icon_img/file-doc.svg b/static/icon_img/file-doc.svg new file mode 100644 index 0000000..53293d9 --- /dev/null +++ b/static/icon_img/file-doc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/mrdoc/mrdoc-admin.css b/static/mrdoc/mrdoc-admin.css index acb9407..a4e45c5 100644 --- a/static/mrdoc/mrdoc-admin.css +++ b/static/mrdoc/mrdoc-admin.css @@ -57,6 +57,17 @@ color:red; } +/* 表格列中的链接样式 */ +.table-col-link{ + color:#2D8CF0; + font-weight: 700; +} +.table-col-link:hover{ + color:#2D8CF0; + text-decoration: underline; + font-weight: 700; +} + /* 覆盖layUI样式 */ .layui-layout-admin .layui-body{ /* background-color: #f0f2f5; */ diff --git a/template/app_doc/manage/import_local_doc_to_project.html b/template/app_doc/manage/import_local_doc_to_project.html new file mode 100644 index 0000000..f1f2df3 --- /dev/null +++ b/template/app_doc/manage/import_local_doc_to_project.html @@ -0,0 +1,327 @@ +{% extends 'app_doc/user/user_base.html' %} +{% load static %} +{% load i18n %} +{% block title %}{% trans "导入文集" %}{% endblock %} +{% block content %} + + + +
+ +
+
+
+
+ {% trans "文档文集设置" %} +
+
+
+
+ +
+ +
+
*必选
+
+
+ +
+ + + +
+
+
+
+
+
+
+ +
+
+
+
+ {% trans "文档上传" %} +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ {% trans "文档排序(文档导入后状态为草稿)" %} + +
+
+
+
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_script %} + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/template/app_doc/manage/manage_project_import.html b/template/app_doc/manage/manage_project_import.html index 52cd43e..023cae2 100644 --- a/template/app_doc/manage/manage_project_import.html +++ b/template/app_doc/manage/manage_project_import.html @@ -40,6 +40,26 @@ + + +
    +
    +
    + {% trans "导入本地文档到文集" %} +
    + +
    +
    {% endblock %} {% block custom_script %}