mirror of
https://github.com/haiwen/seahub.git
synced 2025-07-16 08:16:55 +00:00
Merge branch '7.0'
This commit is contained in:
commit
6cb4d38788
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
@ -15924,9 +15924,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"seafile-js": {
|
"seafile-js": {
|
||||||
"version": "0.2.101",
|
"version": "0.2.106",
|
||||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.101.tgz",
|
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.106.tgz",
|
||||||
"integrity": "sha512-CqF+4FKnUhnUYKCjiVbp10GkI9Ej+d4UfaWRvdGNaR74tK+0D9dUq7RNXUGS/MpIAlif7hbz5k+LUFafkfGmNg==",
|
"integrity": "sha512-lT2gLD8hz75rlc7PW4kwW9SWe2JUywsDr5EDYYdQoPUz437i0YEq5wMIJq8jcOHaTSqgwbIfrWpVmOIs8Lkilg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"form-data": "^2.3.2",
|
"form-data": "^2.3.2",
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
"react-responsive": "^6.1.1",
|
"react-responsive": "^6.1.1",
|
||||||
"react-select": "^2.4.1",
|
"react-select": "^2.4.1",
|
||||||
"reactstrap": "^6.4.0",
|
"reactstrap": "^6.4.0",
|
||||||
"seafile-js": "^0.2.101",
|
"seafile-js": "^0.2.106",
|
||||||
"socket.io-client": "^2.2.0",
|
"socket.io-client": "^2.2.0",
|
||||||
"sw-precache-webpack-plugin": "0.11.4",
|
"sw-precache-webpack-plugin": "0.11.4",
|
||||||
"unified": "^7.0.0",
|
"unified": "^7.0.0",
|
||||||
|
@ -41,6 +41,7 @@ class GenerateShareLink extends React.Component {
|
|||||||
'can_download': true
|
'can_download': true
|
||||||
};
|
};
|
||||||
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0);
|
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0);
|
||||||
|
this.isOfficeFile = Utils.isOfficeFile(this.props.itemPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -57,12 +58,13 @@ class GenerateShareLink extends React.Component {
|
|||||||
this.setState({isLoading: false});
|
this.setState({isLoading: false});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (this.isOfficeFile) {
|
||||||
seafileAPI.getFileInfo(repoID, path).then((res) => {
|
seafileAPI.getFileInfo(repoID, path).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
this.setState({fileInfo: res.data});
|
this.setState({fileInfo: res.data});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPasswordInputChecked = () => {
|
onPasswordInputChecked = () => {
|
||||||
@ -402,7 +404,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>
|
||||||
{(Utils.isOfficeFile(this.props.itemPath) && fileInfo && fileInfo.can_edit) &&
|
{(this.isOfficeFile && fileInfo && fileInfo.can_edit) &&
|
||||||
<FormGroup check className="permission">
|
<FormGroup check className="permission">
|
||||||
<Label className="form-check-label">
|
<Label className="form-check-label">
|
||||||
<Input type="radio" name="radio1" onChange={() => this.setPermission('editOnCloudAndDownload')} />{' '}{gettext('Edit on cloud and download')}
|
<Input type="radio" name="radio1" onChange={() => this.setPermission('editOnCloudAndDownload')} />{' '}{gettext('Edit on cloud and download')}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
|
import moment from 'moment';
|
||||||
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
|
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
|
||||||
import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
|
import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
@ -25,7 +26,9 @@ class GenerateUploadLink extends React.Component {
|
|||||||
password: '',
|
password: '',
|
||||||
passwdnew: '',
|
passwdnew: '',
|
||||||
sharedUploadInfo: null,
|
sharedUploadInfo: null,
|
||||||
isSendLinkShown: false
|
isSendLinkShown: false,
|
||||||
|
isExpireChecked: false,
|
||||||
|
expireDays: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,29 +93,61 @@ class GenerateUploadLink extends React.Component {
|
|||||||
generateUploadLink = () => {
|
generateUploadLink = () => {
|
||||||
let path = this.props.itemPath;
|
let path = this.props.itemPath;
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
|
let { password, expireDays } = this.state;
|
||||||
|
|
||||||
if (this.state.showPasswordInput && (this.state.password == '')) {
|
let isValid = this.validateParamsInput();
|
||||||
this.setState({
|
if (isValid) {
|
||||||
errorInfo: gettext('Please enter password')
|
seafileAPI.createUploadLink(repoID, path, password, expireDays).then((res) => {
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (this.state.showPasswordInput && (this.state.showPasswordInput && this.state.password.length < shareLinkPasswordMinLength)) {
|
|
||||||
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 {
|
|
||||||
seafileAPI.createUploadLink(repoID, path, this.state.password).then((res) => {
|
|
||||||
let sharedUploadInfo = new SharedUploadInfo(res.data);
|
let sharedUploadInfo = new SharedUploadInfo(res.data);
|
||||||
this.setState({sharedUploadInfo: sharedUploadInfo});
|
this.setState({sharedUploadInfo: sharedUploadInfo});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateParamsInput = () => {
|
||||||
|
let { showPasswordInput , password, passwordnew, isExpireChecked, expireDays } = this.state;
|
||||||
|
|
||||||
|
// check password params
|
||||||
|
if (showPasswordInput) {
|
||||||
|
if (password.length === 0) {
|
||||||
|
this.setState({errorInfo: gettext('Please enter password')});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (password.length < shareLinkPasswordMinLength) {
|
||||||
|
this.setState({errorInfo: gettext('Password is too short')});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (password !== passwordnew) {
|
||||||
|
this.setState({errorInfo: gettext('Passwords don\'t match')});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check expire day params
|
||||||
|
let reg = /^\d+$/;
|
||||||
|
if (isExpireChecked) {
|
||||||
|
if (!expireDays) {
|
||||||
|
this.setState({errorInfo: gettext('Please enter days')});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!reg.test(expireDays)) {
|
||||||
|
this.setState({errorInfo: gettext('Please enter a non-negative integer')});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({expireDays: parseInt(expireDays)});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpireChecked = (e) => {
|
||||||
|
this.setState({isExpireChecked: e.target.checked});
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpireDaysChanged = (e) => {
|
||||||
|
let day = e.target.value.trim();
|
||||||
|
this.setState({expireDays: day});
|
||||||
|
}
|
||||||
|
|
||||||
onCopyUploadLink = () => {
|
onCopyUploadLink = () => {
|
||||||
let uploadLink = this.state.sharedUploadInfo.link;
|
let uploadLink = this.state.sharedUploadInfo.link;
|
||||||
copy(uploadLink);
|
copy(uploadLink);
|
||||||
@ -156,6 +191,12 @@ class GenerateUploadLink extends React.Component {
|
|||||||
<span className="far fa-copy action-icon" onClick={this.onCopyUploadLink}></span>
|
<span className="far fa-copy action-icon" onClick={this.onCopyUploadLink}></span>
|
||||||
</dd>
|
</dd>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{sharedUploadInfo.expire_date && (
|
||||||
|
<FormGroup className="mb-0">
|
||||||
|
<dt className="text-secondary font-weight-normal">{gettext('Expiration Date:')}</dt>
|
||||||
|
<dd>{moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD hh:mm:ss')}</dd>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
{canSendShareLinkEmail && !isSendLinkShown && <Button onClick={this.toggleSendLink} className="mr-2">{gettext('Send')}</Button>}
|
{canSendShareLinkEmail && !isSendLinkShown && <Button onClick={this.toggleSendLink} className="mr-2">{gettext('Send')}</Button>}
|
||||||
{!isSendLinkShown && <Button onClick={this.deleteUploadLink}>{gettext('Delete')}</Button>}
|
{!isSendLinkShown && <Button onClick={this.deleteUploadLink}>{gettext('Delete')}</Button>}
|
||||||
@ -192,6 +233,18 @@ class GenerateUploadLink extends React.Component {
|
|||||||
<Input className="passwd" type={this.state.passwordVisible ? 'text' : 'password'} value={this.state.passwordnew || ''} onChange={this.inputPasswordNew} />
|
<Input className="passwd" type={this.state.passwordVisible ? 'text' : 'password'} value={this.state.passwordnew || ''} onChange={this.inputPasswordNew} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
}
|
}
|
||||||
|
<FormGroup check>
|
||||||
|
<Label check>
|
||||||
|
<Input className="expire-checkbox" type="checkbox" onChange={this.onExpireChecked}/>{' '}{gettext('Add auto expiration')}
|
||||||
|
</Label>
|
||||||
|
</FormGroup>
|
||||||
|
{this.state.isExpireChecked &&
|
||||||
|
<FormGroup check>
|
||||||
|
<Label check>
|
||||||
|
<Input className="expire-input expire-input-border" type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} readOnly={!this.state.isExpireChecked}/><span className="expir-span">{gettext('days')}</span>
|
||||||
|
</Label>
|
||||||
|
</FormGroup>
|
||||||
|
}
|
||||||
{this.state.errorInfo && <Alert color="danger" className="mt-2">{this.state.errorInfo}</Alert>}
|
{this.state.errorInfo && <Alert color="danger" className="mt-2">{this.state.errorInfo}</Alert>}
|
||||||
<Button className="generate-link-btn" onClick={this.generateUploadLink}>{gettext('Generate')}</Button>
|
<Button className="generate-link-btn" onClick={this.generateUploadLink}>{gettext('Generate')}</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
43
frontend/src/components/dialog/leave-group-dialog.js
Normal file
43
frontend/src/components/dialog/leave-group-dialog.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext, username } from '../../utils/constants';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
|
||||||
|
|
||||||
|
class LeaveGroupDialog extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveGroup = () => {
|
||||||
|
seafileAPI.quitGroup(this.props.groupID, username).then((res)=> {
|
||||||
|
this.props.onGroupChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return(
|
||||||
|
<Modal isOpen={true} toggle={this.props.toggleLeaveGroupDialog}>
|
||||||
|
<ModalHeader toggle={this.props.toggleLeaveGroupDialog}>{gettext('Leave Group')}</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p>{gettext('Really want to leave this group?')}</p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggleLeaveGroupDialog}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.leaveGroup}>{gettext('Leave')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeaveGroupDialogPropTypes = {
|
||||||
|
toggleLeaveGroupDialog: PropTypes.func.isRequired,
|
||||||
|
groupID: PropTypes.string.isRequired,
|
||||||
|
onGroupChanged: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
LeaveGroupDialog.propTypes = LeaveGroupDialogPropTypes;
|
||||||
|
|
||||||
|
export default LeaveGroupDialog;
|
@ -128,43 +128,31 @@ class FileUploader extends React.Component {
|
|||||||
this.resumable.on('dragstart', this.onDragStart.bind(this));
|
this.resumable.on('dragstart', this.onDragStart.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
onChunkingComplete = (file) => {
|
onChunkingComplete = (resumableFile) => {
|
||||||
if (file.relativePath !== file.fileName) {
|
//get parent_dir relative_path
|
||||||
return; // is upload a folder;
|
|
||||||
}
|
|
||||||
if (enableResumableFileUpload) {
|
|
||||||
let repoID = this.props.repoID;
|
|
||||||
seafileAPI.getFileUploadedBytes(repoID, this.props.path, file.fileName).then(res => {
|
|
||||||
let uploadedBytes = res.data.uploadedBytes;
|
|
||||||
let offset = Math.floor(uploadedBytes / (1024 * 1024));
|
|
||||||
file.markChunksCompleted(offset);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFileAdded = (resumableFile, files) => {
|
|
||||||
//get parent_dir、relative_path;
|
|
||||||
let path = this.props.path === '/' ? '/' : this.props.path + '/';
|
let path = this.props.path === '/' ? '/' : this.props.path + '/';
|
||||||
let fileName = resumableFile.fileName;
|
let fileName = resumableFile.fileName;
|
||||||
let relativePath = resumableFile.relativePath;
|
let relativePath = resumableFile.relativePath;
|
||||||
let isFile = fileName === relativePath;
|
let isFile = fileName === relativePath;
|
||||||
|
|
||||||
//update formdata;
|
//update formdata
|
||||||
resumableFile.formData = {};
|
resumableFile.formData = {};
|
||||||
if (isFile) {
|
if (isFile) { // upload file
|
||||||
resumableFile.formData = {
|
resumableFile.formData = {
|
||||||
parent_dir: path,
|
parent_dir: path,
|
||||||
};
|
};
|
||||||
} else {
|
} else { // upload folder
|
||||||
let relative_path = relativePath.slice(0, relativePath.lastIndexOf('/') + 1);
|
let relative_path = relativePath.slice(0, relativePath.lastIndexOf('/') + 1);
|
||||||
resumableFile.formData = {
|
resumableFile.formData = {
|
||||||
parent_dir: path,
|
parent_dir: path,
|
||||||
relative_path: relative_path
|
relative_path: relative_path
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//check repetition
|
onFileAdded = (resumableFile, files) => {
|
||||||
//uploading is file and only upload one file
|
let isFile = resumableFile.fileName === resumableFile.relativePath;
|
||||||
|
// uploading is file and only upload one file
|
||||||
if (isFile && files.length === 1) {
|
if (isFile && files.length === 1) {
|
||||||
let hasRepetition = false;
|
let hasRepetition = false;
|
||||||
let direntList = this.props.direntList;
|
let direntList = this.props.direntList;
|
||||||
@ -181,14 +169,28 @@ class FileUploader extends React.Component {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setUploadFileList(this.resumable.files);
|
this.setUploadFileList(this.resumable.files);
|
||||||
|
this.resumableUpload(resumableFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setUploadFileList(this.resumable.files);
|
||||||
|
if (isFile) {
|
||||||
|
this.resumableUpload(resumableFile);
|
||||||
|
} else {
|
||||||
this.resumable.upload();
|
this.resumable.upload();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.setUploadFileList(this.resumable.files);
|
|
||||||
this.resumable.upload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resumableUpload = (resumableFile) => {
|
||||||
|
let { repoID, path } = this.props;
|
||||||
|
seafileAPI.getFileUploadedBytes(repoID, path, resumableFile.fileName).then(res => {
|
||||||
|
let uploadedBytes = res.data.uploadedBytes;
|
||||||
|
let offset = Math.floor(uploadedBytes / (1024 * 1024));
|
||||||
|
resumableFile.markChunksCompleted(offset);
|
||||||
|
this.resumable.upload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
filesAddedComplete = (resumable, files) => {
|
filesAddedComplete = (resumable, files) => {
|
||||||
// single file uploading can check repetition, because custom dialog conn't prevent program execution;
|
// single file uploading can check repetition, because custom dialog conn't prevent program execution;
|
||||||
}
|
}
|
||||||
@ -416,7 +418,7 @@ class FileUploader extends React.Component {
|
|||||||
this.uploadInput.current.removeAttribute('webkitdirectory');
|
this.uploadInput.current.removeAttribute('webkitdirectory');
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
||||||
this.resumable.opts.target = res.data;
|
this.resumable.opts.target = res.data + '?ret-json=1';
|
||||||
if (Utils.isIEBrower()) {
|
if (Utils.isIEBrower()) {
|
||||||
this.uploadInput.current.click();
|
this.uploadInput.current.click();
|
||||||
}
|
}
|
||||||
@ -430,7 +432,7 @@ class FileUploader extends React.Component {
|
|||||||
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
|
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
||||||
this.resumable.opts.target = res.data;
|
this.resumable.opts.target = res.data + '?ret-json=1';
|
||||||
if (Utils.isIEBrower()) {
|
if (Utils.isIEBrower()) {
|
||||||
this.uploadInput.current.click();
|
this.uploadInput.current.click();
|
||||||
}
|
}
|
||||||
@ -444,7 +446,7 @@ class FileUploader extends React.Component {
|
|||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
|
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
|
||||||
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
|
||||||
this.resumable.opts.target = res.data;
|
this.resumable.opts.target = res.data + '?ret-json=1';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import MediaQuery from 'react-responsive';
|
import MediaQuery from 'react-responsive';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { gettext, siteRoot } from '../../utils/constants';
|
import { gettext, siteRoot } from '../../utils/constants';
|
||||||
import SearchResultItem from './search-result-item';
|
import SearchResultItem from './search-result-item';
|
||||||
import editorUtilities from '../../utils/editor-utilties';
|
import editorUtilities from '../../utils/editor-utilties';
|
||||||
import More from '../more';
|
import More from '../more';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
isPublic: PropTypes.bool,
|
||||||
repoID: PropTypes.string,
|
repoID: PropTypes.string,
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
onSearchedClick: PropTypes.func.isRequired,
|
onSearchedClick: PropTypes.func.isRequired,
|
||||||
@ -93,27 +95,53 @@ class Search extends Component {
|
|||||||
|
|
||||||
sendRequest(queryData, cancelToken) {
|
sendRequest(queryData, cancelToken) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
editorUtilities.searchFiles(queryData,cancelToken).then(res => {
|
let isPublic = this.props.isPublic;
|
||||||
if (!res.data.total) {
|
|
||||||
|
if (isPublic) {
|
||||||
|
seafileAPI.searchFilesInPublishedRepo(queryData.q, queryData.search_repo).then(res => {
|
||||||
|
if (!res.data.total) {
|
||||||
|
_this.setState({
|
||||||
|
resultItems: [],
|
||||||
|
isResultGetted: true
|
||||||
|
});
|
||||||
|
_this.source = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = _this.formatResultItems(res.data.results);
|
||||||
_this.setState({
|
_this.setState({
|
||||||
resultItems: [],
|
resultItems: items,
|
||||||
isResultGetted: true
|
isResultGetted: true
|
||||||
});
|
});
|
||||||
_this.source = null;
|
_this.source = null;
|
||||||
return;
|
}).catch(res => {
|
||||||
}
|
/* eslint-disable */
|
||||||
|
console.log(res);
|
||||||
let items = _this.formatResultItems(res.data.results);
|
/* eslint-enable */
|
||||||
_this.setState({
|
|
||||||
resultItems: items,
|
|
||||||
isResultGetted: true
|
|
||||||
});
|
});
|
||||||
_this.source = null;
|
} else {
|
||||||
}).catch(res => {
|
editorUtilities.searchFiles(queryData,cancelToken).then(res => {
|
||||||
/* eslint-disable */
|
if (!res.data.total) {
|
||||||
console.log(res);
|
_this.setState({
|
||||||
/* eslint-enable */
|
resultItems: [],
|
||||||
});
|
isResultGetted: true
|
||||||
|
});
|
||||||
|
_this.source = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = _this.formatResultItems(res.data.results);
|
||||||
|
_this.setState({
|
||||||
|
resultItems: items,
|
||||||
|
isResultGetted: true
|
||||||
|
});
|
||||||
|
_this.source = null;
|
||||||
|
}).catch(res => {
|
||||||
|
/* eslint-disable */
|
||||||
|
console.log(res);
|
||||||
|
/* eslint-enable */
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelRequest() {
|
cancelRequest() {
|
||||||
|
@ -10,6 +10,8 @@ class SharedUploadInfo {
|
|||||||
this.ctime = object.ctime;
|
this.ctime = object.ctime;
|
||||||
this.token = object.token;
|
this.token = object.token;
|
||||||
this.view_cnt = object.view_cnt;
|
this.view_cnt = object.view_cnt;
|
||||||
|
this.expire_date = object.expire_date;
|
||||||
|
this.is_expired = object.is_expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import RenameGroupDialog from '../../components/dialog/rename-group-dialog';
|
|||||||
import TransferGroupDialog from '../../components/dialog/transfer-group-dialog';
|
import TransferGroupDialog from '../../components/dialog/transfer-group-dialog';
|
||||||
// import ImportMembersDialog from '../../components/dialog/import-members-dialog';
|
// import ImportMembersDialog from '../../components/dialog/import-members-dialog';
|
||||||
import ManageMembersDialog from '../../components/dialog/manage-members-dialog';
|
import ManageMembersDialog from '../../components/dialog/manage-members-dialog';
|
||||||
|
import LeaveGroupDialog from '../../components/dialog/leave-group-dialog';
|
||||||
import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view';
|
import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view';
|
||||||
import LibDetail from '../../components/dirent-detail/lib-details';
|
import LibDetail from '../../components/dirent-detail/lib-details';
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ class GroupView extends React.Component {
|
|||||||
showManageMembersDialog: false,
|
showManageMembersDialog: false,
|
||||||
groupMembers: [],
|
groupMembers: [],
|
||||||
isShowDetails: false,
|
isShowDetails: false,
|
||||||
|
isLeaveGroupDialogOpen: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +309,13 @@ class GroupView extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleLeaveGroupDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
isLeaveGroupDialogOpen: !this.state.isLeaveGroupDialogOpen,
|
||||||
|
showGroupDropdown: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
listGroupMembers = () => {
|
listGroupMembers = () => {
|
||||||
seafileAPI.listGroupMembers(this.props.groupID).then((res) => {
|
seafileAPI.listGroupMembers(this.props.groupID).then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -407,7 +416,7 @@ class GroupView extends React.Component {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="path-tool">
|
<div className="path-tool">
|
||||||
{ (isShowSettingIcon && this.state.isStaff) &&
|
{ isShowSettingIcon &&
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<a href="#" className="sf2-icon-cog1 action-icon group-top-action-icon" title="Settings" id="settings"
|
<a href="#" className="sf2-icon-cog1 action-icon group-top-action-icon" title="Settings" id="settings"
|
||||||
onClick={this.toggleGroupDropdown}></a>
|
onClick={this.toggleGroupDropdown}></a>
|
||||||
@ -419,6 +428,7 @@ class GroupView extends React.Component {
|
|||||||
onClick={this.toggleGroupDropdown}></a>
|
onClick={this.toggleGroupDropdown}></a>
|
||||||
</div>
|
</div>
|
||||||
<div className="sf-popover-con">
|
<div className="sf-popover-con">
|
||||||
|
{(this.state.isStaff || this.state.isOwner) &&
|
||||||
<ul className="sf-popover-list">
|
<ul className="sf-popover-list">
|
||||||
<li><a href="#" className="sf-popover-item" onClick={this.toggleRenameGroupDialog} >{gettext('Rename')}</a></li>
|
<li><a href="#" className="sf-popover-item" onClick={this.toggleRenameGroupDialog} >{gettext('Rename')}</a></li>
|
||||||
{
|
{
|
||||||
@ -426,16 +436,25 @@ class GroupView extends React.Component {
|
|||||||
<li><a href="#" className="sf-popover-item" onClick={this.toggleTransferGroupDialog} >{gettext('Transfer')}</a></li>
|
<li><a href="#" className="sf-popover-item" onClick={this.toggleTransferGroupDialog} >{gettext('Transfer')}</a></li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
}
|
||||||
|
{(this.state.isStaff || this.state.isOwner) &&
|
||||||
<ul className="sf-popover-list">
|
<ul className="sf-popover-list">
|
||||||
{/* <li><a href="#" className="sf-popover-item" onClick={this.toggleImportMembersDialog} >{gettext('Import Members')}</a></li> */}
|
{/* <li><a href="#" className="sf-popover-item" onClick={this.toggleImportMembersDialog} >{gettext('Import Members')}</a></li> */}
|
||||||
<li><a href="#" className="sf-popover-item" onClick={this.toggleManageMembersDialog} >{gettext('Manage Members')}</a></li>
|
<li><a href="#" className="sf-popover-item" onClick={this.toggleManageMembersDialog} >{gettext('Manage Members')}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
}
|
||||||
{
|
{
|
||||||
this.state.isOwner &&
|
this.state.isOwner &&
|
||||||
<ul className="sf-popover-list">
|
<ul className="sf-popover-list">
|
||||||
<li><a href="#" className="sf-popover-item" onClick={this.toggleDismissGroupDialog}>{gettext('Delete Group')}</a></li>
|
<li><a href="#" className="sf-popover-item" onClick={this.toggleDismissGroupDialog}>{gettext('Delete Group')}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
|
{/* gourp owner only can dissmiss group, admin could not quit, department member could not quit */}
|
||||||
|
{(!this.state.isOwner && !this.state.isStaff && !isDepartmentGroup) &&
|
||||||
|
<ul className="sf-popover-list">
|
||||||
|
<li><a href="#" className="sf-popover-item" onClick={this.toggleLeaveGroupDialog}>{gettext('Leave Group')}</a></li>
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -557,6 +576,13 @@ class GroupView extends React.Component {
|
|||||||
isOwner={this.state.isOwner}
|
isOwner={this.state.isOwner}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
{this.state.isLeaveGroupDialogOpen &&
|
||||||
|
<LeaveGroupDialog
|
||||||
|
toggleLeaveGroupDialog={this.toggleLeaveGroupDialog}
|
||||||
|
groupID={this.props.groupID}
|
||||||
|
onGroupChanged={this.props.onGroupChanged}
|
||||||
|
/>
|
||||||
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import { Link } from '@reach/router';
|
import { Link } from '@reach/router';
|
||||||
|
import moment from 'moment';
|
||||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||||
import { gettext, siteRoot, loginUrl, canGenerateShareLink } from '../../utils/constants';
|
import { gettext, siteRoot, loginUrl, canGenerateShareLink } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
@ -52,9 +53,10 @@ class Content extends Component {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="4%">{/*icon*/}</th>
|
<th width="4%">{/*icon*/}</th>
|
||||||
<th width="42%">{gettext('Name')}</th>
|
<th width="30%">{gettext('Name')}</th>
|
||||||
<th width="30%">{gettext('Library')}</th>
|
<th width="24%">{gettext('Library')}</th>
|
||||||
<th width="14%">{gettext('Visits')}</th>
|
<th width="16%">{gettext('Visits')}</th>
|
||||||
|
<th width="16%">{gettext('Expiration')}</th>
|
||||||
<th width="10%">{/*Operations*/}</th>
|
<th width="10%">{/*Operations*/}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -114,6 +116,24 @@ class Item extends Component {
|
|||||||
return { iconUrl, uploadUrl };
|
return { iconUrl, uploadUrl };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderExpriedData = () => {
|
||||||
|
let item = this.props.item;
|
||||||
|
if (!item.expire_date) {
|
||||||
|
return (
|
||||||
|
<Fragment>--</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let expire_date = moment(item.expire_date).format('YYYY-MM-DD');
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{item.is_expired ?
|
||||||
|
<span className="error">{expire_date}</span> :
|
||||||
|
expire_date
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let item = this.props.item;
|
let item = this.props.item;
|
||||||
let { iconUrl, uploadUrl } = this.getUploadParams();
|
let { iconUrl, uploadUrl } = this.getUploadParams();
|
||||||
@ -128,6 +148,7 @@ class Item extends Component {
|
|||||||
<td><Link to={uploadUrl}>{item.obj_name}</Link></td>
|
<td><Link to={uploadUrl}>{item.obj_name}</Link></td>
|
||||||
<td><Link to={`${siteRoot}library/${item.repo_id}/${item.repo_name}`}>{item.repo_name}</Link></td>
|
<td><Link to={`${siteRoot}library/${item.repo_id}/${item.repo_name}`}>{item.repo_name}</Link></td>
|
||||||
<td>{item.view_cnt}</td>
|
<td>{item.view_cnt}</td>
|
||||||
|
<td>{this.renderExpriedData()}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" className={linkIconClassName} title={gettext('View')} onClick={this.viewLink}></a>
|
<a href="#" className={linkIconClassName} title={gettext('View')} onClick={this.viewLink}></a>
|
||||||
<a href="#" className={deleteIconClassName} title={gettext('Remove')} onClick={this.removeLink}></a>
|
<a href="#" className={deleteIconClassName} title={gettext('Remove')} onClick={this.removeLink}></a>
|
||||||
|
@ -5,7 +5,7 @@ import Logo from '../../components/logo';
|
|||||||
import { gettext, siteRoot, isPro, isDefaultAdmin, canViewSystemInfo, canViewStatistic,
|
import { gettext, siteRoot, isPro, isDefaultAdmin, canViewSystemInfo, canViewStatistic,
|
||||||
canConfigSystem, canManageLibrary, canManageUser, canManageGroup, canViewUserLog,
|
canConfigSystem, canManageLibrary, canManageUser, canManageGroup, canViewUserLog,
|
||||||
canViewAdminLog, constanceEnabled, multiTenancy, multiInstitution, sysadminExtraEnabled,
|
canViewAdminLog, constanceEnabled, multiTenancy, multiInstitution, sysadminExtraEnabled,
|
||||||
enableGuestInvitation, enableTermsAndConditions, enableFileScan, enableWorkWeixinDepartments } from '../../utils/constants';
|
enableGuestInvitation, enableTermsAndConditions, enableFileScan, enableWorkWeixin } from '../../utils/constants';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
isSidePanelClosed: PropTypes.bool.isRequired,
|
isSidePanelClosed: PropTypes.bool.isRequired,
|
||||||
@ -174,7 +174,7 @@ class SidePanel extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
{isDefaultAdmin && enableWorkWeixinDepartments &&
|
{isDefaultAdmin && enableWorkWeixin &&
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<Link className={`nav-link ellipsis ${this.getActiveClass('departments')}`} to={siteRoot + 'sys/work-weixin/departments/'}>
|
<Link className={`nav-link ellipsis ${this.getActiveClass('departments')}`} to={siteRoot + 'sys/work-weixin/departments/'}>
|
||||||
<span className="sf3-font-enterprise-wechat sf3-font" aria-hidden="true"></span>
|
<span className="sf3-font-enterprise-wechat sf3-font" aria-hidden="true"></span>
|
||||||
|
@ -7,6 +7,7 @@ import WikiMarkdownViewer from '../../components/wiki-markdown-viewer';
|
|||||||
import WikiDirListView from '../../components/wiki-dir-list-view/wiki-dir-list-view';
|
import WikiDirListView from '../../components/wiki-dir-list-view/wiki-dir-list-view';
|
||||||
import Loading from '../../components/loading';
|
import Loading from '../../components/loading';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
|
import Search from '../../components/search/search';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
@ -77,6 +78,21 @@ class MainPanel extends Component {
|
|||||||
return (
|
return (
|
||||||
<div className="main-panel wiki-main-panel o-hidden">
|
<div className="main-panel wiki-main-panel o-hidden">
|
||||||
<div className="main-panel-north panel-top border-left-show">
|
<div className="main-panel-north panel-top border-left-show">
|
||||||
|
{!username &&
|
||||||
|
<Fragment>
|
||||||
|
<div className="cur-view-toolbar">
|
||||||
|
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title="Side Nav Menu" onClick={this.onMenuClick}></span>
|
||||||
|
</div>
|
||||||
|
<div className="common-toolbar">
|
||||||
|
<Search
|
||||||
|
isPublic={true}
|
||||||
|
repoID={repoID}
|
||||||
|
onSearchedClick={this.props.onSearchedClick}
|
||||||
|
placeholder={gettext('Search files in this library')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
{username && (
|
{username && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="cur-view-toolbar">
|
<div className="cur-view-toolbar">
|
||||||
|
@ -119,5 +119,5 @@ export const canManageUser = window.sysadmin ? window.sysadmin.pageOptions.admin
|
|||||||
export const canManageGroup = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_manage_group : '';
|
export const canManageGroup = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_manage_group : '';
|
||||||
export const canViewUserLog = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_view_user_log : '';
|
export const canViewUserLog = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_view_user_log : '';
|
||||||
export const canViewAdminLog = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_view_admin_log : '';
|
export const canViewAdminLog = window.sysadmin ? window.sysadmin.pageOptions.admin_permissions.can_view_admin_log : '';
|
||||||
export const enableWorkWeixinDepartments = window.sysadmin ? window.sysadmin.pageOptions.enable_work_weixin_departments : '';
|
export const enableWorkWeixin = window.sysadmin ? window.sysadmin.pageOptions.enable_work_weixin : '';
|
||||||
|
|
||||||
|
@ -240,7 +240,8 @@ export const Utils = {
|
|||||||
|
|
||||||
isSupportUploadFolder: function() {
|
isSupportUploadFolder: function() {
|
||||||
return navigator.userAgent.indexOf('Firefox')!=-1 ||
|
return navigator.userAgent.indexOf('Firefox')!=-1 ||
|
||||||
navigator.userAgent.indexOf('Chrome') > -1;
|
navigator.userAgent.indexOf('Chrome') > -1 ||
|
||||||
|
navigator.userAgent.indexOf("Safari") > -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
isIEBrower: function() { // is ie <= ie11 not include Edge
|
isIEBrower: function() { // is ie <= ie11 not include Edge
|
||||||
|
102
seahub/api2/endpoints/public_repos_search.py
Normal file
102
seahub/api2/endpoints/public_repos_search.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticatedOrReadOnly
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
from seaserv import seafile_api
|
||||||
|
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.utils.repo import is_valid_repo_id_format
|
||||||
|
from seahub.utils import HAS_FILE_SEARCH
|
||||||
|
from seahub.wiki.models import Wiki
|
||||||
|
if HAS_FILE_SEARCH:
|
||||||
|
from seahub_extra.search.utils import search_files
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('seafes')
|
||||||
|
|
||||||
|
|
||||||
|
class PublishedRepoSearchView(APIView):
|
||||||
|
""" Search public repos
|
||||||
|
"""
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticatedOrReadOnly,)
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
# is search supported
|
||||||
|
if not HAS_FILE_SEARCH:
|
||||||
|
error_msg = 'Search not supported.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# argument check
|
||||||
|
keyword = request.GET.get('q', None)
|
||||||
|
if not keyword:
|
||||||
|
error_msg = 'q invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
repo_id = request.GET.get('repo_id', None)
|
||||||
|
if not is_valid_repo_id_format(repo_id):
|
||||||
|
error_msg = 'repo_id invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# recourse check
|
||||||
|
repo = seafile_api.get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
wiki = Wiki.objects.filter(repo_id=repo_id)[0]
|
||||||
|
if not wiki.has_read_perm(request):
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_page = int(request.GET.get('page', '1'))
|
||||||
|
per_page = int(request.GET.get('per_page', '10'))
|
||||||
|
if per_page > 100:
|
||||||
|
per_page = 100
|
||||||
|
except ValueError:
|
||||||
|
current_page = 1
|
||||||
|
per_page = 10
|
||||||
|
|
||||||
|
start = (current_page - 1) * per_page
|
||||||
|
size = per_page
|
||||||
|
if start < 0 or size < 0:
|
||||||
|
error_msg = 'page or per_page invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
repo_id_map = {}
|
||||||
|
map_id = repo.origin_repo_id if repo.origin_repo_id else repo_id
|
||||||
|
repo_id_map[map_id] = repo
|
||||||
|
# search file
|
||||||
|
try:
|
||||||
|
results, total = search_files(
|
||||||
|
repo_id_map, None, keyword, None, start, size, org_id=None
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
for result in results:
|
||||||
|
result.pop('repo', None)
|
||||||
|
result.pop('exists', None)
|
||||||
|
result.pop('last_modified_by', None)
|
||||||
|
result.pop('name_highlight', None)
|
||||||
|
result.pop('score', None)
|
||||||
|
result['repo_type'] = 'public'
|
||||||
|
|
||||||
|
has_more = True if total > current_page * per_page else False
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
"total": total,
|
||||||
|
"results": results,
|
||||||
|
"has_more": has_more
|
||||||
|
})
|
@ -2,6 +2,7 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from constance import config
|
from constance import config
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
@ -9,6 +10,7 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
@ -48,6 +50,11 @@ def get_upload_link_info(uls):
|
|||||||
else:
|
else:
|
||||||
ctime = ''
|
ctime = ''
|
||||||
|
|
||||||
|
if uls.expire_date:
|
||||||
|
expire_date = datetime_to_isoformat_timestr(uls.expire_date)
|
||||||
|
else:
|
||||||
|
expire_date = ''
|
||||||
|
|
||||||
data['repo_id'] = repo_id
|
data['repo_id'] = repo_id
|
||||||
data['repo_name'] = repo.repo_name if repo else ''
|
data['repo_name'] = repo.repo_name if repo else ''
|
||||||
data['path'] = path
|
data['path'] = path
|
||||||
@ -57,6 +64,8 @@ def get_upload_link_info(uls):
|
|||||||
data['link'] = gen_shared_upload_link(token)
|
data['link'] = gen_shared_upload_link(token)
|
||||||
data['token'] = token
|
data['token'] = token
|
||||||
data['username'] = uls.username
|
data['username'] = uls.username
|
||||||
|
data['expire_date'] = expire_date
|
||||||
|
data['is_expired'] = uls.is_expired()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -143,6 +152,12 @@ class UploadLinks(APIView):
|
|||||||
error_msg = _('Password is too short')
|
error_msg = _('Password is too short')
|
||||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
expire_days = int(request.data.get('expire_days', 0))
|
||||||
|
except ValueError:
|
||||||
|
error_msg = 'expire_days invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
# resource check
|
# resource check
|
||||||
repo = seafile_api.get_repo(repo_id)
|
repo = seafile_api.get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
@ -164,11 +179,16 @@ class UploadLinks(APIView):
|
|||||||
error_msg = 'Permission denied.'
|
error_msg = 'Permission denied.'
|
||||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
if expire_days <= 0:
|
||||||
|
expire_date = None
|
||||||
|
else:
|
||||||
|
expire_date = timezone.now() + relativedelta(days=expire_days)
|
||||||
|
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
uls = UploadLinkShare.objects.get_upload_link_by_path(username, repo_id, path)
|
uls = UploadLinkShare.objects.get_upload_link_by_path(username, repo_id, path)
|
||||||
if not uls:
|
if not uls:
|
||||||
uls = UploadLinkShare.objects.create_upload_link_share(username,
|
uls = UploadLinkShare.objects.create_upload_link_share(username,
|
||||||
repo_id, path, password)
|
repo_id, path, password, expire_date)
|
||||||
|
|
||||||
link_info = get_upload_link_info(uls)
|
link_info = get_upload_link_info(uls)
|
||||||
return Response(link_info)
|
return Response(link_info)
|
||||||
|
@ -186,7 +186,7 @@ def login(request, template_name='registration/login.html',
|
|||||||
getattr(settings, 'ENABLE_OAUTH', False) or \
|
getattr(settings, 'ENABLE_OAUTH', False) or \
|
||||||
getattr(settings, 'ENABLE_CAS', False) or \
|
getattr(settings, 'ENABLE_CAS', False) or \
|
||||||
getattr(settings, 'ENABLE_REMOTE_USER_AUTHENTICATION', False) or \
|
getattr(settings, 'ENABLE_REMOTE_USER_AUTHENTICATION', False) or \
|
||||||
getattr(settings, 'ENABLE_WORK_WEIXIN_OAUTH', False)
|
getattr(settings, 'ENABLE_WORK_WEIXIN', False)
|
||||||
|
|
||||||
login_bg_image_path = get_login_bg_image_path()
|
login_bg_image_path = get_login_bg_image_path()
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ try:
|
|||||||
from seahub.settings import ENABLE_FILE_SCAN
|
from seahub.settings import ENABLE_FILE_SCAN
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENABLE_FILE_SCAN = False
|
ENABLE_FILE_SCAN = False
|
||||||
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN_DEPARTMENTS
|
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN
|
||||||
|
|
||||||
|
|
||||||
def base(request):
|
def base(request):
|
||||||
@ -134,7 +134,7 @@ def base(request):
|
|||||||
'enable_resumable_fileupload': dj_settings.ENABLE_RESUMABLE_FILEUPLOAD,
|
'enable_resumable_fileupload': dj_settings.ENABLE_RESUMABLE_FILEUPLOAD,
|
||||||
'service_url': get_service_url().rstrip('/'),
|
'service_url': get_service_url().rstrip('/'),
|
||||||
'enable_file_scan': ENABLE_FILE_SCAN,
|
'enable_file_scan': ENABLE_FILE_SCAN,
|
||||||
'enable_work_weixin_departments': ENABLE_WORK_WEIXIN_DEPARTMENTS,
|
'enable_work_weixin': ENABLE_WORK_WEIXIN,
|
||||||
'avatar_url': avatar_url if avatar_url else '',
|
'avatar_url': avatar_url if avatar_url else '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from seahub.auth.backends import RemoteUserBackend
|
|||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
from registration.models import (notify_admins_on_activate_request,
|
from registration.models import (notify_admins_on_activate_request,
|
||||||
notify_admins_on_register_complete)
|
notify_admins_on_register_complete)
|
||||||
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN_OAUTH
|
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN
|
||||||
|
|
||||||
class OauthRemoteUserBackend(RemoteUserBackend):
|
class OauthRemoteUserBackend(RemoteUserBackend):
|
||||||
"""
|
"""
|
||||||
@ -23,7 +23,7 @@ class OauthRemoteUserBackend(RemoteUserBackend):
|
|||||||
# Create active user by default.
|
# Create active user by default.
|
||||||
activate_after_creation = getattr(settings, 'OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
|
activate_after_creation = getattr(settings, 'OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
|
||||||
|
|
||||||
if ENABLE_WORK_WEIXIN_OAUTH:
|
if ENABLE_WORK_WEIXIN:
|
||||||
create_unknown_user = getattr(settings, 'WORK_WEIXIN_OAUTH_CREATE_UNKNOWN_USER', True)
|
create_unknown_user = getattr(settings, 'WORK_WEIXIN_OAUTH_CREATE_UNKNOWN_USER', True)
|
||||||
activate_after_creation = getattr(settings, 'WORK_WEIXIN_OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
|
activate_after_creation = getattr(settings, 'WORK_WEIXIN_OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
|
||||||
|
|
||||||
|
@ -303,12 +303,8 @@ SOCIAL_AUTH_PIPELINE = (
|
|||||||
ENABLE_OAUTH = False
|
ENABLE_OAUTH = False
|
||||||
ENABLE_WATERMARK = False
|
ENABLE_WATERMARK = False
|
||||||
|
|
||||||
# allow user scan the work weixin qrcode to login
|
# enable work weixin
|
||||||
ENABLE_WORK_WEIXIN_OAUTH = False
|
ENABLE_WORK_WEIXIN = False
|
||||||
# allow seafile admin import user from work weixin
|
|
||||||
ENABLE_WORK_WEIXIN_DEPARTMENTS = False
|
|
||||||
# allow send unread msg to work weixin
|
|
||||||
ENABLE_WORK_WEIXIN_NOTIFICATIONS = False
|
|
||||||
|
|
||||||
# allow user to clean library trash
|
# allow user to clean library trash
|
||||||
ENABLE_USER_CLEAN_TRASH = True
|
ENABLE_USER_CLEAN_TRASH = True
|
||||||
@ -917,5 +913,5 @@ if ENABLE_REMOTE_USER_AUTHENTICATION:
|
|||||||
MIDDLEWARE_CLASSES += ('seahub.auth.middleware.SeafileRemoteUserMiddleware',)
|
MIDDLEWARE_CLASSES += ('seahub.auth.middleware.SeafileRemoteUserMiddleware',)
|
||||||
AUTHENTICATION_BACKENDS += ('seahub.auth.backends.SeafileRemoteUserBackend',)
|
AUTHENTICATION_BACKENDS += ('seahub.auth.backends.SeafileRemoteUserBackend',)
|
||||||
|
|
||||||
if ENABLE_OAUTH or ENABLE_WORK_WEIXIN_OAUTH:
|
if ENABLE_OAUTH or ENABLE_WORK_WEIXIN:
|
||||||
AUTHENTICATION_BACKENDS += ('seahub.oauth.backends.OauthRemoteUserBackend',)
|
AUTHENTICATION_BACKENDS += ('seahub.oauth.backends.OauthRemoteUserBackend',)
|
||||||
|
@ -451,6 +451,12 @@ class UploadLinkShare(models.Model):
|
|||||||
def is_owner(self, owner):
|
def is_owner(self, owner):
|
||||||
return owner == self.username
|
return owner == self.username
|
||||||
|
|
||||||
|
def is_expired(self):
|
||||||
|
if self.expire_date is not None and timezone.now() > self.expire_date:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
class PrivateFileDirShareManager(models.Manager):
|
class PrivateFileDirShareManager(models.Manager):
|
||||||
def add_private_file_share(self, from_user, to_user, repo_id, path, perm):
|
def add_private_file_share(self, from_user, to_user, repo_id, path, perm):
|
||||||
"""
|
"""
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_default_admin and enable_work_weixin_departments %}
|
{% if is_default_admin and enable_work_weixin %}
|
||||||
<li class="tab">
|
<li class="tab">
|
||||||
<a href="{{ SITE_ROOT }}sys/work-weixin/departments/"><span class="sf3-font-enterprise-wechat sf3-font"></span>企业微信集成</a>
|
<a href="{{ SITE_ROOT }}sys/work-weixin/departments/"><span class="sf3-font-enterprise-wechat sf3-font"></span>企业微信集成</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -133,7 +133,7 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_default_admin and enable_work_weixin_departments %}
|
{% if is_default_admin and enable_work_weixin %}
|
||||||
<li class="tab">
|
<li class="tab">
|
||||||
<a href="{{ SITE_ROOT }}sys/work-weixin/departments/"><span class="sf3-font-enterprise-wechat sf3-font"></span>企业微信集成</a>
|
<a href="{{ SITE_ROOT }}sys/work-weixin/departments/"><span class="sf3-font-enterprise-wechat sf3-font"></span>企业微信集成</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -108,7 +108,7 @@ app["pageOptions"] = {
|
|||||||
cur_note: {% if request.cur_note %} {'id': '{{ request.cur_note.id }}'} {% else %} null {% endif %},
|
cur_note: {% if request.cur_note %} {'id': '{{ request.cur_note.id }}'} {% else %} null {% endif %},
|
||||||
is_default_admin: {% if is_default_admin %} true {% else %} false {% endif %},
|
is_default_admin: {% if is_default_admin %} true {% else %} false {% endif %},
|
||||||
enable_file_scan: {% if enable_file_scan %} true {% else %} false {% endif %},
|
enable_file_scan: {% if enable_file_scan %} true {% else %} false {% endif %},
|
||||||
enable_work_weixin_departments: {% if enable_work_weixin_departments %} true {% else %} false {% endif %},
|
enable_work_weixin: {% if enable_work_weixin %} true {% else %} false {% endif %},
|
||||||
admin_permissions: {
|
admin_permissions: {
|
||||||
"can_view_system_info": {% if user.admin_permissions.can_view_system_info %} true {% else %} false {% endif %},
|
"can_view_system_info": {% if user.admin_permissions.can_view_system_info %} true {% else %} false {% endif %},
|
||||||
"can_view_statistic": {% if user.admin_permissions.can_view_statistic %} true {% else %} false {% endif %},
|
"can_view_statistic": {% if user.admin_permissions.can_view_statistic %} true {% else %} false {% endif %},
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
enable_terms_and_conditions: {% if enable_terms_and_conditions %} true {% else %} false {% endif %},
|
enable_terms_and_conditions: {% if enable_terms_and_conditions %} true {% else %} false {% endif %},
|
||||||
is_default_admin: {% if is_default_admin %} true {% else %} false {% endif %},
|
is_default_admin: {% if is_default_admin %} true {% else %} false {% endif %},
|
||||||
enable_file_scan: {% if enable_file_scan %} true {% else %} false {% endif %},
|
enable_file_scan: {% if enable_file_scan %} true {% else %} false {% endif %},
|
||||||
enable_work_weixin_departments: {% if enable_work_weixin_departments %} true {% else %} false {% endif %},
|
enable_work_weixin: {% if enable_work_weixin %} true {% else %} false {% endif %},
|
||||||
admin_permissions: {
|
admin_permissions: {
|
||||||
"can_view_system_info": {% if user.admin_permissions.can_view_system_info %} true {% else %} false {% endif %},
|
"can_view_system_info": {% if user.admin_permissions.can_view_system_info %} true {% else %} false {% endif %},
|
||||||
"can_view_statistic": {% if user.admin_permissions.can_view_statistic %} true {% else %} false {% endif %},
|
"can_view_statistic": {% if user.admin_permissions.can_view_statistic %} true {% else %} false {% endif %},
|
||||||
|
@ -89,10 +89,12 @@ from seahub.api2.endpoints.related_files import RelatedFilesView, RelatedFileVie
|
|||||||
from seahub.api2.endpoints.webdav_secret import WebdavSecretView
|
from seahub.api2.endpoints.webdav_secret import WebdavSecretView
|
||||||
from seahub.api2.endpoints.starred_items import StarredItems
|
from seahub.api2.endpoints.starred_items import StarredItems
|
||||||
from seahub.api2.endpoints.markdown_lint import MarkdownLintView
|
from seahub.api2.endpoints.markdown_lint import MarkdownLintView
|
||||||
|
from seahub.api2.endpoints.public_repos_search import PublishedRepoSearchView
|
||||||
from seahub.api2.endpoints.dtable import WorkspacesView, DTableView, DTablesView, \
|
from seahub.api2.endpoints.dtable import WorkspacesView, DTableView, DTablesView, \
|
||||||
DTableUpdateLinkView, DTableAssetUploadLinkView, dtable_file_view, dtable_asset_access
|
DTableUpdateLinkView, DTableAssetUploadLinkView, dtable_file_view, dtable_asset_access
|
||||||
from seahub.api2.endpoints.dtable_share import SharedDTablesView, DTableShareView
|
from seahub.api2.endpoints.dtable_share import SharedDTablesView, DTableShareView
|
||||||
|
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
|
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
|
||||||
from seahub.api2.endpoints.admin.login_logs import LoginLogs, AdminLoginLogs
|
from seahub.api2.endpoints.admin.login_logs import LoginLogs, AdminLoginLogs
|
||||||
@ -366,6 +368,9 @@ urlpatterns = [
|
|||||||
# user: markdown-lint
|
# user: markdown-lint
|
||||||
url(r'^api/v2.1/markdown-lint/$', MarkdownLintView.as_view(), name='api-v2.1-markdown-lint'),
|
url(r'^api/v2.1/markdown-lint/$', MarkdownLintView.as_view(), name='api-v2.1-markdown-lint'),
|
||||||
|
|
||||||
|
# public repos search
|
||||||
|
url(r'^api/v2.1/published-repo-search/$', PublishedRepoSearchView.as_view(), name='api-v2.1-published-repo-search'),
|
||||||
|
|
||||||
# user: workspaces
|
# user: workspaces
|
||||||
url(r'^api/v2.1/workspaces/$', WorkspacesView.as_view(), name='api-v2.1-workspaces'),
|
url(r'^api/v2.1/workspaces/$', WorkspacesView.as_view(), name='api-v2.1-workspaces'),
|
||||||
url(r'^api/v2.1/dtables/$', DTablesView.as_view(), name='api-v2.1-dtables'),
|
url(r'^api/v2.1/dtables/$', DTablesView.as_view(), name='api-v2.1-dtables'),
|
||||||
|
@ -35,7 +35,7 @@ def sso(request):
|
|||||||
if getattr(settings, 'ENABLE_CAS', False):
|
if getattr(settings, 'ENABLE_CAS', False):
|
||||||
return HttpResponseRedirect(reverse('cas_ng_login') + next_param)
|
return HttpResponseRedirect(reverse('cas_ng_login') + next_param)
|
||||||
|
|
||||||
if getattr(settings, 'ENABLE_WORK_WEIXIN_OAUTH', False):
|
if getattr(settings, 'ENABLE_WORK_WEIXIN', False):
|
||||||
return HttpResponseRedirect(reverse('work_weixin_oauth_login') + next_param)
|
return HttpResponseRedirect(reverse('work_weixin_oauth_login') + next_param)
|
||||||
|
|
||||||
return HttpResponseRedirect(next_page)
|
return HttpResponseRedirect(next_page)
|
||||||
|
@ -98,7 +98,7 @@ try:
|
|||||||
from seahub.settings import ENABLE_FILE_SCAN
|
from seahub.settings import ENABLE_FILE_SCAN
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENABLE_FILE_SCAN = False
|
ENABLE_FILE_SCAN = False
|
||||||
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN_DEPARTMENTS
|
from seahub.work_weixin.settings import ENABLE_WORK_WEIXIN
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -133,7 +133,7 @@ def sysadmin(request):
|
|||||||
'enable_limit_ipaddress': ENABLE_LIMIT_IPADDRESS,
|
'enable_limit_ipaddress': ENABLE_LIMIT_IPADDRESS,
|
||||||
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
||||||
'enable_file_scan': ENABLE_FILE_SCAN,
|
'enable_file_scan': ENABLE_FILE_SCAN,
|
||||||
'enable_work_weixin_departments': ENABLE_WORK_WEIXIN_DEPARTMENTS,
|
'enable_work_weixin': ENABLE_WORK_WEIXIN,
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@ -148,7 +148,7 @@ def sysadmin_react_fake_view(request):
|
|||||||
'enable_guest_invitation': ENABLE_GUEST_INVITATION,
|
'enable_guest_invitation': ENABLE_GUEST_INVITATION,
|
||||||
'enable_terms_and_conditions': config.ENABLE_TERMS_AND_CONDITIONS,
|
'enable_terms_and_conditions': config.ENABLE_TERMS_AND_CONDITIONS,
|
||||||
'enable_file_scan': ENABLE_FILE_SCAN,
|
'enable_file_scan': ENABLE_FILE_SCAN,
|
||||||
'enable_work_weixin_departments': ENABLE_WORK_WEIXIN_DEPARTMENTS,
|
'enable_work_weixin': ENABLE_WORK_WEIXIN,
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
# # work weixin base
|
# # work weixin base
|
||||||
|
ENABLE_WORK_WEIXIN = getattr(settings, 'ENABLE_WORK_WEIXIN', False)
|
||||||
WORK_WEIXIN_CORP_ID = getattr(settings, 'WORK_WEIXIN_CORP_ID', '')
|
WORK_WEIXIN_CORP_ID = getattr(settings, 'WORK_WEIXIN_CORP_ID', '')
|
||||||
WORK_WEIXIN_AGENT_SECRET = getattr(settings, 'WORK_WEIXIN_AGENT_SECRET', '')
|
WORK_WEIXIN_AGENT_SECRET = getattr(settings, 'WORK_WEIXIN_AGENT_SECRET', '')
|
||||||
WORK_WEIXIN_ACCESS_TOKEN_URL = getattr(settings, 'WORK_WEIXIN_ACCESS_TOKEN_URL',
|
WORK_WEIXIN_ACCESS_TOKEN_URL = getattr(settings, 'WORK_WEIXIN_ACCESS_TOKEN_URL',
|
||||||
'https://qyapi.weixin.qq.com/cgi-bin/gettoken')
|
'https://qyapi.weixin.qq.com/cgi-bin/gettoken')
|
||||||
|
|
||||||
# # admin work weixin departments
|
# # admin work weixin departments
|
||||||
ENABLE_WORK_WEIXIN_DEPARTMENTS = getattr(settings, 'ENABLE_WORK_WEIXIN_DEPARTMENTS', False)
|
|
||||||
WORK_WEIXIN_DEPARTMENTS_URL = getattr(settings, 'WORK_WEIXIN_DEPARTMENTS_URL',
|
WORK_WEIXIN_DEPARTMENTS_URL = getattr(settings, 'WORK_WEIXIN_DEPARTMENTS_URL',
|
||||||
'https://qyapi.weixin.qq.com/cgi-bin/department/list')
|
'https://qyapi.weixin.qq.com/cgi-bin/department/list')
|
||||||
WORK_WEIXIN_DEPARTMENT_MEMBERS_URL = getattr(settings, 'WORK_WEIXIN_DEPARTMENT_MEMBERS_URL',
|
WORK_WEIXIN_DEPARTMENT_MEMBERS_URL = getattr(settings, 'WORK_WEIXIN_DEPARTMENT_MEMBERS_URL',
|
||||||
@ -17,7 +17,6 @@ WORK_WEIXIN_DEPARTMENT_MEMBERS_URL = getattr(settings, 'WORK_WEIXIN_DEPARTMENT_M
|
|||||||
|
|
||||||
# # work weixin oauth
|
# # work weixin oauth
|
||||||
WORK_WEIXIN_AGENT_ID = getattr(settings, 'WORK_WEIXIN_AGENT_ID', '')
|
WORK_WEIXIN_AGENT_ID = getattr(settings, 'WORK_WEIXIN_AGENT_ID', '')
|
||||||
ENABLE_WORK_WEIXIN_OAUTH = getattr(settings, 'ENABLE_WORK_WEIXIN_OAUTH', False)
|
|
||||||
WORK_WEIXIN_UID_PREFIX = WORK_WEIXIN_CORP_ID + '_'
|
WORK_WEIXIN_UID_PREFIX = WORK_WEIXIN_CORP_ID + '_'
|
||||||
WORK_WEIXIN_USER_INFO_AUTO_UPDATE = getattr(settings, 'WORK_WEIXIN_USER_INFO_AUTO_UPDATE', True)
|
WORK_WEIXIN_USER_INFO_AUTO_UPDATE = getattr(settings, 'WORK_WEIXIN_USER_INFO_AUTO_UPDATE', True)
|
||||||
WORK_WEIXIN_AUTHORIZATION_URL = getattr(settings, 'WORK_WEIXIN_AUTHORIZATION_URL',
|
WORK_WEIXIN_AUTHORIZATION_URL = getattr(settings, 'WORK_WEIXIN_AUTHORIZATION_URL',
|
||||||
@ -28,7 +27,6 @@ WORK_WEIXIN_GET_USER_PROFILE_URL = getattr(settings, 'WORK_WEIXIN_GET_USER_PROFI
|
|||||||
'https://qyapi.weixin.qq.com/cgi-bin/user/get')
|
'https://qyapi.weixin.qq.com/cgi-bin/user/get')
|
||||||
|
|
||||||
# # work weixin notifications
|
# # work weixin notifications
|
||||||
ENABLE_WORK_WEIXIN_NOTIFICATIONS = getattr(settings, 'ENABLE_WORK_WEIXIN_NOTIFICATIONS', False)
|
|
||||||
WORK_WEIXIN_NOTIFICATIONS_URL = getattr(settings, 'WORK_WEIXIN_NOTIFICATIONS_URL',
|
WORK_WEIXIN_NOTIFICATIONS_URL = getattr(settings, 'WORK_WEIXIN_NOTIFICATIONS_URL',
|
||||||
'https://qyapi.weixin.qq.com/cgi-bin/message/send')
|
'https://qyapi.weixin.qq.com/cgi-bin/message/send')
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ import requests
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from seahub.utils import normalize_cache_key
|
from seahub.utils import normalize_cache_key
|
||||||
from seahub.work_weixin.settings import WORK_WEIXIN_CORP_ID, WORK_WEIXIN_AGENT_SECRET, \
|
from seahub.work_weixin.settings import WORK_WEIXIN_CORP_ID, WORK_WEIXIN_AGENT_SECRET, \
|
||||||
WORK_WEIXIN_ACCESS_TOKEN_URL, ENABLE_WORK_WEIXIN_DEPARTMENTS, \
|
WORK_WEIXIN_ACCESS_TOKEN_URL, ENABLE_WORK_WEIXIN, \
|
||||||
WORK_WEIXIN_DEPARTMENTS_URL, WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, \
|
WORK_WEIXIN_DEPARTMENTS_URL, WORK_WEIXIN_DEPARTMENT_MEMBERS_URL, \
|
||||||
ENABLE_WORK_WEIXIN_OAUTH, WORK_WEIXIN_AGENT_ID, WORK_WEIXIN_AUTHORIZATION_URL, \
|
WORK_WEIXIN_AGENT_ID, WORK_WEIXIN_AUTHORIZATION_URL, \
|
||||||
WORK_WEIXIN_GET_USER_INFO_URL, WORK_WEIXIN_GET_USER_PROFILE_URL, \
|
WORK_WEIXIN_GET_USER_INFO_URL, WORK_WEIXIN_GET_USER_PROFILE_URL, \
|
||||||
ENABLE_WORK_WEIXIN_NOTIFICATIONS, WORK_WEIXIN_NOTIFICATIONS_URL
|
WORK_WEIXIN_NOTIFICATIONS_URL
|
||||||
from seahub.profile.models import Profile
|
from seahub.profile.models import Profile
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -66,34 +66,35 @@ def handler_work_weixin_api_response(response):
|
|||||||
def work_weixin_base_check():
|
def work_weixin_base_check():
|
||||||
""" work weixin base check
|
""" work weixin base check
|
||||||
"""
|
"""
|
||||||
|
if not ENABLE_WORK_WEIXIN:
|
||||||
|
return False
|
||||||
|
|
||||||
if not WORK_WEIXIN_CORP_ID or not WORK_WEIXIN_AGENT_SECRET or not WORK_WEIXIN_ACCESS_TOKEN_URL:
|
if not WORK_WEIXIN_CORP_ID or not WORK_WEIXIN_AGENT_SECRET or not WORK_WEIXIN_ACCESS_TOKEN_URL:
|
||||||
logger.error('work weixin base relevant settings invalid.')
|
logger.error('work weixin base relevant settings invalid.')
|
||||||
logger.error('WORK_WEIXIN_CORP_ID: %s' % WORK_WEIXIN_CORP_ID)
|
logger.error('WORK_WEIXIN_CORP_ID: %s' % WORK_WEIXIN_CORP_ID)
|
||||||
logger.error('WORK_WEIXIN_AGENT_SECRET: %s' % WORK_WEIXIN_AGENT_SECRET)
|
logger.error('WORK_WEIXIN_AGENT_SECRET: %s' % WORK_WEIXIN_AGENT_SECRET)
|
||||||
logger.error('WORK_WEIXIN_ACCESS_TOKEN_URL: %s' % WORK_WEIXIN_ACCESS_TOKEN_URL)
|
logger.error('WORK_WEIXIN_ACCESS_TOKEN_URL: %s' % WORK_WEIXIN_ACCESS_TOKEN_URL)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def work_weixin_oauth_check():
|
def work_weixin_oauth_check():
|
||||||
""" use for work weixin login and profile bind
|
""" use for work weixin login and profile bind
|
||||||
"""
|
"""
|
||||||
if not ENABLE_WORK_WEIXIN_OAUTH:
|
if not work_weixin_base_check():
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
if not work_weixin_base_check():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not WORK_WEIXIN_AGENT_ID \
|
if not WORK_WEIXIN_AGENT_ID \
|
||||||
or not WORK_WEIXIN_GET_USER_INFO_URL \
|
or not WORK_WEIXIN_GET_USER_INFO_URL \
|
||||||
or not WORK_WEIXIN_AUTHORIZATION_URL \
|
or not WORK_WEIXIN_AUTHORIZATION_URL \
|
||||||
or not WORK_WEIXIN_GET_USER_PROFILE_URL:
|
or not WORK_WEIXIN_GET_USER_PROFILE_URL:
|
||||||
logger.error('work weixin oauth relevant settings invalid.')
|
logger.error('work weixin oauth relevant settings invalid.')
|
||||||
logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
|
logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
|
||||||
logger.error('WORK_WEIXIN_GET_USER_INFO_URL: %s' % WORK_WEIXIN_GET_USER_INFO_URL)
|
logger.error('WORK_WEIXIN_GET_USER_INFO_URL: %s' % WORK_WEIXIN_GET_USER_INFO_URL)
|
||||||
logger.error('WORK_WEIXIN_AUTHORIZATION_URL: %s' % WORK_WEIXIN_AUTHORIZATION_URL)
|
logger.error('WORK_WEIXIN_AUTHORIZATION_URL: %s' % WORK_WEIXIN_AUTHORIZATION_URL)
|
||||||
logger.error('WORK_WEIXIN_GET_USER_PROFILE_URL: %s' % WORK_WEIXIN_GET_USER_PROFILE_URL)
|
logger.error('WORK_WEIXIN_GET_USER_PROFILE_URL: %s' % WORK_WEIXIN_GET_USER_PROFILE_URL)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -101,18 +102,15 @@ def work_weixin_oauth_check():
|
|||||||
def admin_work_weixin_departments_check():
|
def admin_work_weixin_departments_check():
|
||||||
""" use for admin work weixin departments
|
""" use for admin work weixin departments
|
||||||
"""
|
"""
|
||||||
if not ENABLE_WORK_WEIXIN_DEPARTMENTS:
|
if not work_weixin_base_check():
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
if not work_weixin_base_check():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not WORK_WEIXIN_DEPARTMENTS_URL \
|
if not WORK_WEIXIN_DEPARTMENTS_URL \
|
||||||
or not WORK_WEIXIN_DEPARTMENT_MEMBERS_URL:
|
or not WORK_WEIXIN_DEPARTMENT_MEMBERS_URL:
|
||||||
logger.error('admin work weixin departments relevant settings invalid.')
|
logger.error('admin work weixin departments relevant settings invalid.')
|
||||||
logger.error('WORK_WEIXIN_DEPARTMENTS_URL: %s' % WORK_WEIXIN_DEPARTMENTS_URL)
|
logger.error('WORK_WEIXIN_DEPARTMENTS_URL: %s' % WORK_WEIXIN_DEPARTMENTS_URL)
|
||||||
logger.error('WORK_WEIXIN_DEPARTMENT_MEMBERS_URL: %s' % WORK_WEIXIN_DEPARTMENT_MEMBERS_URL)
|
logger.error('WORK_WEIXIN_DEPARTMENT_MEMBERS_URL: %s' % WORK_WEIXIN_DEPARTMENT_MEMBERS_URL)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -120,18 +118,15 @@ def admin_work_weixin_departments_check():
|
|||||||
def work_weixin_notifications_check():
|
def work_weixin_notifications_check():
|
||||||
""" use for send work weixin notifications
|
""" use for send work weixin notifications
|
||||||
"""
|
"""
|
||||||
if not ENABLE_WORK_WEIXIN_NOTIFICATIONS:
|
if not work_weixin_base_check():
|
||||||
return False
|
return False
|
||||||
else:
|
|
||||||
if not work_weixin_base_check():
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not WORK_WEIXIN_AGENT_ID \
|
if not WORK_WEIXIN_AGENT_ID \
|
||||||
or not WORK_WEIXIN_NOTIFICATIONS_URL:
|
or not WORK_WEIXIN_NOTIFICATIONS_URL:
|
||||||
logger.error('work weixin notifications relevant settings invalid.')
|
logger.error('work weixin notifications relevant settings invalid.')
|
||||||
logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
|
logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
|
||||||
logger.error('WORK_WEIXIN_NOTIFICATIONS_URL: %s' % WORK_WEIXIN_NOTIFICATIONS_URL)
|
logger.error('WORK_WEIXIN_NOTIFICATIONS_URL: %s' % WORK_WEIXIN_NOTIFICATIONS_URL)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import json
|
import json
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
@ -29,9 +31,9 @@ class UploadLinksTest(BaseTestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.remove_repo()
|
self.remove_repo()
|
||||||
|
|
||||||
def _add_upload_link(self):
|
def _add_upload_link(self, expire_date=None):
|
||||||
upload_link = UploadLinkShare.objects.create_upload_link_share(self.user_name,
|
upload_link = UploadLinkShare.objects.create_upload_link_share(self.user_name,
|
||||||
self.repo_id, self.folder_path, None, None)
|
self.repo_id, self.folder_path, None, expire_date=expire_date)
|
||||||
|
|
||||||
return upload_link.token
|
return upload_link.token
|
||||||
|
|
||||||
@ -50,6 +52,8 @@ class UploadLinksTest(BaseTestCase):
|
|||||||
|
|
||||||
assert json_resp[0]['link'] is not None
|
assert json_resp[0]['link'] is not None
|
||||||
assert json_resp[0]['token'] is not None
|
assert json_resp[0]['token'] is not None
|
||||||
|
assert json_resp[0]['is_expired'] is not None
|
||||||
|
|
||||||
|
|
||||||
assert token in json_resp[0]['link']
|
assert token in json_resp[0]['link']
|
||||||
assert 'u/d' in json_resp[0]['link']
|
assert 'u/d' in json_resp[0]['link']
|
||||||
@ -58,6 +62,20 @@ class UploadLinksTest(BaseTestCase):
|
|||||||
|
|
||||||
self._remove_upload_link(token)
|
self._remove_upload_link(token)
|
||||||
|
|
||||||
|
def test_get_expired_upload_link(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
# create a upload link expired one day ago.
|
||||||
|
expire_date = timezone.now() + relativedelta(days=-1)
|
||||||
|
token = self._add_upload_link(expire_date=expire_date)
|
||||||
|
|
||||||
|
resp = self.client.get(self.url + '?path=' + self.folder_path + '&repo_id=' + self.repo_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp[0]['is_expired'] == True
|
||||||
|
|
||||||
|
self._remove_upload_link(token)
|
||||||
|
|
||||||
@patch.object(CanGenerateUploadLink, 'has_permission')
|
@patch.object(CanGenerateUploadLink, 'has_permission')
|
||||||
def test_get_link_with_invalid_user_role_permission(self, mock_has_permission):
|
def test_get_link_with_invalid_user_role_permission(self, mock_has_permission):
|
||||||
self.login_as(self.user)
|
self.login_as(self.user)
|
||||||
|
Loading…
Reference in New Issue
Block a user