diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index f9f20618be..ba09c63344 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -15924,9 +15924,9 @@
}
},
"seafile-js": {
- "version": "0.2.101",
- "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.101.tgz",
- "integrity": "sha512-CqF+4FKnUhnUYKCjiVbp10GkI9Ej+d4UfaWRvdGNaR74tK+0D9dUq7RNXUGS/MpIAlif7hbz5k+LUFafkfGmNg==",
+ "version": "0.2.106",
+ "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.106.tgz",
+ "integrity": "sha512-lT2gLD8hz75rlc7PW4kwW9SWe2JUywsDr5EDYYdQoPUz437i0YEq5wMIJq8jcOHaTSqgwbIfrWpVmOIs8Lkilg==",
"requires": {
"axios": "^0.18.0",
"form-data": "^2.3.2",
diff --git a/frontend/package.json b/frontend/package.json
index 03eafb9173..ba51651d87 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -37,7 +37,7 @@
"react-responsive": "^6.1.1",
"react-select": "^2.4.1",
"reactstrap": "^6.4.0",
- "seafile-js": "^0.2.101",
+ "seafile-js": "^0.2.106",
"socket.io-client": "^2.2.0",
"sw-precache-webpack-plugin": "0.11.4",
"unified": "^7.0.0",
diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js
index c6929b5378..d03de0404b 100644
--- a/frontend/src/components/dialog/generate-share-link.js
+++ b/frontend/src/components/dialog/generate-share-link.js
@@ -41,6 +41,7 @@ class GenerateShareLink extends React.Component {
'can_download': true
};
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0);
+ this.isOfficeFile = Utils.isOfficeFile(this.props.itemPath);
}
componentDidMount() {
@@ -57,12 +58,13 @@ class GenerateShareLink extends React.Component {
this.setState({isLoading: false});
}
});
-
- seafileAPI.getFileInfo(repoID, path).then((res) => {
- if (res.data) {
- this.setState({fileInfo: res.data});
- }
- });
+ if (this.isOfficeFile) {
+ seafileAPI.getFileInfo(repoID, path).then((res) => {
+ if (res.data) {
+ this.setState({fileInfo: res.data});
+ }
+ });
+ }
}
onPasswordInputChecked = () => {
@@ -402,7 +404,7 @@ class GenerateShareLink extends React.Component {
this.setPermission('preview')} />{' '}{gettext('Preview only')}
- {(Utils.isOfficeFile(this.props.itemPath) && fileInfo && fileInfo.can_edit) &&
+ {(this.isOfficeFile && fileInfo && fileInfo.can_edit) &&
this.setPermission('editOnCloudAndDownload')} />{' '}{gettext('Edit on cloud and download')}
diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js
index 05a24e9294..1983fc2818 100644
--- a/frontend/src/components/dialog/generate-upload-link.js
+++ b/frontend/src/components/dialog/generate-upload-link.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import copy from 'copy-to-clipboard';
+import moment from 'moment';
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
@@ -25,7 +26,9 @@ class GenerateUploadLink extends React.Component {
password: '',
passwdnew: '',
sharedUploadInfo: null,
- isSendLinkShown: false
+ isSendLinkShown: false,
+ isExpireChecked: false,
+ expireDays: 0,
};
}
@@ -90,29 +93,61 @@ class GenerateUploadLink extends React.Component {
generateUploadLink = () => {
let path = this.props.itemPath;
let repoID = this.props.repoID;
+ let { password, expireDays } = this.state;
- if (this.state.showPasswordInput && (this.state.password == '')) {
- this.setState({
- errorInfo: gettext('Please enter password')
- });
- }
- else if (this.state.showPasswordInput && (this.state.showPasswordInput && this.state.password.length < 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 isValid = this.validateParamsInput();
+ if (isValid) {
+ seafileAPI.createUploadLink(repoID, path, password, expireDays).then((res) => {
let sharedUploadInfo = new SharedUploadInfo(res.data);
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 = () => {
let uploadLink = this.state.sharedUploadInfo.link;
copy(uploadLink);
@@ -156,6 +191,12 @@ class GenerateUploadLink extends React.Component {
+ {sharedUploadInfo.expire_date && (
+
+ {gettext('Expiration Date:')}
+ {moment(sharedUploadInfo.expire_date).format('YYYY-MM-DD hh:mm:ss')}
+
+ )}
{canSendShareLinkEmail && !isSendLinkShown && {gettext('Send')} }
{!isSendLinkShown && {gettext('Delete')} }
@@ -192,6 +233,18 @@ class GenerateUploadLink extends React.Component {
}
+
+
+ {' '}{gettext('Add auto expiration')}
+
+
+ {this.state.isExpireChecked &&
+
+
+ {gettext('days')}
+
+
+ }
{this.state.errorInfo && {this.state.errorInfo} }
{gettext('Generate')}
diff --git a/frontend/src/components/dialog/leave-group-dialog.js b/frontend/src/components/dialog/leave-group-dialog.js
new file mode 100644
index 0000000000..c5caf9997f
--- /dev/null
+++ b/frontend/src/components/dialog/leave-group-dialog.js
@@ -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(
+
+ {gettext('Leave Group')}
+
+ {gettext('Really want to leave this group?')}
+
+
+ {gettext('Cancel')}
+ {gettext('Leave')}
+
+
+ );
+ }
+}
+
+const LeaveGroupDialogPropTypes = {
+ toggleLeaveGroupDialog: PropTypes.func.isRequired,
+ groupID: PropTypes.string.isRequired,
+ onGroupChanged: PropTypes.func.isRequired,
+};
+
+LeaveGroupDialog.propTypes = LeaveGroupDialogPropTypes;
+
+export default LeaveGroupDialog;
diff --git a/frontend/src/components/file-uploader/file-uploader.js b/frontend/src/components/file-uploader/file-uploader.js
index da30a6ad22..ab6b980e47 100644
--- a/frontend/src/components/file-uploader/file-uploader.js
+++ b/frontend/src/components/file-uploader/file-uploader.js
@@ -128,43 +128,31 @@ class FileUploader extends React.Component {
this.resumable.on('dragstart', this.onDragStart.bind(this));
}
- onChunkingComplete = (file) => {
- if (file.relativePath !== file.fileName) {
- 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;
+ onChunkingComplete = (resumableFile) => {
+ //get parent_dir relative_path
let path = this.props.path === '/' ? '/' : this.props.path + '/';
let fileName = resumableFile.fileName;
let relativePath = resumableFile.relativePath;
let isFile = fileName === relativePath;
- //update formdata;
+ //update formdata
resumableFile.formData = {};
- if (isFile) {
+ if (isFile) { // upload file
resumableFile.formData = {
parent_dir: path,
};
- } else {
+ } else { // upload folder
let relative_path = relativePath.slice(0, relativePath.lastIndexOf('/') + 1);
resumableFile.formData = {
parent_dir: path,
relative_path: relative_path
};
}
+ }
- //check repetition
- //uploading is file and only upload one file
+ onFileAdded = (resumableFile, files) => {
+ let isFile = resumableFile.fileName === resumableFile.relativePath;
+ // uploading is file and only upload one file
if (isFile && files.length === 1) {
let hasRepetition = false;
let direntList = this.props.direntList;
@@ -181,14 +169,28 @@ class FileUploader extends React.Component {
});
} else {
this.setUploadFileList(this.resumable.files);
+ this.resumableUpload(resumableFile);
+ }
+ } else {
+ this.setUploadFileList(this.resumable.files);
+ if (isFile) {
+ this.resumableUpload(resumableFile);
+ } else {
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) => {
// 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');
let repoID = this.props.repoID;
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()) {
this.uploadInput.current.click();
}
@@ -430,7 +432,7 @@ class FileUploader extends React.Component {
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
let repoID = this.props.repoID;
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()) {
this.uploadInput.current.click();
}
@@ -444,7 +446,7 @@ class FileUploader extends React.Component {
let repoID = this.props.repoID;
this.uploadInput.current.setAttribute('webkitdirectory', 'webkitdirectory');
seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
- this.resumable.opts.target = res.data;
+ this.resumable.opts.target = res.data + '?ret-json=1';
});
}
diff --git a/frontend/src/components/search/search.js b/frontend/src/components/search/search.js
index 6e9674b5c7..e6d273b3d6 100644
--- a/frontend/src/components/search/search.js
+++ b/frontend/src/components/search/search.js
@@ -1,12 +1,14 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import MediaQuery from 'react-responsive';
+import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot } from '../../utils/constants';
import SearchResultItem from './search-result-item';
import editorUtilities from '../../utils/editor-utilties';
import More from '../more';
const propTypes = {
+ isPublic: PropTypes.bool,
repoID: PropTypes.string,
placeholder: PropTypes.string,
onSearchedClick: PropTypes.func.isRequired,
@@ -93,27 +95,53 @@ class Search extends Component {
sendRequest(queryData, cancelToken) {
var _this = this;
- editorUtilities.searchFiles(queryData,cancelToken).then(res => {
- if (!res.data.total) {
+ let isPublic = this.props.isPublic;
+
+ 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({
- resultItems: [],
+ resultItems: items,
isResultGetted: true
});
_this.source = null;
- return;
- }
-
- let items = _this.formatResultItems(res.data.results);
- _this.setState({
- resultItems: items,
- isResultGetted: true
+ }).catch(res => {
+ /* eslint-disable */
+ console.log(res);
+ /* eslint-enable */
});
- _this.source = null;
- }).catch(res => {
- /* eslint-disable */
- console.log(res);
- /* eslint-enable */
- });
+ } else {
+ editorUtilities.searchFiles(queryData,cancelToken).then(res => {
+ if (!res.data.total) {
+ _this.setState({
+ 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() {
diff --git a/frontend/src/models/shared-upload-info.js b/frontend/src/models/shared-upload-info.js
index e9776e62e9..cd7f0f9b8c 100644
--- a/frontend/src/models/shared-upload-info.js
+++ b/frontend/src/models/shared-upload-info.js
@@ -10,6 +10,8 @@ class SharedUploadInfo {
this.ctime = object.ctime;
this.token = object.token;
this.view_cnt = object.view_cnt;
+ this.expire_date = object.expire_date;
+ this.is_expired = object.is_expired;
}
}
diff --git a/frontend/src/pages/groups/group-view.js b/frontend/src/pages/groups/group-view.js
index b1ee881984..a72869ab34 100644
--- a/frontend/src/pages/groups/group-view.js
+++ b/frontend/src/pages/groups/group-view.js
@@ -20,6 +20,7 @@ import RenameGroupDialog from '../../components/dialog/rename-group-dialog';
import TransferGroupDialog from '../../components/dialog/transfer-group-dialog';
// import ImportMembersDialog from '../../components/dialog/import-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 LibDetail from '../../components/dirent-detail/lib-details';
@@ -60,6 +61,7 @@ class GroupView extends React.Component {
showManageMembersDialog: false,
groupMembers: [],
isShowDetails: false,
+ isLeaveGroupDialogOpen: false,
};
}
@@ -307,6 +309,13 @@ class GroupView extends React.Component {
});
}
+ toggleLeaveGroupDialog = () => {
+ this.setState({
+ isLeaveGroupDialogOpen: !this.state.isLeaveGroupDialogOpen,
+ showGroupDropdown: false,
+ });
+ }
+
listGroupMembers = () => {
seafileAPI.listGroupMembers(this.props.groupID).then((res) => {
this.setState({
@@ -407,7 +416,7 @@ class GroupView extends React.Component {
)}
- { (isShowSettingIcon && this.state.isStaff) &&
+ { isShowSettingIcon &&
@@ -419,6 +428,7 @@ class GroupView extends React.Component {
onClick={this.toggleGroupDropdown}>
+ {(this.state.isStaff || this.state.isOwner) &&
+ }
+ {(this.state.isStaff || this.state.isOwner) &&
+ }
{
this.state.isOwner &&
}
+ {/* gourp owner only can dissmiss group, admin could not quit, department member could not quit */}
+ {(!this.state.isOwner && !this.state.isStaff && !isDepartmentGroup) &&
+
+ }
@@ -557,6 +576,13 @@ class GroupView extends React.Component {
isOwner={this.state.isOwner}
/>
}
+ {this.state.isLeaveGroupDialogOpen &&
+
+ }
);
}
diff --git a/frontend/src/pages/share-admin/upload-links.js b/frontend/src/pages/share-admin/upload-links.js
index 97d3efb2ae..130d26b7e3 100644
--- a/frontend/src/pages/share-admin/upload-links.js
+++ b/frontend/src/pages/share-admin/upload-links.js
@@ -1,5 +1,6 @@
-import React, { Component } from 'react';
+import React, { Component, Fragment } from 'react';
import { Link } from '@reach/router';
+import moment from 'moment';
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
import { gettext, siteRoot, loginUrl, canGenerateShareLink } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
@@ -52,9 +53,10 @@ class Content extends Component {
{/*icon*/}
- {gettext('Name')}
- {gettext('Library')}
- {gettext('Visits')}
+ {gettext('Name')}
+ {gettext('Library')}
+ {gettext('Visits')}
+ {gettext('Expiration')}
{/*Operations*/}
@@ -114,6 +116,24 @@ class Item extends Component {
return { iconUrl, uploadUrl };
}
+ renderExpriedData = () => {
+ let item = this.props.item;
+ if (!item.expire_date) {
+ return (
+ --
+ );
+ }
+ let expire_date = moment(item.expire_date).format('YYYY-MM-DD');
+ return (
+
+ {item.is_expired ?
+ {expire_date} :
+ expire_date
+ }
+
+ );
+ }
+
render() {
let item = this.props.item;
let { iconUrl, uploadUrl } = this.getUploadParams();
@@ -128,6 +148,7 @@ class Item extends Component {
{item.obj_name}
{item.repo_name}
{item.view_cnt}
+ {this.renderExpriedData()}
diff --git a/frontend/src/pages/sys-admin/side-panel.js b/frontend/src/pages/sys-admin/side-panel.js
index 449e5f6488..8dbb63328d 100644
--- a/frontend/src/pages/sys-admin/side-panel.js
+++ b/frontend/src/pages/sys-admin/side-panel.js
@@ -5,7 +5,7 @@ import Logo from '../../components/logo';
import { gettext, siteRoot, isPro, isDefaultAdmin, canViewSystemInfo, canViewStatistic,
canConfigSystem, canManageLibrary, canManageUser, canManageGroup, canViewUserLog,
canViewAdminLog, constanceEnabled, multiTenancy, multiInstitution, sysadminExtraEnabled,
- enableGuestInvitation, enableTermsAndConditions, enableFileScan, enableWorkWeixinDepartments } from '../../utils/constants';
+ enableGuestInvitation, enableTermsAndConditions, enableFileScan, enableWorkWeixin } from '../../utils/constants';
const propTypes = {
isSidePanelClosed: PropTypes.bool.isRequired,
@@ -174,7 +174,7 @@ class SidePanel extends React.Component {
}
- {isDefaultAdmin && enableWorkWeixinDepartments &&
+ {isDefaultAdmin && enableWorkWeixin &&
diff --git a/frontend/src/pages/wiki/main-panel.js b/frontend/src/pages/wiki/main-panel.js
index d53a83c9db..2e66391356 100644
--- a/frontend/src/pages/wiki/main-panel.js
+++ b/frontend/src/pages/wiki/main-panel.js
@@ -7,6 +7,7 @@ import WikiMarkdownViewer from '../../components/wiki-markdown-viewer';
import WikiDirListView from '../../components/wiki-dir-list-view/wiki-dir-list-view';
import Loading from '../../components/loading';
import { Utils } from '../../utils/utils';
+import Search from '../../components/search/search';
const propTypes = {
path: PropTypes.string.isRequired,
@@ -77,6 +78,21 @@ class MainPanel extends Component {
return (
+ {!username &&
+
+
+
+
+
+
+
+
+ }
{username && (
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index 6dfcf9fc76..9540149924 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -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 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 enableWorkWeixinDepartments = window.sysadmin ? window.sysadmin.pageOptions.enable_work_weixin_departments : '';
+export const enableWorkWeixin = window.sysadmin ? window.sysadmin.pageOptions.enable_work_weixin : '';
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index 1fc09db32b..c672780604 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -240,7 +240,8 @@ export const Utils = {
isSupportUploadFolder: function() {
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
diff --git a/seahub/api2/endpoints/public_repos_search.py b/seahub/api2/endpoints/public_repos_search.py
new file mode 100644
index 0000000000..bdb0d7d4de
--- /dev/null
+++ b/seahub/api2/endpoints/public_repos_search.py
@@ -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
+ })
diff --git a/seahub/api2/endpoints/upload_links.py b/seahub/api2/endpoints/upload_links.py
index 03777cc832..290b0ebff7 100644
--- a/seahub/api2/endpoints/upload_links.py
+++ b/seahub/api2/endpoints/upload_links.py
@@ -2,6 +2,7 @@
import os
import logging
from constance import config
+from dateutil.relativedelta import relativedelta
from rest_framework.authentication import SessionAuthentication
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 import status
+from django.utils import timezone
from django.utils.translation import ugettext as _
from seaserv import seafile_api
@@ -48,6 +50,11 @@ def get_upload_link_info(uls):
else:
ctime = ''
+ if uls.expire_date:
+ expire_date = datetime_to_isoformat_timestr(uls.expire_date)
+ else:
+ expire_date = ''
+
data['repo_id'] = repo_id
data['repo_name'] = repo.repo_name if repo else ''
data['path'] = path
@@ -57,6 +64,8 @@ def get_upload_link_info(uls):
data['link'] = gen_shared_upload_link(token)
data['token'] = token
data['username'] = uls.username
+ data['expire_date'] = expire_date
+ data['is_expired'] = uls.is_expired()
return data
@@ -143,6 +152,12 @@ class UploadLinks(APIView):
error_msg = _('Password is too short')
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
repo = seafile_api.get_repo(repo_id)
if not repo:
@@ -164,11 +179,16 @@ class UploadLinks(APIView):
error_msg = 'Permission denied.'
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
uls = UploadLinkShare.objects.get_upload_link_by_path(username, repo_id, path)
if not uls:
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)
return Response(link_info)
diff --git a/seahub/auth/views.py b/seahub/auth/views.py
index 756c5aff6a..5b3efb70d3 100644
--- a/seahub/auth/views.py
+++ b/seahub/auth/views.py
@@ -186,7 +186,7 @@ def login(request, template_name='registration/login.html',
getattr(settings, 'ENABLE_OAUTH', False) or \
getattr(settings, 'ENABLE_CAS', 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()
diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py
index d7c5a035ee..1c16b55ca9 100644
--- a/seahub/base/context_processors.py
+++ b/seahub/base/context_processors.py
@@ -50,7 +50,7 @@ try:
from seahub.settings import ENABLE_FILE_SCAN
except ImportError:
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):
@@ -134,7 +134,7 @@ def base(request):
'enable_resumable_fileupload': dj_settings.ENABLE_RESUMABLE_FILEUPLOAD,
'service_url': get_service_url().rstrip('/'),
'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 '',
}
diff --git a/seahub/oauth/backends.py b/seahub/oauth/backends.py
index 6cad124101..4f826dc0d0 100644
--- a/seahub/oauth/backends.py
+++ b/seahub/oauth/backends.py
@@ -4,7 +4,7 @@ from seahub.auth.backends import RemoteUserBackend
from seahub.base.accounts import User
from registration.models import (notify_admins_on_activate_request,
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):
"""
@@ -23,7 +23,7 @@ class OauthRemoteUserBackend(RemoteUserBackend):
# Create active user by default.
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)
activate_after_creation = getattr(settings, 'WORK_WEIXIN_OAUTH_ACTIVATE_USER_AFTER_CREATION', True)
diff --git a/seahub/settings.py b/seahub/settings.py
index fa17564503..1542d0b257 100644
--- a/seahub/settings.py
+++ b/seahub/settings.py
@@ -303,12 +303,8 @@ SOCIAL_AUTH_PIPELINE = (
ENABLE_OAUTH = False
ENABLE_WATERMARK = False
-# allow user scan the work weixin qrcode to login
-ENABLE_WORK_WEIXIN_OAUTH = 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
+# enable work weixin
+ENABLE_WORK_WEIXIN = False
# allow user to clean library trash
ENABLE_USER_CLEAN_TRASH = True
@@ -917,5 +913,5 @@ if ENABLE_REMOTE_USER_AUTHENTICATION:
MIDDLEWARE_CLASSES += ('seahub.auth.middleware.SeafileRemoteUserMiddleware',)
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',)
diff --git a/seahub/share/models.py b/seahub/share/models.py
index 408ab90772..6291cc5683 100644
--- a/seahub/share/models.py
+++ b/seahub/share/models.py
@@ -451,6 +451,12 @@ class UploadLinkShare(models.Model):
def is_owner(self, owner):
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):
def add_private_file_share(self, from_user, to_user, repo_id, path, perm):
"""
diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html
index 26eaaaaa54..a3c8739120 100644
--- a/seahub/templates/js/sysadmin-templates.html
+++ b/seahub/templates/js/sysadmin-templates.html
@@ -122,7 +122,7 @@
{% endif %}
- {% if is_default_admin and enable_work_weixin_departments %}
+ {% if is_default_admin and enable_work_weixin %}
企业微信集成
diff --git a/seahub/templates/sysadmin/base.html b/seahub/templates/sysadmin/base.html
index 6a1dc78941..688b59f599 100644
--- a/seahub/templates/sysadmin/base.html
+++ b/seahub/templates/sysadmin/base.html
@@ -133,7 +133,7 @@
{% endif %}
- {% if is_default_admin and enable_work_weixin_departments %}
+ {% if is_default_admin and enable_work_weixin %}
企业微信集成
diff --git a/seahub/templates/sysadmin/sysadmin_backbone.html b/seahub/templates/sysadmin/sysadmin_backbone.html
index c584b86834..064bbf2acf 100644
--- a/seahub/templates/sysadmin/sysadmin_backbone.html
+++ b/seahub/templates/sysadmin/sysadmin_backbone.html
@@ -108,7 +108,7 @@ app["pageOptions"] = {
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 %},
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: {
"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 %},
diff --git a/seahub/templates/sysadmin/sysadmin_react_app.html b/seahub/templates/sysadmin/sysadmin_react_app.html
index 7b7ae200ca..bc1a5eb039 100644
--- a/seahub/templates/sysadmin/sysadmin_react_app.html
+++ b/seahub/templates/sysadmin/sysadmin_react_app.html
@@ -15,7 +15,7 @@
enable_terms_and_conditions: {% if enable_terms_and_conditions %} 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_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: {
"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 %},
diff --git a/seahub/urls.py b/seahub/urls.py
index 1f48efa364..54d8a56253 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -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.starred_items import StarredItems
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, \
DTableUpdateLinkView, DTableAssetUploadLinkView, dtable_file_view, dtable_asset_access
from seahub.api2.endpoints.dtable_share import SharedDTablesView, DTableShareView
+
# Admin
from seahub.api2.endpoints.admin.revision_tag import AdminTaggedItemsView
from seahub.api2.endpoints.admin.login_logs import LoginLogs, AdminLoginLogs
@@ -366,6 +368,9 @@ urlpatterns = [
# user: 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
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'),
diff --git a/seahub/views/sso.py b/seahub/views/sso.py
index 22ee8471e9..28d63a3e92 100644
--- a/seahub/views/sso.py
+++ b/seahub/views/sso.py
@@ -35,7 +35,7 @@ def sso(request):
if getattr(settings, 'ENABLE_CAS', False):
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(next_page)
diff --git a/seahub/views/sysadmin.py b/seahub/views/sysadmin.py
index c7a07d8b33..19274811e8 100644
--- a/seahub/views/sysadmin.py
+++ b/seahub/views/sysadmin.py
@@ -98,7 +98,7 @@ try:
from seahub.settings import ENABLE_FILE_SCAN
except ImportError:
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__)
@@ -133,7 +133,7 @@ def sysadmin(request):
'enable_limit_ipaddress': ENABLE_LIMIT_IPADDRESS,
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
'enable_file_scan': ENABLE_FILE_SCAN,
- 'enable_work_weixin_departments': ENABLE_WORK_WEIXIN_DEPARTMENTS,
+ 'enable_work_weixin': ENABLE_WORK_WEIXIN,
})
@login_required
@@ -148,7 +148,7 @@ def sysadmin_react_fake_view(request):
'enable_guest_invitation': ENABLE_GUEST_INVITATION,
'enable_terms_and_conditions': config.ENABLE_TERMS_AND_CONDITIONS,
'enable_file_scan': ENABLE_FILE_SCAN,
- 'enable_work_weixin_departments': ENABLE_WORK_WEIXIN_DEPARTMENTS,
+ 'enable_work_weixin': ENABLE_WORK_WEIXIN,
})
@login_required
diff --git a/seahub/work_weixin/settings.py b/seahub/work_weixin/settings.py
index f3623a8e3a..c3e36a1d01 100644
--- a/seahub/work_weixin/settings.py
+++ b/seahub/work_weixin/settings.py
@@ -3,13 +3,13 @@
from django.conf import settings
# # work weixin base
+ENABLE_WORK_WEIXIN = getattr(settings, 'ENABLE_WORK_WEIXIN', False)
WORK_WEIXIN_CORP_ID = getattr(settings, 'WORK_WEIXIN_CORP_ID', '')
WORK_WEIXIN_AGENT_SECRET = getattr(settings, 'WORK_WEIXIN_AGENT_SECRET', '')
WORK_WEIXIN_ACCESS_TOKEN_URL = getattr(settings, 'WORK_WEIXIN_ACCESS_TOKEN_URL',
'https://qyapi.weixin.qq.com/cgi-bin/gettoken')
# # 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',
'https://qyapi.weixin.qq.com/cgi-bin/department/list')
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_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_USER_INFO_AUTO_UPDATE = getattr(settings, 'WORK_WEIXIN_USER_INFO_AUTO_UPDATE', True)
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')
# # work weixin notifications
-ENABLE_WORK_WEIXIN_NOTIFICATIONS = getattr(settings, 'ENABLE_WORK_WEIXIN_NOTIFICATIONS', False)
WORK_WEIXIN_NOTIFICATIONS_URL = getattr(settings, 'WORK_WEIXIN_NOTIFICATIONS_URL',
'https://qyapi.weixin.qq.com/cgi-bin/message/send')
diff --git a/seahub/work_weixin/utils.py b/seahub/work_weixin/utils.py
index feb27c9258..0904cdfb1c 100644
--- a/seahub/work_weixin/utils.py
+++ b/seahub/work_weixin/utils.py
@@ -8,11 +8,11 @@ import requests
from django.core.cache import cache
from seahub.utils import normalize_cache_key
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, \
- 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, \
- ENABLE_WORK_WEIXIN_NOTIFICATIONS, WORK_WEIXIN_NOTIFICATIONS_URL
+ WORK_WEIXIN_NOTIFICATIONS_URL
from seahub.profile.models import Profile
logger = logging.getLogger(__name__)
@@ -66,34 +66,35 @@ def handler_work_weixin_api_response(response):
def 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:
logger.error('work weixin base relevant settings invalid.')
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_ACCESS_TOKEN_URL: %s' % WORK_WEIXIN_ACCESS_TOKEN_URL)
return False
+
return True
def work_weixin_oauth_check():
""" use for work weixin login and profile bind
"""
- if not ENABLE_WORK_WEIXIN_OAUTH:
+ if not work_weixin_base_check():
return False
- else:
- if not work_weixin_base_check():
- return False
- if not WORK_WEIXIN_AGENT_ID \
- or not WORK_WEIXIN_GET_USER_INFO_URL \
- or not WORK_WEIXIN_AUTHORIZATION_URL \
- or not WORK_WEIXIN_GET_USER_PROFILE_URL:
- logger.error('work weixin oauth relevant settings invalid.')
- 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_AUTHORIZATION_URL: %s' % WORK_WEIXIN_AUTHORIZATION_URL)
- logger.error('WORK_WEIXIN_GET_USER_PROFILE_URL: %s' % WORK_WEIXIN_GET_USER_PROFILE_URL)
- return False
+ if not WORK_WEIXIN_AGENT_ID \
+ or not WORK_WEIXIN_GET_USER_INFO_URL \
+ or not WORK_WEIXIN_AUTHORIZATION_URL \
+ or not WORK_WEIXIN_GET_USER_PROFILE_URL:
+ logger.error('work weixin oauth relevant settings invalid.')
+ 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_AUTHORIZATION_URL: %s' % WORK_WEIXIN_AUTHORIZATION_URL)
+ logger.error('WORK_WEIXIN_GET_USER_PROFILE_URL: %s' % WORK_WEIXIN_GET_USER_PROFILE_URL)
+ return False
return True
@@ -101,18 +102,15 @@ def work_weixin_oauth_check():
def admin_work_weixin_departments_check():
""" use for admin work weixin departments
"""
- if not ENABLE_WORK_WEIXIN_DEPARTMENTS:
+ if not work_weixin_base_check():
return False
- else:
- if not work_weixin_base_check():
- return False
- if not WORK_WEIXIN_DEPARTMENTS_URL \
- or not WORK_WEIXIN_DEPARTMENT_MEMBERS_URL:
- logger.error('admin work weixin departments relevant settings invalid.')
- logger.error('WORK_WEIXIN_DEPARTMENTS_URL: %s' % WORK_WEIXIN_DEPARTMENTS_URL)
- logger.error('WORK_WEIXIN_DEPARTMENT_MEMBERS_URL: %s' % WORK_WEIXIN_DEPARTMENT_MEMBERS_URL)
- return False
+ if not WORK_WEIXIN_DEPARTMENTS_URL \
+ or not WORK_WEIXIN_DEPARTMENT_MEMBERS_URL:
+ logger.error('admin work weixin departments relevant settings invalid.')
+ logger.error('WORK_WEIXIN_DEPARTMENTS_URL: %s' % WORK_WEIXIN_DEPARTMENTS_URL)
+ logger.error('WORK_WEIXIN_DEPARTMENT_MEMBERS_URL: %s' % WORK_WEIXIN_DEPARTMENT_MEMBERS_URL)
+ return False
return True
@@ -120,18 +118,15 @@ def admin_work_weixin_departments_check():
def work_weixin_notifications_check():
""" use for send work weixin notifications
"""
- if not ENABLE_WORK_WEIXIN_NOTIFICATIONS:
+ if not work_weixin_base_check():
return False
- else:
- if not work_weixin_base_check():
- return False
- if not WORK_WEIXIN_AGENT_ID \
- or not WORK_WEIXIN_NOTIFICATIONS_URL:
- logger.error('work weixin notifications relevant settings invalid.')
- logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
- logger.error('WORK_WEIXIN_NOTIFICATIONS_URL: %s' % WORK_WEIXIN_NOTIFICATIONS_URL)
- return False
+ if not WORK_WEIXIN_AGENT_ID \
+ or not WORK_WEIXIN_NOTIFICATIONS_URL:
+ logger.error('work weixin notifications relevant settings invalid.')
+ logger.error('WORK_WEIXIN_AGENT_ID: %s' % WORK_WEIXIN_AGENT_ID)
+ logger.error('WORK_WEIXIN_NOTIFICATIONS_URL: %s' % WORK_WEIXIN_NOTIFICATIONS_URL)
+ return False
return True
diff --git a/tests/api/endpoints/test_upload_links.py b/tests/api/endpoints/test_upload_links.py
index 98534e9c59..e2337830f3 100644
--- a/tests/api/endpoints/test_upload_links.py
+++ b/tests/api/endpoints/test_upload_links.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import json
from mock import patch
+from dateutil.relativedelta import relativedelta
+from django.utils import timezone
from django.core.urlresolvers import reverse
@@ -29,9 +31,9 @@ class UploadLinksTest(BaseTestCase):
def tearDown(self):
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,
- self.repo_id, self.folder_path, None, None)
+ self.repo_id, self.folder_path, None, expire_date=expire_date)
return upload_link.token
@@ -50,6 +52,8 @@ class UploadLinksTest(BaseTestCase):
assert json_resp[0]['link'] 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 'u/d' in json_resp[0]['link']
@@ -58,6 +62,20 @@ class UploadLinksTest(BaseTestCase):
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')
def test_get_link_with_invalid_user_role_permission(self, mock_has_permission):
self.login_as(self.user)