diff --git a/frontend/config/webpack.entry.js b/frontend/config/webpack.entry.js
index 97fc4db6c7..7d20199442 100644
--- a/frontend/config/webpack.entry.js
+++ b/frontend/config/webpack.entry.js
@@ -30,6 +30,7 @@ const entryFiles = {
viewFileText: '/view-file-text.js',
viewFileSdoc: '/view-file-sdoc.js',
viewFileDocument: '/view-file-document.js',
+ viewFileOnlyoffice: '/view-file-onlyoffice.js',
viewFileSpreadsheet: '/view-file-spreadsheet.js',
settings: '/settings.js',
repoHistory: '/repo-history.js',
diff --git a/frontend/src/assets/icons/double-arrow-down.svg b/frontend/src/assets/icons/double-arrow-down.svg
new file mode 100644
index 0000000000..2c923a931d
--- /dev/null
+++ b/frontend/src/assets/icons/double-arrow-down.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/frontend/src/assets/icons/double-arrow-up.svg b/frontend/src/assets/icons/double-arrow-up.svg
new file mode 100644
index 0000000000..e3512b1187
--- /dev/null
+++ b/frontend/src/assets/icons/double-arrow-up.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/frontend/src/components/dirent-detail/embedded-file-details/index.js b/frontend/src/components/dirent-detail/embedded-file-details/index.js
index 17062c448b..18ccd40057 100644
--- a/frontend/src/components/dirent-detail/embedded-file-details/index.js
+++ b/frontend/src/components/dirent-detail/embedded-file-details/index.js
@@ -10,7 +10,7 @@ import { MetadataContext } from '../../../metadata';
import './index.css';
-const EmbeddedFileDetails = ({ repoID, repoInfo, dirent, path, onClose, width = 300, className, component }) => {
+const EmbeddedFileDetails = ({ repoID, repoInfo, dirent, path, onClose, width = 300, className, component = {} }) => {
const { headerComponent } = component;
const [direntDetail, setDirentDetail] = useState('');
diff --git a/frontend/src/components/file-view/file-info.js b/frontend/src/components/file-view/file-info.js
index ec6c22c2c8..d5f567107d 100644
--- a/frontend/src/components/file-view/file-info.js
+++ b/frontend/src/components/file-view/file-info.js
@@ -7,7 +7,8 @@ import InternalLinkOperation from '../operations/internal-link-operation';
const propTypes = {
toggleStar: PropTypes.func.isRequired,
isLocked: PropTypes.bool.isRequired,
- isStarred: PropTypes.bool.isRequired
+ isStarred: PropTypes.bool.isRequired,
+ isOnlyofficeFile: PropTypes.bool.isRequired
};
const { fileName, repoID, filePath,
@@ -26,7 +27,7 @@ class FileInfo extends React.PureComponent {
};
render() {
- const { isStarred, isLocked } = this.props;
+ const { isStarred, isLocked, isOnlyofficeFile } = this.props;
const starredText = isStarred ? gettext('starred') : gettext('unstarred');
const lockedText = gettext('locked');
return (
@@ -41,7 +42,7 @@ class FileInfo extends React.PureComponent {
onClick={this.toggleStar}>
- {(isPro && isLocked) &&
+ {(isPro && isLocked && !isOnlyofficeFile) &&
}
-
+ {!isOnlyofficeFile && (
+
+ )}
);
}
diff --git a/frontend/src/components/file-view/file-toolbar.js b/frontend/src/components/file-view/file-toolbar.js
index 98d9f1bbff..adaa534c24 100644
--- a/frontend/src/components/file-view/file-toolbar.js
+++ b/frontend/src/components/file-view/file-toolbar.js
@@ -84,8 +84,9 @@ class FileToolbar extends React.Component {
return null;
}
- const { isLocked, lockedByMe } = this.props;
const { moreDropdownOpen } = this.state;
+
+ const { isLocked, lockedByMe } = this.props;
let showLockUnlockBtn = false;
let lockUnlockText; let lockUnlockIcon;
if (canLockUnlockFile) {
diff --git a/frontend/src/components/file-view/file-view.js b/frontend/src/components/file-view/file-view.js
index 6132f5b6a3..5d7a18cabd 100644
--- a/frontend/src/components/file-view/file-view.js
+++ b/frontend/src/components/file-view/file-view.js
@@ -2,12 +2,16 @@ import React from 'react';
import PropTypes from 'prop-types';
import watermark from 'watermark-dom';
import { seafileAPI } from '../../utils/seafile-api';
-import { siteName } from '../../utils/constants';
+import { gettext, siteName } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
+import IconButton from '../icon-button';
import FileInfo from './file-info';
import FileToolbar from './file-toolbar';
+import OnlyofficeFileToolbar from './onlyoffice-file-toolbar';
import FileDetails from '../dirent-detail/old-file-details';
+import EmbeddedFileDetails from '../dirent-detail/embedded-file-details';
+import { CollaboratorsProvider, EnableMetadataProvider } from '../../metadata';
import '../../css/file-view.css';
@@ -16,16 +20,16 @@ const propTypes = {
content: PropTypes.object.isRequired,
isSaving: PropTypes.bool,
needSave: PropTypes.bool,
+ isOnlyofficeFile: PropTypes.bool,
participants: PropTypes.array,
onParticipantsChange: PropTypes.func,
};
const { isStarred, isLocked, lockedByMe,
- repoID, filePath, enableWatermark, userNickName,
+ repoID, filePath, filePerm, enableWatermark, userNickName,
repoName, parentDir, fileName
} = window.app.pageOptions;
-
class FileView extends React.Component {
constructor(props) {
@@ -34,6 +38,7 @@ class FileView extends React.Component {
isStarred: isStarred,
isLocked: isLocked,
lockedByMe: lockedByMe,
+ isHeaderShown: true,
isDetailsPanelOpen: false
};
}
@@ -93,38 +98,76 @@ class FileView extends React.Component {
}
};
+ toggleHeader = () => {
+ this.setState({
+ isHeaderShown: !this.state.isHeaderShown
+ });
+ };
+
render() {
- const { isDetailsPanelOpen } = this.state;
+ const { isOnlyofficeFile = false } = this.props;
+ const { isDetailsPanelOpen, isHeaderShown } = this.state;
return (
-
+
-
-
-
- {this.props.content}
- {isDetailsPanelOpen &&
-
+ {isOnlyofficeFile ?
+ :
+
}
+
+ {(isOnlyofficeFile && !isHeaderShown) &&
+
+ }
+ {this.props.content}
+ {isDetailsPanelOpen && (
+ <>
+ {isOnlyofficeFile ?
+
+
+
+
+
+ :
+
+ }
+ >
+ )}
+
);
}
diff --git a/frontend/src/components/file-view/onlyoffice-file-toolbar.js b/frontend/src/components/file-view/onlyoffice-file-toolbar.js
new file mode 100644
index 0000000000..f8b3006fd8
--- /dev/null
+++ b/frontend/src/components/file-view/onlyoffice-file-toolbar.js
@@ -0,0 +1,95 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
+import { gettext, siteRoot } from '../../utils/constants';
+import { Utils } from '../../utils/utils';
+import Icon from '../../components/icon';
+import IconButton from '../icon-button';
+
+const propTypes = {
+ toggleDetailsPanel: PropTypes.func.isRequired,
+ toggleHeader: PropTypes.func.isRequired
+};
+
+const {
+ repoID, repoName, parentDir
+} = window.app.pageOptions;
+
+class OnlyofficeFileToolbar extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ dropdownOpen: false,
+ moreDropdownOpen: false
+ };
+ }
+
+ toggleMoreOpMenu = () => {
+ this.setState({
+ moreDropdownOpen: !this.state.moreDropdownOpen
+ });
+ };
+
+ toggle = () => {
+ this.setState({
+ dropdownOpen: !this.state.dropdownOpen
+ });
+ };
+
+ render() {
+ const { moreDropdownOpen } = this.state;
+ return (
+
+
+
+
+
+
+
+
+ {gettext('Fold')}
+ {gettext('Details')}
+
+
+ {gettext('Open parent folder')}
+
+
+
+
+
+ );
+ }
+}
+
+OnlyofficeFileToolbar.propTypes = propTypes;
+
+export default OnlyofficeFileToolbar;
diff --git a/frontend/src/css/file-view.css b/frontend/src/css/file-view.css
index c4af35cc9d..7095ed0c52 100644
--- a/frontend/src/css/file-view.css
+++ b/frontend/src/css/file-view.css
@@ -13,10 +13,39 @@ body {
flex-shrink: 0;
}
+.onlyoffice-file-view-header-shown {
+ transition: height .3s ease-in-out;
+ height: 56px;
+}
+
+.onlyoffice-file-view-header-hidden {
+ transition: all .3s ease-in-out;
+ display: none!important;
+}
+
+#unfold-onlyoffice-file-view-header {
+ position: absolute;
+ top: 0;
+ right: 40px;
+ width: 28px;
+ height: 28px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(255,255,255, 0.6);
+ border-radius: 0 0 14px 14px;
+ box-shadow: 0 0 8px rgba(0,0,0, 0.4);
+ cursor: pointer;
+}
+
+#unfold-onlyoffice-file-view-header:hover {
+ background: rgba(255,255,255, 1);
+}
+
.file-view-header .file-toolbar-btn {
width: 28px;
height: 28px;
- margin-right: 10px;
+ margin-left: 10px;
cursor: pointer;
display: flex;
justify-content: center;
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index e676e4be5c..8c04d283ac 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -399,14 +399,14 @@ export const Utils = {
if (!dirent) return '';
let size = Utils.isHiDPI() ? 48 : 24;
size = isBig ? 192 : size;
- if (dirent.isDir()) {
+ if (dirent.type == 'file') {
+ return Utils.getFileIconUrl(dirent.name);
+ } else {
let readonly = false;
if (dirent.permission && (dirent.permission === 'r' || dirent.permission === 'preview')) {
readonly = true;
}
return Utils.getFolderIconUrl(readonly, size, dirent.has_been_shared_out);
- } else {
- return Utils.getFileIconUrl(dirent.name);
}
},
diff --git a/frontend/src/view-file-onlyoffice.js b/frontend/src/view-file-onlyoffice.js
new file mode 100644
index 0000000000..76ea6a2ad6
--- /dev/null
+++ b/frontend/src/view-file-onlyoffice.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import ReactDom from 'react-dom';
+import FileView from './components/file-view/file-view';
+import FileViewTip from './components/file-view/file-view-tip';
+
+const {
+ err
+} = window.app.pageOptions;
+
+class ViewFileOnlyoffice extends React.Component {
+ render() {
+ return (
+
} isOnlyofficeFile={true} />
+ );
+ }
+}
+
+class FileContent extends React.Component {
+
+ render() {
+ if (err) {
+ return
;
+ }
+
+ return (
+
+ );
+ }
+}
+
+ReactDom.render(
, document.getElementById('wrapper'));
diff --git a/seahub/templates/onlyoffice_file_view_react.html b/seahub/templates/onlyoffice_file_view_react.html
new file mode 100644
index 0000000000..5fdefa168a
--- /dev/null
+++ b/seahub/templates/onlyoffice_file_view_react.html
@@ -0,0 +1,220 @@
+{% extends 'file_view_react.html' %}
+{% load render_bundle from webpack_loader %}
+{% load seahub_tags avatar_tags i18n static %}
+
+{% block extra_style %}
+{% render_bundle 'viewFileOnlyoffice' 'css' %}
+{% endblock %}
+
+{% block extra_data %}
+{% endblock %}
+
+{% block render_bundle %}
+{% render_bundle 'viewFileOnlyoffice' 'js' %}
+
+{% get_current_language as LANGUAGE_CODE %}
+
+
+
+
+
+{% if request_from_onlyoffice_desktop_editor %}
+
+{% endif %}
+{% endblock %}
diff --git a/seahub/views/file.py b/seahub/views/file.py
index 8dfb520234..1a3a732f1a 100644
--- a/seahub/views/file.py
+++ b/seahub/views/file.py
@@ -509,7 +509,7 @@ def view_lib_file(request, repo_id, path):
token = seafile_api.get_fileserver_access_token(
repo_id, file_id, operation, username,
use_onetime=settings.FILESERVER_TOKEN_ONCE_ONLY)
-
+
if not token:
return render_permission_error(request, _('Unable to view file'))
@@ -675,7 +675,7 @@ def view_lib_file(request, repo_id, path):
return_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
# draft
-
+
# revision
revision_info = is_seadoc_revision(file_uuid)
@@ -863,7 +863,7 @@ def view_lib_file(request, repo_id, path):
send_file_access_msg(request, repo, path, 'web')
- return render(request, 'view_file_onlyoffice.html', onlyoffice_dict)
+ return render(request, 'onlyoffice_file_view_react.html', {**return_dict, **onlyoffice_dict})
else:
return_dict['err'] = _('Error when prepare OnlyOffice file preview page.')
@@ -1112,13 +1112,13 @@ def _download_file_from_share_link(request, fileshare, use_tmp_token=False):
if not obj_id:
messages.error(request, _('Unable to download file, wrong file path'))
return HttpResponseRedirect(next_page)
-
+
if use_tmp_token:
dl_token = seafile_api.get_fileserver_access_token(repo.id,
obj_id, 'download-link', fileshare.username, use_onetime=False)
if not dl_token:
messages.error(request, _('Unable to download file.'))
-
+
return HttpResponseRedirect(gen_file_get_url(dl_token, filename))
return HttpResponseRedirect(gen_file_get_url_by_sharelink(fileshare.token))