mirror of
https://github.com/haiwen/seahub.git
synced 2025-07-16 16:21:48 +00:00
commit
bd13a10a50
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
@ -10793,9 +10793,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"seafile-js": {
|
"seafile-js": {
|
||||||
"version": "0.2.45",
|
"version": "0.2.46",
|
||||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.45.tgz",
|
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.46.tgz",
|
||||||
"integrity": "sha512-qkKuqUMRKJxyXYYFI2Q4PPCbbxuA7wc6RWs4WTF67cHvh1VwAMNlE4xsRZhKQP0ZdUS+cHf2W8enZvb+dvucJQ==",
|
"integrity": "sha512-dD2Vd4o6z40bjLy6RsUKZMiWPzlruc4pApVsGzMGorKxrRVbNt+YSTqOE+7Q4wb6WaiiHj8PdY7hTMcP5UdmQA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"form-data": "^2.3.2",
|
"form-data": "^2.3.2",
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"react-moment": "^0.7.9",
|
"react-moment": "^0.7.9",
|
||||||
"react-select": "^2.1.1",
|
"react-select": "^2.1.1",
|
||||||
"reactstrap": "^6.4.0",
|
"reactstrap": "^6.4.0",
|
||||||
"seafile-js": "^0.2.45",
|
"seafile-js": "^0.2.46",
|
||||||
"seafile-ui": "^0.1.10",
|
"seafile-ui": "^0.1.10",
|
||||||
"sw-precache-webpack-plugin": "0.11.4",
|
"sw-precache-webpack-plugin": "0.11.4",
|
||||||
"unified": "^7.0.0",
|
"unified": "^7.0.0",
|
||||||
|
@ -107,7 +107,7 @@ class App extends Component {
|
|||||||
let newWindow = window.open('markdown-editor');
|
let newWindow = window.open('markdown-editor');
|
||||||
newWindow.location.href = url;
|
newWindow.location.href = url;
|
||||||
} else {
|
} 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');
|
let newWindow = window.open('about:blank');
|
||||||
newWindow.location.href = url;
|
newWindow.location.href = url;
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,8 @@ class CreateDepartmentRepoDialog extends React.Component {
|
|||||||
let isValid = this.validateRepoName();
|
let isValid = this.validateRepoName();
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
let repo = this.createRepo(this.state.repoName);
|
let repo = this.createRepo(this.state.repoName);
|
||||||
this.props.onCreateRepo(repo);
|
this.props.onCreateRepo(repo, 'department');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
handleKeyPress = (e) => {
|
||||||
|
@ -14,9 +14,10 @@ class GenerateShareLink extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
passwordVisible: false,
|
|
||||||
showPasswordInput: false,
|
|
||||||
isValidate: false,
|
isValidate: false,
|
||||||
|
isShowPasswordInput: false,
|
||||||
|
isPasswordVisible: false,
|
||||||
|
isExpireChecked: false,
|
||||||
password: '',
|
password: '',
|
||||||
passwdnew: '',
|
passwdnew: '',
|
||||||
expireDays: '',
|
expireDays: '',
|
||||||
@ -28,6 +29,7 @@ class GenerateShareLink extends React.Component {
|
|||||||
'can_edit': false,
|
'can_edit': false,
|
||||||
'can_download': true
|
'can_download': true
|
||||||
};
|
};
|
||||||
|
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -47,9 +49,9 @@ class GenerateShareLink extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addPassword = () => {
|
onPasswordInputChecked = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showPasswordInput: !this.state.showPasswordInput,
|
isShowPasswordInput: !this.state.isShowPasswordInput,
|
||||||
password: '',
|
password: '',
|
||||||
passwdnew: '',
|
passwdnew: '',
|
||||||
errorInfo: ''
|
errorInfo: ''
|
||||||
@ -58,7 +60,7 @@ class GenerateShareLink extends React.Component {
|
|||||||
|
|
||||||
togglePasswordVisible = () => {
|
togglePasswordVisible = () => {
|
||||||
this.setState({
|
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);
|
let val = Math.random().toString(36).substr(5);
|
||||||
this.setState({
|
this.setState({
|
||||||
password: val,
|
password: val,
|
||||||
passwordnew: val
|
passwdnew: val
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPassword = (e) => {
|
inputPassword = (e) => {
|
||||||
this.setState({
|
let passwd = e.target.value.trim();
|
||||||
password: e.target.value
|
this.setState({password: passwd});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPasswordNew = (e) => {
|
inputPasswordNew = (e) => {
|
||||||
this.setState({
|
let passwd = e.target.value.trim();
|
||||||
passwordnew: e.target.value
|
this.setState({passwdnew: passwd});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setPermission = (permission) => {
|
setPermission = (permission) => {
|
||||||
@ -97,35 +97,14 @@ class GenerateShareLink extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generateShareLink = () => {
|
generateShareLink = () => {
|
||||||
let path = this.props.itemPath;
|
let isValid = this.validateParamsInput();
|
||||||
let repoID = this.props.repoID;
|
if (isValid) {
|
||||||
if (this.state.showPasswordInput && (this.state.password == '')) {
|
this.setState({errorInfo: ''});
|
||||||
this.setState({
|
let { itemPath, repoID } = this.props;
|
||||||
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 { password, expireDays } = this.state;
|
let { password, expireDays } = this.state;
|
||||||
let permissions = this.permissions;
|
let permissions = this.permissions;
|
||||||
permissions = JSON.stringify(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({
|
this.setState({
|
||||||
link: res.data.link,
|
link: res.data.link,
|
||||||
token: res.data.token
|
token: res.data.token
|
||||||
@ -139,49 +118,102 @@ class GenerateShareLink extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
link: '',
|
link: '',
|
||||||
token: '',
|
token: '',
|
||||||
showPasswordInput: false,
|
|
||||||
password: '',
|
password: '',
|
||||||
passwordnew: '',
|
passwordnew: '',
|
||||||
|
isShowPasswordInput: false,
|
||||||
|
expireDays: '',
|
||||||
|
isExpireChecked: false,
|
||||||
|
errorInfo: '',
|
||||||
});
|
});
|
||||||
this.permissions = {
|
this.permissions = {
|
||||||
'can_edit': false,
|
'can_edit': false,
|
||||||
'can_download': true
|
'can_download': true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onExpireHandler = (e) => {
|
onExpireChecked = (e) => {
|
||||||
let day = e.target.value;
|
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 reg = /^\d+$/;
|
||||||
let flag = reg.test(day);
|
if (this.isExpireDaysNoLimit) {
|
||||||
if (!flag) {
|
if (isExpireChecked) {
|
||||||
this.setState({
|
if (!expireDays) {
|
||||||
isValidate: false,
|
this.setState({errorInfo: 'Please enter days'});
|
||||||
errorInfo: gettext('Please enter a non-negative integer'),
|
return false;
|
||||||
expireDays: day,
|
}
|
||||||
});
|
let flag = reg.test(expireDays);
|
||||||
return;
|
if (!flag) {
|
||||||
}
|
this.setState({errorInfo: 'Please enter a non-negative integer'});
|
||||||
|
return false;
|
||||||
day = parseInt(day);
|
}
|
||||||
|
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) {
|
expireDays = parseInt(expireDays);
|
||||||
let errorMessage = gettext('Please enter a value between day1 and day2');
|
let minDays = parseInt(shareLinkExpireDaysMin);
|
||||||
errorMessage = errorMessage.replace('day1', shareLinkExpireDaysMin);
|
let maxDays = parseInt(shareLinkExpireDaysMax);
|
||||||
errorMessage = errorMessage.replace('day2', shareLinkExpireDaysMax);
|
|
||||||
this.setState({
|
if (minDays !== 0 && maxDays !== maxDays) {
|
||||||
isValidate: false,
|
if (expireDays < minDays) {
|
||||||
errorInfo: errorMessage,
|
this.setState({errorInfo: 'Please enter valid days'});
|
||||||
expireDays: day
|
return false;
|
||||||
});
|
}
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
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({
|
return true;
|
||||||
isValidate: true,
|
|
||||||
errorInfo: '',
|
|
||||||
expireDays: day
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -197,35 +229,48 @@ class GenerateShareLink extends React.Component {
|
|||||||
<Form className="generate-share-link">
|
<Form className="generate-share-link">
|
||||||
<FormGroup check>
|
<FormGroup check>
|
||||||
<Label check>
|
<Label check>
|
||||||
<Input type="checkbox" onChange={this.addPassword}/>{' '}{gettext('Add password protection')}
|
<Input type="checkbox" onChange={this.onPasswordInputChecked}/>{' '}{gettext('Add password protection')}
|
||||||
</Label>
|
</Label>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{this.state.showPasswordInput &&
|
{this.state.isShowPasswordInput &&
|
||||||
<FormGroup className="link-operation-content">
|
<FormGroup className="link-operation-content">
|
||||||
<Label>{gettext('Password')}</Label><span className="tip"> ({gettext('at least 8 characters')}) </span>
|
<Label>{gettext('Password')}</Label><span className="tip"> ({gettext('at least 8 characters')}) </span>
|
||||||
<InputGroup className="passwd">
|
<InputGroup className="passwd">
|
||||||
<Input type={this.state.passwordVisible ? 'text' : 'password'} value={this.state.password || ''} onChange={this.inputPassword}/>
|
<Input type={this.state.isPasswordVisible ? 'text' : 'password'} value={this.state.password || ''} onChange={this.inputPassword}/>
|
||||||
<InputGroupAddon addonType="append">
|
<InputGroupAddon addonType="append">
|
||||||
<Button onClick={this.togglePasswordVisible}><i className={`link-operation-icon fas ${this.state.passwordVisible ? 'fa-eye': 'fa-eye-slash'}`}></i></Button>
|
<Button onClick={this.togglePasswordVisible}><i className={`link-operation-icon fas ${this.state.isPasswordVisible ? 'fa-eye': 'fa-eye-slash'}`}></i></Button>
|
||||||
<Button onClick={this.generatePassword}><i className="link-operation-icon fas fa-magic"></i></Button>
|
<Button onClick={this.generatePassword}><i className="link-operation-icon fas fa-magic"></i></Button>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<Label>{gettext('Password again')}</Label>
|
<Label>{gettext('Password again')}</Label>
|
||||||
<Input className="passwd" type={this.state.passwordVisible ? 'text' : 'password'} value={this.state.passwordnew || ''} onChange={this.inputPasswordNew} />
|
<Input className="passwd" type={this.state.isPasswordVisible ? 'text' : 'password'} value={this.state.passwdnew || ''} onChange={this.inputPasswordNew} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
}
|
}
|
||||||
<FormGroup check>
|
{this.isExpireDaysNoLimit && (
|
||||||
<Label check>
|
<FormGroup check>
|
||||||
<Input className="expire-checkbox" type="checkbox" checked readOnly/>{' '}{gettext('Add auto expiration')}
|
<Label check>
|
||||||
<Input className="expire-input" type="text" value={this.state.expireDays} onChange={this.onExpireHandler}/> <span>{gettext('days')}</span>
|
<Input className="expire-checkbox" type="checkbox" onChange={this.onExpireChecked}/>{' '}{gettext('Add auto expiration')}
|
||||||
{parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && (
|
<Input className="expire-input" type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} readOnly={!this.state.isExpireChecked}/><span>{gettext('days')}</span>
|
||||||
<span> ({gettext('no limit')})</span>
|
</Label>
|
||||||
)}
|
</FormGroup>
|
||||||
{parseInt(shareLinkExpireDaysMax) !== 0 && (
|
)}
|
||||||
<span> ({shareLinkExpireDaysMin} - {shareLinkExpireDaysMax}{gettext('days')})</span>
|
{!this.isExpireDaysNoLimit && (
|
||||||
)}
|
<FormGroup check>
|
||||||
</Label>
|
<Label check>
|
||||||
</FormGroup>
|
<Input className="expire-checkbox" type="checkbox" onChange={this.onExpireChecked} checked readOnly/>{' '}{gettext('Add auto expiration')}
|
||||||
|
<Input className="expire-input" type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} /> <span>{gettext('days')}</span>
|
||||||
|
{(parseInt(shareLinkExpireDaysMin) !== 0 && parseInt(shareLinkExpireDaysMax) !== 0) && (
|
||||||
|
<span> ({shareLinkExpireDaysMin} - {shareLinkExpireDaysMax}{' '}{gettext('days')})</span>
|
||||||
|
)}
|
||||||
|
{(parseInt(shareLinkExpireDaysMin) !== 0 && parseInt(shareLinkExpireDaysMax) === 0) && (
|
||||||
|
<span> ({gettext('Greater than or equal to')} {shareLinkExpireDaysMin}{' '}{gettext('days')})</span>
|
||||||
|
)}
|
||||||
|
{(parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) !== 0) && (
|
||||||
|
<span> ({gettext('Less than or equal to')} {shareLinkExpireDaysMax}{' '}{gettext('days')})</span>
|
||||||
|
)}
|
||||||
|
</Label>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
<FormGroup check>
|
<FormGroup check>
|
||||||
<Label check>
|
<Label check>
|
||||||
<Input type="checkbox" checked readOnly/>{' '}{gettext('Set permission')}
|
<Input type="checkbox" checked readOnly/>{' '}{gettext('Set permission')}
|
||||||
@ -241,7 +286,7 @@ class GenerateShareLink extends React.Component {
|
|||||||
<Input type="radio" name="radio1" onChange={this.setPermission('preview')} />{' '}{gettext('Preview only')}
|
<Input type="radio" name="radio1" onChange={this.setPermission('preview')} />{' '}{gettext('Preview only')}
|
||||||
</Label>
|
</Label>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Label className="err-message">{this.state.errorInfo}</Label><br />
|
<Label className="err-message">{gettext(this.state.errorInfo)}</Label><br />
|
||||||
<Button onClick={this.generateShareLink}>{gettext('Generate')}</Button>
|
<Button onClick={this.generateShareLink}>{gettext('Generate')}</Button>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -9,10 +9,11 @@ import GenerateUploadLink from './generate-upload-link';
|
|||||||
import '../../css/share-link-dialog.css';
|
import '../../css/share-link-dialog.css';
|
||||||
|
|
||||||
const propTypes = {
|
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,
|
itemName: PropTypes.string.isRequired,
|
||||||
|
itemPath: PropTypes.string.isRequired,
|
||||||
toggleDialog: PropTypes.func.isRequired,
|
toggleDialog: PropTypes.func.isRequired,
|
||||||
isDir: PropTypes.bool.isRequired,
|
|
||||||
repoID: PropTypes.string.isRequired
|
repoID: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,10 +68,10 @@ class ShareDialog extends React.Component {
|
|||||||
<GenerateUploadLink itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
<GenerateUploadLink itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tabId="shareToUser">
|
<TabPane tabId="shareToUser">
|
||||||
<ShareToUser itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
<ShareToUser isGroupOwnedRepo={this.props.isGroupOwnedRepo} itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
<TabPane tabId="shareToGroup">
|
<TabPane tabId="shareToGroup">
|
||||||
<ShareToGroup itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
<ShareToGroup isGroupOwnedRepo={this.props.isGroupOwnedRepo} itemPath={this.props.itemPath} repoID={this.props.repoID} />
|
||||||
</TabPane>
|
</TabPane>
|
||||||
</TabContent>
|
</TabContent>
|
||||||
</div>
|
</div>
|
||||||
@ -79,14 +80,13 @@ class ShareDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderFileContent = () => {
|
renderFileContent = () => {
|
||||||
let activeTab = this.state.activeTab;
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="share-dialog-side">
|
<div className="share-dialog-side">
|
||||||
<Nav pills vertical>
|
<Nav pills vertical>
|
||||||
<NavItem>
|
<NavItem>
|
||||||
<NavLink
|
<NavLink
|
||||||
className={activeTab === 'shareLink' ? 'active' : ''} onClick={() => {this.toggle.bind(this, 'shareLink');}}>
|
className="active" onClick={() => {this.toggle.bind(this, 'shareLink');}}>
|
||||||
{gettext('Share Link')}
|
{gettext('Share Link')}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</NavItem>
|
</NavItem>
|
||||||
@ -104,15 +104,14 @@ class ShareDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let itemName = this.props.itemName;
|
let { itemType, itemName } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Modal isOpen={true} style={{maxWidth: '720px'}} className="share-dialog">
|
<Modal isOpen={true} style={{maxWidth: '720px'}} className="share-dialog">
|
||||||
<ModalHeader toggle={this.props.toggleDialog}>Share <span className="sf-font" title={itemName}>{itemName}</span></ModalHeader>
|
<ModalHeader toggle={this.props.toggleDialog}>{gettext('Share')} <span className="sf-font" title={itemName}>{itemName}</span></ModalHeader>
|
||||||
<ModalBody className="share-dialog-content">
|
<ModalBody className="share-dialog-content">
|
||||||
{this.props.isDir && this.renderDirContent()}
|
{(itemType === 'library' || itemType === 'dir') && this.renderDirContent()}
|
||||||
{!this.props.isDir && this.renderFileContent()}
|
{itemType === 'file' && this.renderFileContent()}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { Utils } from '../../utils/utils';
|
|||||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
isGroupOwnedRepo: PropTypes.bool,
|
||||||
itemPath: PropTypes.string.isRequired,
|
itemPath: PropTypes.string.isRequired,
|
||||||
repoID: PropTypes.string.isRequired
|
repoID: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
@ -26,9 +27,7 @@ class ShareToGroup extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({
|
this.setState({selectedOption: option});
|
||||||
selectedOption: option,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -85,37 +84,76 @@ class ShareToGroup extends React.Component {
|
|||||||
let groups = [];
|
let groups = [];
|
||||||
let path = this.props.itemPath;
|
let path = this.props.itemPath;
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
|
let isGroupOwnedRepo = this.props.isGroupOwnedRepo;
|
||||||
if (this.state.selectedOption.length > 0 ) {
|
if (this.state.selectedOption.length > 0 ) {
|
||||||
for (let i = 0; i < this.state.selectedOption.length; i ++) {
|
for (let i = 0; i < this.state.selectedOption.length; i ++) {
|
||||||
groups[i] = this.state.selectedOption[i].id;
|
groups[i] = this.state.selectedOption[i].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seafileAPI.shareFolder(repoID, path, 'group', this.state.permission, groups).then(res => {
|
if (isGroupOwnedRepo) {
|
||||||
if (res.data.failed.length > 0) {
|
seafileAPI.shareGroupOwnedRepoToGroup(repoID, this.state.permission, groups).then(res => {
|
||||||
let errorMsg = [];
|
if (res.data.failed.length > 0) {
|
||||||
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
let errorMsg = [];
|
||||||
errorMsg[i] = res.data.failed[i];
|
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||||
|
errorMsg[i] = res.data.failed[i];
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMsg: errorMsg
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.setState({
|
|
||||||
errorMsg: errorMsg
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
// todo modify api
|
||||||
sharedItems: this.state.sharedItems.concat(res.data.success)
|
let items = res.data.success.map(item => {
|
||||||
|
let sharedItem = {
|
||||||
|
'group_info': { 'id': item.group_id, 'name': item.group_name},
|
||||||
|
'permission': item.permission,
|
||||||
|
'share_type': 'group',
|
||||||
|
};
|
||||||
|
return sharedItem;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.concat(items),
|
||||||
|
selectedOption: null,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
seafileAPI.shareFolder(repoID, path, 'group', this.state.permission, groups).then(res => {
|
||||||
|
if (res.data.failed.length > 0) {
|
||||||
|
let errorMsg = [];
|
||||||
|
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||||
|
errorMsg[i] = res.data.failed[i];
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
errorMsg: errorMsg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.concat(res.data.success),
|
||||||
|
selectedOption: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteShareItem = (e, groupID) => {
|
deleteShareItem = (e, groupID) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let path = this.props.itemPath;
|
let path = this.props.itemPath;
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
seafileAPI.deleteShareToGroupItem(repoID, path, 'group', groupID).then(() => {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
this.setState({
|
seafileAPI.deleteGroupOwnedRepoSharedGroupItem(repoID, groupID).then(() => {
|
||||||
sharedItems: this.state.sharedItems.filter(item => { return item.group_info.id !== groupID; })
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.filter(item => { return item.group_info.id !== groupID; })
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
seafileAPI.deleteShareToGroupItem(repoID, path, 'group', groupID).then(() => {
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.filter(item => { return item.group_info.id !== groupID; })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -135,6 +173,7 @@ class ShareToGroup extends React.Component {
|
|||||||
options={this.options}
|
options={this.options}
|
||||||
components={makeAnimated()}
|
components={makeAnimated()}
|
||||||
inputId={'react-select-2-input'}
|
inputId={'react-select-2-input'}
|
||||||
|
value={this.state.selectedOption}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -150,14 +189,16 @@ class ShareToGroup extends React.Component {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
{this.state.errorMsg.length > 0 &&
|
<td colSpan={3}>
|
||||||
this.state.errorMsg.map((item, index = 0, arr) => {
|
{this.state.errorMsg.length > 0 &&
|
||||||
return (
|
this.state.errorMsg.map((item, index = 0, arr) => {
|
||||||
<p className="error" key={index}>{this.state.errorMsg[index].group_name}
|
return (
|
||||||
{': '}{this.state.errorMsg[index].error_msg}</p>
|
<p className="error" key={index}>{this.state.errorMsg[index].group_name}
|
||||||
);
|
{': '}{this.state.errorMsg[index].error_msg}</p>
|
||||||
})
|
);
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<GroupList items={this.state.sharedItems} deleteShareItem={this.deleteShareItem} />
|
<GroupList items={this.state.sharedItems} deleteShareItem={this.deleteShareItem} />
|
||||||
@ -173,7 +214,7 @@ function GroupList(props) {
|
|||||||
<tr key={index}>
|
<tr key={index}>
|
||||||
<td>{item.group_info.name}</td>
|
<td>{item.group_info.name}</td>
|
||||||
<td>{Utils.sharePerms[item.permission]}</td>
|
<td>{Utils.sharePerms[item.permission]}</td>
|
||||||
<td><i onClick={(e) => {props.deleteShareItem(e, item.group_info.id);}} className="sf2-icon-delete" title="Delete"></i></td>
|
<td><a href="#" onClick={(e) => {props.deleteShareItem(e, item.group_info.id);}} className="sf2-icon-x3 sf2-x op-icon" title={gettext('Delete')}></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -7,6 +7,7 @@ import { Button, Input } from 'reactstrap';
|
|||||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
isGroupOwnedRepo: PropTypes.bool,
|
||||||
itemPath: PropTypes.string.isRequired,
|
itemPath: PropTypes.string.isRequired,
|
||||||
repoID: PropTypes.string.isRequired
|
repoID: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
@ -92,29 +93,64 @@ class ShareToUser extends React.Component {
|
|||||||
users[i] = this.state.selectedOption[i].email;
|
users[i] = this.state.selectedOption[i].email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seafileAPI.shareFolder(repoID, path, 'user', this.state.permission, users).then(res => {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
if (res.data.failed.length > 0) {
|
seafileAPI.shareGroupOwnedRepoToUser(repoID, this.state.permission, users).then(res => {
|
||||||
let errorMsg = [];
|
if (res.data.failed.length > 0) {
|
||||||
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
let errorMsg = [];
|
||||||
errorMsg[i] = res.data.failed[i];
|
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||||
|
errorMsg[i] = res.data.failed[i];
|
||||||
|
}
|
||||||
|
this.setState({errorMsg: errorMsg});
|
||||||
}
|
}
|
||||||
this.setState({errorMsg: errorMsg});
|
// todo modify api
|
||||||
}
|
|
||||||
this.setState({
|
let items = res.data.success.map(item => {
|
||||||
sharedItems: this.state.sharedItems.concat(res.data.success)
|
let sharedItem = {
|
||||||
|
'user_info': { 'nickname': item.user_name, 'name': item.user_email},
|
||||||
|
'permission': item.permission,
|
||||||
|
'share_type': 'user',
|
||||||
|
};
|
||||||
|
return sharedItem;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.concat(items),
|
||||||
|
selectedOption: null,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
seafileAPI.shareFolder(repoID, path, 'user', this.state.permission, users).then(res => {
|
||||||
|
if (res.data.failed.length > 0) {
|
||||||
|
let errorMsg = [];
|
||||||
|
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||||
|
errorMsg[i] = res.data.failed[i];
|
||||||
|
}
|
||||||
|
this.setState({errorMsg: errorMsg});
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.concat(res.data.success),
|
||||||
|
selectedOption: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteShareItem = (e, username) => {
|
deleteShareItem = (e, username) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let path = this.props.itemPath;
|
let path = this.props.itemPath;
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
seafileAPI.deleteShareToUserItem(repoID, path, 'user', username).then(res => {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
this.setState({
|
seafileAPI.deleteGroupOwnedRepoSharedUserItem(repoID, username).then(res => {
|
||||||
sharedItems: this.state.sharedItems.filter( item => { return item.user_info.name !== username; })
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.filter( item => { return item.user_info.name !== username; })
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
seafileAPI.deleteShareToUserItem(repoID, path, 'user', username).then(res => {
|
||||||
|
this.setState({
|
||||||
|
sharedItems: this.state.sharedItems.filter( item => { return item.user_info.name !== username; })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -130,12 +166,16 @@ class ShareToUser extends React.Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
className='reviewer-select' isMulti isFocused
|
|
||||||
loadOptions={this.loadOptions}
|
|
||||||
placeholder={gettext('Please enter 1 or more character')}
|
|
||||||
onChange={this.handleSelectChange}
|
|
||||||
isClearable classNamePrefix
|
|
||||||
inputId={'react-select-1-input'}
|
inputId={'react-select-1-input'}
|
||||||
|
className='reviewer-select'
|
||||||
|
placeholder={gettext('Please enter 1 or more character')}
|
||||||
|
loadOptions={this.loadOptions}
|
||||||
|
onChange={this.handleSelectChange}
|
||||||
|
value={this.state.selectedOption}
|
||||||
|
isMulti
|
||||||
|
isFocused
|
||||||
|
isClearable
|
||||||
|
classNamePrefix
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -152,14 +192,16 @@ class ShareToUser extends React.Component {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
{this.state.errorMsg.length > 0 &&
|
<td colSpan={3}>
|
||||||
this.state.errorMsg.map((item, index = 0, arr) => {
|
{this.state.errorMsg.length > 0 &&
|
||||||
return (
|
this.state.errorMsg.map((item, index = 0, arr) => {
|
||||||
<p className="error" key={index}>{this.state.errorMsg[index].email}
|
return (
|
||||||
{': '}{this.state.errorMsg[index].error_msg}</p>
|
<p className="error" key={index}>{this.state.errorMsg[index].email}
|
||||||
);
|
{': '}{this.state.errorMsg[index].error_msg}</p>
|
||||||
})
|
);
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<UserList items={sharedItems} deleteShareItem={this.deleteShareItem} />
|
<UserList items={sharedItems} deleteShareItem={this.deleteShareItem} />
|
||||||
@ -175,7 +217,7 @@ function UserList(props) {
|
|||||||
<tr key={index}>
|
<tr key={index}>
|
||||||
<td>{item.user_info.nickname}</td>
|
<td>{item.user_info.nickname}</td>
|
||||||
<td>{Utils.sharePerms[item.permission]}</td>
|
<td>{Utils.sharePerms[item.permission]}</td>
|
||||||
<td><i onClick={(e) => {props.deleteShareItem(e, item.user_info.name);}} className="sf2-icon-delete" title="Delete"></i></td>
|
<td><a href="#" onClick={(e) => {props.deleteShareItem(e, item.user_info.name);}} className="sf2-icon-x3 sf2-x op-icon" title={gettext('Delete')}></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -96,7 +96,7 @@ class DirView extends React.Component {
|
|||||||
window.history.pushState({url: fileUrl, path: direntPath}, direntPath, fileUrl);
|
window.history.pushState({url: fileUrl, path: direntPath}, direntPath, fileUrl);
|
||||||
} else {
|
} else {
|
||||||
const w=window.open('about:blank');
|
const w=window.open('about:blank');
|
||||||
const url = siteRoot + 'lib/' + this.state.repoID + '/file' + direntPath;
|
const url = siteRoot + 'lib/' + this.state.repoID + '/file' + Utils.encodePath(direntPath);
|
||||||
w.location.href = url;
|
w.location.href = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,9 +477,9 @@ class DirentListItem extends React.Component {
|
|||||||
{this.state.isShareDialogShow &&
|
{this.state.isShareDialogShow &&
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
<ShareDialog
|
<ShareDialog
|
||||||
isDir={dirent.isDir()}
|
itemType={dirent.type}
|
||||||
itemPath={direntPath}
|
|
||||||
itemName={dirent.name}
|
itemName={dirent.name}
|
||||||
|
itemPath={direntPath}
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
toggleDialog={this.onItemShare}
|
toggleDialog={this.onItemShare}
|
||||||
/>
|
/>
|
||||||
|
@ -5,6 +5,8 @@ import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap
|
|||||||
import { Link } from '@reach/router';
|
import { Link } from '@reach/router';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { gettext, siteRoot, isPro, username, folderPermEnabled } from '../../utils/constants';
|
import { gettext, siteRoot, isPro, username, folderPermEnabled } from '../../utils/constants';
|
||||||
|
import ModalPotal from '../../components/modal-portal';
|
||||||
|
import ShareDialog from '../../components/dialog/share-dialog';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentGroup: PropTypes.object,
|
currentGroup: PropTypes.object,
|
||||||
@ -22,6 +24,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
highlight: false,
|
highlight: false,
|
||||||
isOperationShow: false,
|
isOperationShow: false,
|
||||||
isItemMenuShow: false,
|
isItemMenuShow: false,
|
||||||
|
isShowSharedDialog: false,
|
||||||
};
|
};
|
||||||
this.isDeparementOnwerGroupMember = false;
|
this.isDeparementOnwerGroupMember = false;
|
||||||
}
|
}
|
||||||
@ -90,7 +93,6 @@ class SharedRepoListItem extends React.Component {
|
|||||||
'permission': repo.permission
|
'permission': repo.permission
|
||||||
});
|
});
|
||||||
|
|
||||||
//todo change to library; div-view is not compatibility
|
|
||||||
let libPath = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`;
|
let libPath = `${siteRoot}library/${repo.repo_id}/${Utils.encodePath(repo.repo_name)}/`;
|
||||||
|
|
||||||
return { iconUrl, iconTitle, libPath };
|
return { iconUrl, iconTitle, libPath };
|
||||||
@ -109,7 +111,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
this.onItemDetails();
|
this.onItemDetails();
|
||||||
break;
|
break;
|
||||||
case 'Share':
|
case 'Share':
|
||||||
this.onItemShared();
|
this.onItemShare();
|
||||||
break;
|
break;
|
||||||
case 'Unshare':
|
case 'Unshare':
|
||||||
this.onItemUnshare();
|
this.onItemUnshare();
|
||||||
@ -132,16 +134,19 @@ class SharedRepoListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onItemShare = () => {
|
onItemShare = () => {
|
||||||
// todo
|
this.setState({isShowSharedDialog: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemUnshare = () => {
|
onItemUnshare = () => {
|
||||||
// todo
|
|
||||||
this.props.onItemUnshare(this.props.repo);
|
this.props.onItemUnshare(this.props.repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemDelete = () => {
|
onItemDelete = () => {
|
||||||
// todo
|
this.props.onItemDelete(this.props.repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShareDialog = () => {
|
||||||
|
this.setState({isShowSharedDialog: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
generatorOperations = () => {
|
generatorOperations = () => {
|
||||||
@ -217,7 +222,7 @@ class SharedRepoListItem extends React.Component {
|
|||||||
// scene one: (share, delete, itemToggle and other operations);
|
// scene one: (share, delete, itemToggle and other operations);
|
||||||
// scene two: (share, unshare), (share), (unshare)
|
// scene two: (share, unshare), (share), (unshare)
|
||||||
let operations = this.generatorOperations();
|
let operations = this.generatorOperations();
|
||||||
const shareOperation = <a href="#" className="sf2-icon-share sf2-x op-icon" title={gettext("Share")} onClick={this.onItemShared}></a>;
|
const shareOperation = <a href="#" className="sf2-icon-share sf2-x op-icon" title={gettext("Share")} onClick={this.onItemShare}></a>;
|
||||||
const unshareOperation = <a href="#" className="sf2-icon-x3 sf2-x op-icon" title={gettext("Unshare")} onClick={this.onItemUnshare}></a>
|
const unshareOperation = <a href="#" className="sf2-icon-x3 sf2-x op-icon" title={gettext("Unshare")} onClick={this.onItemUnshare}></a>
|
||||||
const deleteOperation = <a href="#" className="sf2-icon-delete sf2-x op-icon" title={gettext('Delete')} onClick={this.onItemDelete}></a>;
|
const deleteOperation = <a href="#" className="sf2-icon-delete sf2-x op-icon" title={gettext('Delete')} onClick={this.onItemDelete}></a>;
|
||||||
|
|
||||||
@ -266,32 +271,62 @@ class SharedRepoListItem extends React.Component {
|
|||||||
renderPCUI = () => {
|
renderPCUI = () => {
|
||||||
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
||||||
let { repo } = this.props;
|
let { repo } = this.props;
|
||||||
|
let isGroupOwnedRepo = repo.owner_email.indexOf('@seafile_group') > -1;
|
||||||
return (
|
return (
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
|
<Fragment>
|
||||||
<td><img src={iconUrl} title={repo.iconTitle} alt={iconTitle} width="24" /></td>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
|
||||||
<td><Link to={libPath}>{repo.repo_name}</Link></td>
|
<td><img src={iconUrl} title={repo.iconTitle} alt={iconTitle} width="24" /></td>
|
||||||
<td>{this.state.isOperationShow && this.generatorPCMenu()}</td>
|
<td><Link to={libPath}>{repo.repo_name}</Link></td>
|
||||||
<td>{repo.size}</td>
|
<td>{this.state.isOperationShow && this.generatorPCMenu()}</td>
|
||||||
<td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td>
|
<td>{repo.size}</td>
|
||||||
<td title={repo.owner_contact_email}>{repo.owner_name}</td>
|
<td title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</td>
|
||||||
</tr>
|
<td title={repo.owner_contact_email}>{repo.owner_name}</td>
|
||||||
|
</tr>
|
||||||
|
{this.state.isShowSharedDialog && (
|
||||||
|
<ModalPotal>
|
||||||
|
<ShareDialog
|
||||||
|
itemType={'library'}
|
||||||
|
itemName={repo.repo_name}
|
||||||
|
itemPath={'/'}
|
||||||
|
repoID={repo.repo_id}
|
||||||
|
isGroupOwnedRepo={isGroupOwnedRepo}
|
||||||
|
toggleDialog={this.toggleShareDialog}
|
||||||
|
/>
|
||||||
|
</ModalPotal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMobileUI = () => {
|
renderMobileUI = () => {
|
||||||
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
let { iconUrl, iconTitle, libPath } = this.getRepoComputeParams();
|
||||||
let { repo } = this.props;
|
let { repo } = this.props;
|
||||||
|
let isGroupOwnedRepo = repo.owner_email.indexOf('@seafile_group') > -1;
|
||||||
return (
|
return (
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
|
<Fragment>
|
||||||
<td><img src={iconUrl} title={iconTitle} alt={iconTitle}/></td>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
|
||||||
<td>
|
<td><img src={iconUrl} title={iconTitle} alt={iconTitle}/></td>
|
||||||
<Link to={libPath}>{repo.repo_name}</Link><br />
|
<td>
|
||||||
<span className="item-meta-info" title={repo.owner_contact_email}>{repo.owner_name}</span>
|
<Link to={libPath}>{repo.repo_name}</Link><br />
|
||||||
<span className="item-meta-info">{repo.size}</span>
|
<span className="item-meta-info" title={repo.owner_contact_email}>{repo.owner_name}</span>
|
||||||
<span className="item-meta-info" title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</span>
|
<span className="item-meta-info">{repo.size}</span>
|
||||||
</td>
|
<span className="item-meta-info" title={moment(repo.last_modified).format('llll')}>{moment(repo.last_modified).fromNow()}</span>
|
||||||
<td>{this.generatorMobileMenu()}</td>
|
</td>
|
||||||
</tr>
|
<td>{this.generatorMobileMenu()}</td>
|
||||||
|
</tr>
|
||||||
|
{this.state.isShowSharedDialog && (
|
||||||
|
<ModalPotal>
|
||||||
|
<ShareDialog
|
||||||
|
itemType={'library'}
|
||||||
|
itemName={repo.repo_name}
|
||||||
|
itemPath={'/'}
|
||||||
|
repoID={repo.repo_id}
|
||||||
|
isGroupOwnedRepo={isGroupOwnedRepo}
|
||||||
|
toggleDialog={this.toggleShareDialog}
|
||||||
|
/>
|
||||||
|
</ModalPotal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ const propTypes = {
|
|||||||
repoList: PropTypes.array.isRequired,
|
repoList: PropTypes.array.isRequired,
|
||||||
isShowTableThread: PropTypes.bool,
|
isShowTableThread: PropTypes.bool,
|
||||||
onItemUnshare: PropTypes.func.isRequired,
|
onItemUnshare: PropTypes.func.isRequired,
|
||||||
|
onItemDelete: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SharedRepoListView extends React.Component {
|
class SharedRepoListView extends React.Component {
|
||||||
@ -37,6 +38,7 @@ class SharedRepoListView extends React.Component {
|
|||||||
isItemFreezed={this.state.isItemFreezed}
|
isItemFreezed={this.state.isItemFreezed}
|
||||||
onFreezedItem={this.onFreezedItem}
|
onFreezedItem={this.onFreezedItem}
|
||||||
onItemUnshare={this.props.onItemUnshare}
|
onItemUnshare={this.props.onItemUnshare}
|
||||||
|
onItemDelete={this.props.onItemDelete}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.department-group-icon { /* for cur-view-path*/
|
.department-group-icon {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
color:#888;
|
color:#888;
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.share-dialog-content label {
|
.share-dialog-content label {
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.5rem 0 0.25rem;
|
padding: 0.5rem 0 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.share-dialog-content label.form-check-label {
|
.share-dialog-content label.form-check-label {
|
||||||
font-weight: normal;
|
|
||||||
padding: 0.25rem 0;
|
padding: 0.25rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,16 +152,49 @@ class GroupView extends React.Component {
|
|||||||
this.setState({isCreateRepoDialogShow: !this.state.isCreateRepoDialogShow});
|
this.setState({isCreateRepoDialogShow: !this.state.isCreateRepoDialogShow});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreateRepo = (repo) => {
|
onCreateRepo = (repo, groupOwnerType) => {
|
||||||
let groupId = this.props.groupID;
|
let groupId = this.props.groupID;
|
||||||
seafileAPI.createGroupRepo(groupId, repo).then(res => {
|
if (groupOwnerType && groupOwnerType === 'department') {
|
||||||
let repo = new RepoInfo(res.data);
|
seafileAPI.createGroupOwnedLibrary(groupId, repo).then(res => { //need modify endpoint api
|
||||||
let repoList = this.addRepoItem(repo);
|
let object = {
|
||||||
|
repo_id: res.data.id,
|
||||||
|
repo_name: res.data.name,
|
||||||
|
owner_name: res.data.group_name,
|
||||||
|
owner_email: res.data.owner,
|
||||||
|
permission: res.data.permission,
|
||||||
|
mtime: res.data.mtime,
|
||||||
|
size: res.data.size,
|
||||||
|
encrypted: res.data.encrypted,
|
||||||
|
};
|
||||||
|
let repo = new RepoInfo(object);
|
||||||
|
let repoList = this.addRepoItem(repo);
|
||||||
|
this.setState({repoList: repoList});
|
||||||
|
}).then(() => {
|
||||||
|
//todo
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
seafileAPI.createGroupRepo(groupId, repo).then(res => {
|
||||||
|
let repo = new RepoInfo(res.data);
|
||||||
|
let repoList = this.addRepoItem(repo);
|
||||||
|
this.setState({repoList: repoList});
|
||||||
|
}).catch(() => {
|
||||||
|
//todo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.onCreateRepoToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemDelete = (repo) => {
|
||||||
|
let groupID = this.props.groupID;
|
||||||
|
seafileAPI.deleteGroupOwnedLibrary(groupID, repo.repo_id).then(() => {
|
||||||
|
let repoList = this.state.repoList.filter(item => {
|
||||||
|
return item.repo_id !== repo.repo_id;
|
||||||
|
});
|
||||||
this.setState({repoList: repoList});
|
this.setState({repoList: repoList});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
//todo
|
// todo;
|
||||||
});
|
});
|
||||||
this.onCreateRepoToggle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addRepoItem = (repo) => {
|
addRepoItem = (repo) => {
|
||||||
@ -232,6 +265,7 @@ class GroupView extends React.Component {
|
|||||||
repoList={this.state.repoList}
|
repoList={this.state.repoList}
|
||||||
currentGroup={this.state.currentGroup}
|
currentGroup={this.state.currentGroup}
|
||||||
onItemUnshare={this.onItemUnshare}
|
onItemUnshare={this.onItemUnshare}
|
||||||
|
onItemDelete={this.onItemDelete}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -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() {
|
render() {
|
||||||
let group = this.props.group;
|
let group = this.props.group;
|
||||||
const emptyTip = <p className="group-item-empty-tip">{gettext('No libraries')}</p>;
|
const emptyTip = <p className="group-item-empty-tip">{gettext('No libraries')}</p>;
|
||||||
@ -60,6 +72,7 @@ class RepoListViewPanel extends React.Component {
|
|||||||
currentGroup={this.props.group}
|
currentGroup={this.props.group}
|
||||||
repoList={this.state.repoList}
|
repoList={this.state.repoList}
|
||||||
onItemUnshare={this.onItemUnshare}
|
onItemUnshare={this.onItemUnshare}
|
||||||
|
onItemDelete={this.onItemDelete}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@ -178,6 +191,5 @@ const GroupsViewPropTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
GroupsView.propTypes = GroupsViewPropTypes;
|
GroupsView.propTypes = GroupsViewPropTypes;
|
||||||
// Groups.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default GroupsView;
|
export default GroupsView;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Link } from '@reach/router';
|
import { Link } from '@reach/router';
|
||||||
@ -8,6 +8,8 @@ import { gettext, siteRoot, storages, canGenerateShareLink, canGenerateUploadLin
|
|||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import RenameInput from '../../components/rename-input';
|
import RenameInput from '../../components/rename-input';
|
||||||
|
import ModalPotal from '../../components/modal-portal';
|
||||||
|
import ShareDialog from '../../components/dialog/share-dialog';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
@ -29,6 +31,7 @@ class Item extends Component {
|
|||||||
showOpIcon: false,
|
showOpIcon: false,
|
||||||
operationMenuOpen: false,
|
operationMenuOpen: false,
|
||||||
showChangeLibName: false,
|
showChangeLibName: false,
|
||||||
|
isShowSharedDialog: false,
|
||||||
highlight: false,
|
highlight: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -73,7 +76,11 @@ class Item extends Component {
|
|||||||
|
|
||||||
share = (e) => {
|
share = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// TODO
|
this.setState({isShowSharedDialog: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShareDialog = () => {
|
||||||
|
this.setState({isShowSharedDialog: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
showDeleteItemPopup = (e) => {
|
showDeleteItemPopup = (e) => {
|
||||||
@ -232,43 +239,69 @@ class Item extends Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const desktopItem = (
|
const desktopItem = (
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
<Fragment>
|
||||||
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||||
<td>
|
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
|
||||||
{this.state.showChangeLibName && (
|
<td>
|
||||||
<RenameInput
|
{this.state.showChangeLibName && (
|
||||||
name={data.repo_name}
|
<RenameInput
|
||||||
onRenameConfirm={this.onRenameConfirm}
|
name={data.repo_name}
|
||||||
onRenameCancel={this.onRenameCancel}
|
onRenameConfirm={this.onRenameConfirm}
|
||||||
|
onRenameCancel={this.onRenameCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!this.state.showChangeLibName && data.repo_name && (
|
||||||
|
<Link to={data.url}>{data.repo_name}</Link>
|
||||||
|
)}
|
||||||
|
{!this.state.showChangeLibName && !data.repo_name &&
|
||||||
|
(gettext('Broken (please contact your administrator to fix this library)'))
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>{data.repo_name ? desktopOperations : ''}</td>
|
||||||
|
<td>{Utils.formatSize({bytes: data.size})}</td>
|
||||||
|
{storages.length ? <td>{data.storage_name}</td> : null}
|
||||||
|
<td title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</td>
|
||||||
|
</tr>
|
||||||
|
{this.state.isShowSharedDialog && (
|
||||||
|
<ModalPotal>
|
||||||
|
<ShareDialog
|
||||||
|
itemType={'library'}
|
||||||
|
itemName={data.repo_name}
|
||||||
|
itemPath={'/'}
|
||||||
|
repoID={data.repo_id}
|
||||||
|
toggleDialog={this.toggleShareDialog}
|
||||||
/>
|
/>
|
||||||
)}
|
</ModalPotal>
|
||||||
{!this.state.showChangeLibName && data.repo_name && (
|
)}
|
||||||
<Link to={data.url}>{data.repo_name}</Link>
|
</Fragment>
|
||||||
)}
|
|
||||||
{!this.state.showChangeLibName && !data.repo_name &&
|
|
||||||
(gettext('Broken (please contact your administrator to fix this library)'))
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>{data.repo_name ? desktopOperations : ''}</td>
|
|
||||||
<td>{Utils.formatSize({bytes: data.size})}</td>
|
|
||||||
{storages.length ? <td>{data.storage_name}</td> : null}
|
|
||||||
<td title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const mobileItem = (
|
const mobileItem = (
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
<Fragment>
|
||||||
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||||
<td>
|
<td><img src={data.icon_url} title={data.icon_title} alt={data.icon_title} width="24" /></td>
|
||||||
{data.repo_name ?
|
<td>
|
||||||
<Link to={data.url}>{data.repo_name}</Link> :
|
{data.repo_name ?
|
||||||
gettext('Broken (please contact your administrator to fix this library)')}
|
<Link to={data.url}>{data.repo_name}</Link> :
|
||||||
<br />
|
gettext('Broken (please contact your administrator to fix this library)')}
|
||||||
<span className="item-meta-info">{Utils.formatSize({bytes: data.size})}</span>
|
<br />
|
||||||
<span className="item-meta-info" title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</span>
|
<span className="item-meta-info">{Utils.formatSize({bytes: data.size})}</span>
|
||||||
</td>
|
<span className="item-meta-info" title={moment(data.last_modified).format('llll')}>{moment(data.last_modified).fromNow()}</span>
|
||||||
<td>{data.repo_name ? mobileOperations : ''}</td>
|
</td>
|
||||||
</tr>
|
<td>{data.repo_name ? mobileOperations : ''}</td>
|
||||||
|
</tr>
|
||||||
|
{this.state.isShowSharedDialog && (
|
||||||
|
<ModalPotal>
|
||||||
|
<ShareDialog
|
||||||
|
itemType={'library'}
|
||||||
|
itemName={data.repo_name}
|
||||||
|
itemPath={'/'}
|
||||||
|
repoID={data.repo_id}
|
||||||
|
toggleDialog={this.toggleShareDialog}
|
||||||
|
/>
|
||||||
|
</ModalPotal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|
||||||
return window.innerWidth >= 768 ? desktopItem : mobileItem;
|
return window.innerWidth >= 768 ? desktopItem : mobileItem;
|
||||||
|
@ -48,8 +48,17 @@ class MyLibraries extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCreateRepo = (repo) => {
|
onCreateRepo = (repo) => {
|
||||||
|
let permission = repo.permission;
|
||||||
seafileAPI.createMineRepo(repo).then((res) => {
|
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.state.items.push(repo);
|
||||||
this.setState({items: this.state.items});
|
this.setState({items: this.state.items});
|
||||||
});
|
});
|
||||||
|
@ -334,7 +334,7 @@ class Wiki extends Component {
|
|||||||
this.showFile(node.path);
|
this.showFile(node.path);
|
||||||
}
|
}
|
||||||
} else {
|
} 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');
|
let newWindow = window.open('about:blank');
|
||||||
newWindow.location.href = url;
|
newWindow.location.href = url;
|
||||||
}
|
}
|
||||||
@ -367,7 +367,7 @@ class Wiki extends Component {
|
|||||||
this.showDir(node.path);
|
this.showDir(node.path);
|
||||||
} else {
|
} else {
|
||||||
const w=window.open('about:blank');
|
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;
|
w.location.href = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,9 @@ export const Utils = {
|
|||||||
return encodeURIComponent(e);
|
return encodeURIComponent(e);
|
||||||
}).join('/');
|
}).join('/');
|
||||||
*/
|
*/
|
||||||
|
if (!path) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
var path_arr = path.split('/');
|
var path_arr = path.split('/');
|
||||||
var path_arr_ = [];
|
var path_arr_ = [];
|
||||||
for (var i = 0, len = path_arr.length; i < len; i++) {
|
for (var i = 0, len = path_arr.length; i < len; i++) {
|
||||||
|
@ -133,7 +133,7 @@ class Wiki extends Component {
|
|||||||
this.enterViewFileState(tree, node, node.path);
|
this.enterViewFileState(tree, node, node.path);
|
||||||
}
|
}
|
||||||
} else {
|
} 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');
|
let newWindow = window.open('about:blank');
|
||||||
newWindow.location.href = url;
|
newWindow.location.href = url;
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ class Wiki extends Component {
|
|||||||
this.exitViewFileState(tree, node);
|
this.exitViewFileState(tree, node);
|
||||||
} else {
|
} else {
|
||||||
const w=window.open('about:blank');
|
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;
|
w.location.href = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ urlpatterns = [
|
|||||||
url(r'^shared-libs/$', react_fake_view, name="shared_libs"),
|
url(r'^shared-libs/$', react_fake_view, name="shared_libs"),
|
||||||
url(r'^my-libs/$', react_fake_view, name="my_libs"),
|
url(r'^my-libs/$', react_fake_view, name="my_libs"),
|
||||||
url(r'^groups/$', react_fake_view, name="groups"),
|
url(r'^groups/$', react_fake_view, name="groups"),
|
||||||
url(r'^group/(?P<group_id>\d+)/$', react_group, name="group"),
|
url(r'^group/.*$', react_fake_view, name="group"),
|
||||||
url(r'^library/.*$', react_fake_view, name="lib_view"),
|
url(r'^library/.*$', react_fake_view, name="lib_view"),
|
||||||
url(r'^my-libs/deleted/$', react_fake_view, name="my_libs_deleted"),
|
url(r'^my-libs/deleted/$', react_fake_view, name="my_libs_deleted"),
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user