1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 19:01:42 +00:00

Upload folder notification (#6013)

* upload folder user notification

* update
This commit is contained in:
lian
2024-04-09 22:09:07 +08:00
committed by GitHub
parent 6b880bddfc
commit a80fe2c5ff
8 changed files with 240 additions and 40 deletions

View File

@@ -15,6 +15,7 @@ const MSG_TYPE_REPO_SHARE = 'repo_share';
const MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group';
const MSG_TYPE_REPO_TRANSFER = 'repo_transfer';
const MSG_TYPE_FILE_UPLOADED = 'file_uploaded';
const MSG_TYPE_FOLDER_UPLOADED = 'folder_uploaded';
// const MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted';
const MSG_TYPE_REPO_MONITOR = 'repo_monitor';
const MSG_TYPE_DELETED_FILES = 'deleted_files';
@@ -221,6 +222,40 @@ class NoticeItem extends React.Component {
return {avatar_url, notice};
}
if (noticeType === MSG_TYPE_FOLDER_UPLOADED) {
let avatar_url = detail.uploaded_user_avatar_url;
let folderName = detail.folder_name;
let folderLink = siteRoot + 'library/' + detail.repo_id + '/' + detail.repo_name + detail.folder_path;
let parentDirName = detail.parent_dir_name;
let parentDirLink = siteRoot + 'library/' + detail.repo_id + '/' + detail.repo_name + detail.parent_dir_path;
let notice = '';
if (detail.repo_id) { // todo is repo exist ?
// 1. handle translate
notice = gettext('A folder named {upload_folder_link} is uploaded to {uploaded_link}.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{upload_folder_link}', `{tagA}${folderName}{/tagA}`);
notice = notice.replace('{uploaded_link}', `{tagB}${parentDirName}{/tagB}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(folderLink)}>`);
notice = notice.replace('{/tagA}', '</a>');
notice = notice.replace('{tagB}', `<a href=${Utils.encodePath(parentDirLink)}>`);
notice = notice.replace('{/tagB}', '</a>');
} else {
// 1. handle translate
notice = gettext('A folder named {upload_folder_link} is uploaded to {uploaded_link}.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{upload_folder_link}', `${folderName}`);
notice = Utils.HTMLescape(notice);
notice = notice.replace('{uploaded_link}', '<strong>Deleted Library</strong>');
}
return {avatar_url, notice};
}
if (noticeType === MSG_TYPE_REPO_MONITOR) {
const {
op_user_avatar_url: avatar_url,

View File

@@ -25,7 +25,8 @@ class SharedUploadLink extends React.Component {
onFileUploadSuccess = (direntObject) => {
const { name } = direntObject;
seafileAPI.shareLinksUploadDone(token, Utils.joinPath(path, name));
const isDir = direntObject.type === 'dir';
seafileAPI.shareLinksUploadDone(token, Utils.joinPath(path, name), isDir);
};
render() {

View File

@@ -51,7 +51,7 @@ from seahub.settings import SHARE_LINK_EXPIRE_DAYS_MAX, \
from seahub.wiki.models import Wiki
from seahub.views.file import can_edit_file
from seahub.views import check_folder_permission
from seahub.signals import upload_file_successful
from seahub.signals import upload_file_successful, upload_folder_successful
from seahub.repo_tags.models import RepoTags
logger = logging.getLogger(__name__)
@@ -1129,33 +1129,17 @@ class ShareLinkUploadDone(APIView):
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
parent_dir = share_link.path
if seafile_api.check_permission_by_path(repo_id, parent_dir, share_link.username) != 'rw':
link_owner = share_link.username
if seafile_api.check_permission_by_path(repo_id,
parent_dir,
link_owner) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
file_path = request.data.get('file_path')
if not file_path:
error_msg = 'file_path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
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)
# send singal
upload_file_successful.send(sender=None,
repo_id=repo_id,
file_path=file_path,
owner=share_link.username)
return Response({'success': True})
if upload_link:
if upload_link.is_encrypted() and not check_share_link_access(request,
token,
is_upload_link=True):
if upload_link.is_encrypted() and not \
check_share_link_access(request, token, is_upload_link=True):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@@ -1170,24 +1154,42 @@ class ShareLinkUploadDone(APIView):
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
parent_dir = upload_link.path
if seafile_api.check_permission_by_path(repo_id, parent_dir, upload_link.username) != 'rw':
link_owner = upload_link.username
if seafile_api.check_permission_by_path(repo_id,
parent_dir,
link_owner) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
file_path = request.data.get('file_path')
if not file_path:
# send signal
dirent_path = request.data.get('file_path')
if not dirent_path:
error_msg = 'file_path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
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
is_dir = request.data.get('is_dir', 'false')
if is_dir.lower() == 'true':
dir_id = seafile_api.get_dir_id_by_path(repo_id, dirent_path)
if not dir_id:
error_msg = 'Folder %s not found.' % dirent_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# send singal
upload_folder_successful.send(sender=None,
repo_id=repo_id,
folder_path=dirent_path,
owner=link_owner)
else:
file_id = seafile_api.get_file_id_by_path(repo_id, dirent_path)
if not file_id:
error_msg = 'File %s not found.' % dirent_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# send singal
upload_file_successful.send(sender=None,
repo_id=repo_id,
file_path=file_path,
owner=upload_link.username)
file_path=dirent_path,
owner=link_owner)
return Response({'success': True})

View File

@@ -153,6 +153,32 @@ class Command(BaseCommand):
notice.avatar_src = self.get_default_avatar_src()
return notice
def format_folder_uploaded_msg(self, notice):
d = json.loads(notice.detail)
folder_name = d['folder_name']
repo_id = d['repo_id']
repo = seafile_api.get_repo(repo_id)
uploaded_to = d['uploaded_to']
if uploaded_to != '/':
uploaded_to = d['uploaded_to'].rstrip('/')
folder_path = uploaded_to + '/' + folder_name
parent_dir_link = reverse('lib_view', args=[repo_id, repo.name, uploaded_to.strip('/')])
parent_dir_name = os.path.basename(uploaded_to)
else:
folder_path = '/' + folder_name
parent_dir_link = reverse('lib_view', args=[repo_id, repo.name, ''])
parent_dir_name = repo.name
folder_link = reverse('lib_view', args=[repo_id, repo.name, folder_path.strip('/')])
notice.folder_link = folder_link
notice.folder_name = folder_name
notice.parent_dir_link = parent_dir_link
notice.parent_dir_name = parent_dir_name
notice.avatar_src = self.get_default_avatar_src()
return notice
def format_group_join_request(self, notice):
d = json.loads(notice.detail)
username = d['username']
@@ -299,13 +325,17 @@ class Command(BaseCommand):
results = {}
for notice in all_unseen_notices:
if notice.to_user not in results:
results[notice.to_user] = {'notices': [notice], 'sdoc_notices': [] , 'interval': DEFAULT_COLLABORATE_EMAIL_INTERVAL}
results[notice.to_user] = {'notices': [notice],
'sdoc_notices': [],
'interval': DEFAULT_COLLABORATE_EMAIL_INTERVAL}
else:
results[notice.to_user]['notices'].append(notice)
for sdoc_notice in all_unseen_sdoc_notices:
if sdoc_notice.username not in results:
results[sdoc_notice.username] = {'notices': [], 'sdoc_notices': [sdoc_notice], 'interval': DEFAULT_COLLABORATE_EMAIL_INTERVAL}
results[sdoc_notice.username] = {'notices': [],
'sdoc_notices': [sdoc_notice],
'interval': DEFAULT_COLLABORATE_EMAIL_INTERVAL}
else:
results[sdoc_notice.username]['sdoc_notices'].append(sdoc_notice)
@@ -323,7 +353,9 @@ class Command(BaseCommand):
else:
results[email]['interval'] = interval
return [(key, value['interval'], value['notices'], value['sdoc_notices'], sdoc_queryset) for key, value in results.items()]
return [(key, value['interval'],
value['notices'], value['sdoc_notices'],
sdoc_queryset) for key, value in results.items()]
def do_action(self):
@@ -408,6 +440,9 @@ class Command(BaseCommand):
elif notice.is_file_uploaded_msg():
notice = self.format_file_uploaded_msg(notice)
elif notice.is_folder_uploaded_msg():
notice = self.format_folder_uploaded_msg(notice)
elif notice.is_group_join_request():
notice = self.format_group_join_request(notice)

View File

@@ -66,6 +66,7 @@ class NotificationForm(ModelForm):
MSG_TYPE_GROUP_JOIN_REQUEST = 'group_join_request'
MSG_TYPE_ADD_USER_TO_GROUP = 'add_user_to_group'
MSG_TYPE_FILE_UPLOADED = 'file_uploaded'
MSG_TYPE_FOLDER_UPLOADED = 'folder_uploaded'
MSG_TYPE_REPO_SHARE = 'repo_share'
MSG_TYPE_REPO_SHARE_PERM_CHANGE = 'repo_share_perm_change'
MSG_TYPE_REPO_SHARE_PERM_DELETE = 'repo_share_perm_delete'
@@ -88,6 +89,12 @@ def file_uploaded_msg_to_json(file_name, repo_id, uploaded_to):
return json.dumps({'file_name': file_name, 'repo_id': repo_id,
'uploaded_to': uploaded_to})
def folder_uploaded_msg_to_json(folder_name, repo_id, uploaded_to):
"""Encode folder uploaded message to json string.
"""
return json.dumps({'folder_name': folder_name, 'repo_id': repo_id,
'uploaded_to': uploaded_to})
def repo_share_msg_to_json(share_from, repo_id, path, org_id):
return json.dumps({'share_from': share_from, 'repo_id': repo_id,
'path': path, 'org_id': org_id})
@@ -265,6 +272,18 @@ class UserNotificationManager(models.Manager):
return self._add_user_notification(to_user,
MSG_TYPE_FILE_UPLOADED, detail)
def add_folder_uploaded_msg(self, to_user, detail):
"""
Arguments:
- `self`:
- `to_user`:
- `folder_name`:
- `upload_to`:
"""
return self._add_user_notification(to_user,
MSG_TYPE_FOLDER_UPLOADED, detail)
def add_repo_share_msg(self, to_user, detail):
"""Notify ``to_user`` that others shared a repo to him/her.
@@ -378,6 +397,14 @@ class UserNotification(models.Model):
"""
return self.msg_type == MSG_TYPE_FILE_UPLOADED
def is_folder_uploaded_msg(self):
"""
Arguments:
- `self`:
"""
return self.msg_type == MSG_TYPE_FOLDER_UPLOADED
def is_repo_share_msg(self):
"""
@@ -473,6 +500,8 @@ class UserNotification(models.Model):
def format_msg(self):
if self.is_file_uploaded_msg():
return self.format_file_uploaded_msg()
if self.is_folder_uploaded_msg():
return self.format_folder_uploaded_msg()
elif self.is_repo_share_msg():
return self.format_repo_share_msg()
elif self.is_repo_share_to_group_msg():
@@ -535,6 +564,48 @@ class UserNotification(models.Model):
return msg
def format_folder_uploaded_msg(self):
"""
Arguments:
- `self`:
"""
try:
d = json.loads(self.detail)
except Exception as e:
logger.error(e)
return _("Internal Server Error")
foldername = d['folder_name']
repo_id = d['repo_id']
repo = seafile_api.get_repo(repo_id)
if repo:
if d['uploaded_to'] == '/':
# current upload path is '/'
folder_path = '/' + foldername
link = reverse('lib_view', args=[repo_id, repo.name, ''])
name = repo.name
else:
uploaded_to = d['uploaded_to'].rstrip('/')
folder_path = uploaded_to + '/' + foldername
link = reverse('lib_view', args=[repo_id, repo.name, uploaded_to.lstrip('/')])
name = os.path.basename(uploaded_to)
folder_link = reverse('lib_view', args=[repo_id, repo.name, folder_path])
msg = _("A folder named <a href='%(folder_link)s'>%(folder_name)s</a> is uploaded to <a href='%(link)s'>%(name)s</a>") % {
'folder_link': folder_link,
'folder_name': escape(foldername),
'link': link,
'name': escape(name),
}
else:
msg = _("A folder named <strong>%(folder_name)s</strong> is uploaded to <strong>Deleted Library</strong>") % {
'folder_name': escape(foldername),
}
return msg
def format_repo_share_msg(self):
"""
@@ -810,7 +881,8 @@ class UserNotification(models.Model):
########## handle signals
from django.dispatch import receiver
from seahub.signals import upload_file_successful, comment_file_successful, repo_transfer
from seahub.signals import upload_file_successful, upload_folder_successful,\
comment_file_successful, repo_transfer
from seahub.group.signals import group_join_request, add_user_to_group
from seahub.share.signals import share_repo_to_user_successful, \
share_repo_to_group_successful, change_repo_perm_successful, delete_repo_perm_successful
@@ -836,6 +908,23 @@ def add_upload_file_msg_cb(sender, **kwargs):
detail = file_uploaded_msg_to_json(filename, repo_id, folder_path)
UserNotification.objects.add_file_uploaded_msg(owner, detail)
@receiver(upload_folder_successful)
def add_upload_folder_msg_cb(sender, **kwargs):
"""Notify repo owner when others upload folder to his/her folder from shared link.
"""
repo_id = kwargs.get('repo_id', None)
folder_path = kwargs.get('folder_path', None)
owner = kwargs.get('owner', None)
assert repo_id and folder_path and owner is not None, 'Arguments error'
folder_name = os.path.basename(folder_path.rstrip('/'))
parent_dir = os.path.dirname(folder_path.rstrip('/'))
detail = folder_uploaded_msg_to_json(folder_name, repo_id, parent_dir)
UserNotification.objects.add_folder_uploaded_msg(owner, detail)
@receiver(share_repo_to_user_successful)
def add_share_repo_msg_cb(sender, **kwargs):
"""Notify user when others share repos to him/her.

View File

@@ -50,6 +50,9 @@ You've got {{num}} new notices on {{ site_name }}:
{% elif notice.is_file_uploaded_msg %}
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with file_url=notice.file_link file_name=notice.file_name folder_url=notice.folder_link folder_name=notice.folder_name %}A file named <a href="{{url_base}}{{file_url}}">{{file_name}}</a> is uploaded to your folder <a href="{{url_base}}{{folder_url}}">{{folder_name}}</a>.{% endblocktrans %}</p>
{% elif notice.is_folder_uploaded_msg %}
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with folder_url=notice.folder_link folder_name=notice.folder_name parent_dir_url=notice.parent_dir_link parent_dir_name=notice.parent_dir_name %}A folder named <a href="{{url_base}}{{folder_url}}">{{folder_name}}</a> is uploaded to your folder <a href="{{url_base}}{{parent_dir_url}}">{{parent_dir_name}}</a>.{% endblocktrans %}</p>
{% elif notice.is_group_join_request %}
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with user_url=notice.grpjoin_user_profile_url user=notice.notice_from grp_url=notice.grpjoin_group_url grp_name=notice.grpjoin_group_name msg=notice.grpjoin_request_msg %}User <a href="{{url_base}}{{user_url}}">{{user}}</a> has asked to join group <a href="{{url_base}}{{grp_url}}">{{grp_name}}</a>, verification message: {{msg}}{% endblocktrans %}</p>

View File

@@ -144,7 +144,6 @@ def update_notice_detail(request, notices):
except Exception as e:
logger.error(e)
elif notice.is_repo_share_to_group_msg():
try:
d = json.loads(notice.detail)
@@ -285,6 +284,41 @@ def update_notice_detail(request, notices):
except Exception as e:
logger.error(e)
elif notice.is_folder_uploaded_msg():
try:
d = json.loads(notice.detail)
foldername = d['folder_name']
repo_id = d['repo_id']
if repo_id in repo_dict:
repo = repo_dict[repo_id]
else:
repo = seafile_api.get_repo(repo_id)
repo_dict[repo_id] = repo
if repo:
if d['uploaded_to'] == '/':
# current upload path is '/'
folder_path = '/' + foldername
name = repo.name
else:
uploaded_to = d['uploaded_to'].rstrip('/')
folder_path = uploaded_to + '/' + foldername
name = os.path.basename(uploaded_to)
d['repo_name'] = repo.name
d['parent_dir_path'] = d.pop('uploaded_to')
d['parent_dir_name'] = name
d['folder_path'] = folder_path
url, is_default, date_uploaded = api_avatar_url('', 32)
d['uploaded_user_avatar_url'] = url
notice.detail = d
else:
notice.detail = None
except Exception as e:
logger.error(e)
elif notice.is_file_comment_msg():
try:
d = json.loads(notice.detail)

View File

@@ -8,5 +8,6 @@ repo_transfer = Signal()
clean_up_repo_trash = Signal()
repo_restored = Signal()
upload_file_successful = Signal()
upload_folder_successful = Signal()
comment_file_successful = Signal()
institution_deleted = Signal()