mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-06 09:21:54 +00:00
Watch file changes (#5364)
* [my libraries] redesigned 'watch/unwatch file changes' - added 'watch file changes' & 'unwatch file changes' to the operation menu - added icon & tooltip for monitored libraries - added support for mobile * [my libs] updated 'watch/unwatch file changes' * [notifications] update for 'watch/unwatch file changes'
This commit is contained in:
@@ -182,12 +182,12 @@ module.exports = function (webpackEnv) {
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
// module chunks which are built will work in web workers as well.
|
||||
globalObject: 'this',
|
||||
}
|
||||
};
|
||||
if (isEnvDevelopment) {
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
output = Object.assign({}, output, {publicPath: "http://127.0.0.1:3000/assets/bundles/"});
|
||||
output = Object.assign({}, output, {publicPath: 'http://127.0.0.1:3000/assets/bundles/'});
|
||||
}
|
||||
return output;
|
||||
};
|
||||
@@ -524,6 +524,24 @@ module.exports = function (webpackEnv) {
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'svg-sprite-loader', options: {}
|
||||
},
|
||||
{ loader: 'svgo-loader', options: {
|
||||
plugins:[
|
||||
'removeTitle',
|
||||
'removeStyleElement',
|
||||
'cleanupIDs',
|
||||
'inlineStyles',
|
||||
'removeXMLProcInst',
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
@@ -715,9 +733,9 @@ module.exports = function (webpackEnv) {
|
||||
contentBase: '../assets',
|
||||
historyApiFallback: true,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization'
|
||||
}
|
||||
},
|
||||
|
||||
|
1354
frontend/package-lock.json
generated
1354
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,8 @@
|
||||
"reactstrap": "8.9.0",
|
||||
"seafile-js": "0.2.191",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"svgo-loader": "^3.0.1",
|
||||
"unified": "^7.0.0",
|
||||
"url-parse": "^1.4.3",
|
||||
"video.js": "^7.4.1",
|
||||
|
26
frontend/src/assets/icons/monitor.svg
Normal file
26
frontend/src/assets/icons/monitor.svg
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 32 32" style="enable-background:new 0 0 32 32;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#999999;}
|
||||
</style>
|
||||
<title>monitor</title>
|
||||
<g id="monitor">
|
||||
<g id="形状" transform="translate(1.000000, 1.000000)">
|
||||
<path class="st0" fill="currentColor" d="M25.1,20.8c-0.5-0.2-0.8-0.6-0.9-1.1v-4.7c0-4.4-4.2-7.3-6-8.3c-0.1-0.5-0.4-1.4-1-2
|
||||
c-0.6-0.6-1.4-0.9-2.3-0.8h-0.1c-0.8,0-1.6,0.3-2.3,0.8c-0.7,0.6-0.9,1.5-1,2c-1.8,0.9-6,3.8-6,8.3v4.6c0,0.5-0.4,0.9-0.9,1.1
|
||||
c-1.6,0.7-1.8,2.1-1.4,3.1c0.2,0.5,0.6,1,3.6,1h2.5c0.2,2.6,2.5,4.6,5.4,4.6s5.3-2.1,5.4-4.6h2.6c3,0,3.4-0.5,3.6-1
|
||||
C26.9,22.9,26.7,21.5,25.1,20.8L25.1,20.8z M14.9,27.8c-1.9,0-3.4-1.3-3.6-2.9h7.1C18.3,26.5,16.8,27.8,14.9,27.8L14.9,27.8z
|
||||
M22.9,23.1H7c-0.6,0-1.3,0-1.9-0.1c0-0.3,0.1-0.4,0.5-0.6c1.1-0.5,1.9-1.5,1.9-2.7v-4.7c0-3.9,4.5-6.5,5.3-6.9L13.4,8V7.4
|
||||
c0-0.2,0.1-1.7,1.5-1.7H15c1.4,0,1.5,1.5,1.5,1.7V8L17,8.2c0.8,0.3,5.3,2.9,5.3,6.9v4.7c0,1.2,0.8,2.2,1.9,2.7
|
||||
c0.4,0.2,0.5,0.3,0.5,0.6C24.2,23.1,23.5,23.1,22.9,23.1L22.9,23.1z"/>
|
||||
<path class="st0" d="M23.9,0.2c0.4,0.3,0.6,0.9,0.3,1.3l-2.1,3.2c-0.3,0.4-0.9,0.6-1.3,0.3s-0.6-0.9-0.3-1.3l2.1-3.2
|
||||
C22.8,0,23.4-0.1,23.9,0.2z M29.9,7.1c0.2,0.5,0,1.1-0.5,1.3L25.9,10c-0.5,0.2-1,0-1.3-0.5c-0.2-0.5,0-1,0.5-1.3l3.5-1.6
|
||||
C29.1,6.4,29.7,6.6,29.9,7.1L29.9,7.1z M0.1,7.1c0.1-0.2,0.3-0.4,0.5-0.5c0.2-0.1,0.5-0.1,0.7,0l3.5,1.6c0.3,0.1,0.5,0.5,0.5,0.8
|
||||
c0,0.3-0.1,0.7-0.4,0.9c-0.3,0.2-0.6,0.2-1,0.1L0.6,8.4C0.1,8.2-0.1,7.6,0.1,7.1L0.1,7.1z M6.1,0.2C6.3,0,6.6,0,6.8,0
|
||||
s0.5,0.2,0.6,0.4l2.1,3.2c0.2,0.3,0.2,0.7,0.1,1S9.1,5.2,8.8,5.2C8.4,5.2,8.1,5,7.9,4.7L5.8,1.5C5.7,1.3,5.6,1,5.7,0.8
|
||||
C5.8,0.5,5.9,0.3,6.1,0.2L6.1,0.2L6.1,0.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
@@ -165,7 +165,7 @@ class NoticeItem extends React.Component {
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{upload_file_link}', `${fileName}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
notice = notice.replace('{uploaded_link}', `<strong>Deleted Library</strong>`);
|
||||
notice = notice.replace('{uploaded_link}', '<strong>Deleted Library</strong>');
|
||||
}
|
||||
return {avatar_url, notice};
|
||||
}
|
||||
@@ -226,69 +226,85 @@ class NoticeItem extends React.Component {
|
||||
}
|
||||
|
||||
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 = '';
|
||||
const {
|
||||
op_user_avatar_url: avatar_url,
|
||||
op_user_email,
|
||||
op_user_name,
|
||||
op_type,
|
||||
repo_id, repo_name,
|
||||
obj_type,
|
||||
obj_path_list,
|
||||
old_obj_path_list
|
||||
} = detail;
|
||||
|
||||
let op = '';
|
||||
if (detail.obj_type == 'file') {
|
||||
switch (detail.op_type) {
|
||||
const userProfileURL = `${siteRoot}profile/${encodeURIComponent(op_user_email)}`;
|
||||
const userLink = `<a href=${userProfileURL} target="_blank">${Utils.HTMLescape(op_user_name)}</a>`;
|
||||
|
||||
const repoURL = `${siteRoot}library/${repo_id}/${encodeURIComponent(repo_name)}/`;
|
||||
const repoLink = `<a href=${repoURL} target="_blank">${Utils.HTMLescape(repo_name)}</a>`;
|
||||
|
||||
let notice = '';
|
||||
if (obj_type == 'file') {
|
||||
const fileName = Utils.getFileName(obj_path_list[0]);
|
||||
const fileURL = `${siteRoot}lib/${repo_id}/file${Utils.encodePath(obj_path_list[0])}`;
|
||||
const fileLink = `<a href=${fileURL} target="_blank">${Utils.HTMLescape(fileName)}</a>`;
|
||||
switch (op_type) {
|
||||
case 'create':
|
||||
op = gettext('created file');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} created file {fileName} in library {libraryName}.') : gettext('{user} created file {fileName} and {fileCount} other file(s) in library {libraryName}.');
|
||||
break;
|
||||
case 'delete':
|
||||
op = gettext('deleted file');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} deleted file {fileName} in library {libraryName}.') : gettext('{user} deleted file {fileName} and {fileCount} other file(s) in library {libraryName}.');
|
||||
notice = notice.replace('{fileName}', fileName);
|
||||
break;
|
||||
case 'recover':
|
||||
op = gettext('restored file');
|
||||
notice = gettext('{user} restored file {fileName} in library {libraryName}.');
|
||||
break;
|
||||
case 'rename':
|
||||
op = gettext('renamed file');
|
||||
notice = gettext('{user} renamed file {oldFileName} {fileName} in library {libraryName}.');
|
||||
notice = notice.replace('{oldFileName}', Utils.getFileName(old_obj_path_list[0]));
|
||||
break;
|
||||
case 'move':
|
||||
op = gettext('moved file');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} moved file {fileName} in library {libraryName}.') : gettext('{user} moved file {fileName} and {fileCount} other file(s) in library {libraryName}.');
|
||||
break;
|
||||
case 'edit':
|
||||
op = gettext('updated file');
|
||||
notice = gettext('{user} updated file {fileName} in library {libraryName}.');
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
notice = notice.replace('{fileName}', fileLink);
|
||||
notice = notice.replace('{fileCount}', obj_path_list.length - 1);
|
||||
} else { // dir
|
||||
const folderName = Utils.getFolderName(obj_path_list[0]);
|
||||
const folderURL = `${siteRoot}library/${repo_id}/${encodeURIComponent(repo_name)}${Utils.encodePath(obj_path_list[0])}`;
|
||||
const folderLink = `<a href=${folderURL} target="_blank">${Utils.HTMLescape(folderName)}</a>`;
|
||||
switch (detail.op_type) {
|
||||
case 'create':
|
||||
op = gettext('created folder');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} created folder {folderName} in library {libraryName}.') : gettext('{user} created folder {folderName} and {folderCount} other folder(s) in library {libraryName}.');
|
||||
break;
|
||||
case 'delete':
|
||||
op = gettext('deleted folder');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} deleted folder {folderName} in library {libraryName}.') : gettext('{user} deleted folder {folderName} and {folderCount} other folder(s) in library {libraryName}.');
|
||||
notice = notice.replace('{folderName}', folderName);
|
||||
break;
|
||||
case 'recover':
|
||||
op = gettext('restored folder');
|
||||
notice = gettext('{user} restored folder {folderName} in library {libraryName}.');
|
||||
break;
|
||||
case 'rename':
|
||||
op = gettext('renamed folder');
|
||||
notice = gettext('{user} renamed folder {oldFolderName} {folderName} in library {libraryName}.');
|
||||
notice = notice.replace('{oldFolderName}', Utils.getFolderName(old_obj_path_list[0]));
|
||||
break;
|
||||
case 'move':
|
||||
op = gettext('moved folder');
|
||||
notice = obj_path_list.length == 1 ? gettext('{user} moved folder {folderName} in library {libraryName}.') : gettext('{user} moved folder {folderName} and {folderCount} other folder(s) in library {libraryName}.');
|
||||
break;
|
||||
// no default
|
||||
}
|
||||
notice = notice.replace('{folderName}', folderLink);
|
||||
notice = notice.replace('{folderCount}', obj_path_list.length - 1);
|
||||
}
|
||||
|
||||
// 1. handle translate
|
||||
notice = gettext('{op_user} {op_type} {obj_name} in {repo_link}.');
|
||||
notice = notice.replace('{user}', userLink);
|
||||
notice = notice.replace('{libraryName}', repoLink);
|
||||
|
||||
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};
|
||||
return { avatar_url, notice };
|
||||
}
|
||||
|
||||
// if (noticeType === MSG_TYPE_GUEST_INVITATION_ACCEPTED) {
|
||||
|
30
frontend/src/components/icon.js
Normal file
30
frontend/src/components/icon.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import '../css/icon.css';
|
||||
|
||||
const importAll = (requireContext) => {
|
||||
requireContext.keys().forEach(requireContext);
|
||||
};
|
||||
try {
|
||||
importAll(require.context('../assets/icons', true, /\.svg$/));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
const Icon = (props) => {
|
||||
const { className, symbol } = props;
|
||||
const iconClass = `seafile-multicolor-icon seafile-multicolor-icon-${symbol} ${className || ''}`;
|
||||
return (
|
||||
<svg className={iconClass}>
|
||||
<use xlinkHref={`#${symbol}`} />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
Icon.propTypes = {
|
||||
symbol: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Icon;
|
12
frontend/src/css/icon.css
Normal file
12
frontend/src/css/icon.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.seafile-multicolor-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.seafile-multicolor-icon-monitor {
|
||||
font-size: 14px;
|
||||
fill: #444;
|
||||
color: #bdbdbd;
|
||||
}
|
@@ -112,6 +112,16 @@ class MyLibraries extends Component {
|
||||
this.setState({repoList: repoList});
|
||||
}
|
||||
|
||||
onMonitorRepo = (repo, monitored) => {
|
||||
let repoList = this.state.repoList.map(item => {
|
||||
if (item.repo_id === repo.repo_id) {
|
||||
item.monitored = monitored;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setState({repoList: repoList});
|
||||
}
|
||||
|
||||
onDeleteRepo = (repo) => {
|
||||
let repoList = this.state.repoList.filter(item => {
|
||||
return item.repo_id !== repo.repo_id;
|
||||
@@ -168,6 +178,7 @@ class MyLibraries extends Component {
|
||||
onRenameRepo={this.onRenameRepo}
|
||||
onDeleteRepo={this.onDeleteRepo}
|
||||
onTransferRepo={this.onTransferRepo}
|
||||
onMonitorRepo={this.onMonitorRepo}
|
||||
onRepoClick={this.onRepoClick}
|
||||
sortRepoList={this.sortRepoList}
|
||||
/>
|
||||
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import MediaQuery from 'react-responsive';
|
||||
import moment from 'moment';
|
||||
import { Link, navigate } from '@gatsbyjs/reach-router';
|
||||
import { UncontrolledTooltip } from 'reactstrap';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext, siteRoot, storages } from '../../utils/constants';
|
||||
@@ -21,6 +22,7 @@ import MylibRepoMenu from './mylib-repo-menu';
|
||||
import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog';
|
||||
import RepoShareUploadLinksDialog from '../../components/dialog/repo-share-upload-links-dialog';
|
||||
import LibOldFilesAutoDelDialog from '../../components/dialog/lib-old-files-auto-del-dialog';
|
||||
import Icon from '../../components/icon';
|
||||
|
||||
const propTypes = {
|
||||
repo: PropTypes.object.isRequired,
|
||||
@@ -40,7 +42,6 @@ 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,
|
||||
@@ -110,6 +111,12 @@ class MylibRepoListItem extends React.Component {
|
||||
case 'Reset Password':
|
||||
this.onResetPasswordToggle();
|
||||
break;
|
||||
case 'Watch File Changes':
|
||||
this.watchFileChanges();
|
||||
break;
|
||||
case 'Unwatch File Changes':
|
||||
this.unwatchFileChanges();
|
||||
break;
|
||||
case 'Folder Permission':
|
||||
this.onFolderPermissionToggle();
|
||||
break;
|
||||
@@ -166,30 +173,24 @@ 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);
|
||||
watchFileChanges = () => {
|
||||
const { repo } = this.props;
|
||||
seafileAPI.monitorRepo(repo.repo_id).then(() => {
|
||||
this.props.onMonitorRepo(repo, true);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
unwatchFileChanges = () => {
|
||||
const { repo } = this.props;
|
||||
seafileAPI.unMonitorRepo(repo.repo_id).then(() => {
|
||||
this.props.onMonitorRepo(repo, false);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onShareToggle = (e) => {
|
||||
@@ -324,13 +325,6 @@ 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 && (
|
||||
@@ -341,7 +335,22 @@ class MylibRepoListItem extends React.Component {
|
||||
/>
|
||||
)}
|
||||
{!this.state.isRenaming && repo.repo_name && (
|
||||
<Fragment>
|
||||
<Link to={repoURL}>{repo.repo_name}</Link>
|
||||
{repo.monitored && (
|
||||
<Fragment>
|
||||
<span id={`watching-${repo.repo_id}`} className="ml-1">
|
||||
<Icon symbol='monitor' />
|
||||
</span>
|
||||
<UncontrolledTooltip
|
||||
placement="bottom"
|
||||
target={`#watching-${repo.repo_id}`}
|
||||
>
|
||||
{gettext('You are watching file changes of this library.')}
|
||||
</UncontrolledTooltip>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
{!this.state.isRenaming && !repo.repo_name &&
|
||||
(gettext('Broken (please contact your administrator to fix this library)'))
|
||||
@@ -387,7 +396,22 @@ class MylibRepoListItem extends React.Component {
|
||||
/>
|
||||
)}
|
||||
{!this.state.isRenaming && repo.repo_name && (
|
||||
<div><Link to={repoURL}>{repo.repo_name}</Link></div>
|
||||
<div>
|
||||
<Link to={repoURL}>{repo.repo_name}</Link>
|
||||
{repo.monitored && (
|
||||
<Fragment>
|
||||
<span id={`watching-${repo.repo_id}`} className="ml-1">
|
||||
<Icon symbol='monitor' />
|
||||
</span>
|
||||
<UncontrolledTooltip
|
||||
placement="bottom"
|
||||
target={`#watching-${repo.repo_id}`}
|
||||
>
|
||||
{gettext('You are watching file changes of this library.')}
|
||||
</UncontrolledTooltip>
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!this.state.isRenaming && !repo.repo_name &&
|
||||
<div>(gettext('Broken (please contact your administrator to fix this library)'))</div>
|
||||
|
@@ -68,6 +68,7 @@ class MylibRepoListView extends React.Component {
|
||||
onRenameRepo={this.props.onRenameRepo}
|
||||
onDeleteRepo={this.props.onDeleteRepo}
|
||||
onTransferRepo={this.props.onTransferRepo}
|
||||
onMonitorRepo={this.props.onMonitorRepo}
|
||||
onRepoClick={this.props.onRepoClick}
|
||||
/>
|
||||
);
|
||||
@@ -85,9 +86,8 @@ 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="10%"><span className="sr-only">{gettext('Actions')}</span></th>
|
||||
<th width="14%"><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>
|
||||
|
@@ -70,12 +70,19 @@ class MylibRepoMenu extends React.Component {
|
||||
operations.push('Folder Permission');
|
||||
}
|
||||
operations.push('Share Links Admin', 'Divider');
|
||||
|
||||
if (repo.encrypted) {
|
||||
operations.push('Change Password');
|
||||
}
|
||||
if (showResetPasswordMenuItem) {
|
||||
operations.push('Reset Password');
|
||||
}
|
||||
|
||||
if (isPro) {
|
||||
const monitorOp = repo.monitored ? 'Unwatch File Changes' : 'Watch File Changes';
|
||||
operations.push(monitorOp);
|
||||
}
|
||||
|
||||
operations.push('History Setting', 'API Token');
|
||||
if (this.props.isPC && enableRepoSnapshotLabel) {
|
||||
operations.push('Label Current State');
|
||||
@@ -116,6 +123,12 @@ class MylibRepoMenu extends React.Component {
|
||||
case 'Reset Password':
|
||||
translateResult = gettext('Reset Password');
|
||||
break;
|
||||
case 'Watch File Changes':
|
||||
translateResult = gettext('Watch File Changes');
|
||||
break;
|
||||
case 'Unwatch File Changes':
|
||||
translateResult = gettext('Unwatch File Changes');
|
||||
break;
|
||||
case 'Folder Permission':
|
||||
translateResult = gettext('Folder Permission');
|
||||
break;
|
||||
|
Reference in New Issue
Block a user