1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-20 02:48:51 +00:00

repo monitor (#5265)

Co-authored-by: lian <lian@seafile.com>
This commit is contained in:
lian
2023-01-16 17:56:05 +08:00
committed by GitHub
parent 1f25cf459c
commit 00b1007cae
11 changed files with 297 additions and 24 deletions

View File

@@ -19,6 +19,7 @@ const MSG_TYPE_FILE_COMMENT = 'file_comment';
const MSG_TYPE_DRAFT_COMMENT = 'draft_comment';
const MSG_TYPE_DRAFT_REVIEWER = 'draft_reviewer';
const MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted';
const MSG_TYPE_REPO_MONITOR = 'repo_monitor';
class NoticeItem extends React.Component {
@@ -70,7 +71,7 @@ class NoticeItem extends React.Component {
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
notice = notice.replace('{/tagA}', '</a>');
@@ -98,13 +99,13 @@ class NoticeItem extends React.Component {
} else {
notice = gettext('{share_from} has shared a folder named {repo_link} to group {group_link}.');
}
// 2. handle xss(cross-site scripting)
notice = notice.replace('{share_from}', shareFrom);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = notice.replace('{group_link}', `{tagB}${groupName}{/tagB}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
notice = notice.replace('{/tagA}', '</a>');
@@ -128,7 +129,7 @@ class NoticeItem extends React.Component {
notice = notice.replace('{user}', repoOwner);
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(repoUrl)}>`);
notice = notice.replace('{/tagA}', '</a>');
@@ -151,7 +152,7 @@ class NoticeItem extends React.Component {
notice = notice.replace('{upload_file_link}', `{tagA}${fileName}{/tagA}`);
notice = notice.replace('{uploaded_link}', `{tagB}${folderName}{/tagB}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(fileLink)}>`);
notice = notice.replace('{/tagA}', '</a>');
@@ -180,12 +181,12 @@ class NoticeItem extends React.Component {
// 1. handle translate
let notice = gettext('File {file_link} has a new comment form user {author}.');
// 2. handle xss(cross-site scripting)
notice = notice.replace('{file_link}', `{tagA}${fileName}{/tagA}`);
notice = notice.replace('{author}', author);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(fileUrl)}>`);
notice = notice.replace('{/tagA}', '</a>');
@@ -224,6 +225,72 @@ class NoticeItem extends React.Component {
return {avatar_url, notice};
}
if (noticeType === MSG_TYPE_REPO_MONITOR) {
let avatar_url = detail.op_user_avatar_url;
let repoLink = siteRoot + 'library/' + detail.repo_id + '/' + detail.repo_name + '/';
let notice = '';
let op = '';
if (detail.obj_type == 'file') {
switch (detail.op_type) {
case 'create':
op = gettext('created file');
break;
case 'delete':
op = gettext('deleted file');
break;
case 'recover':
op = gettext('restored file');
break;
case 'rename':
op = gettext('renamed file');
break;
case 'move':
op = gettext('moved file');
break;
case 'edit':
op = gettext('updated file');
break;
}
} else { // dir
switch (detail.op_type) {
case 'create':
op = gettext('created folder');
break;
case 'delete':
op = gettext('deleted folder');
break;
case 'recover':
op = gettext('restored folder');
break;
case 'rename':
op = gettext('renamed folder');
break;
case 'move':
op = gettext('moved folder');
break;
}
}
// 1. handle translate
notice = gettext('{op_user} {op_type} {obj_name} in {repo_link}.');
let obj_name = Utils.getFileName(detail.obj_path_list[0]);
// 2. handle xss(cross-site scripting)
notice = notice.replace('{op_user}', `${detail.op_user_name}`);
notice = notice.replace('{op_type}', `${op}`);
notice = notice.replace('{obj_name}', `${obj_name}`);
notice = notice.replace('{repo_link}', `{tagA}${detail.repo_name}{/tagA}`);
notice = Utils.HTMLescape(notice);
// 3. add jump link
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(repoLink)}>`);
notice = notice.replace('{/tagA}', '</a>');
return {avatar_url, notice};
}
// if (noticeType === MSG_TYPE_GUEST_INVITATION_ACCEPTED) {
// }

View File

@@ -17,6 +17,7 @@ class Repo {
this.modifier_name = object.modifier_name;
this.type = object.type;
this.starred = object.starred;
this.monitored = object.monitored;
this.status = object.status;
this.storage_name = object.storage_name;
if (object.is_admin != undefined) {

View File

@@ -40,6 +40,7 @@ class MylibRepoListItem extends React.Component {
this.state = {
isOpIconShow: false,
isStarred: this.props.repo.starred,
isMonitored: this.props.repo.monitored,
isRenaming: false,
isShareDialogShow: false,
isDeleteDialogShow: false,
@@ -165,6 +166,32 @@ class MylibRepoListItem extends React.Component {
}
}
onToggleMonitorRepo = (e) => {
e.preventDefault();
const repoName = this.props.repo.repo_name;
if (this.state.isMonitored) {
seafileAPI.unMonitorRepo(this.props.repo.repo_id).then(() => {
this.setState({isMonitored: !this.state.isMonitored});
const msg = gettext('Successfully unmonitored {library_name_placeholder}.')
.replace('{library_name_placeholder}', repoName);
toaster.success(msg);
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
} else {
seafileAPI.monitorRepo(this.props.repo.repo_id).then(() => {
this.setState({isMonitored: !this.state.isMonitored});
const msg = gettext('Successfully monitored {library_name_placeholder}.')
.replace('{library_name_placeholder}', repoName);
toaster.success(msg);
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
}
onShareToggle = (e) => {
// when close share dialog after send share link email,
// there is no event
@@ -297,6 +324,13 @@ class MylibRepoListItem extends React.Component {
<i className={`fa-star ${this.state.isStarred ? 'fas' : 'far star-empty'}`}></i>
</a>
</td>
<td className="text-center">
<a href="#" role="button" aria-label={this.state.isMonitored ? gettext('unMonitor') : gettext('Monitor')} onClick={this.onToggleMonitorRepo}>
<i className={`fa-star ${this.state.isMonitored ? 'fas' : 'far star-empty'}`}></i>
</a>
</td>
<td><img src={iconUrl} title={iconTitle} alt={iconTitle} width="24" /></td>
<td>
{this.state.isRenaming && (

View File

@@ -85,8 +85,9 @@ class MylibRepoListView extends React.Component {
<tr>
<th width="4%"></th>
<th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="4%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width={showStorageBackend ? '33%' : '38%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByName}>{gettext('Name')} {this.props.sortBy === 'name' && sortIcon}</a></th>
<th width="14%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width="10%"><span className="sr-only">{gettext('Actions')}</span></th>
<th width={showStorageBackend ? '15%' : '20%'}><a className="d-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {this.props.sortBy === 'size' && sortIcon}</a></th>
{showStorageBackend ? <th width="15%">{gettext('Storage Backend')}</th> : null}
<th width={showStorageBackend ? '15%' : '20%'}><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {this.props.sortBy === 'time' && sortIcon}</a></th>