diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 0f80be5ae3..c1355e767f 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10793,9 +10793,9 @@
}
},
"seafile-js": {
- "version": "0.2.45",
- "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.45.tgz",
- "integrity": "sha512-qkKuqUMRKJxyXYYFI2Q4PPCbbxuA7wc6RWs4WTF67cHvh1VwAMNlE4xsRZhKQP0ZdUS+cHf2W8enZvb+dvucJQ==",
+ "version": "0.2.46",
+ "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.46.tgz",
+ "integrity": "sha512-dD2Vd4o6z40bjLy6RsUKZMiWPzlruc4pApVsGzMGorKxrRVbNt+YSTqOE+7Q4wb6WaiiHj8PdY7hTMcP5UdmQA==",
"requires": {
"axios": "^0.18.0",
"form-data": "^2.3.2",
diff --git a/frontend/package.json b/frontend/package.json
index 2d6d50c10e..2b7f6c30ba 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -30,7 +30,7 @@
"react-moment": "^0.7.9",
"react-select": "^2.1.1",
"reactstrap": "^6.4.0",
- "seafile-js": "^0.2.45",
+ "seafile-js": "^0.2.46",
"seafile-ui": "^0.1.10",
"sw-precache-webpack-plugin": "0.11.4",
"unified": "^7.0.0",
diff --git a/frontend/src/app.js b/frontend/src/app.js
index 8861d4a008..7439201870 100644
--- a/frontend/src/app.js
+++ b/frontend/src/app.js
@@ -107,7 +107,7 @@ class App extends Component {
let newWindow = window.open('markdown-editor');
newWindow.location.href = url;
} else {
- let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + selectedItem.path;
+ let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path);
let newWindow = window.open('about:blank');
newWindow.location.href = url;
}
diff --git a/frontend/src/components/dialog/create-department-repo-dialog.js b/frontend/src/components/dialog/create-department-repo-dialog.js
index b320cdae46..c5a31687bb 100644
--- a/frontend/src/components/dialog/create-department-repo-dialog.js
+++ b/frontend/src/components/dialog/create-department-repo-dialog.js
@@ -28,9 +28,8 @@ class CreateDepartmentRepoDialog extends React.Component {
let isValid = this.validateRepoName();
if (isValid) {
let repo = this.createRepo(this.state.repoName);
- this.props.onCreateRepo(repo);
+ this.props.onCreateRepo(repo, 'department');
}
-
}
handleKeyPress = (e) => {
diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js
index 30338de2da..20388da451 100644
--- a/frontend/src/components/dialog/generate-share-link.js
+++ b/frontend/src/components/dialog/generate-share-link.js
@@ -14,9 +14,10 @@ class GenerateShareLink extends React.Component {
constructor(props) {
super(props);
this.state = {
- passwordVisible: false,
- showPasswordInput: false,
isValidate: false,
+ isShowPasswordInput: false,
+ isPasswordVisible: false,
+ isExpireChecked: false,
password: '',
passwdnew: '',
expireDays: '',
@@ -28,6 +29,7 @@ class GenerateShareLink extends React.Component {
'can_edit': false,
'can_download': true
};
+ this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0);
}
componentDidMount() {
@@ -47,9 +49,9 @@ class GenerateShareLink extends React.Component {
});
}
- addPassword = () => {
+ onPasswordInputChecked = () => {
this.setState({
- showPasswordInput: !this.state.showPasswordInput,
+ isShowPasswordInput: !this.state.isShowPasswordInput,
password: '',
passwdnew: '',
errorInfo: ''
@@ -58,7 +60,7 @@ class GenerateShareLink extends React.Component {
togglePasswordVisible = () => {
this.setState({
- passwordVisible: !this.state.passwordVisible
+ isPasswordVisible: !this.state.isPasswordVisible
});
}
@@ -66,20 +68,18 @@ class GenerateShareLink extends React.Component {
let val = Math.random().toString(36).substr(5);
this.setState({
password: val,
- passwordnew: val
+ passwdnew: val
});
}
inputPassword = (e) => {
- this.setState({
- password: e.target.value
- });
+ let passwd = e.target.value.trim();
+ this.setState({password: passwd});
}
inputPasswordNew = (e) => {
- this.setState({
- passwordnew: e.target.value
- });
+ let passwd = e.target.value.trim();
+ this.setState({passwdnew: passwd});
}
setPermission = (permission) => {
@@ -97,35 +97,14 @@ class GenerateShareLink extends React.Component {
}
generateShareLink = () => {
- let path = this.props.itemPath;
- let repoID = this.props.repoID;
- if (this.state.showPasswordInput && (this.state.password == '')) {
- this.setState({
- errorInfo: gettext('Please enter password')
- });
- }
- else if (this.state.showPasswordInput && (this.state.showPasswordInput && this.state.password.length < 8)) {
- this.setState({
- errorInfo: gettext('Password is too short')
- });
- }
- else if (this.state.showPasswordInput && (this.state.password !== this.state.passwordnew)) {
- this.setState({
- errorInfo: gettext('Passwords don\'t match')
- });
- }
- else if (this.state.expireDays === '') {
- this.setState({
- errorInfo: gettext('Please enter days')
- });
- } else if (!this.state.isValidate) {
- // errMessage had been setted
- return;
- } else {
+ let isValid = this.validateParamsInput();
+ if (isValid) {
+ this.setState({errorInfo: ''});
+ let { itemPath, repoID } = this.props;
let { password, expireDays } = this.state;
let permissions = this.permissions;
permissions = JSON.stringify(permissions);
- seafileAPI.createShareLink(repoID, path, password, expireDays, permissions).then((res) => {
+ seafileAPI.createShareLink(repoID, itemPath, password, expireDays, permissions).then((res) => {
this.setState({
link: res.data.link,
token: res.data.token
@@ -139,49 +118,102 @@ class GenerateShareLink extends React.Component {
this.setState({
link: '',
token: '',
- showPasswordInput: false,
password: '',
passwordnew: '',
+ isShowPasswordInput: false,
+ expireDays: '',
+ isExpireChecked: false,
+ errorInfo: '',
});
this.permissions = {
'can_edit': false,
'can_download': true
};
});
- }
+ }
- onExpireHandler = (e) => {
- let day = e.target.value;
+ onExpireChecked = (e) => {
+ this.setState({isExpireChecked: e.target.checked});
+ }
+
+ onExpireDaysChanged = (e) => {
+ let day = e.target.value.trim();
+ this.setState({expireDays: day});
+ }
+
+ validateParamsInput = () => {
+ let { isShowPasswordInput , password, passwdnew, isExpireChecked, expireDays } = this.state;
+ // validate password
+ if (isShowPasswordInput) {
+ if (password.length === 0) {
+ this.setState({errorInfo: 'Please enter password'});
+ return false;
+ }
+ if (password.length < 8) {
+ this.setState({errorInfo: 'Password is too short'});
+ return false;
+ }
+ if (password !== passwdnew) {
+ this.setState({errorInfo: 'Passwords don\'t match'});
+ return false;
+ }
+ }
+
+ // validate days
+ // no limit
let reg = /^\d+$/;
- let flag = reg.test(day);
- if (!flag) {
- this.setState({
- isValidate: false,
- errorInfo: gettext('Please enter a non-negative integer'),
- expireDays: day,
- });
- return;
- }
-
- day = parseInt(day);
+ if (this.isExpireDaysNoLimit) {
+ if (isExpireChecked) {
+ if (!expireDays) {
+ this.setState({errorInfo: 'Please enter days'});
+ return false;
+ }
+ let flag = reg.test(expireDays);
+ if (!flag) {
+ this.setState({errorInfo: 'Please enter a non-negative integer'});
+ return false;
+ }
+ this.setState({expireDays: parseInt(expireDays)});
+ }
+ } else {
+ if (!expireDays) {
+ this.setState({errorInfo: 'Please enter days'});
+ return false;
+ }
+ let flag = reg.test(expireDays);
+ if (!flag) {
+ this.setState({errorInfo: 'Please enter a non-negative integer'});
+ return false;
+ }
- if (day < shareLinkExpireDaysMin || day > shareLinkExpireDaysMax) {
- let errorMessage = gettext('Please enter a value between day1 and day2');
- errorMessage = errorMessage.replace('day1', shareLinkExpireDaysMin);
- errorMessage = errorMessage.replace('day2', shareLinkExpireDaysMax);
- this.setState({
- isValidate: false,
- errorInfo: errorMessage,
- expireDays: day
- });
- return;
+ expireDays = parseInt(expireDays);
+ let minDays = parseInt(shareLinkExpireDaysMin);
+ let maxDays = parseInt(shareLinkExpireDaysMax);
+
+ if (minDays !== 0 && maxDays !== maxDays) {
+ if (expireDays < minDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+
+ if (minDays === 0 && maxDays !== 0 ) {
+ if (expireDays > maxDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+
+ if (minDays !== 0 && maxDays !== 0) {
+ if (expireDays < minDays || expireDays < maxDays) {
+ this.setState({errorInfo: 'Please enter valid days'});
+ return false;
+ }
+ }
+ this.setState({expireDays: expireDays});
}
- this.setState({
- isValidate: true,
- errorInfo: '',
- expireDays: day
- });
+ return true;
}
render() {
@@ -197,35 +229,48 @@ class GenerateShareLink extends React.Component {
);
diff --git a/frontend/src/components/dialog/share-dialog.js b/frontend/src/components/dialog/share-dialog.js
index d02768f826..c8fe662241 100644
--- a/frontend/src/components/dialog/share-dialog.js
+++ b/frontend/src/components/dialog/share-dialog.js
@@ -9,10 +9,11 @@ import GenerateUploadLink from './generate-upload-link';
import '../../css/share-link-dialog.css';
const propTypes = {
- itemPath: PropTypes.string.isRequired,
+ isGroupOwnedRepo: PropTypes.bool,
+ itemType: PropTypes.string.isRequired, // there will be three choose: ['library', 'dir', 'file']
itemName: PropTypes.string.isRequired,
+ itemPath: PropTypes.string.isRequired,
toggleDialog: PropTypes.func.isRequired,
- isDir: PropTypes.bool.isRequired,
repoID: PropTypes.string.isRequired
};
@@ -67,10 +68,10 @@ class ShareDialog extends React.Component {
-
+
-
+
@@ -79,14 +80,13 @@ class ShareDialog extends React.Component {
}
renderFileContent = () => {
- let activeTab = this.state.activeTab;
return (
diff --git a/frontend/src/pages/groups/groups-view.js b/frontend/src/pages/groups/groups-view.js
index f828168f6f..b5f1eb4706 100644
--- a/frontend/src/pages/groups/groups-view.js
+++ b/frontend/src/pages/groups/groups-view.js
@@ -44,6 +44,18 @@ class RepoListViewPanel extends React.Component {
});
}
+ onItemDelete = (repo) => {
+ let group = this.props.group;
+ seafileAPI.deleteGroupOwnedLibrary(group.id, repo.repo_id).then(() => {
+ let repoList = this.state.repoList.filter(item => {
+ return item.repo_id !== repo.repo_id;
+ });
+ this.setState({repoList: repoList});
+ }).catch(() => {
+ // todo;
+ });
+ }
+
render() {
let group = this.props.group;
const emptyTip = {gettext('No libraries')}
;
@@ -60,6 +72,7 @@ class RepoListViewPanel extends React.Component {
currentGroup={this.props.group}
repoList={this.state.repoList}
onItemUnshare={this.onItemUnshare}
+ onItemDelete={this.onItemDelete}
/>
}
@@ -178,6 +191,5 @@ const GroupsViewPropTypes = {
};
GroupsView.propTypes = GroupsViewPropTypes;
-// Groups.propTypes = propTypes;
export default GroupsView;
diff --git a/frontend/src/pages/my-libs/item.js b/frontend/src/pages/my-libs/item.js
index 0d9e2b9a76..f9ced61e6b 100644
--- a/frontend/src/pages/my-libs/item.js
+++ b/frontend/src/pages/my-libs/item.js
@@ -1,5 +1,5 @@
-import React, { Component } from 'react';
+import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Link } from '@reach/router';
@@ -8,6 +8,8 @@ import { gettext, siteRoot, storages, canGenerateShareLink, canGenerateUploadLin
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import RenameInput from '../../components/rename-input';
+import ModalPotal from '../../components/modal-portal';
+import ShareDialog from '../../components/dialog/share-dialog';
const propTypes = {
data: PropTypes.object.isRequired,
@@ -29,6 +31,7 @@ class Item extends Component {
showOpIcon: false,
operationMenuOpen: false,
showChangeLibName: false,
+ isShowSharedDialog: false,
highlight: false,
};
}
@@ -73,7 +76,11 @@ class Item extends Component {
share = (e) => {
e.preventDefault();
- // TODO
+ this.setState({isShowSharedDialog: true});
+ }
+
+ toggleShareDialog = () => {
+ this.setState({isShowSharedDialog: false});
}
showDeleteItemPopup = (e) => {
@@ -232,43 +239,69 @@ class Item extends Component {
);
const desktopItem = (
-
-  |
-
- {this.state.showChangeLibName && (
-
+
+  |
+
+ {this.state.showChangeLibName && (
+
+ )}
+ {!this.state.showChangeLibName && data.repo_name && (
+ {data.repo_name}
+ )}
+ {!this.state.showChangeLibName && !data.repo_name &&
+ (gettext('Broken (please contact your administrator to fix this library)'))
+ }
+ |
+ {data.repo_name ? desktopOperations : ''} |
+ {Utils.formatSize({bytes: data.size})} |
+ {storages.length ? {data.storage_name} | : null}
+ {moment(data.last_modified).fromNow()} |
+
+ {this.state.isShowSharedDialog && (
+
+
- )}
- {!this.state.showChangeLibName && data.repo_name && (
- {data.repo_name}
- )}
- {!this.state.showChangeLibName && !data.repo_name &&
- (gettext('Broken (please contact your administrator to fix this library)'))
- }
- |
- {data.repo_name ? desktopOperations : ''} |
- {Utils.formatSize({bytes: data.size})} |
- {storages.length ? {data.storage_name} | : null}
- {moment(data.last_modified).fromNow()} |
-
+
+ )}
+
);
const mobileItem = (
-
-  |
-
- {data.repo_name ?
- {data.repo_name} :
- gettext('Broken (please contact your administrator to fix this library)')}
-
- {Utils.formatSize({bytes: data.size})}
- {moment(data.last_modified).fromNow()}
- |
- {data.repo_name ? mobileOperations : ''} |
-
+
+
+  |
+
+ {data.repo_name ?
+ {data.repo_name} :
+ gettext('Broken (please contact your administrator to fix this library)')}
+
+ {Utils.formatSize({bytes: data.size})}
+ {moment(data.last_modified).fromNow()}
+ |
+ {data.repo_name ? mobileOperations : ''} |
+
+ {this.state.isShowSharedDialog && (
+
+
+
+ )}
+
);
return window.innerWidth >= 768 ? desktopItem : mobileItem;
diff --git a/frontend/src/pages/my-libs/my-libs.js b/frontend/src/pages/my-libs/my-libs.js
index 9cbb7863a2..d66b043cac 100644
--- a/frontend/src/pages/my-libs/my-libs.js
+++ b/frontend/src/pages/my-libs/my-libs.js
@@ -48,8 +48,17 @@ class MyLibraries extends Component {
}
onCreateRepo = (repo) => {
+ let permission = repo.permission;
seafileAPI.createMineRepo(repo).then((res) => {
- let repo = res.data;
+ let repo = {
+ repo_id: res.data.repo_id,
+ repo_name: res.data.repo_name,
+ size: res.data.repo_size,
+ mtime: res.data.mtime,
+ owner_email: res.data.email,
+ encrypted: res.data.encrypted,
+ permission: permission,
+ };
this.state.items.push(repo);
this.setState({items: this.state.items});
});
diff --git a/frontend/src/repo-wiki-mode.js b/frontend/src/repo-wiki-mode.js
index a2f509dd53..e6e028260b 100644
--- a/frontend/src/repo-wiki-mode.js
+++ b/frontend/src/repo-wiki-mode.js
@@ -334,7 +334,7 @@ class Wiki extends Component {
this.showFile(node.path);
}
} else {
- let url = siteRoot + 'lib/' + item.repo_id + '/file' + item.path;
+ let url = siteRoot + 'lib/' + item.repo_id + '/file' + Utils.encodePath(item.path);
let newWindow = window.open('about:blank');
newWindow.location.href = url;
}
@@ -367,7 +367,7 @@ class Wiki extends Component {
this.showDir(node.path);
} else {
const w=window.open('about:blank');
- const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
+ const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path);
w.location.href = url;
}
}
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index 3beb944c52..39e68cecb3 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -147,7 +147,9 @@ export const Utils = {
return encodeURIComponent(e);
}).join('/');
*/
-
+ if (!path) {
+ return '';
+ }
var path_arr = path.split('/');
var path_arr_ = [];
for (var i = 0, len = path_arr.length; i < len; i++) {
diff --git a/frontend/src/wiki.js b/frontend/src/wiki.js
index a29c208c82..4773cfc3d0 100644
--- a/frontend/src/wiki.js
+++ b/frontend/src/wiki.js
@@ -133,7 +133,7 @@ class Wiki extends Component {
this.enterViewFileState(tree, node, node.path);
}
} else {
- let url = siteRoot + 'lib/' + item.repo_id + '/file' + item.path;
+ let url = siteRoot + 'lib/' + item.repo_id + '/file' + Utils.encodePath(item.path);
let newWindow = window.open('about:blank');
newWindow.location.href = url;
}
@@ -161,7 +161,7 @@ class Wiki extends Component {
this.exitViewFileState(tree, node);
} else {
const w=window.open('about:blank');
- const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
+ const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path);
w.location.href = url;
}
}
diff --git a/seahub/urls.py b/seahub/urls.py
index 66824debbc..8a4ef1739a 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -204,7 +204,7 @@ urlpatterns = [
url(r'^shared-libs/$', react_fake_view, name="shared_libs"),
url(r'^my-libs/$', react_fake_view, name="my_libs"),
url(r'^groups/$', react_fake_view, name="groups"),
- url(r'^group/(?P\d+)/$', react_group, name="group"),
+ url(r'^group/.*$', react_fake_view, name="group"),
url(r'^library/.*$', react_fake_view, name="lib_view"),
url(r'^my-libs/deleted/$', react_fake_view, name="my_libs_deleted"),