mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-26 07:22:34 +00:00
update (#8140)
* update * update * Update utils.py * Update utils.py * add fontend code * Update utils.py * update --------- Co-authored-by: 小强 <shuntian@Mac.lan>
This commit is contained in:
@@ -32,6 +32,12 @@ class CommentPanel extends React.Component {
|
|||||||
this.toBeAddedParticipant = [];
|
this.toBeAddedParticipant = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceUpdate = () => {
|
||||||
|
this.listComments();
|
||||||
|
this.getParticipants();
|
||||||
|
this.listRepoRelatedUsers();
|
||||||
|
};
|
||||||
|
|
||||||
listComments = () => {
|
listComments = () => {
|
||||||
seafileAPI.listComments(repoID, fileUuid).then((res) => {
|
seafileAPI.listComments(repoID, fileUuid).then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@@ -22,7 +22,8 @@ const propTypes = {
|
|||||||
toggleCommentPanel: PropTypes.func.isRequired,
|
toggleCommentPanel: PropTypes.func.isRequired,
|
||||||
toggleDetailsPanel: PropTypes.func.isRequired,
|
toggleDetailsPanel: PropTypes.func.isRequired,
|
||||||
setImageScale: PropTypes.func,
|
setImageScale: PropTypes.func,
|
||||||
rotateImage: PropTypes.func
|
rotateImage: PropTypes.func,
|
||||||
|
isCommentUpdated: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -103,7 +104,7 @@ class FileToolbar extends React.Component {
|
|||||||
|
|
||||||
const { moreDropdownOpen } = this.state;
|
const { moreDropdownOpen } = this.state;
|
||||||
|
|
||||||
const { isLocked, lockedByMe } = this.props;
|
const { isLocked, lockedByMe, isCommentUpdated } = this.props;
|
||||||
let showLockUnlockBtn = false;
|
let showLockUnlockBtn = false;
|
||||||
let lockUnlockText; let lockUnlockIcon;
|
let lockUnlockText; let lockUnlockIcon;
|
||||||
if (canLockUnlockFile) {
|
if (canLockUnlockFile) {
|
||||||
@@ -214,6 +215,7 @@ class FileToolbar extends React.Component {
|
|||||||
aria-label={gettext('Comment')}
|
aria-label={gettext('Comment')}
|
||||||
>
|
>
|
||||||
<i className="sdocfont sdoc-comments"></i>
|
<i className="sdocfont sdoc-comments"></i>
|
||||||
|
{isCommentUpdated && <span className='comment-tip'></span>}
|
||||||
</div>
|
</div>
|
||||||
{showShareBtn && (
|
{showShareBtn && (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@@ -15,6 +15,7 @@ import OnlyofficeFileToolbar from './onlyoffice-file-toolbar';
|
|||||||
import EmbeddedFileDetails from '../dirent-detail/embedded-file-details';
|
import EmbeddedFileDetails from '../dirent-detail/embedded-file-details';
|
||||||
import { MetadataMiddlewareProvider, MetadataStatusProvider } from '../../hooks';
|
import { MetadataMiddlewareProvider, MetadataStatusProvider } from '../../hooks';
|
||||||
import Loading from '../loading';
|
import Loading from '../loading';
|
||||||
|
import WebSocketClient from '../../utils/websocket-service';
|
||||||
|
|
||||||
import '../../css/file-view.css';
|
import '../../css/file-view.css';
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ const propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { isStarred, isLocked, lockedByMe,
|
const { isStarred, isLocked, lockedByMe,
|
||||||
repoID, filePath, filePerm, enableWatermark, userNickName,
|
repoID, fileUuid, filePath, filePerm, enableWatermark, userNickName,
|
||||||
fileName, repoEncrypted, isRepoAdmin, fileType
|
fileName, repoEncrypted, isRepoAdmin, fileType
|
||||||
} = window.app.pageOptions;
|
} = window.app.pageOptions;
|
||||||
|
|
||||||
@@ -44,8 +45,11 @@ class FileView extends React.Component {
|
|||||||
lockedByMe: lockedByMe,
|
lockedByMe: lockedByMe,
|
||||||
isCommentPanelOpen: false,
|
isCommentPanelOpen: false,
|
||||||
isHeaderShown: (storedIsHeaderShown === null) || (storedIsHeaderShown == 'true'),
|
isHeaderShown: (storedIsHeaderShown === null) || (storedIsHeaderShown == 'true'),
|
||||||
isDetailsPanelOpen: false
|
isDetailsPanelOpen: false,
|
||||||
|
isCommentUpdated: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.socketManager = new WebSocketClient(this.onMessageCallback, repoID);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -53,10 +57,25 @@ class FileView extends React.Component {
|
|||||||
document.getElementById('favicon').href = fileIcon;
|
document.getElementById('favicon').href = fileIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMessageCallback = (data) => {
|
||||||
|
const { type, content } = data;
|
||||||
|
if (type === 'comment-update') {
|
||||||
|
const { repo_id, file_uuid } = content;
|
||||||
|
if (repoID === repo_id && file_uuid === fileUuid) {
|
||||||
|
if (!this.state.isCommentPanelOpen) {
|
||||||
|
this.setState({ isCommentUpdated: true });
|
||||||
|
} else {
|
||||||
|
this.commentPanelRef.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
toggleCommentPanel = () => {
|
toggleCommentPanel = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isCommentPanelOpen: !this.state.isCommentPanelOpen,
|
isCommentPanelOpen: !this.state.isCommentPanelOpen,
|
||||||
isDetailsPanelOpen: false,
|
isDetailsPanelOpen: false,
|
||||||
|
isCommentUpdated: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -121,6 +140,10 @@ class FileView extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setCommentPanelRef = (ref) => {
|
||||||
|
this.commentPanelRef = ref;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isOnlyofficeFile = false } = this.props;
|
const { isOnlyofficeFile = false } = this.props;
|
||||||
const { isDetailsPanelOpen, isHeaderShown } = this.state;
|
const { isDetailsPanelOpen, isHeaderShown } = this.state;
|
||||||
@@ -142,6 +165,7 @@ class FileView extends React.Component {
|
|||||||
/>
|
/>
|
||||||
{isOnlyofficeFile ?
|
{isOnlyofficeFile ?
|
||||||
<OnlyofficeFileToolbar
|
<OnlyofficeFileToolbar
|
||||||
|
isCommentUpdated={this.state.isCommentUpdated}
|
||||||
toggleDetailsPanel={this.toggleDetailsPanel}
|
toggleDetailsPanel={this.toggleDetailsPanel}
|
||||||
toggleHeader={this.toggleHeader}
|
toggleHeader={this.toggleHeader}
|
||||||
toggleCommentPanel={this.toggleCommentPanel}
|
toggleCommentPanel={this.toggleCommentPanel}
|
||||||
@@ -149,6 +173,7 @@ class FileView extends React.Component {
|
|||||||
<FileToolbar
|
<FileToolbar
|
||||||
isLocked={this.state.isLocked}
|
isLocked={this.state.isLocked}
|
||||||
lockedByMe={this.state.lockedByMe}
|
lockedByMe={this.state.lockedByMe}
|
||||||
|
isCommentUpdated={this.state.isCommentUpdated}
|
||||||
onSave={this.props.onSave}
|
onSave={this.props.onSave}
|
||||||
isSaving={this.props.isSaving}
|
isSaving={this.props.isSaving}
|
||||||
needSave={this.props.needSave}
|
needSave={this.props.needSave}
|
||||||
@@ -175,6 +200,7 @@ class FileView extends React.Component {
|
|||||||
{this.props.content}
|
{this.props.content}
|
||||||
{this.state.isCommentPanelOpen &&
|
{this.state.isCommentPanelOpen &&
|
||||||
<CommentPanel
|
<CommentPanel
|
||||||
|
ref={this.setCommentPanelRef}
|
||||||
toggleCommentPanel={this.toggleCommentPanel}
|
toggleCommentPanel={this.toggleCommentPanel}
|
||||||
participants={this.props.participants}
|
participants={this.props.participants}
|
||||||
onParticipantsChange={this.props.onParticipantsChange}
|
onParticipantsChange={this.props.onParticipantsChange}
|
||||||
|
@@ -7,8 +7,9 @@ import Icon from '../../components/icon';
|
|||||||
import IconButton from '../icon-button';
|
import IconButton from '../icon-button';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
isCommentUpdated: PropTypes.bool,
|
||||||
toggleDetailsPanel: PropTypes.func.isRequired,
|
toggleDetailsPanel: PropTypes.func.isRequired,
|
||||||
toggleHeader: PropTypes.func.isRequired
|
toggleHeader: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -38,6 +39,7 @@ class OnlyofficeFileToolbar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { isCommentUpdated } = this.props;
|
||||||
const { moreDropdownOpen } = this.state;
|
const { moreDropdownOpen } = this.state;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@@ -54,6 +56,7 @@ class OnlyofficeFileToolbar extends React.Component {
|
|||||||
aria-label={gettext('Comment')}
|
aria-label={gettext('Comment')}
|
||||||
>
|
>
|
||||||
<i className="sdocfont sdoc-comments"></i>
|
<i className="sdocfont sdoc-comments"></i>
|
||||||
|
{isCommentUpdated && <span className='comment-tip'></span>}
|
||||||
</div>
|
</div>
|
||||||
<Dropdown isOpen={moreDropdownOpen} toggle={this.toggleMoreOpMenu}>
|
<Dropdown isOpen={moreDropdownOpen} toggle={this.toggleMoreOpMenu}>
|
||||||
<DropdownToggle
|
<DropdownToggle
|
||||||
|
@@ -43,6 +43,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.file-view-header .file-toolbar-btn {
|
.file-view-header .file-toolbar-btn {
|
||||||
|
position: relative;
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
@@ -78,6 +79,17 @@ body {
|
|||||||
color: #ED7109;
|
color: #ED7109;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-view-header .file-toolbar-btn .comment-tip {
|
||||||
|
position: absolute;
|
||||||
|
left: 4px;
|
||||||
|
top: 4px;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
z-index: 100;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
.file-view-header .file-toolbar-more-operations {
|
.file-view-header .file-toolbar-more-operations {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -140,6 +152,7 @@ body {
|
|||||||
right: -500px;
|
right: -500px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@@ -14,7 +14,7 @@ from django.utils import timezone
|
|||||||
from seahub.api2.authentication import TokenAuthentication
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
from seahub.api2.permissions import IsRepoAccessible
|
from seahub.api2.permissions import IsRepoAccessible
|
||||||
from seahub.api2.throttling import UserRateThrottle
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
from seahub.api2.utils import api_error, user_to_dict, to_python_boolean
|
from seahub.api2.utils import api_error, user_to_dict, to_python_boolean, send_comment_update_event
|
||||||
from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
||||||
from seahub.base.models import FileComment
|
from seahub.base.models import FileComment
|
||||||
from seahub.utils.repo import get_repo_owner
|
from seahub.utils.repo import get_repo_owner
|
||||||
@@ -132,6 +132,7 @@ class FileCommentsView(APIView):
|
|||||||
notification = detail
|
notification = detail
|
||||||
notification['to_users'] = to_users
|
notification['to_users'] = to_users
|
||||||
comment['notification'] = notification
|
comment['notification'] = notification
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response(comment)
|
return Response(comment)
|
||||||
|
|
||||||
|
|
||||||
@@ -166,6 +167,7 @@ class FileCommentView(APIView):
|
|||||||
|
|
||||||
file_comment.delete()
|
file_comment.delete()
|
||||||
SeadocCommentReply.objects.filter(comment_id=comment_id).delete()
|
SeadocCommentReply.objects.filter(comment_id=comment_id).delete()
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response({'success': True})
|
return Response({'success': True})
|
||||||
|
|
||||||
def put(self, request, repo_id, file_uuid, comment_id):
|
def put(self, request, repo_id, file_uuid, comment_id):
|
||||||
@@ -204,6 +206,7 @@ class FileCommentView(APIView):
|
|||||||
|
|
||||||
comment = file_comment.to_dict()
|
comment = file_comment.to_dict()
|
||||||
comment.update(user_to_dict(file_comment.author, request=request))
|
comment.update(user_to_dict(file_comment.author, request=request))
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response(comment)
|
return Response(comment)
|
||||||
|
|
||||||
|
|
||||||
@@ -313,6 +316,7 @@ class FileCommentRepliesView(APIView):
|
|||||||
notification = detail
|
notification = detail
|
||||||
notification['to_users'] = to_users
|
notification['to_users'] = to_users
|
||||||
data['notification'] = notification
|
data['notification'] = notification
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
@@ -353,6 +357,7 @@ class FileCommentReplyView(APIView):
|
|||||||
if not reply:
|
if not reply:
|
||||||
return api_error(status.HTTP_404_NOT_FOUND, 'reply not found.')
|
return api_error(status.HTTP_404_NOT_FOUND, 'reply not found.')
|
||||||
reply.delete()
|
reply.delete()
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response({'success': True})
|
return Response({'success': True})
|
||||||
|
|
||||||
def put(self, request, repo_id, file_uuid, comment_id, reply_id):
|
def put(self, request, repo_id, file_uuid, comment_id, reply_id):
|
||||||
@@ -380,4 +385,5 @@ class FileCommentReplyView(APIView):
|
|||||||
data = reply.to_dict()
|
data = reply.to_dict()
|
||||||
data.update(
|
data.update(
|
||||||
user_to_dict(reply.author, request=request))
|
user_to_dict(reply.author, request=request))
|
||||||
|
send_comment_update_event(file_uuid)
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
@@ -8,7 +8,7 @@ import json
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import jwt
|
import jwt
|
||||||
|
import requests
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
@@ -34,9 +34,11 @@ from seahub.utils import get_user_repos
|
|||||||
from seahub.utils.mail import send_html_email_with_dj_template
|
from seahub.utils.mail import send_html_email_with_dj_template
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
import seahub.settings as settings
|
import seahub.settings as settings
|
||||||
|
from seahub.tags.models import FileUUIDMap
|
||||||
|
|
||||||
JWT_PRIVATE_KEY = getattr(settings, 'JWT_PRIVATE_KEY', '')
|
JWT_PRIVATE_KEY = getattr(settings, 'JWT_PRIVATE_KEY', '')
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def api_error(code, msg):
|
def api_error(code, msg):
|
||||||
@@ -361,3 +363,37 @@ def is_valid_internal_jwt(auth):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def send_comment_update_event(file_uuid):
|
||||||
|
if not settings.ENABLE_NOTIFICATION_SERVER:
|
||||||
|
return
|
||||||
|
|
||||||
|
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
|
||||||
|
if not uuid_map:
|
||||||
|
return
|
||||||
|
repo_id = uuid_map.repo_id
|
||||||
|
event_data = {
|
||||||
|
"type": "comment-update",
|
||||||
|
"content": {
|
||||||
|
"repo_id": repo_id,
|
||||||
|
"type": "comment_updated",
|
||||||
|
"file_uuid": file_uuid,
|
||||||
|
"file_path": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notification_server_event_url = "%s/events" % settings.INNER_NOTIFICATION_SERVER_URL.rstrip('/')
|
||||||
|
payload = {
|
||||||
|
'exp': int(time.time()) + 500
|
||||||
|
}
|
||||||
|
jwt_token = jwt.encode(payload, JWT_PRIVATE_KEY, algorithm='HS256')
|
||||||
|
headers = {
|
||||||
|
'Authorization': 'Token %s' % jwt_token,
|
||||||
|
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
resp = requests.post(notification_server_event_url, json=event_data, headers=headers)
|
||||||
|
if not resp.ok:
|
||||||
|
logger.error(f'Send comment update event failed: {resp.content}')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Send comment update event error. ERROR: {e}')
|
||||||
|
@@ -982,6 +982,7 @@ ENABLE_WHITEBOARD = False
|
|||||||
|
|
||||||
ENABLE_NOTIFICATION_SERVER = os.environ.get('ENABLE_NOTIFICATION_SERVER', 'false') == 'true'
|
ENABLE_NOTIFICATION_SERVER = os.environ.get('ENABLE_NOTIFICATION_SERVER', 'false') == 'true'
|
||||||
NOTIFICATION_SERVER_URL = os.environ.get('NOTIFICATION_SERVER_URL', '')
|
NOTIFICATION_SERVER_URL = os.environ.get('NOTIFICATION_SERVER_URL', '')
|
||||||
|
INNER_NOTIFICATION_SERVER_URL = os.environ.get('INNER_NOTIFICATION_SERVER_URL', 'http://127.0.0.1:8083' )
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# Settings for Seahub Priv #
|
# Settings for Seahub Priv #
|
||||||
|
Reference in New Issue
Block a user