mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-01 23:38:37 +00:00
Multi office suite (#7127)
* initiate * update * test * Update file.py * update * update * update * optimize fronted * optimize * update * update * update * Update test_utils.py * update * remove-useless-code * Update repo_office_suite.py * optimize ui and sql * optimize * update * add is pro version for api * Update settings.py * update * Update models.py * add-repo-owner-validation --------- Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
This commit is contained in:
parent
1b89e8e2f1
commit
037c3f3a3d
112
frontend/src/components/dialog/repo-office-suite-dialog.js
Normal file
112
frontend/src/components/dialog/repo-office-suite-dialog.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, TabContent, TabPane } from 'reactstrap';
|
||||||
|
import makeAnimated from 'react-select/animated';
|
||||||
|
import { userAPI } from '../../utils/user-api';
|
||||||
|
import { gettext, isPro } from '../../utils/constants';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import toaster from '../toast';
|
||||||
|
import { SeahubSelect } from '../common/select';
|
||||||
|
import '../../css/repo-office-suite-dialog.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
itemName: PropTypes.string.isRequired,
|
||||||
|
toggleDialog: PropTypes.func.isRequired,
|
||||||
|
submit: PropTypes.func.isRequired,
|
||||||
|
repoID: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class OfficeSuiteDialog extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
selectedOption: null,
|
||||||
|
errorMsg: []
|
||||||
|
};
|
||||||
|
this.options = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectChange = (option) => {
|
||||||
|
this.setState({ selectedOption: option });
|
||||||
|
};
|
||||||
|
|
||||||
|
submit = () => {
|
||||||
|
let suite_id = this.state.selectedOption.value;
|
||||||
|
this.props.submit(suite_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (isPro) {
|
||||||
|
userAPI.getOfficeSuite(this.props.repoID).then((res) => {
|
||||||
|
this.updateOptions(res);
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOptions = (officeSuites) => {
|
||||||
|
officeSuites.data.suites_info.forEach(item => {
|
||||||
|
let option = {
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
is_selected: item.is_selected,
|
||||||
|
};
|
||||||
|
this.options.push(option);
|
||||||
|
});
|
||||||
|
let selectedOption = this.options.find(op => op.is_selected);
|
||||||
|
this.setState({ selectedOption });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
renderOfficeSuiteContent = () => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="repo-office-suite-dialog-main">
|
||||||
|
<TabContent>
|
||||||
|
{isPro &&
|
||||||
|
<TabPane role="tabpanel" id="office-suite-panel">
|
||||||
|
<SeahubSelect
|
||||||
|
isClearable
|
||||||
|
maxMenuHeight={200}
|
||||||
|
hideSelectedOptions={true}
|
||||||
|
components={makeAnimated()}
|
||||||
|
placeholder={gettext('Select a office suite')}
|
||||||
|
options={this.options}
|
||||||
|
onChange={this.handleSelectChange}
|
||||||
|
value={this.state.selectedOption}
|
||||||
|
className="repo-select-office-suite"
|
||||||
|
/>
|
||||||
|
</TabPane>}
|
||||||
|
</TabContent>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { itemName: repoName } = this.props;
|
||||||
|
let title = gettext('{library_name} Office Suite');
|
||||||
|
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} toggle={this.props.toggleDialog} className="repo-office-suite-dialog">
|
||||||
|
<ModalHeader toggle={this.props.toggleDialog}>
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: title }} className="d-flex mw-100"></span>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody className="repo-office-suite-dialog-content" role="tablist">
|
||||||
|
{this.renderOfficeSuiteContent()}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.submit}>{gettext('Submit')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OfficeSuiteDialog.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default OfficeSuiteDialog;
|
30
frontend/src/css/repo-office-suite-dialog.css
Normal file
30
frontend/src/css/repo-office-suite-dialog.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.repo-office-suite-dialog .repo-office-suite-dialog-content {
|
||||||
|
padding: 0;
|
||||||
|
min-height: 5.5rem;
|
||||||
|
min-width: 10rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 268px) {
|
||||||
|
.repo-office-suite-dialog .repo-office-suite-dialog-content {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-office-suite-dialog-content .repo-office-suite-dialog-main {
|
||||||
|
display: flex;
|
||||||
|
flex-basis: 48%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repo-office-suite-dialog-content .repo-office-suite-dialog-main .tab-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.repo-office-suite-dialog-content .repo-office-suite-dialog-main .repo-select-office-suite {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -23,6 +23,7 @@ class RepoInfo {
|
|||||||
this.lib_need_decrypt = object.lib_need_decrypt;
|
this.lib_need_decrypt = object.lib_need_decrypt;
|
||||||
this.last_modified = object.last_modified;
|
this.last_modified = object.last_modified;
|
||||||
this.status = object.status;
|
this.status = object.status;
|
||||||
|
this.enable_onlyoffice = object.enable_onlyoffice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import Rename from '../../components/rename';
|
|||||||
import MylibRepoMenu from './mylib-repo-menu';
|
import MylibRepoMenu from './mylib-repo-menu';
|
||||||
import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog';
|
import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog';
|
||||||
import RepoShareAdminDialog from '../../components/dialog/repo-share-admin-dialog';
|
import RepoShareAdminDialog from '../../components/dialog/repo-share-admin-dialog';
|
||||||
|
import OfficeSuiteDialog from '../../components/dialog/repo-office-suite-dialog';
|
||||||
import RepoMonitoredIcon from '../../components/repo-monitored-icon';
|
import RepoMonitoredIcon from '../../components/repo-monitored-icon';
|
||||||
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
import { LIST_MODE } from '../../components/dir-view-mode/constants';
|
||||||
import { userAPI } from '../../utils/user-api';
|
import { userAPI } from '../../utils/user-api';
|
||||||
@ -57,6 +58,7 @@ class MylibRepoListItem extends React.Component {
|
|||||||
isAPITokenDialogShow: false,
|
isAPITokenDialogShow: false,
|
||||||
isRepoShareAdminDialogOpen: false,
|
isRepoShareAdminDialogOpen: false,
|
||||||
isRepoDeleted: false,
|
isRepoDeleted: false,
|
||||||
|
isOfficeSuiteDialogShow: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +130,9 @@ class MylibRepoListItem extends React.Component {
|
|||||||
case 'Share Admin':
|
case 'Share Admin':
|
||||||
this.toggleRepoShareAdminDialog();
|
this.toggleRepoShareAdminDialog();
|
||||||
break;
|
break;
|
||||||
|
case 'Office Suite':
|
||||||
|
this.onOfficeSuiteToggle();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -221,6 +226,10 @@ class MylibRepoListItem extends React.Component {
|
|||||||
this.setState({ isAPITokenDialogShow: !this.state.isAPITokenDialogShow });
|
this.setState({ isAPITokenDialogShow: !this.state.isAPITokenDialogShow });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onOfficeSuiteToggle = () => {
|
||||||
|
this.setState({ isOfficeSuiteDialogShow: !this.state.isOfficeSuiteDialogShow });
|
||||||
|
};
|
||||||
|
|
||||||
toggleRepoShareAdminDialog = () => {
|
toggleRepoShareAdminDialog = () => {
|
||||||
this.setState({ isRepoShareAdminDialogOpen: !this.state.isRepoShareAdminDialogOpen });
|
this.setState({ isRepoShareAdminDialogOpen: !this.state.isRepoShareAdminDialogOpen });
|
||||||
};
|
};
|
||||||
@ -266,6 +275,21 @@ class MylibRepoListItem extends React.Component {
|
|||||||
this.onTransferToggle();
|
this.onTransferToggle();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onOfficeSuiteChange = (suiteID) => {
|
||||||
|
let repoID = this.props.repo.repo_id;
|
||||||
|
userAPI.setOfficeSuite(repoID, suiteID).then(res => {
|
||||||
|
let message = gettext('Successfully change office suite.');
|
||||||
|
toaster.success(message);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error.response) {
|
||||||
|
toaster.danger(error.response.data.error_msg || gettext('Error'), { duration: 3 });
|
||||||
|
} else {
|
||||||
|
toaster.danger(gettext('Failed. Please check the network.'), { duration: 3 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.onOfficeSuiteToggle();
|
||||||
|
};
|
||||||
|
|
||||||
onDeleteRepo = (repo) => {
|
onDeleteRepo = (repo) => {
|
||||||
seafileAPI.deleteRepo(repo.repo_id).then((res) => {
|
seafileAPI.deleteRepo(repo.repo_id).then((res) => {
|
||||||
|
|
||||||
@ -548,6 +572,17 @@ class MylibRepoListItem extends React.Component {
|
|||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{this.state.isOfficeSuiteDialogShow && (
|
||||||
|
<ModalPortal>
|
||||||
|
<OfficeSuiteDialog
|
||||||
|
repoID={repo.repo_id}
|
||||||
|
itemName={repo.repo_name}
|
||||||
|
submit={this.onOfficeSuiteChange}
|
||||||
|
toggleDialog={this.onOfficeSuiteToggle}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||||
import { gettext, isPro, folderPermEnabled, enableRepoSnapshotLabel, enableResetEncryptedRepoPassword, isEmailConfigured } from '../../utils/constants';
|
import { gettext, isPro, folderPermEnabled, enableRepoSnapshotLabel, enableResetEncryptedRepoPassword, isEmailConfigured, enableMultipleOfficeSuite } from '../../utils/constants';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
@ -120,6 +120,9 @@ class MylibRepoMenu extends React.Component {
|
|||||||
if (this.props.isPC && enableRepoSnapshotLabel) {
|
if (this.props.isPC && enableRepoSnapshotLabel) {
|
||||||
operations.push('Label Current State');
|
operations.push('Label Current State');
|
||||||
}
|
}
|
||||||
|
if (enableMultipleOfficeSuite && isPro) {
|
||||||
|
operations.push('Office Suite');
|
||||||
|
}
|
||||||
return operations;
|
return operations;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -174,6 +177,9 @@ class MylibRepoMenu extends React.Component {
|
|||||||
case 'SeaTable integration':
|
case 'SeaTable integration':
|
||||||
translateResult = gettext('SeaTable integration');
|
translateResult = gettext('SeaTable integration');
|
||||||
break;
|
break;
|
||||||
|
case 'Office Suite':
|
||||||
|
translateResult = gettext('Office Suite');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ export const ocmRemoteServers = window.app.pageOptions.ocmRemoteServers;
|
|||||||
export const enableOCMViaWebdav = window.app.pageOptions.enableOCMViaWebdav;
|
export const enableOCMViaWebdav = window.app.pageOptions.enableOCMViaWebdav;
|
||||||
export const enableSSOToThirdpartWebsite = window.app.pageOptions.enableSSOToThirdpartWebsite;
|
export const enableSSOToThirdpartWebsite = window.app.pageOptions.enableSSOToThirdpartWebsite;
|
||||||
export const enableSeadoc = window.app.pageOptions.enableSeadoc;
|
export const enableSeadoc = window.app.pageOptions.enableSeadoc;
|
||||||
|
export const enableMultipleOfficeSuite = window.app.pageOptions.enableMultipleOfficeSuite;
|
||||||
|
|
||||||
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
|
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
|
||||||
export const curNoteID = window.app.pageOptions.curNoteID;
|
export const curNoteID = window.app.pageOptions.curNoteID;
|
||||||
|
@ -63,6 +63,18 @@ class UserAPI {
|
|||||||
form.append('reshare', reshare);
|
form.append('reshare', reshare);
|
||||||
return this.req.put(url, form);
|
return this.req.put(url, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOfficeSuite(repoID) {
|
||||||
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/office-suite/';
|
||||||
|
return this.req.get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOfficeSuite(repoID, suiteID) {
|
||||||
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/office-suite/';
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('suite_id', suiteID);
|
||||||
|
return this.req.put(url, form);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let userAPI = new UserAPI();
|
let userAPI = new UserAPI();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice, enableSeadoc, enableFileTags, enableRepoSnapshotLabel,
|
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableSeadoc, enableFileTags, enableRepoSnapshotLabel,
|
||||||
enableResetEncryptedRepoPassword, isEmailConfigured, isSystemStaff } from './constants';
|
enableResetEncryptedRepoPassword, isEmailConfigured, isSystemStaff } from './constants';
|
||||||
import TextTranslation from './text-translation';
|
import TextTranslation from './text-translation';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -664,7 +664,7 @@ export const Utils = {
|
|||||||
list.push(HISTORY);
|
list.push(HISTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission == 'rw' && enableOnlyoffice &&
|
if (permission == 'rw' && currentRepoInfo.enable_onlyoffice &&
|
||||||
onlyofficeConverterExtensions.includes(Utils.getFileExtension(dirent.name, false))) {
|
onlyofficeConverterExtensions.includes(Utils.getFileExtension(dirent.name, false))) {
|
||||||
list.push(ONLYOFFICE_CONVERT);
|
list.push(ONLYOFFICE_CONVERT);
|
||||||
}
|
}
|
||||||
|
88
seahub/api2/endpoints/repo_office_suite.py
Normal file
88
seahub/api2/endpoints/repo_office_suite.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from seaserv import seafile_api
|
||||||
|
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.api2.permissions import IsProVersion
|
||||||
|
|
||||||
|
from seahub.onlyoffice.models import RepoExtraConfig, REPO_OFFICE_CONFIG
|
||||||
|
from seahub.settings import OFFICE_SUITE_LIST
|
||||||
|
from seahub.utils.repo import get_repo_owner
|
||||||
|
|
||||||
|
class OfficeSuiteConfig(APIView):
|
||||||
|
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, IsProVersion)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def get(self, request, repo_id):
|
||||||
|
if not request.user.permissions.can_choose_office_suite():
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
repo_owner = get_repo_owner(request, repo_id)
|
||||||
|
if '@seafile_group' in repo_owner:
|
||||||
|
error_msg = 'Department repo can not use this feature.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
current_suite = RepoExtraConfig.objects.filter(repo_id=repo_id, config_type=REPO_OFFICE_CONFIG).first()
|
||||||
|
suites_info = []
|
||||||
|
for office_suite in OFFICE_SUITE_LIST:
|
||||||
|
suite_info = {}
|
||||||
|
suite_info['id'] = office_suite.get('id')
|
||||||
|
suite_info['name'] = office_suite.get('name')
|
||||||
|
suite_info['is_default'] = office_suite.get('is_default')
|
||||||
|
if current_suite:
|
||||||
|
config_details = json.loads(current_suite.config_details)
|
||||||
|
office_config = config_details.get('office_suite')
|
||||||
|
suite_info['is_selected'] = (True if office_config and office_config.get('suite_id') == office_suite.get('id') else False)
|
||||||
|
else:
|
||||||
|
suite_info['is_selected'] = office_suite.get('is_default')
|
||||||
|
suites_info.append(suite_info)
|
||||||
|
|
||||||
|
return Response({'suites_info': suites_info})
|
||||||
|
|
||||||
|
def put(self, request, repo_id):
|
||||||
|
# arguments check
|
||||||
|
suite_id = request.data.get('suite_id', '')
|
||||||
|
if suite_id not in ['collabora', 'onlyoffice']:
|
||||||
|
error_msg = 'suite_id invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if not request.user.permissions.can_choose_office_suite():
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
# resource 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)
|
||||||
|
|
||||||
|
repo_owner = get_repo_owner(request, repo_id)
|
||||||
|
if '@seafile_group' in repo_owner:
|
||||||
|
error_msg = 'Department repo can not use this feature.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
config_details = {
|
||||||
|
'office_suite': {
|
||||||
|
'suite_id': suite_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RepoExtraConfig.objects.update_or_create(repo_id=repo_id, config_type=REPO_OFFICE_CONFIG,
|
||||||
|
defaults= {'config_details':json.dumps(config_details)} )
|
||||||
|
|
||||||
|
return Response({"success": True}, status=status.HTTP_200_OK)
|
@ -36,6 +36,7 @@ from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
|||||||
from seahub.settings import ENABLE_STORAGE_CLASSES
|
from seahub.settings import ENABLE_STORAGE_CLASSES
|
||||||
|
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
|
from seahub.views.file import get_office_feature_by_repo
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -125,6 +126,8 @@ class ReposView(APIView):
|
|||||||
continue
|
continue
|
||||||
url, _, _ = api_avatar_url(email)
|
url, _, _ = api_avatar_url(email)
|
||||||
|
|
||||||
|
enable_onlyoffice, _ = get_office_feature_by_repo(r)
|
||||||
|
|
||||||
repo_info = {
|
repo_info = {
|
||||||
"type": "mine",
|
"type": "mine",
|
||||||
"repo_id": r.id,
|
"repo_id": r.id,
|
||||||
@ -144,6 +147,7 @@ class ReposView(APIView):
|
|||||||
"monitored": r.repo_id in monitored_repo_id_list,
|
"monitored": r.repo_id in monitored_repo_id_list,
|
||||||
"status": normalize_repo_status_code(r.status),
|
"status": normalize_repo_status_code(r.status),
|
||||||
"salt": r.salt if r.enc_version >= 3 else '',
|
"salt": r.salt if r.enc_version >= 3 else '',
|
||||||
|
"enable_onlyoffice": enable_onlyoffice
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_pro_version() and ENABLE_STORAGE_CLASSES:
|
if is_pro_version() and ENABLE_STORAGE_CLASSES:
|
||||||
@ -199,6 +203,8 @@ class ReposView(APIView):
|
|||||||
owner_contact_email = '' if is_group_owned_repo else contact_email_dict.get(owner_email, '')
|
owner_contact_email = '' if is_group_owned_repo else contact_email_dict.get(owner_email, '')
|
||||||
url, _, _ = api_avatar_url(owner_email)
|
url, _, _ = api_avatar_url(owner_email)
|
||||||
|
|
||||||
|
enable_onlyoffice, _ = get_office_feature_by_repo(r)
|
||||||
|
|
||||||
repo_info = {
|
repo_info = {
|
||||||
"type": "shared",
|
"type": "shared",
|
||||||
"repo_id": r.repo_id,
|
"repo_id": r.repo_id,
|
||||||
@ -218,6 +224,7 @@ class ReposView(APIView):
|
|||||||
"monitored": r.repo_id in monitored_repo_id_list,
|
"monitored": r.repo_id in monitored_repo_id_list,
|
||||||
"status": normalize_repo_status_code(r.status),
|
"status": normalize_repo_status_code(r.status),
|
||||||
"salt": r.salt if r.enc_version >= 3 else '',
|
"salt": r.salt if r.enc_version >= 3 else '',
|
||||||
|
"enable_onlyoffice": enable_onlyoffice
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.repo_id in repos_with_admin_share_to:
|
if r.repo_id in repos_with_admin_share_to:
|
||||||
@ -259,7 +266,7 @@ class ReposView(APIView):
|
|||||||
if is_wiki_repo(r):
|
if is_wiki_repo(r):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
enable_onlyoffice, _ = get_office_feature_by_repo(r)
|
||||||
repo_info = {
|
repo_info = {
|
||||||
"type": "group",
|
"type": "group",
|
||||||
"group_id": r.group_id,
|
"group_id": r.group_id,
|
||||||
@ -277,6 +284,7 @@ class ReposView(APIView):
|
|||||||
"monitored": r.repo_id in monitored_repo_id_list,
|
"monitored": r.repo_id in monitored_repo_id_list,
|
||||||
"status": normalize_repo_status_code(r.status),
|
"status": normalize_repo_status_code(r.status),
|
||||||
"salt": r.salt if r.enc_version >= 3 else '',
|
"salt": r.salt if r.enc_version >= 3 else '',
|
||||||
|
"enable_onlyoffice": enable_onlyoffice
|
||||||
}
|
}
|
||||||
repo_info_list.append(repo_info)
|
repo_info_list.append(repo_info)
|
||||||
|
|
||||||
@ -310,6 +318,7 @@ class ReposView(APIView):
|
|||||||
|
|
||||||
repo_owner = repo_id_owner_dict[r.repo_id]
|
repo_owner = repo_id_owner_dict[r.repo_id]
|
||||||
url, _, _ = api_avatar_url(repo_owner)
|
url, _, _ = api_avatar_url(repo_owner)
|
||||||
|
enable_onlyoffice, _ = get_office_feature_by_repo(r)
|
||||||
repo_info = {
|
repo_info = {
|
||||||
"type": "public",
|
"type": "public",
|
||||||
"repo_id": r.repo_id,
|
"repo_id": r.repo_id,
|
||||||
@ -328,6 +337,7 @@ class ReposView(APIView):
|
|||||||
"starred": r.repo_id in starred_repo_id_list,
|
"starred": r.repo_id in starred_repo_id_list,
|
||||||
"status": normalize_repo_status_code(r.status),
|
"status": normalize_repo_status_code(r.status),
|
||||||
"salt": r.salt if r.enc_version >= 3 else '',
|
"salt": r.salt if r.enc_version >= 3 else '',
|
||||||
|
"enable_onlyoffice": enable_onlyoffice
|
||||||
}
|
}
|
||||||
repo_info_list.append(repo_info)
|
repo_info_list.append(repo_info)
|
||||||
|
|
||||||
@ -369,6 +379,7 @@ class RepoView(APIView):
|
|||||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
|
enable_onlyoffice, _ = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
lib_need_decrypt = False
|
lib_need_decrypt = False
|
||||||
if repo.encrypted \
|
if repo.encrypted \
|
||||||
@ -405,6 +416,7 @@ class RepoView(APIView):
|
|||||||
"lib_need_decrypt": lib_need_decrypt,
|
"lib_need_decrypt": lib_need_decrypt,
|
||||||
"last_modified": timestamp_to_isoformat_timestr(repo.last_modify),
|
"last_modified": timestamp_to_isoformat_timestr(repo.last_modify),
|
||||||
"status": normalize_repo_status_code(repo.status),
|
"status": normalize_repo_status_code(repo.status),
|
||||||
|
"enable_onlyoffice": enable_onlyoffice
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response(result)
|
return Response(result)
|
||||||
|
@ -110,6 +110,7 @@ from seahub.settings import THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, \
|
|||||||
from seahub.subscription.utils import subscription_check
|
from seahub.subscription.utils import subscription_check
|
||||||
from seahub.organizations.models import OrgAdminSettings, DISABLE_ORG_ENCRYPTED_LIBRARY
|
from seahub.organizations.models import OrgAdminSettings, DISABLE_ORG_ENCRYPTED_LIBRARY
|
||||||
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_image_parent_path, get_seadoc_asset_upload_link
|
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_image_parent_path, get_seadoc_asset_upload_link
|
||||||
|
from seahub.views.file import get_office_feature_by_repo
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import CLOUD_MODE
|
from seahub.settings import CLOUD_MODE
|
||||||
@ -124,11 +125,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
ORG_MEMBER_QUOTA_DEFAULT = None
|
ORG_MEMBER_QUOTA_DEFAULT = None
|
||||||
|
|
||||||
try:
|
|
||||||
from seahub.settings import ENABLE_OFFICE_WEB_APP
|
|
||||||
except ImportError:
|
|
||||||
ENABLE_OFFICE_WEB_APP = False
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import ORG_MEMBER_QUOTA_ENABLED
|
from seahub.settings import ORG_MEMBER_QUOTA_ENABLED
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -2828,6 +2824,8 @@ class OwaFileView(APIView):
|
|||||||
error_msg = 'Library %s not found.' % repo_id
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
_, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
action = request.GET.get('action', 'view')
|
action = request.GET.get('action', 'view')
|
||||||
if action not in ('view', 'edit'):
|
if action not in ('view', 'edit'):
|
||||||
error_msg = 'action invalid.'
|
error_msg = 'action invalid.'
|
||||||
|
@ -450,6 +450,11 @@ class UserPermissions(object):
|
|||||||
if not settings.ENABLE_WIKI:
|
if not settings.ENABLE_WIKI:
|
||||||
return False
|
return False
|
||||||
return self._get_perm_by_roles('can_publish_wiki')
|
return self._get_perm_by_roles('can_publish_wiki')
|
||||||
|
|
||||||
|
def can_choose_office_suite(self):
|
||||||
|
if not settings.ENABLE_MULTIPLE_OFFICE_SUITE:
|
||||||
|
return False
|
||||||
|
return self._get_perm_by_roles('can_choose_office_suite')
|
||||||
|
|
||||||
|
|
||||||
class AdminPermissions(object):
|
class AdminPermissions(object):
|
||||||
|
@ -14,3 +14,15 @@ class OnlyOfficeDocKey(models.Model):
|
|||||||
file_path = models.TextField()
|
file_path = models.TextField()
|
||||||
repo_id_file_path_md5 = models.CharField(max_length=100, db_index=True, unique=True)
|
repo_id_file_path_md5 = models.CharField(max_length=100, db_index=True, unique=True)
|
||||||
created_time = models.DateTimeField(default=datetime.datetime.now)
|
created_time = models.DateTimeField(default=datetime.datetime.now)
|
||||||
|
|
||||||
|
|
||||||
|
REPO_OFFICE_CONFIG = 'office'
|
||||||
|
class RepoExtraConfig(models.Model):
|
||||||
|
|
||||||
|
repo_id = models.CharField(max_length=36, db_index=True)
|
||||||
|
config_type = models.CharField(max_length=50)
|
||||||
|
config_details = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'repo_extra_config'
|
||||||
|
unique_together = ('repo_id', 'config_type')
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from seahub.settings import ENABLE_MULTIPLE_OFFICE_SUITE, OFFICE_SUITE_LIST, OFFICE_SUITE_ENABLED_FILE_TYPES, OFFICE_SUITE_ENABLED_EDIT_FILE_TYPES
|
||||||
|
|
||||||
ENABLE_ONLYOFFICE = getattr(settings, 'ENABLE_ONLYOFFICE', False)
|
ENABLE_ONLYOFFICE = getattr(settings, 'ENABLE_ONLYOFFICE', False)
|
||||||
ONLYOFFICE_APIJS_URL = getattr(settings, 'ONLYOFFICE_APIJS_URL', '')
|
ONLYOFFICE_APIJS_URL = getattr(settings, 'ONLYOFFICE_APIJS_URL', '')
|
||||||
@ -44,3 +45,23 @@ EXT_DOCUMENT = [
|
|||||||
".html", ".htm", ".mht", ".xml",
|
".html", ".htm", ".mht", ".xml",
|
||||||
".pdf", ".djvu", ".fb2", ".epub", ".xps"
|
".pdf", ".djvu", ".fb2", ".epub", ".xps"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if ENABLE_MULTIPLE_OFFICE_SUITE:
|
||||||
|
OFFICE_SUITE_ONLY_OFFICE = 'onlyoffice'
|
||||||
|
office_info = {}
|
||||||
|
for s in OFFICE_SUITE_LIST:
|
||||||
|
if s.get('id') == OFFICE_SUITE_ONLY_OFFICE:
|
||||||
|
office_info = s
|
||||||
|
break
|
||||||
|
ONLYOFFICE_APIJS_URL = office_info.get('ONLYOFFICE_APIJS_URL')
|
||||||
|
ONLYOFFICE_CONVERTER_URL = ONLYOFFICE_APIJS_URL and ONLYOFFICE_APIJS_URL.replace("/web-apps/apps/api/documents/api.js", "/ConvertService.ashx")
|
||||||
|
ONLYOFFICE_FORCE_SAVE = office_info.get('ONLYOFFICE_FORCE_SAVE', False)
|
||||||
|
ONLYOFFICE_JWT_SECRET = office_info.get('ONLYOFFICE_JWT_SECRET', '')
|
||||||
|
ONLYOFFICE_JWT_HEADER = office_info.get('ONLYOFFICE_JWT_HEADER', 'Authorization')
|
||||||
|
ONLYOFFICE_DESKTOP_EDITOR_HTTP_USER_AGENT = office_info.get('ONLYOFFICE_DESKTOP_EDITOR_HTTP_USER_AGENT', 'AscDesktopEditor')
|
||||||
|
VERIFY_ONLYOFFICE_CERTIFICATE = office_info.get('VERIFY_ONLYOFFICE_CERTIFICATE', True)
|
||||||
|
ONLYOFFICE_FILE_EXTENSION = OFFICE_SUITE_ENABLED_FILE_TYPES
|
||||||
|
ONLYOFFICE_EDIT_FILE_EXTENSION = OFFICE_SUITE_ENABLED_EDIT_FILE_TYPES
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
|
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
|
|
||||||
from seahub.onlyoffice.models import OnlyOfficeDocKey
|
|
||||||
from seahub.onlyoffice.settings import VERIFY_ONLYOFFICE_CERTIFICATE, ONLYOFFICE_JWT_SECRET
|
from seahub.onlyoffice.settings import VERIFY_ONLYOFFICE_CERTIFICATE, ONLYOFFICE_JWT_SECRET
|
||||||
from seahub.onlyoffice.utils import get_onlyoffice_dict, get_doc_key_by_repo_id_file_path
|
from seahub.onlyoffice.utils import get_onlyoffice_dict, get_doc_key_by_repo_id_file_path
|
||||||
from seahub.onlyoffice.utils import delete_doc_key, get_file_info_by_doc_key
|
from seahub.onlyoffice.utils import delete_doc_key, get_file_info_by_doc_key
|
||||||
@ -40,6 +40,7 @@ from seahub.views import check_folder_permission
|
|||||||
from seahub.utils.file_types import SPREADSHEET
|
from seahub.utils.file_types import SPREADSHEET
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
logger = logging.getLogger('onlyoffice')
|
logger = logging.getLogger('onlyoffice')
|
||||||
|
|
||||||
@ -506,4 +507,3 @@ class OnlyofficeGetReferenceData(APIView):
|
|||||||
}
|
}
|
||||||
result['token'] = jwt.encode(result, ONLYOFFICE_JWT_SECRET)
|
result['token'] = jwt.encode(result, ONLYOFFICE_JWT_SECRET)
|
||||||
return Response({'data': result})
|
return Response({'data': result})
|
||||||
|
|
||||||
|
@ -48,7 +48,8 @@ DEFAULT_ENABLED_ROLE_PERMISSIONS = {
|
|||||||
'upload_rate_limit': 0,
|
'upload_rate_limit': 0,
|
||||||
'download_rate_limit': 0,
|
'download_rate_limit': 0,
|
||||||
'monthly_rate_limit': '',
|
'monthly_rate_limit': '',
|
||||||
'monthly_rate_limit_per_user': ''
|
'monthly_rate_limit_per_user': '',
|
||||||
|
'can_choose_office_suite': True,
|
||||||
},
|
},
|
||||||
GUEST_USER: {
|
GUEST_USER: {
|
||||||
'can_add_repo': False,
|
'can_add_repo': False,
|
||||||
@ -73,7 +74,8 @@ DEFAULT_ENABLED_ROLE_PERMISSIONS = {
|
|||||||
'upload_rate_limit': 0,
|
'upload_rate_limit': 0,
|
||||||
'download_rate_limit': 0,
|
'download_rate_limit': 0,
|
||||||
'monthly_rate_limit': '',
|
'monthly_rate_limit': '',
|
||||||
'monthly_rate_limit_per_user': ''
|
'monthly_rate_limit_per_user': '',
|
||||||
|
'can_choose_office_suite': False,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,6 +973,26 @@ ENABLE_METADATA_MANAGEMENT = False
|
|||||||
METADATA_SERVER_URL = ''
|
METADATA_SERVER_URL = ''
|
||||||
METADATA_SERVER_SECRET_KEY = ''
|
METADATA_SERVER_SECRET_KEY = ''
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# multi office suite support
|
||||||
|
#############################
|
||||||
|
ENABLE_MULTIPLE_OFFICE_SUITE = False
|
||||||
|
OFFICE_SUITE_LIST = [
|
||||||
|
{
|
||||||
|
"id": "onlyoffice",
|
||||||
|
"name": "OnlyOffice",
|
||||||
|
"is_default": True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "collabora",
|
||||||
|
"name": "CollaboraOnline",
|
||||||
|
"is_default": False,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
ROLES_DEFAULT_OFFCICE_SUITE = {}
|
||||||
|
OFFICE_SUITE_ENABLED_FILE_TYPES = []
|
||||||
|
OFFICE_SUITE_ENABLED_EDIT_FILE_TYPES = []
|
||||||
|
|
||||||
# file tags
|
# file tags
|
||||||
ENABLE_FILE_TAGS = True
|
ENABLE_FILE_TAGS = True
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@
|
|||||||
enableMetadataManagement: {% if enable_metadata_management %} true {% else %} false {% endif %},
|
enableMetadataManagement: {% if enable_metadata_management %} true {% else %} false {% endif %},
|
||||||
enableFileTags: {% if enable_file_tags %} true {% else %} false {% endif %},
|
enableFileTags: {% if enable_file_tags %} true {% else %} false {% endif %},
|
||||||
enableShowAbout: {% if enable_show_about %} true {% else %} false {% endif %},
|
enableShowAbout: {% if enable_show_about %} true {% else %} false {% endif %},
|
||||||
|
enableMultipleOfficeSuite: {% if user.permissions.can_choose_office_suite %} true {% else %} false {% endif %},
|
||||||
showWechatSupportGroup: {% if show_wechat_support_group %} true {% else %} false {% endif %},
|
showWechatSupportGroup: {% if show_wechat_support_group %} true {% else %} false {% endif %},
|
||||||
baiduMapKey: '{{ baidu_map_key }}',
|
baiduMapKey: '{{ baidu_map_key }}',
|
||||||
googleMapKey: '{{ google_map_key }}',
|
googleMapKey: '{{ google_map_key }}',
|
||||||
|
@ -212,6 +212,7 @@ from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView,
|
|||||||
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
|
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
|
||||||
from seahub.api2.endpoints.user_list import UserListView
|
from seahub.api2.endpoints.user_list import UserListView
|
||||||
from seahub.api2.endpoints.seahub_io import SeahubIOStatus
|
from seahub.api2.endpoints.seahub_io import SeahubIOStatus
|
||||||
|
from seahub.api2.endpoints.repo_office_suite import OfficeSuiteConfig
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -463,6 +464,7 @@ urlpatterns = [
|
|||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/upload-links/(?P<token>[a-f0-9]+)/$', RepoUploadLink.as_view(), name='api-v2.1-repo-upload-link'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/upload-links/(?P<token>[a-f0-9]+)/$', RepoUploadLink.as_view(), name='api-v2.1-repo-upload-link'),
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-info/$', RepoShareInfoView.as_view(), name='api-v2.1-repo-share-info-view'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-info/$', RepoShareInfoView.as_view(), name='api-v2.1-repo-share-info-view'),
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/image-rotate/$', RepoImageRotateView.as_view(), name='api-v2.1-repo-image-rotate-view'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/image-rotate/$', RepoImageRotateView.as_view(), name='api-v2.1-repo-image-rotate-view'),
|
||||||
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/office-suite/$', OfficeSuiteConfig.as_view(), name='api-v2.1-repo-office-suite'),
|
||||||
|
|
||||||
## user:: repo-api-tokens
|
## user:: repo-api-tokens
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/repo-api-tokens/$', RepoAPITokensView.as_view(), name='api-v2.1-repo-api-tokens'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/repo-api-tokens/$', RepoAPITokensView.as_view(), name='api-v2.1-repo-api-tokens'),
|
||||||
@ -1006,7 +1008,7 @@ if getattr(settings, 'ENABLE_MULTI_ADFS', False) or getattr(settings, 'ENABLE_AD
|
|||||||
path('saml2/complete/', auth_complete, name='saml2_complete'),
|
path('saml2/complete/', auth_complete, name='saml2_complete'),
|
||||||
]
|
]
|
||||||
|
|
||||||
if getattr(settings, 'ENABLE_ONLYOFFICE', False):
|
if getattr(settings, 'ENABLE_ONLYOFFICE', False) or getattr(settings, 'ENABLE_MULTIPLE_OFFICE_SUITE', False):
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
path('onlyoffice/', include('seahub.onlyoffice.urls')),
|
path('onlyoffice/', include('seahub.onlyoffice.urls')),
|
||||||
path('onlyoffice-api/', include('seahub.onlyoffice.api_urls')),
|
path('onlyoffice-api/', include('seahub.onlyoffice.api_urls')),
|
||||||
|
@ -1150,7 +1150,6 @@ def react_fake_view(request, **kwargs):
|
|||||||
'enable_metadata_management': ENABLE_METADATA_MANAGEMENT,
|
'enable_metadata_management': ENABLE_METADATA_MANAGEMENT,
|
||||||
'enable_file_tags': settings.ENABLE_FILE_TAGS,
|
'enable_file_tags': settings.ENABLE_FILE_TAGS,
|
||||||
'enable_show_about': settings.ENABLE_SHOW_ABOUT
|
'enable_show_about': settings.ENABLE_SHOW_ABOUT
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ENABLE_METADATA_MANAGEMENT:
|
if ENABLE_METADATA_MANAGEMENT:
|
||||||
|
@ -34,17 +34,18 @@ from django.template.defaultfilters import filesizeformat
|
|||||||
from seaserv import seafile_api, ccnet_api
|
from seaserv import seafile_api, ccnet_api
|
||||||
from seaserv import get_repo, get_commits, \
|
from seaserv import get_repo, get_commits, \
|
||||||
get_file_id_by_path, get_commit, get_file_size, \
|
get_file_id_by_path, get_commit, get_file_size, \
|
||||||
seafserv_threaded_rpc
|
seafserv_threaded_rpc, get_org_id_by_repo_id
|
||||||
|
|
||||||
from seahub.settings import SITE_ROOT
|
from seahub.settings import SITE_ROOT
|
||||||
from seahub.share.utils import check_share_link_user_access
|
from seahub.share.utils import check_share_link_user_access
|
||||||
from seahub.tags.models import FileUUIDMap
|
from seahub.tags.models import FileUUIDMap
|
||||||
from seahub.wopi.utils import get_wopi_dict
|
from seahub.wopi.utils import get_wopi_dict
|
||||||
from seahub.onlyoffice.utils import get_onlyoffice_dict
|
from seahub.onlyoffice.utils import get_onlyoffice_dict
|
||||||
|
from seahub.onlyoffice.models import RepoExtraConfig, REPO_OFFICE_CONFIG
|
||||||
from seahub.auth.decorators import login_required
|
from seahub.auth.decorators import login_required
|
||||||
from seahub.auth import SESSION_MOBILE_LOGIN_KEY
|
from seahub.auth import SESSION_MOBILE_LOGIN_KEY
|
||||||
from seahub.base.decorators import repo_passwd_set_required
|
from seahub.base.decorators import repo_passwd_set_required
|
||||||
from seahub.base.accounts import ANONYMOUS_EMAIL
|
from seahub.base.accounts import ANONYMOUS_EMAIL, User
|
||||||
from seahub.base.templatetags.seahub_tags import file_icon_filter
|
from seahub.base.templatetags.seahub_tags import file_icon_filter
|
||||||
from seahub.share.models import FileShare, check_share_link_common
|
from seahub.share.models import FileShare, check_share_link_common
|
||||||
from seahub.share.decorators import share_link_audit, share_link_login_required
|
from seahub.share.decorators import share_link_audit, share_link_login_required
|
||||||
@ -66,6 +67,7 @@ from seahub.utils.http import json_response, \
|
|||||||
BadRequestException
|
BadRequestException
|
||||||
from seahub.utils.file_op import check_file_lock, \
|
from seahub.utils.file_op import check_file_lock, \
|
||||||
ONLINE_OFFICE_LOCK_OWNER, if_locked_by_online_office
|
ONLINE_OFFICE_LOCK_OWNER, if_locked_by_online_office
|
||||||
|
from seahub.utils.user_permissions import get_user_role
|
||||||
from seahub.views import check_folder_permission, \
|
from seahub.views import check_folder_permission, \
|
||||||
get_unencry_rw_repos_by_user
|
get_unencry_rw_repos_by_user
|
||||||
from seahub.utils.repo import is_repo_owner, parse_repo_perm, is_repo_admin
|
from seahub.utils.repo import is_repo_owner, parse_repo_perm, is_repo_admin
|
||||||
@ -87,33 +89,34 @@ from seahub.settings import FILE_ENCODING_LIST, FILE_PREVIEW_MAX_SIZE, \
|
|||||||
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_EXPIRE_DAYS_MAX, SHARE_LINK_PASSWORD_MIN_LENGTH, \
|
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_EXPIRE_DAYS_MAX, SHARE_LINK_PASSWORD_MIN_LENGTH, \
|
||||||
SHARE_LINK_FORCE_USE_PASSWORD, SHARE_LINK_PASSWORD_STRENGTH_LEVEL, \
|
SHARE_LINK_FORCE_USE_PASSWORD, SHARE_LINK_PASSWORD_STRENGTH_LEVEL, \
|
||||||
SHARE_LINK_EXPIRE_DAYS_DEFAULT, ENABLE_SHARE_LINK_REPORT_ABUSE, SEADOC_SERVER_URL, \
|
SHARE_LINK_EXPIRE_DAYS_DEFAULT, ENABLE_SHARE_LINK_REPORT_ABUSE, SEADOC_SERVER_URL, \
|
||||||
ENABLE_METADATA_MANAGEMENT, BAIDU_MAP_KEY, GOOGLE_MAP_KEY, GOOGLE_MAP_ID
|
ENABLE_METADATA_MANAGEMENT, BAIDU_MAP_KEY, GOOGLE_MAP_KEY, GOOGLE_MAP_ID, ENABLE_MULTIPLE_OFFICE_SUITE, \
|
||||||
|
OFFICE_SUITE_LIST
|
||||||
|
|
||||||
|
|
||||||
# wopi
|
# wopi
|
||||||
try:
|
try:
|
||||||
from seahub.settings import ENABLE_OFFICE_WEB_APP
|
from seahub.wopi.settings import ENABLE_OFFICE_WEB_APP
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENABLE_OFFICE_WEB_APP = False
|
ENABLE_OFFICE_WEB_APP = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import ENABLE_OFFICE_WEB_APP_EDIT
|
from seahub.wopi.settings import ENABLE_OFFICE_WEB_APP_EDIT
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENABLE_OFFICE_WEB_APP_EDIT = False
|
ENABLE_OFFICE_WEB_APP_EDIT = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import OFFICE_WEB_APP_FILE_EXTENSION
|
from seahub.wopi.settings import OFFICE_WEB_APP_FILE_EXTENSION
|
||||||
except ImportError:
|
except ImportError:
|
||||||
OFFICE_WEB_APP_FILE_EXTENSION = ()
|
OFFICE_WEB_APP_FILE_EXTENSION = ()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import OFFICE_WEB_APP_EDIT_FILE_EXTENSION
|
from seahub.wopi.settings import OFFICE_WEB_APP_EDIT_FILE_EXTENSION
|
||||||
except ImportError:
|
except ImportError:
|
||||||
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = ()
|
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = ()
|
||||||
|
|
||||||
# onlyoffice
|
# onlyoffice
|
||||||
try:
|
try:
|
||||||
from seahub.settings import ENABLE_ONLYOFFICE
|
from seahub.onlyoffice.settings import ENABLE_ONLYOFFICE
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENABLE_ONLYOFFICE = False
|
ENABLE_ONLYOFFICE = False
|
||||||
|
|
||||||
@ -130,6 +133,7 @@ except ImportError:
|
|||||||
from seahub.thirdparty_editor.settings import ENABLE_THIRDPARTY_EDITOR
|
from seahub.thirdparty_editor.settings import ENABLE_THIRDPARTY_EDITOR
|
||||||
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACTION_URL_DICT
|
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACTION_URL_DICT
|
||||||
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACCESS_TOKEN_EXPIRATION
|
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACCESS_TOKEN_EXPIRATION
|
||||||
|
from seahub.settings import ROLES_DEFAULT_OFFCICE_SUITE
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -140,6 +144,57 @@ FILE_TYPE_FOR_NEW_FILE_LINK = [
|
|||||||
MARKDOWN
|
MARKDOWN
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def _check_feature(repo_id):
|
||||||
|
office_suite = RepoExtraConfig.objects.filter(repo_id=repo_id, config_type=REPO_OFFICE_CONFIG).first()
|
||||||
|
if office_suite:
|
||||||
|
repo_config_details = json.loads(office_suite.config_details)
|
||||||
|
office_config = repo_config_details.get('office_suite')
|
||||||
|
return office_config.get('suite_id') if office_config else None
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_office_feature_by_repo(repo):
|
||||||
|
enable_onlyoffice, enable_office_app = False, False
|
||||||
|
if not ENABLE_MULTIPLE_OFFICE_SUITE:
|
||||||
|
return ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP
|
||||||
|
|
||||||
|
if not OFFICE_SUITE_LIST:
|
||||||
|
return ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP
|
||||||
|
|
||||||
|
org_id = get_org_id_by_repo_id(repo.repo_id)
|
||||||
|
if org_id > 0:
|
||||||
|
repo_owner = seafile_api.get_org_repo_owner(repo.repo_id)
|
||||||
|
else:
|
||||||
|
repo_owner = seafile_api.get_repo_owner(repo.repo_id)
|
||||||
|
if '@seafile_group' in repo_owner:
|
||||||
|
repo_feature = None
|
||||||
|
else:
|
||||||
|
repo_feature = _check_feature(repo.repo_id)
|
||||||
|
|
||||||
|
if not repo_feature and '@seafile_group' not in repo_owner:
|
||||||
|
user = User.objects.get(email=repo_owner)
|
||||||
|
role = get_user_role(user)
|
||||||
|
repo_feature = ROLES_DEFAULT_OFFCICE_SUITE.get(role)
|
||||||
|
|
||||||
|
if not repo_feature:
|
||||||
|
default_suite = {}
|
||||||
|
for s in OFFICE_SUITE_LIST:
|
||||||
|
if s.get('is_default'):
|
||||||
|
default_suite = s
|
||||||
|
break
|
||||||
|
if default_suite.get('id') == 'onlyoffice':
|
||||||
|
enable_onlyoffice = True
|
||||||
|
if default_suite.get('id') == 'collabora':
|
||||||
|
enable_office_app = True
|
||||||
|
else:
|
||||||
|
if repo_feature == 'onlyoffice':
|
||||||
|
enable_onlyoffice = True
|
||||||
|
if repo_feature == 'collabora':
|
||||||
|
enable_office_app = True
|
||||||
|
|
||||||
|
return enable_onlyoffice, enable_office_app
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def gen_path_link(path, repo_name):
|
def gen_path_link(path, repo_name):
|
||||||
"""
|
"""
|
||||||
Generate navigate paths and links in repo page.
|
Generate navigate paths and links in repo page.
|
||||||
@ -333,6 +388,8 @@ def can_preview_file(file_name, file_size, repo):
|
|||||||
|
|
||||||
filetype, fileext = get_file_type_and_ext(file_name)
|
filetype, fileext = get_file_type_and_ext(file_name)
|
||||||
|
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
# Seafile defines 10 kinds of filetype:
|
# Seafile defines 10 kinds of filetype:
|
||||||
# TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG
|
# TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG
|
||||||
if filetype in (TEXT, MARKDOWN, IMAGE) or fileext in get_conf_text_ext():
|
if filetype in (TEXT, MARKDOWN, IMAGE) or fileext in get_conf_text_ext():
|
||||||
@ -387,7 +444,7 @@ def can_edit_file(file_name, file_size, repo):
|
|||||||
"""Check whether Seafile supports edit file.
|
"""Check whether Seafile supports edit file.
|
||||||
Returns (True, None) if Yes, otherwise (False, error_msg).
|
Returns (True, None) if Yes, otherwise (False, error_msg).
|
||||||
"""
|
"""
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
can_preview, err_msg = can_preview_file(file_name, file_size, repo)
|
can_preview, err_msg = can_preview_file(file_name, file_size, repo)
|
||||||
if not can_preview:
|
if not can_preview:
|
||||||
return False, err_msg
|
return False, err_msg
|
||||||
@ -474,7 +531,7 @@ def convert_repo_path_when_can_not_view_file(request, repo_id, path):
|
|||||||
@login_required
|
@login_required
|
||||||
@repo_passwd_set_required
|
@repo_passwd_set_required
|
||||||
def view_lib_file(request, repo_id, path):
|
def view_lib_file(request, repo_id, path):
|
||||||
|
|
||||||
# resource check
|
# resource check
|
||||||
repo = seafile_api.get_repo(repo_id)
|
repo = seafile_api.get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
@ -484,6 +541,8 @@ def view_lib_file(request, repo_id, path):
|
|||||||
file_id = seafile_api.get_file_id_by_path(repo_id, path)
|
file_id = seafile_api.get_file_id_by_path(repo_id, path)
|
||||||
if not file_id:
|
if not file_id:
|
||||||
return render_error(request, _('File does not exist'))
|
return render_error(request, _('File does not exist'))
|
||||||
|
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
# permission check
|
# permission check
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
@ -926,6 +985,8 @@ def view_history_file_common(request, repo_id, ret_dict):
|
|||||||
path = request.GET.get('p', '/')
|
path = request.GET.get('p', '/')
|
||||||
path = normalize_file_path(path)
|
path = normalize_file_path(path)
|
||||||
|
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
commit_id = request.GET.get('commit_id', '')
|
commit_id = request.GET.get('commit_id', '')
|
||||||
if not commit_id:
|
if not commit_id:
|
||||||
raise Http404
|
raise Http404
|
||||||
@ -1176,6 +1237,8 @@ def view_shared_file(request, fileshare):
|
|||||||
obj_id = seafile_api.get_file_id_by_path(repo_id, path)
|
obj_id = seafile_api.get_file_id_by_path(repo_id, path)
|
||||||
if not obj_id:
|
if not obj_id:
|
||||||
return render_error(request, _('File does not exist'))
|
return render_error(request, _('File does not exist'))
|
||||||
|
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
# permission check
|
# permission check
|
||||||
shared_by = fileshare.username
|
shared_by = fileshare.username
|
||||||
@ -1430,6 +1493,8 @@ def view_file_via_shared_dir(request, fileshare):
|
|||||||
if not repo:
|
if not repo:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
ENABLE_ONLYOFFICE, ENABLE_OFFICE_WEB_APP = get_office_feature_by_repo(repo)
|
||||||
|
|
||||||
# recourse check
|
# recourse check
|
||||||
# Get file path from frontend, and construct request file path
|
# Get file path from frontend, and construct request file path
|
||||||
# with fileshare.path to real path, used to fetch file content by RPC.
|
# with fileshare.path to real path, used to fetch file content by RPC.
|
||||||
@ -1671,6 +1736,7 @@ def view_raw_file(request, repo_id, file_path):
|
|||||||
repo = get_repo(repo_id)
|
repo = get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
|
||||||
file_path = file_path.rstrip('/')
|
file_path = file_path.rstrip('/')
|
||||||
if file_path[0] != '/':
|
if file_path[0] != '/':
|
||||||
@ -1734,7 +1800,7 @@ def download_file(request, repo_id, obj_id):
|
|||||||
repo = get_repo(repo_id)
|
repo = get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
if repo.encrypted and not seafile_api.is_password_set(repo_id, username):
|
if repo.encrypted and not seafile_api.is_password_set(repo_id, username):
|
||||||
reverse_url = reverse('lib_view', args=[repo_id, repo.name, ''])
|
reverse_url = reverse('lib_view', args=[repo_id, repo.name, ''])
|
||||||
return HttpResponseRedirect(reverse_url)
|
return HttpResponseRedirect(reverse_url)
|
||||||
@ -1922,6 +1988,7 @@ def office_convert_get_page(request, repo_id, commit_id, path, filename):
|
|||||||
"""
|
"""
|
||||||
if not HAS_OFFICE_CONVERTER:
|
if not HAS_OFFICE_CONVERTER:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
|
||||||
if not _OFFICE_PAGE_PATTERN.match(filename):
|
if not _OFFICE_PAGE_PATTERN.match(filename):
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
import seahub.settings as settings
|
import seahub.settings as settings
|
||||||
|
|
||||||
# OfficeOnlineServer, OnlyOffice, CollaboraOffice
|
# OfficeOnlineServer, OnlyOffice, CollaboraOffice
|
||||||
OFFICE_SERVER_TYPE = getattr(settings, 'OFFICE_SERVER_TYPE', '')
|
OFFICE_SERVER_TYPE = getattr(settings, 'OFFICE_SERVER_TYPE', '')
|
||||||
|
|
||||||
@ -26,6 +25,26 @@ OFFICE_WEB_APP_CLIENT_PEM = getattr(settings, 'OFFICE_WEB_APP_CLIENT_PEM', '')
|
|||||||
|
|
||||||
|
|
||||||
## Server certificates ##
|
## Server certificates ##
|
||||||
|
|
||||||
# Path to a CA_BUNDLE file or directory with certificates of trusted CAs
|
# Path to a CA_BUNDLE file or directory with certificates of trusted CAs
|
||||||
OFFICE_WEB_APP_SERVER_CA = getattr(settings, 'OFFICE_WEB_APP_SERVER_CA', True)
|
OFFICE_WEB_APP_SERVER_CA = getattr(settings, 'OFFICE_WEB_APP_SERVER_CA', True)
|
||||||
|
|
||||||
|
if settings.ENABLE_MULTIPLE_OFFICE_SUITE:
|
||||||
|
OFFICE_SUITE_COLLA = 'collabora'
|
||||||
|
office_info = {}
|
||||||
|
for s in settings.OFFICE_SUITE_LIST:
|
||||||
|
if s.get('id') == OFFICE_SUITE_COLLA:
|
||||||
|
office_info = s
|
||||||
|
break
|
||||||
|
OFFICE_SERVER_TYPE = office_info.get('OFFICE_SERVER_TYPE', 'collaboraoffice')
|
||||||
|
OFFICE_WEB_APP_BASE_URL = office_info.get('OFFICE_WEB_APP_BASE_URL', '')
|
||||||
|
WOPI_ACCESS_TOKEN_EXPIRATION = office_info.get('WOPI_ACCESS_TOKEN_EXPIRATION', 12 * 60 * 60)
|
||||||
|
OFFICE_WEB_APP_DISCOVERY_EXPIRATION = office_info.get('OFFICE_WEB_APP_DISCOVERY_EXPIRATION', 7 * 24 * 60 * 60)
|
||||||
|
OFFICE_WEB_APP_CLIENT_CERT = office_info.get('OFFICE_WEB_APP_CLIENT_CERT', '')
|
||||||
|
OFFICE_WEB_APP_CLIENT_KEY = office_info.get('OFFICE_WEB_APP_CLIENT_KEY', '')
|
||||||
|
OFFICE_WEB_APP_CLIENT_PEM = office_info.get('OFFICE_WEB_APP_CLIENT_PEM', '')
|
||||||
|
OFFICE_WEB_APP_SERVER_CA = office_info.get('OFFICE_WEB_APP_SERVER_CA', '')
|
||||||
|
ENABLE_OFFICE_WEB_APP_EDIT = office_info.get('ENABLE_OFFICE_WEB_APP_EDIT', False)
|
||||||
|
OFFICE_WEB_APP_FILE_EXTENSION = settings.OFFICE_SUITE_ENABLED_FILE_TYPES
|
||||||
|
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = settings.OFFICE_SUITE_ENABLED_EDIT_FILE_TYPES
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,4 +11,4 @@ class UtilsTest(BaseTestCase):
|
|||||||
assert DEFAULT_USER in get_available_roles()
|
assert DEFAULT_USER in get_available_roles()
|
||||||
|
|
||||||
def test_get_enabled_role_permissions_by_role(self):
|
def test_get_enabled_role_permissions_by_role(self):
|
||||||
assert len(list(get_enabled_role_permissions_by_role(DEFAULT_USER).keys())) == 23
|
assert len(list(get_enabled_role_permissions_by_role(DEFAULT_USER).keys())) == 24
|
||||||
|
Loading…
Reference in New Issue
Block a user