-
,
document.getElementById('wrapper')
-);
+);
\ No newline at end of file
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index 861b1b995f..d169ea064e 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -126,3 +126,4 @@ export const canViewAdminLog = window.sysadmin ? window.sysadmin.pageOptions.adm
export const enableWorkWeixin = window.sysadmin ? window.sysadmin.pageOptions.enable_work_weixin : '';
export const enableSysAdminViewRepo = window.sysadmin ? window.sysadmin.pageOptions.enableSysAdminViewRepo : '';
export const haveLDAP = window.sysadmin ? window.sysadmin.pageOptions.haveLDAP : '';
+export const enableShareLinkReportAbuse = window.sysadmin ? window.sysadmin.pageOptions.enable_share_link_report_abuse : '';
diff --git a/seahub/abuse_reports/__init__.py b/seahub/abuse_reports/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/seahub/abuse_reports/migrations/0001_initial.py b/seahub/abuse_reports/migrations/0001_initial.py
new file mode 100644
index 0000000000..f8ae92ff81
--- /dev/null
+++ b/seahub/abuse_reports/migrations/0001_initial.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.23 on 2019-11-05 02:41
+from __future__ import unicode_literals
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AbuseReport',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('reporter', models.TextField(blank=True, null=True)),
+ ('repo_id', models.CharField(max_length=36)),
+ ('repo_name', models.CharField(max_length=255)),
+ ('file_path', models.TextField(blank=True, null=True)),
+ ('abuse_type', models.CharField(choices=[('copyright', 'copyright'), ('virus', 'virus'), ('abuse_content', 'abuse_content'), ('other', 'other')], db_index=True, max_length=255)),
+ ('description', models.TextField(blank=True, null=True)),
+ ('handled', models.BooleanField(db_index=True, default=False)),
+ ('time', models.DateTimeField(default=datetime.datetime.now)),
+ ],
+ options={
+ 'ordering': ['-time'],
+ },
+ ),
+ ]
diff --git a/seahub/abuse_reports/migrations/__init__.py b/seahub/abuse_reports/migrations/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/seahub/abuse_reports/models.py b/seahub/abuse_reports/models.py
new file mode 100644
index 0000000000..48c6df44c0
--- /dev/null
+++ b/seahub/abuse_reports/models.py
@@ -0,0 +1,71 @@
+# Copyright (c) 2012-2017 Seafile Ltd.
+
+import datetime
+
+from django.db import models
+from django.conf import settings
+
+COPYRIGHT_ISSUE = 'copyright'
+VIRUS_ISSUE = 'virus'
+ABUSE_CONTENT_ISSUE = 'abuse_content'
+OTHER_ISSUE = 'other'
+
+
+class AbuseReportManager(models.Manager):
+
+ def add_abuse_report(self, reporter, repo_id, repo_name, file_path,
+ abuse_type, description=None):
+
+ model = super(AbuseReportManager, self).create(
+ reporter=reporter, repo_id=repo_id, repo_name=repo_name,
+ file_path=file_path, abuse_type=abuse_type,
+ description=description)
+
+ model.save()
+
+ return model
+
+ def get_abuse_report_by_id(self, pk):
+
+ try:
+ report = super(AbuseReportManager, self).get(id=pk)
+ except AbuseReport.DoesNotExist:
+ return None
+
+ return report
+
+ def get_abuse_reports(self, abuse_type=None, handled=None):
+
+ reports = super(AbuseReportManager, self).all()
+
+ if abuse_type:
+ reports = reports.filter(abuse_type=abuse_type)
+
+ if handled in (True, False):
+ reports = reports.filter(handled=handled)
+
+ return reports
+
+
+class AbuseReport(models.Model):
+ ABUSE_TYPE_CHOICES = (
+ (COPYRIGHT_ISSUE, 'copyright'),
+ (VIRUS_ISSUE, 'virus'),
+ (ABUSE_CONTENT_ISSUE, 'abuse_content'),
+ (OTHER_ISSUE, 'other'),
+ )
+
+ reporter = models.TextField(blank=True, null=True)
+ repo_id = models.CharField(max_length=36)
+ repo_name = models.CharField(max_length=settings.MAX_FILE_NAME)
+ file_path = models.TextField(blank=True, null=True)
+ abuse_type = models.CharField(max_length=255,
+ db_index=True, choices=ABUSE_TYPE_CHOICES, )
+ description = models.TextField(blank=True, null=True)
+ handled = models.BooleanField(default=False, db_index=True)
+ time = models.DateTimeField(default=datetime.datetime.now)
+
+ objects = AbuseReportManager()
+
+ class Meta:
+ ordering = ["-time"]
diff --git a/seahub/api2/endpoints/abuse_reports.py b/seahub/api2/endpoints/abuse_reports.py
new file mode 100644
index 0000000000..431bd17f06
--- /dev/null
+++ b/seahub/api2/endpoints/abuse_reports.py
@@ -0,0 +1,119 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+import os
+import logging
+import posixpath
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from rest_framework import status
+
+from seaserv import seafile_api
+
+from seahub.api2.utils import api_error
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import AnonRateThrottle
+from seahub.share.models import FileShare
+from seahub.utils import normalize_file_path
+from seahub.utils.timeutils import datetime_to_isoformat_timestr
+from seahub.settings import ENABLE_SHARE_LINK_REPORT_ABUSE
+
+from seahub.abuse_reports.models import COPYRIGHT_ISSUE, \
+ VIRUS_ISSUE, ABUSE_CONTENT_ISSUE, OTHER_ISSUE, AbuseReport
+
+logger = logging.getLogger(__name__)
+
+
+def get_abuse_report_info(report):
+ data = {}
+
+ file_path = report.file_path
+
+ data['id'] = report.id
+ data['reporter'] = report.reporter
+ data['repo_id'] = report.repo_id
+ data['repo_name'] = report.repo_name
+ data['file_path'] = file_path
+ data['file_name'] = os.path.basename(file_path)
+ data['time'] = datetime_to_isoformat_timestr(report.time)
+ data['abuse_type'] = report.abuse_type
+ data['description'] = report.description
+ data['handled'] = report.handled
+
+ return data
+
+
+class AbuseReportsView(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ throttle_classes = (AnonRateThrottle,)
+
+ def post(self, request):
+ """ Create abuse report.
+
+ Permission checking:
+ 1. all user;
+ """
+
+ if not ENABLE_SHARE_LINK_REPORT_ABUSE:
+ error_msg = 'Feature not enabled.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ # argument check
+ share_link_token = request.data.get('share_link_token', '')
+ if not share_link_token:
+ error_msg = 'share_link_token invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ abuse_type = request.data.get('abuse_type', '')
+ if not abuse_type:
+ error_msg = 'abuse_type invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if abuse_type not in (COPYRIGHT_ISSUE, VIRUS_ISSUE,
+ ABUSE_CONTENT_ISSUE, OTHER_ISSUE):
+ error_msg = 'abuse_type invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ reporter = request.data.get('reporter', '')
+ description = request.data.get('description', '')
+
+ # resource check
+ share_link = FileShare.objects.get_valid_file_link_by_token(share_link_token)
+ if not share_link:
+ error_msg = 'Share link %s not found.' % share_link_token
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ repo_id = share_link.repo_id
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ file_path = share_link.path
+ file_path = normalize_file_path(file_path)
+ file_id = seafile_api.get_file_id_by_path(repo_id, file_path)
+
+ if not file_id:
+ # view file via shared dir
+ req_path = request.data.get('file_path', '')
+ if not req_path:
+ file_id = None
+ else:
+ dir_path = normalize_file_path(share_link.path)
+ file_path = posixpath.join(dir_path, normalize_file_path(req_path).lstrip('/'))
+ file_id = seafile_api.get_file_id_by_path(repo_id, file_path)
+
+ if not file_id:
+ error_msg = 'File %s not found.' % file_path
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ try:
+ report = AbuseReport.objects.add_abuse_report(
+ reporter, repo_id, repo.repo_name, file_path, abuse_type, description)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ info = get_abuse_report_info(report)
+ return Response(info)
diff --git a/seahub/api2/endpoints/admin/abuse_reports.py b/seahub/api2/endpoints/admin/abuse_reports.py
new file mode 100644
index 0000000000..1c2fe25079
--- /dev/null
+++ b/seahub/api2/endpoints/admin/abuse_reports.py
@@ -0,0 +1,99 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+import logging
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAdminUser
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from rest_framework import status
+
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.utils import api_error, to_python_boolean
+
+from seahub.api2.endpoints.abuse_reports import get_abuse_report_info
+from seahub.abuse_reports.models import AbuseReport
+from seahub.settings import ENABLE_SHARE_LINK_REPORT_ABUSE
+
+logger = logging.getLogger(__name__)
+
+
+class AdminAbuseReportsView(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAdminUser,)
+ throttle_classes = (UserRateThrottle,)
+
+ def get(self, request):
+ """ Get all abuse reports.
+
+ Permission checking:
+ 1. only admin can perform this action.
+ """
+
+ if not ENABLE_SHARE_LINK_REPORT_ABUSE:
+ error_msg = 'Feature not enabled.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ abuse_type = request.GET.get('abuse_type', '')
+ handled = request.GET.get('handled', '')
+
+ if handled:
+ if handled not in ('true', 'false'):
+ error_msg = 'handled invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ handled = to_python_boolean(handled)
+
+ try:
+ reports = AbuseReport.objects.get_abuse_reports(
+ abuse_type=abuse_type, handled=handled)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ info_list = []
+ for report in reports:
+ info = get_abuse_report_info(report)
+ info_list.append(info)
+
+ return Response({'abuse_report_list': info_list, })
+
+
+class AdminAbuseReportView(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAdminUser,)
+ throttle_classes = (UserRateThrottle,)
+
+ def put(self, request, pk):
+ """ Mark an abuse report handled.
+
+ Permission checking:
+ 1. only admin can perform this action.
+ """
+
+ if not ENABLE_SHARE_LINK_REPORT_ABUSE:
+ error_msg = 'Feature not enabled.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ # argument check
+ handled = request.data.get('handled')
+ if not handled:
+ error_msg = 'handled invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if handled not in ('true', 'false'):
+ error_msg = 'handled invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ report = AbuseReport.objects.get_abuse_report_by_id(pk)
+ if not report:
+ error_msg = 'abuse report %d not found.' % pk
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ report.handled = to_python_boolean(handled)
+ report.save()
+
+ info = get_abuse_report_info(report)
+ return Response(info)
diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py
index 2bc42e7e20..08cbf1358a 100644
--- a/seahub/base/context_processors.py
+++ b/seahub/base/context_processors.py
@@ -21,7 +21,7 @@ from seahub.settings import SEAFILE_VERSION, SITE_TITLE, SITE_NAME, \
FAVICON_PATH, ENABLE_THUMBNAIL, THUMBNAIL_SIZE_FOR_ORIGINAL, \
MEDIA_ROOT, SHOW_LOGOUT_ICON, CUSTOM_LOGO_PATH, CUSTOM_FAVICON_PATH, \
ENABLE_SEAFILE_DOCS, LOGIN_BG_IMAGE_PATH, \
- CUSTOM_LOGIN_BG_PATH
+ CUSTOM_LOGIN_BG_PATH, ENABLE_SHARE_LINK_REPORT_ABUSE
from seahub.constants import DEFAULT_ADMIN
from seahub.utils import get_site_name, get_service_url
@@ -143,6 +143,7 @@ def base(request):
if request.user.is_staff:
result['is_default_admin'] = request.user.admin_role == DEFAULT_ADMIN
+ result['enable_share_link_report_abuse'] = ENABLE_SHARE_LINK_REPORT_ABUSE
return result
diff --git a/seahub/settings.py b/seahub/settings.py
index aa58f1c6e4..554b761ceb 100644
--- a/seahub/settings.py
+++ b/seahub/settings.py
@@ -255,6 +255,7 @@ INSTALLED_APPS = (
'seahub.work_weixin',
'seahub.file_participants',
'seahub.repo_api_tokens',
+ 'seahub.abuse_reports',
)
# Enable or disable view File Scan
@@ -347,6 +348,9 @@ SHARE_LINK_PASSWORD_MIN_LENGTH = 8
# enable or disable share link audit
ENABLE_SHARE_LINK_AUDIT = False
+# enable or disable report abuse file on share link page
+ENABLE_SHARE_LINK_REPORT_ABUSE = False
+
# share link audit code timeout
SHARE_LINK_AUDIT_CODE_TIMEOUT = 60 * 60
diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html
index e1419870c8..fd2e8d88d1 100644
--- a/seahub/templates/js/sysadmin-templates.html
+++ b/seahub/templates/js/sysadmin-templates.html
@@ -118,12 +118,19 @@
{% endif %}
+
{% if is_default_admin and enable_work_weixin %}
企业微信集成
{% endif %}
+ {% if is_default_admin and enable_share_link_report_abuse %}
+
+ {% trans "Abuse Reports" %}
+
+ {% endif %}
+
<% if (cur_tab == 'libraries') { %>
<% if (option == 'all') { %>
@@ -1024,3 +1031,55 @@
|
+
+
+
+
diff --git a/seahub/templates/shared_file_view_react.html b/seahub/templates/shared_file_view_react.html
index 09c5d65d42..847cff4736 100644
--- a/seahub/templates/shared_file_view_react.html
+++ b/seahub/templates/shared_file_view_react.html
@@ -69,7 +69,8 @@ body {
fileType: '{{ filetype }}',
fileExt: '{{ fileext }}',
commitID: '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}',
-
+ enableShareLinkReportAbuse: {% if enable_share_link_report_abuse %}true{% else %}false{% endif %},
+
// for 'view file in shared dir'
{% if zipped %}
zipped: (function() {
diff --git a/seahub/templates/sysadmin/base.html b/seahub/templates/sysadmin/base.html
index d4e681146b..5369952447 100644
--- a/seahub/templates/sysadmin/base.html
+++ b/seahub/templates/sysadmin/base.html
@@ -135,6 +135,12 @@
{% endif %}
+ {% if is_default_admin and enable_share_link_report_abuse %}
+
+ {% trans "Abuse Reports" %}
+
+ {% endif %}
+
{% endblock %}
diff --git a/seahub/templates/sysadmin/sysadmin_react_app.html b/seahub/templates/sysadmin/sysadmin_react_app.html
index 4c6f2de6d5..7453fa4adc 100644
--- a/seahub/templates/sysadmin/sysadmin_react_app.html
+++ b/seahub/templates/sysadmin/sysadmin_react_app.html
@@ -42,6 +42,7 @@
return list;
})(),
haveLDAP: {% if have_ldap %} true {% else %} false {% endif %},
+ enable_share_link_report_abuse: {% if enable_share_link_report_abuse %} true {% else %} false {% endif %},
admin_permissions: {
"can_view_system_info": {% if user.admin_permissions.can_view_system_info %} true {% else %} false {% endif %},
"can_view_statistic": {% if user.admin_permissions.can_view_statistic %} true {% else %} false {% endif %},
diff --git a/seahub/urls.py b/seahub/urls.py
index e8f96da2ff..ffda018b4c 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -92,9 +92,10 @@ from seahub.api2.endpoints.public_repos_search import PublishedRepoSearchView
from seahub.api2.endpoints.recent_added_files import RecentAddedFilesView
from seahub.api2.endpoints.repo_api_tokens import RepoAPITokensView, RepoAPITokenView
from seahub.api2.endpoints.via_repo_token import ViaRepoDirView, ViaRepoUploadLinkView
-
+from seahub.api2.endpoints.abuse_reports import AbuseReportsView
# Admin
+from seahub.api2.endpoints.admin.abuse_reports import AdminAbuseReportsView, AdminAbuseReportView
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
from seahub.api2.endpoints.admin.login_logs import LoginLogs, AdminLoginLogs
from seahub.api2.endpoints.admin.file_audit import FileAudit
@@ -446,6 +447,16 @@ urlpatterns = [
# admin: activities
url(r'^api/v2.1/admin/user-activities/$', UserActivitiesView.as_view(), name='api-v2.1-admin-user-activity'),
+ ## user::abuse-report
+ # user report an abuse file
+ url(r'^api/v2.1/abuse-reports/$', AbuseReportsView.as_view(), name='api-v2.1-abuse-reports'),
+
+ ## admin::abuse-reports
+ # admin get all abuse reports
+ url(r'^api/v2.1/admin/abuse-reports/$', AdminAbuseReportsView.as_view(), name='api-v2.1-admin-abuse-reports'),
+ url(r'^api/v2.1/admin/abuse-reports/(?P