diff --git a/frontend/config/webpack.entry.js b/frontend/config/webpack.entry.js
index 7e46e2ce6a..ce7ab8628c 100644
--- a/frontend/config/webpack.entry.js
+++ b/frontend/config/webpack.entry.js
@@ -10,6 +10,7 @@ const entryFiles = {
fileHistoryOld: "/file-history-old.js",
sdocFileHistory: "/pages/sdoc-file-history/index.js",
sdocRevision: "/pages/sdoc-revision/index.js",
+ sdocRevisions: "/pages/sdoc-revisions/index.js",
app: "/app.js",
draft: "/draft.js",
sharedDirView: "/shared-dir-view.js",
diff --git a/frontend/src/components/dirent-grid-view/dirent-grid-view.js b/frontend/src/components/dirent-grid-view/dirent-grid-view.js
index e17319f950..38686c2ab8 100644
--- a/frontend/src/components/dirent-grid-view/dirent-grid-view.js
+++ b/frontend/src/components/dirent-grid-view/dirent-grid-view.js
@@ -159,6 +159,9 @@ class DirentGridView extends React.Component{
case 'Start revise':
this.onStartRevise(currentObject);
break;
+ case 'List revisions':
+ this.openRevisionsPage(currentObject);
+ break;
case 'Comment':
this.onCommentItem();
break;
@@ -297,14 +300,22 @@ class DirentGridView extends React.Component{
let repoID = this.props.repoID;
let filePath = this.getDirentPath(currentObject);
seafileAPI.sdocStartRevise(repoID, filePath).then((res) => {
- let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
+ const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
window.open(url);
}).catch(error => {
- let errMessage = Utils.getErrorMsg(error);
+ const errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
+ openRevisionsPage = (currentObject) => {
+ const repoID = this.props.repoID;
+ const filePath = this.getDirentPath(currentObject);
+ const url = Utils.generateRevisionsURL(siteRoot, repoID, filePath);
+ if (!url) return;
+ window.open(url);
+ }
+
onCommentItem = () => {
this.props.showDirentDetail('comments');
}
diff --git a/frontend/src/components/dirent-list-view/dirent-list-item.js b/frontend/src/components/dirent-list-view/dirent-list-item.js
index d9a2bbf7c8..87f72f41fe 100644
--- a/frontend/src/components/dirent-list-view/dirent-list-item.js
+++ b/frontend/src/components/dirent-list-view/dirent-list-item.js
@@ -273,6 +273,9 @@ class DirentListItem extends React.Component {
case 'Start revise':
this.onStartRevise();
break;
+ case 'List revisions':
+ this.openRevisionsPage();
+ break;
case 'Comment':
this.props.onDirentClick(this.props.dirent);
this.props.showDirentDetail('comments');
@@ -385,17 +388,25 @@ class DirentListItem extends React.Component {
}
onStartRevise = () => {
- let repoID = this.props.repoID;
- let filePath = this.getDirentPath(this.props.dirent);
+ const repoID = this.props.repoID;
+ const filePath = this.getDirentPath(this.props.dirent);
seafileAPI.sdocStartRevise(repoID, filePath).then((res) => {
- let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
+ const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
window.open(url);
}).catch(error => {
- let errMessage = Utils.getErrorMsg(error);
+ const errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
+ openRevisionsPage = () => {
+ const repoID = this.props.repoID;
+ const filePath = this.getDirentPath(this.props.dirent);
+ const url = Utils.generateRevisionsURL(siteRoot, repoID, filePath);
+ if (!url) return;
+ window.open(url);
+ }
+
onHistory = () => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(this.props.dirent);
diff --git a/frontend/src/css/sdoc-revisions.css b/frontend/src/css/sdoc-revisions.css
new file mode 100644
index 0000000000..3a4813923c
--- /dev/null
+++ b/frontend/src/css/sdoc-revisions.css
@@ -0,0 +1,3 @@
+.sdoc-revisions .sdoc-revision:hover {
+ background-color: #f8f8f8;
+}
diff --git a/frontend/src/pages/sdoc-revisions/index.js b/frontend/src/pages/sdoc-revisions/index.js
new file mode 100644
index 0000000000..4f8caeade1
--- /dev/null
+++ b/frontend/src/pages/sdoc-revisions/index.js
@@ -0,0 +1,122 @@
+import React, { Component, Fragment } from 'react';
+import ReactDom from 'react-dom';
+import moment from 'moment';
+import classnames from 'classnames';
+import { siteRoot, mediaUrl, logoPath, siteTitle, logoHeight, logoWidth, gettext } from '../../utils/constants';
+import '../../css/sdoc-revisions.css';
+
+moment.locale(window.app.config.lang);
+const { filename, zipped, forloopLast, repo, viewLibFile, revisions, currentPage, prevPage,
+ nextPage, perPage, pageNext, extraHref } = window.sdocRevisions;
+const validZipped = JSON.parse(zipped);
+const validRevisions = JSON.parse(revisions);
+
+class SdocRevisions extends Component {
+
+ renderPerPage = (perPageCount, className) => {
+ if (perPage === perPageCount) {
+ return ({perPageCount});
+ }
+ return (
+ {perPageCount}
+ );
+ }
+
+ renderRevisions = () => {
+ if (!Array.isArray(validRevisions) || validRevisions.length === 0) {
+ return (
+
+
{gettext('This file has not revisions yet')}
+
+ );
+ }
+ return (
+
+
+
+ {gettext('User')} |
+ {gettext('File_name')} |
+ {gettext('Ctime')} |
+
+
+
+ {validRevisions.map(revision => {
+ return (
+
+ {revision.nickname} |
+
+
+ {revision.filename}
+
+ |
+ {moment(revision.created_at).format('YYYY-MM-DD HH:MM')} |
+
+ );
+ })}
+
+
+ );
+ }
+
+ renderFooter = () => {
+ return (
+
+ {currentPage !== 1 && (
+
{gettext('Previous')}
+ )}
+ {pageNext && (
+
{gettext('Next')}
+ )}
+ {(currentPage !== 1 || pageNext) && (
{'|'})}
+
{gettext('Per page: ')}
+ {this.renderPerPage(25, 'mr-1')}
+ {this.renderPerPage(50, 'mr-1')}
+ {this.renderPerPage(100)}
+
+ );
+ }
+
+ render() {
+ return (
+ <>
+
+
+
+
+
+ {filename}
+ {gettext('Revisions')}
+
+
+
+ {gettext('Current Path:')}
+ {validZipped.map((item, index) => {
+ if (forloopLast) {
+ return ({item[0]});
+ }
+ return (
+
+ {item[0]}
+ {index !== validZipped.length - 1 && (
+ {'/'}
+ )}
+
+ );
+ })}
+
+
+ {this.renderRevisions()}
+ {this.renderFooter()}
+
+
+
+ >
+ );
+ }
+}
+
+ReactDom.render(, document.getElementById('wrapper'));
diff --git a/frontend/src/utils/text-translation.js b/frontend/src/utils/text-translation.js
index 1a02c976c8..2afa6fdfb1 100644
--- a/frontend/src/utils/text-translation.js
+++ b/frontend/src/utils/text-translation.js
@@ -19,6 +19,7 @@ const TextTranslation = {
'MASK_AS_DRAFT' : {key : 'Mask as draft', value : gettext('Mark as draft')},
'UNMASK_AS_DRAFT' : {key : 'Unmask as draft', value : gettext('Unmark as draft')},
'START_REVISE' : {key : 'Start revise', value : gettext('Start revise')},
+ 'LIST_REVISIONS': { key: 'List revisions', value: gettext('List revisions') },
'COMMENT' : {key : 'Comment', value : gettext('Comment')},
'HISTORY' : {key : 'History', value : gettext('History')},
'ACCESS_LOG' : {key : 'Access Log', value : gettext('Access Log')},
diff --git a/frontend/src/utils/utils.js b/frontend/src/utils/utils.js
index d680eee91d..8dced1abe5 100644
--- a/frontend/src/utils/utils.js
+++ b/frontend/src/utils/utils.js
@@ -527,7 +527,7 @@ export const Utils = {
getFileOperationList: function(isRepoOwner, currentRepoInfo, dirent, isContextmenu) {
let list = [];
const { SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, TAGS, UNLOCK, LOCK, MASK_AS_DRAFT, UNMASK_AS_DRAFT,
- START_REVISE, COMMENT, HISTORY, ACCESS_LOG, OPEN_VIA_CLIENT, ONLYOFFICE_CONVERT } = TextTranslation;
+ START_REVISE, LIST_REVISIONS, COMMENT, HISTORY, ACCESS_LOG, OPEN_VIA_CLIENT, ONLYOFFICE_CONVERT } = TextTranslation;
const permission = dirent.permission;
const { isCustomPermission, customPermission } = Utils.getUserPermission(permission);
@@ -601,6 +601,7 @@ export const Utils = {
list.push(MASK_AS_DRAFT);
}
list.push(START_REVISE);
+ list.push(LIST_REVISIONS);
}
if (enableFileComment) {
list.push(COMMENT);
@@ -1548,6 +1549,12 @@ export const Utils = {
generateRevisionURL: function(siteRoot, repoID, path) {
if (!siteRoot || !repoID || !path) return '';
return siteRoot + 'repo/sdoc_revision/' + repoID + '/?p=' + this.encodePath(path);
+ },
+
+ generateRevisionsURL: function(siteRoot, repoID, path) {
+ if (!siteRoot || !repoID || !path) return '';
+ console.log(siteRoot + 'repo/sdoc_revisions/' + repoID + '/?p=' + this.encodePath(path))
+ return siteRoot + 'repo/sdoc_revisions/' + repoID + '/?p=' + this.encodePath(path);
}
};
diff --git a/frontend/src/view-file-sdoc.js b/frontend/src/view-file-sdoc.js
index 5e238973c0..0735e04b13 100644
--- a/frontend/src/view-file-sdoc.js
+++ b/frontend/src/view-file-sdoc.js
@@ -11,7 +11,7 @@ const { username, name } = window.app.userInfo;
const {
repoID, repoName, parentDir, filePerm,
docPath, docName, docUuid, seadocAccessToken, seadocServerUrl, assetsUrl,
- isSdocRevision, isPublished,
+ isSdocRevision, isPublished, originFilename, revisionCreatedAt,
} = window.app.pageOptions;
window.seafile = {
@@ -36,6 +36,8 @@ window.seafile = {
isSdocRevision,
isPublished,
revisionURL: Utils.generateRevisionURL(siteRoot, repoID, docPath),
+ originFilename,
+ revisionCreatedAt,
};
ReactDom.render(
diff --git a/seahub/seadoc/urls.py b/seahub/seadoc/urls.py
index e881488e60..ebabbdcf81 100644
--- a/seahub/seadoc/urls.py
+++ b/seahub/seadoc/urls.py
@@ -3,7 +3,6 @@ from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, Seado
SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory, SeadocDrafts, SeadocMaskAsDraft, \
SeadocCommentsView, SeadocCommentView, SeadocRevisions, SeadocPublishRevision
-from .views import sdoc_revision
urlpatterns = [
re_path(r'^access-token/(?P[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
diff --git a/seahub/seadoc/utils.py b/seahub/seadoc/utils.py
index 5d9e7fd07c..1d067d8463 100644
--- a/seahub/seadoc/utils.py
+++ b/seahub/seadoc/utils.py
@@ -194,3 +194,29 @@ def is_seadoc_revision(doc_uuid):
else:
info = {'is_sdoc_revision': False}
return info
+
+def gen_path_link(path, repo_name):
+ """
+ Generate navigate paths and links in repo page.
+
+ """
+ if path and path[-1] != '/':
+ path += '/'
+
+ paths = []
+ links = []
+ if path and path != '/':
+ paths = path[1:-1].split('/')
+ i = 1
+ for name in paths:
+ link = '/' + '/'.join(paths[:i])
+ i = i + 1
+ links.append(link)
+ if repo_name:
+ paths.insert(0, repo_name)
+ links.insert(0, '/')
+
+ zipped = list(zip(paths, links))
+
+ return zipped
+
diff --git a/seahub/seadoc/views.py b/seahub/seadoc/views.py
index 8b4530dda2..4b949d76c2 100644
--- a/seahub/seadoc/views.py
+++ b/seahub/seadoc/views.py
@@ -2,12 +2,16 @@ import os
from django.shortcuts import render
from django.utils.translation import gettext as _
from seaserv import get_repo
+from urllib.parse import quote
+import json
+
from seahub.auth.decorators import login_required
from seahub.utils import render_error
from seahub.views import check_folder_permission, validate_owner, get_seadoc_file_uuid
from seahub.tags.models import FileUUIDMap
+from seahub.seadoc.models import SeadocRevision
-from .utils import is_seadoc_revision, get_seadoc_download_link
+from .utils import is_seadoc_revision, get_seadoc_download_link, gen_path_link
@login_required
@@ -63,3 +67,73 @@ def sdoc_revision(request, repo_id):
return render(request, 'sdoc_revision.html', return_dict)
+
+@login_required
+def sdoc_revisions(request, repo_id):
+ """List file revisions in file version history page.
+ """
+ repo = get_repo(repo_id)
+ if not repo:
+ error_msg = _("Library does not exist")
+ return render_error(request, error_msg)
+
+ # perm check
+ if not check_folder_permission(request, repo_id, '/'):
+ error_msg = _("Permission denied.")
+ return render_error(request, error_msg)
+
+ path = request.GET.get('p', '/')
+ if not path:
+ return render_error(request)
+
+ if path[-1] == '/':
+ path = path[:-1]
+ filename = os.path.basename(path)
+
+ # Make sure page request is an int. If not, deliver first page.
+ try:
+ current_page = int(request.GET.get('page', '1'))
+ per_page = int(request.GET.get('per_page', '100'))
+ except ValueError:
+ current_page = 1
+ per_page = 100
+
+ start = per_page * (current_page - 1)
+ limit = per_page + 1
+
+ file_uuid = get_seadoc_file_uuid(repo, path)
+ origin_uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
+
+ revision_queryset = SeadocRevision.objects.list_by_origin_doc_uuid(origin_uuid_map.uuid, start, limit)
+ count = SeadocRevision.objects.filter(origin_doc_uuid=origin_uuid_map.uuid).count()
+ zipped = gen_path_link(path, repo.name)
+ extra_href = "&p=%s" % quote(path)
+
+ uuid_set = set()
+ for item in revision_queryset:
+ uuid_set.add(item.doc_uuid)
+ uuid_set.add(item.origin_doc_uuid)
+
+ fileuuidmap_queryset = FileUUIDMap.objects.filter(uuid__in=list(uuid_set))
+ revisions = [revision.to_dict(fileuuidmap_queryset) for revision in revision_queryset]
+
+ if len(revisions) == per_page + 1:
+ page_next = True
+ else:
+ page_next = False
+
+ return render(request, 'sdoc_revisions.html', {
+ 'repo': repo,
+ 'revisions': json.dumps(revisions),
+ 'count': count,
+ 'docUuid': file_uuid,
+ 'path': path,
+ 'filename': filename,
+ 'zipped': json.dumps(zipped),
+ 'extra_href': extra_href,
+ 'current_page': current_page,
+ 'prev_page': current_page - 1,
+ 'next_page': current_page + 1,
+ 'per_page': per_page,
+ 'page_next': page_next,
+ })
diff --git a/seahub/templates/sdoc_revisions.html b/seahub/templates/sdoc_revisions.html
new file mode 100644
index 0000000000..9fd6211597
--- /dev/null
+++ b/seahub/templates/sdoc_revisions.html
@@ -0,0 +1,43 @@
+{% extends "base_for_react.html" %}
+{% load render_bundle from webpack_loader %}
+{% load seahub_tags avatar_tags i18n static %}
+
+{% block extra_style %}
+
+
+{% render_bundle 'sdocRevisions' 'css'%}
+{% endblock %}
+
+{% block extra_script %}
+
+ {% render_bundle 'sdocRevisions' 'js'%}
+{% endblock %}
diff --git a/seahub/urls.py b/seahub/urls.py
index e3b61d4412..f3b8310641 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -194,7 +194,7 @@ from seahub.api2.endpoints.admin.virus_scan_records import AdminVirusFilesView,
from seahub.api2.endpoints.file_participants import FileParticipantsView, FileParticipantView
from seahub.api2.endpoints.repo_related_users import RepoRelatedUsersView
from seahub.api2.endpoints.repo_auto_delete import RepoAutoDeleteView
-from seahub.seadoc.views import sdoc_revision
+from seahub.seadoc.views import sdoc_revision, sdoc_revisions
from seahub.ocm.settings import OCM_ENDPOINT
@@ -222,6 +222,7 @@ urlpatterns = [
re_path(r'^repo/download_dir/(?P[-0-9a-f]{36})/$', repo_download_dir, name='repo_download_dir'),
re_path(r'^repo/file_revisions/(?P[-0-9a-f]{36})/$', file_revisions, name='file_revisions'),
re_path(r'^repo/sdoc_revision/(?P[-0-9a-f]{36})/$', sdoc_revision, name='sdoc_revision'),
+ re_path(r'^repo/sdoc_revisions/(?P[-0-9a-f]{36})/$', sdoc_revisions, name='sdoc_revisions'),
re_path(r'^repo/file-access/(?P[-0-9a-f]{36})/$', file_access, name='file_access'),
re_path(r'^repo/text_diff/(?P[-0-9a-f]{36})/$', text_diff, name='text_diff'),
re_path(r'^repo/history/(?P[-0-9a-f]{36})/$', repo_history, name='repo_history'),