diff --git a/frontend/config/webpack.entry.js b/frontend/config/webpack.entry.js
index 2b6617403d..575b9281da 100644
--- a/frontend/config/webpack.entry.js
+++ b/frontend/config/webpack.entry.js
@@ -8,6 +8,7 @@ const entryFiles = {
wiki: "/wiki.js",
fileHistory: "/file-history.js",
fileHistoryOld: "/file-history-old.js",
+ sdocFileHistory: "/pages/sdoc-file-history/index.js",
app: "/app.js",
draft: "/draft.js",
sharedDirView: "/shared-dir-view.js",
diff --git a/frontend/src/pages/sdoc-file-history/index.js b/frontend/src/pages/sdoc-file-history/index.js
new file mode 100644
index 0000000000..1454b25c22
--- /dev/null
+++ b/frontend/src/pages/sdoc-file-history/index.js
@@ -0,0 +1,261 @@
+import React, { Fragment } from 'react';
+import ReactDom from 'react-dom';
+import { Button } from 'reactstrap';
+import { Utils } from '../../utils/utils';
+import { seafileAPI } from '../../utils/seafile-api';
+import { gettext, PER_PAGE, filePath, fileName, historyRepoID, canDownload, canCompare } from '../../utils/constants';
+import editUtilities from '../../utils/editor-utilities';
+import Loading from '../../components/loading';
+import Logo from '../../components/logo';
+import CommonToolbar from '../../components/toolbar/common-toolbar';
+import HistoryItem from '../file-history-old/history-item';
+
+import '../../css/layout.css';
+import '../../css/toolbar.css';
+import '../../css/search.css';
+import '../../css/file-history-old.css';
+
+class SdocFileHistory extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ historyList: [],
+ currentPage: 1,
+ hasMore: false,
+ nextCommit: undefined,
+ filePath: '',
+ oldFilePath: '',
+ isLoading: true,
+ isReloadingData: false,
+ };
+ }
+
+ componentDidMount() {
+ this.listOldHistoryRecords(historyRepoID, filePath);
+ }
+
+ listNewHistoryRecords = (filePath, PER_PAGE) => {
+ editUtilities.listFileHistoryRecords(filePath, 1, PER_PAGE).then(res => {
+ let historyData = res.data;
+ if (!historyData) {
+ this.setState({isLoading: false});
+ throw Error('There is an error in server.');
+ }
+ this.initNewRecords(res.data);
+ });
+ }
+
+ listOldHistoryRecords = (repoID, filePath) => {
+ seafileAPI.listOldFileHistoryRecords(repoID, filePath).then((res) => {
+ let historyData = res.data;
+ if (!historyData) {
+ this.setState({isLoading: false});
+ throw Error('There is an error in server.');
+ }
+ this.initOldRecords(res.data);
+ });
+ }
+
+ initNewRecords(result) {
+ if (result.total_count < 5) {
+ if (result.data.length) {
+ let commitID = result.data[result.data.length-1].commit_id;
+ let path = result.data[result.data.length-1].path;
+ let oldPath = result.data[result.data.length-1].old_path;
+ path = oldPath ? oldPath : path;
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, path, commitID).then((res) => {
+ if (!res.data) {
+ this.setState({isLoading: false});
+ throw Error('There is an error in server.');
+ }
+ this.setState({
+ historyList: result.data.concat(res.data.data.slice(1, res.data.data.length)),
+ isLoading: false,
+ });
+ });
+ } else {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath).then((res) => {
+ if (!res.data) {
+ this.setState({isLoading: false});
+ throw Error('There is an error in server.');
+ }
+ this.setState({
+ historyList: res.data.data,
+ isLoading: false,
+ });
+ });
+ }
+ } else {
+ this.setState({
+ historyList: result.data,
+ currentPage: result.page,
+ hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
+ isLoading: false,
+ });
+ }
+ }
+
+ initOldRecords(result) {
+ if (result.data.length) {
+ this.setState({
+ historyList: result.data,
+ nextCommit: result.next_start_commit,
+ filePath: result.data[result.data.length-1].path,
+ oldFilePath: result.data[result.data.length-1].rev_renamed_old_path,
+ isLoading: false,
+ });
+ } else {
+ this.setState({nextCommit: result.next_start_commit,});
+ if (this.state.nextCommit) {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, this.state.nextCommit).then((res) => {
+ this.initOldRecords(res.data);
+ });
+ } else {
+ this.setState({isLoading: false});
+ }
+ }
+ }
+
+ onScrollHandler = (event) => {
+ const clientHeight = event.target.clientHeight;
+ const scrollHeight = event.target.scrollHeight;
+ const scrollTop = event.target.scrollTop;
+ const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
+ let hasMore = this.state.hasMore;
+ if (isBottom && hasMore) {
+ this.reloadMore();
+ }
+ }
+
+ reloadMore = () => {
+ if (!this.state.isReloadingData) {
+ const commitID = this.state.nextCommit;
+ const filePath = this.state.filePath;
+ const oldFilePath = this.state.oldFilePath;
+ this.setState({ isReloadingData: true });
+ if (oldFilePath) {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, oldFilePath, commitID).then((res) => {
+ this.updateOldRecords(res.data, oldFilePath);
+ });
+ } else {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, commitID).then((res) => {
+ this.updateOldRecords(res.data, filePath);
+ });
+ }
+ }
+ }
+
+ updateNewRecords(result) {
+ this.setState({
+ historyList: [...this.state.historyList, ...result.data],
+ currentPage: result.page,
+ hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
+ isReloadingData: false,
+ });
+ }
+
+ updateOldRecords(result, filePath) {
+ if (result.data.length) {
+ this.setState({
+ historyList: [...this.state.historyList, ...result.data],
+ nextCommit: result.next_start_commit,
+ filePath: result.data[result.data.length-1].path,
+ oldFilePath: result.data[result.data.length-1].rev_renamed_old_path,
+ isReloadingData: false,
+ });
+ } else {
+ this.setState({nextCommit: result.next_start_commit,});
+ if (this.state.nextCommit) {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath, this.state.nextCommit).then((res) => {
+ this.updateOldRecords(res.data, filePath);
+ });
+ }
+ }
+ }
+
+ onItemRestore = (item) => {
+ let commitId = item.commit_id;
+ let filePath = item.path;
+ editUtilities.revertFile(filePath, commitId).then(res => {
+ if (res.data.success) {
+ this.setState({ isLoading: true });
+ this.refreshFileList();
+ }
+ });
+ }
+
+ refreshFileList() {
+ seafileAPI.listOldFileHistoryRecords(historyRepoID, filePath).then((res) => {
+ this.initOldRecords(res.data);
+ });
+ }
+
+ onSearchedClick = (searchedItem) => {
+ Utils.handleSearchedItemClick(searchedItem);
+ }
+
+ onBackClick = (event) => {
+ event.preventDefault();
+ window.history.back();
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ {fileName}{' '}{gettext('History Versions')}
+
+
+
+
+
+ {gettext('Time')} |
+ {gettext('Modifier')} |
+ {gettext('Size')} |
+ |
+
+
+ {!this.state.isLoading &&
+
+ {this.state.historyList.map((item, index) => {
+ return (
+
+ );
+ })}
+
+ }
+
+ {(this.state.isReloadingData || this.state.isLoading) && }
+ {this.state.nextCommit && !this.state.isLoading && !this.state.isReloadingData &&
+
+ }
+
+
+
+
+ );
+ }
+}
+
+ReactDom.render(, document.getElementById('wrapper'));
diff --git a/frontend/src/shared-file-view-sdoc.js b/frontend/src/shared-file-view-sdoc.js
index 5825ad927e..a342f43795 100644
--- a/frontend/src/shared-file-view-sdoc.js
+++ b/frontend/src/shared-file-view-sdoc.js
@@ -4,9 +4,10 @@ import { SimpleViewer } from '@seafile/sdoc-editor';
import { I18nextProvider } from 'react-i18next';
import i18n from './_i18n/i18n-sdoc-editor';
import Loading from './components/loading';
+import { Utils } from './utils/utils';
-const { serviceURL } = window.app.config;
-const { username } = window.app.pageOptions;
+const { serviceURL, siteRoot } = window.app.config;
+const { username, filePerm } = window.app.pageOptions;
const { repoID, filePath, fileName, rawPath } = window.shared.pageOptions;
window.seafile = {
@@ -16,6 +17,9 @@ window.seafile = {
docPath: filePath,
serviceUrl: serviceURL,
username,
+ siteRoot,
+ docPerm: filePerm,
+ historyURL: Utils.generateHistoryURL(siteRoot, repoID, filePath),
};
ReactDom.render(
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index 879d7fe100..e901e56f5e 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -1536,6 +1536,11 @@ export const Utils = {
updateTabTitle: function(content) {
const title = document.getElementsByTagName('title')[0];
title.innerText = content;
+ },
+
+ generateHistoryURL: function(siteRoot, repoID, path) {
+ if (!siteRoot || !repoID || !path) return '';
+ return siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + this.encodePath(path);
}
};
diff --git a/frontend/src/view-file-sdoc.js b/frontend/src/view-file-sdoc.js
index 5e414170e0..07d9c9df33 100644
--- a/frontend/src/view-file-sdoc.js
+++ b/frontend/src/view-file-sdoc.js
@@ -4,10 +4,11 @@ import { SimpleEditor } from '@seafile/sdoc-editor';
import { I18nextProvider } from 'react-i18next';
import i18n from './_i18n/i18n-sdoc-editor';
import Loading from './components/loading';
+import { Utils } from './utils/utils';
-const { serviceURL, avatarURL } = window.app.config;
+const { serviceURL, avatarURL, siteRoot } = window.app.config;
const { username, name } = window.app.userInfo;
-const { repoID, docPath, docName, docUuid, seadocAccessToken, seadocServerUrl } = window.app.pageOptions;
+const { repoID, docPath, docName, docUuid, seadocAccessToken, seadocServerUrl, filePerm } = window.app.pageOptions;
window.seafile = {
repoID,
@@ -21,6 +22,9 @@ window.seafile = {
name,
username,
avatarURL,
+ siteRoot,
+ docPerm: filePerm,
+ historyURL: Utils.generateHistoryURL(siteRoot, repoID, docPath),
};
ReactDom.render(
diff --git a/seahub/templates/sdoc_file_revisions.html b/seahub/templates/sdoc_file_revisions.html
new file mode 100644
index 0000000000..73ab7bfecf
--- /dev/null
+++ b/seahub/templates/sdoc_file_revisions.html
@@ -0,0 +1,23 @@
+{% extends "base_for_react.html" %}
+{% load render_bundle from webpack_loader %}
+
+{% block extra_style %}
+{% render_bundle 'sdocFileHistory' 'css'%}
+{% endblock %}
+
+{% block extra_script %}
+
+ {% render_bundle 'sdocFileHistory' 'js'%}
+{% endblock %}
diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py
index 822eb251ff..e7126c0061 100644
--- a/seahub/views/__init__.py
+++ b/seahub/views/__init__.py
@@ -751,8 +751,8 @@ def file_revisions(request, repo_id):
u_filename = os.path.basename(path)
- filetype, file_ext = [x.lower() for x in get_file_type_and_ext(u_filename)]
- if filetype == 'text' or filetype == 'markdown':
+ file_type, file_ext = [x.lower() for x in get_file_type_and_ext(u_filename)]
+ if file_type == 'text' or file_type == 'markdown' or file_type == 'sdoc':
can_compare = True
else:
can_compare = False
@@ -778,6 +778,17 @@ def file_revisions(request, repo_id):
if repo_perm != 'rw' or (is_locked and not locked_by_me):
can_revert_file = False
+ if file_type == 'sdoc':
+ return render(request, 'sdoc_file_revisions.html', {
+ 'repo': repo,
+ 'path': path,
+ 'u_filename': u_filename,
+ 'zipped': zipped,
+ 'is_owner': is_owner,
+ 'can_compare': can_compare,
+ 'can_revert_file': can_revert_file,
+ })
+
# Whether use new file history API which read file history from db.
suffix_list = get_file_history_suffix()
if suffix_list and isinstance(suffix_list, list):
@@ -786,7 +797,7 @@ def file_revisions(request, repo_id):
suffix_list = []
use_new_api = True if file_ext in suffix_list else False
- use_new_style = True if use_new_api and filetype == 'markdown' else False
+ use_new_style = True if use_new_api and file_type == 'markdown' else False
if use_new_style:
return render(request, 'file_revisions_new.html', {