mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-25 18:20:48 +00:00
commit
7b7d4ca53a
@ -9,6 +9,8 @@ const entryFiles = {
|
||||
fileHistory: "/file-history.js",
|
||||
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",
|
||||
|
28
frontend/package-lock.json
generated
28
frontend/package-lock.json
generated
@ -11,7 +11,7 @@
|
||||
"@gatsbyjs/reach-router": "1.3.9",
|
||||
"@seafile/react-image-lightbox": "2.0.2",
|
||||
"@seafile/resumablejs": "1.1.16",
|
||||
"@seafile/sdoc-editor": "0.1.68",
|
||||
"@seafile/sdoc-editor": "0.1.69",
|
||||
"@seafile/seafile-calendar": "0.0.12",
|
||||
"@seafile/seafile-editor": "0.3.132",
|
||||
"@seafile/slate-react": "0.54.13",
|
||||
@ -43,7 +43,7 @@
|
||||
"react-select": "5.7.0",
|
||||
"react-transition-group": "4.4.5",
|
||||
"reactstrap": "8.9.0",
|
||||
"seafile-js": "0.2.202",
|
||||
"seafile-js": "0.2.204",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"svgo-loader": "^3.0.1",
|
||||
@ -5198,9 +5198,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@seafile/sdoc-editor": {
|
||||
"version": "0.1.68",
|
||||
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.1.68.tgz",
|
||||
"integrity": "sha512-HlZHBImvff5EDO243NLHY22h4DnSJ3X8G34f78nPVo0VrNRz2q2odOiVDDOT3eKggVFoVlrutKp4/NOjRTNs1A==",
|
||||
"version": "0.1.69",
|
||||
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.1.69.tgz",
|
||||
"integrity": "sha512-HcUWdXSmN5jChHl75zew/Uh/8usavDiHS278IpHDjRsWjldVcMuB89/hZKzGOKiMO/QNJ/XZBmAASkUlXjXW1w==",
|
||||
"dependencies": {
|
||||
"@seafile/react-image-lightbox": "2.0.2",
|
||||
"@seafile/slate": "0.91.8",
|
||||
@ -24900,9 +24900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/seafile-js": {
|
||||
"version": "0.2.202",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.202.tgz",
|
||||
"integrity": "sha512-OE85UMZxgaM4hq7+ey7xn/O8JmGyaYV5J4nCG7s0a6/JXgzJXNENdFT9Jid40Hlf4Ks7aHtuMM7qx8SbmROlOw==",
|
||||
"version": "0.2.204",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.204.tgz",
|
||||
"integrity": "sha512-t+tDh2TQiT0g9DB2Lzx9TzVb/Pc6lKUScobklnP6WhHEgK8YVhFIWi+JcJR0KDmTbClKV2DSFlCWPG9Vr79KGQ==",
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "7.12.1",
|
||||
"axios": "1.2.1",
|
||||
@ -33359,9 +33359,9 @@
|
||||
"version": "1.1.16"
|
||||
},
|
||||
"@seafile/sdoc-editor": {
|
||||
"version": "0.1.68",
|
||||
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.1.68.tgz",
|
||||
"integrity": "sha512-HlZHBImvff5EDO243NLHY22h4DnSJ3X8G34f78nPVo0VrNRz2q2odOiVDDOT3eKggVFoVlrutKp4/NOjRTNs1A==",
|
||||
"version": "0.1.69",
|
||||
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.1.69.tgz",
|
||||
"integrity": "sha512-HcUWdXSmN5jChHl75zew/Uh/8usavDiHS278IpHDjRsWjldVcMuB89/hZKzGOKiMO/QNJ/XZBmAASkUlXjXW1w==",
|
||||
"requires": {
|
||||
"@seafile/react-image-lightbox": "2.0.2",
|
||||
"@seafile/slate": "0.91.8",
|
||||
@ -47245,9 +47245,9 @@
|
||||
}
|
||||
},
|
||||
"seafile-js": {
|
||||
"version": "0.2.202",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.202.tgz",
|
||||
"integrity": "sha512-OE85UMZxgaM4hq7+ey7xn/O8JmGyaYV5J4nCG7s0a6/JXgzJXNENdFT9Jid40Hlf4Ks7aHtuMM7qx8SbmROlOw==",
|
||||
"version": "0.2.204",
|
||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.204.tgz",
|
||||
"integrity": "sha512-t+tDh2TQiT0g9DB2Lzx9TzVb/Pc6lKUScobklnP6WhHEgK8YVhFIWi+JcJR0KDmTbClKV2DSFlCWPG9Vr79KGQ==",
|
||||
"requires": {
|
||||
"@babel/polyfill": "7.12.1",
|
||||
"axios": "1.2.1",
|
||||
|
@ -6,7 +6,7 @@
|
||||
"@gatsbyjs/reach-router": "1.3.9",
|
||||
"@seafile/react-image-lightbox": "2.0.2",
|
||||
"@seafile/resumablejs": "1.1.16",
|
||||
"@seafile/sdoc-editor": "0.1.68",
|
||||
"@seafile/sdoc-editor": "0.1.69",
|
||||
"@seafile/seafile-calendar": "0.0.12",
|
||||
"@seafile/seafile-editor": "0.3.132",
|
||||
"@seafile/slate-react": "0.54.13",
|
||||
@ -38,7 +38,7 @@
|
||||
"react-select": "5.7.0",
|
||||
"react-transition-group": "4.4.5",
|
||||
"reactstrap": "8.9.0",
|
||||
"seafile-js": "0.2.202",
|
||||
"seafile-js": "0.2.204",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"svgo-loader": "^3.0.1",
|
||||
|
@ -150,6 +150,18 @@ class DirentGridView extends React.Component{
|
||||
case 'Lock':
|
||||
this.onLockItem(currentObject);
|
||||
break;
|
||||
case 'Mask as draft':
|
||||
this.onMaskAsDraft(currentObject);
|
||||
break;
|
||||
case 'Unmask as draft':
|
||||
this.onUnmaskAsDraft(currentObject);
|
||||
break;
|
||||
case 'Start revise':
|
||||
this.onStartRevise(currentObject);
|
||||
break;
|
||||
case 'List revisions':
|
||||
this.openRevisionsPage(currentObject);
|
||||
break;
|
||||
case 'Comment':
|
||||
this.onCommentItem();
|
||||
break;
|
||||
@ -262,6 +274,48 @@ class DirentGridView extends React.Component{
|
||||
});
|
||||
}
|
||||
|
||||
onMaskAsDraft = (currentObject) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(currentObject);
|
||||
seafileAPI.sdocMaskAsDraft(repoID, filePath).then((res) => {
|
||||
this.props.updateDirent(currentObject, 'is_sdoc_draft', true);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onUnmaskAsDraft = (currentObject) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(currentObject);
|
||||
seafileAPI.sdocUnmaskAsDraft(repoID, filePath).then((res) => {
|
||||
this.props.updateDirent(currentObject, 'is_sdoc_draft', false);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onStartRevise = (currentObject) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(currentObject);
|
||||
seafileAPI.sdocStartRevise(repoID, filePath).then((res) => {
|
||||
const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
|
||||
window.open(url);
|
||||
}).catch(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');
|
||||
}
|
||||
|
@ -270,6 +270,12 @@ class DirentListItem extends React.Component {
|
||||
case 'Unmask as draft':
|
||||
this.onUnmaskAsDraft();
|
||||
break;
|
||||
case 'Start revise':
|
||||
this.onStartRevise();
|
||||
break;
|
||||
case 'List revisions':
|
||||
this.openRevisionsPage();
|
||||
break;
|
||||
case 'Comment':
|
||||
this.props.onDirentClick(this.props.dirent);
|
||||
this.props.showDirentDetail('comments');
|
||||
@ -381,6 +387,26 @@ class DirentListItem extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
onStartRevise = () => {
|
||||
const repoID = this.props.repoID;
|
||||
const filePath = this.getDirentPath(this.props.dirent);
|
||||
seafileAPI.sdocStartRevise(repoID, filePath).then((res) => {
|
||||
const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
|
||||
window.open(url);
|
||||
}).catch(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);
|
||||
|
@ -121,6 +121,40 @@ class MultipleDirOperationToolbar extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
onMaskAsDraft = (dirent) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(dirent);
|
||||
seafileAPI.sdocMaskAsDraft(repoID, filePath).then((res) => {
|
||||
this.props.updateDirent(dirent, 'is_sdoc_draft', true);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onUnmaskAsDraft = (dirent) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(dirent);
|
||||
seafileAPI.sdocUnmaskAsDraft(repoID, filePath).then((res) => {
|
||||
this.props.updateDirent(dirent, 'is_sdoc_draft', false);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onStartRevise = (dirent) => {
|
||||
let repoID = this.props.repoID;
|
||||
let filePath = this.getDirentPath(dirent);
|
||||
seafileAPI.sdocStartRevise(repoID, filePath).then((res) => {
|
||||
let url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(res.data.file_path);
|
||||
window.open(url);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onCommentItem = () => {
|
||||
this.props.showDirentDetail('comments');
|
||||
}
|
||||
@ -171,6 +205,15 @@ class MultipleDirOperationToolbar extends React.Component {
|
||||
case 'Unlock':
|
||||
this.unlockFile(dirent);
|
||||
break;
|
||||
case 'Mask as draft':
|
||||
this.onMaskAsDraft(dirent);
|
||||
break;
|
||||
case 'Unmask as draft':
|
||||
this.onUnmaskAsDraft(dirent);
|
||||
break;
|
||||
case 'Start revise':
|
||||
this.onStartRevise(dirent);
|
||||
break;
|
||||
case 'Comment':
|
||||
this.onCommentItem();
|
||||
break;
|
||||
|
51
frontend/src/css/sdoc-revision.css
Normal file
51
frontend/src/css/sdoc-revision.css
Normal file
@ -0,0 +1,51 @@
|
||||
.sdoc-revision .sdoc-revision-container {
|
||||
flex: 1;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-header {
|
||||
height: 50px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-header .sdoc-revision-header-left {
|
||||
font-size: 1.25rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-header .file-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-header .sdoc-revision-header-right {
|
||||
height: 100%;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 20px 40px;
|
||||
background-color: #F5F5F5;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-content .sdoc-revision-viewer {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
word-break: break-word;
|
||||
border: 1px solid #e6e6dd;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-content .sdoc-editor-content {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.sdoc-revision .sdoc-revision-content .article {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
3
frontend/src/css/sdoc-revisions.css
Normal file
3
frontend/src/css/sdoc-revisions.css
Normal file
@ -0,0 +1,3 @@
|
||||
.sdoc-revisions .sdoc-revision:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
137
frontend/src/pages/sdoc-revision/index.js
Normal file
137
frontend/src/pages/sdoc-revision/index.js
Normal file
@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import classnames from 'classnames';
|
||||
import { Button } from 'reactstrap';
|
||||
import { DiffViewer } from '@seafile/sdoc-editor';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import Loading from '../../components/loading';
|
||||
import GoBack from '../../components/common/go-back';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
import '../../css/layout.css';
|
||||
import '../../css/sdoc-revision.css';
|
||||
import toaster from '../../components/toast';
|
||||
|
||||
const { serviceURL, avatarURL, siteRoot } = window.app.config;
|
||||
const { username, name } = window.app.pageOptions;
|
||||
const { repoID, fileName, filePath, docUuid, assetsUrl, fileDownloadLink, originFileDownloadLink } = window.sdocRevision;
|
||||
|
||||
window.seafile = {
|
||||
repoID,
|
||||
docPath: filePath,
|
||||
docName: fileName,
|
||||
docUuid,
|
||||
isOpenSocket: false,
|
||||
serviceUrl: serviceURL,
|
||||
name,
|
||||
username,
|
||||
avatarURL,
|
||||
siteRoot,
|
||||
assetsUrl,
|
||||
};
|
||||
|
||||
class SdocRevision extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
errorMessage: '',
|
||||
revisionContent: '',
|
||||
originContent: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
fetch(fileDownloadLink).then(res => {
|
||||
return res.json();
|
||||
}).then(revisionContent => {
|
||||
fetch(originFileDownloadLink).then(res => {
|
||||
return res.json();
|
||||
}).then(originContent => {
|
||||
this.setState({ revisionContent, originContent, isLoading: false, errorMessage: '' });
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error, true);
|
||||
this.setState({ isLoading: false, errorMessage });
|
||||
});
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error, true);
|
||||
this.setState({ isLoading: false, errorMessage });
|
||||
});
|
||||
}
|
||||
|
||||
edit = (event) => {
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
window.location.href = `${siteRoot}lib/${repoID}/file${filePath}`;
|
||||
}
|
||||
|
||||
publishRevision = (event) => {
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
seafileAPI.sdocPublishRevision(docUuid).then(res => {
|
||||
const { origin_file_path } = res.data;
|
||||
window.location.href = `${siteRoot}lib/${repoID}/file${origin_file_path}`;
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error, false);
|
||||
toaster.danger(gettext(errorMessage));
|
||||
});
|
||||
}
|
||||
|
||||
renderContent = () => {
|
||||
const { isLoading, errorMessage, revisionContent, originContent } = this.state;
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="sdoc-revision-viewer h-100 d-flex align-items-center justify-content-center">
|
||||
<Loading />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<div className="sdoc-revision-viewer h-100 d-flex align-items-center justify-content-center">
|
||||
{gettext(errorMessage)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DiffViewer
|
||||
currentContent={revisionContent}
|
||||
lastContent={originContent}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoading, errorMessage } = this.state;
|
||||
|
||||
return (
|
||||
<div className="sdoc-revision d-flex h-100 w-100 o-hidden">
|
||||
<div className="sdoc-revision-container d-flex flex-column">
|
||||
<div className="sdoc-revision-header pl-4 pr-4 d-flex justify-content-between w-100 o-hidden">
|
||||
<div className={classnames('sdoc-revision-header-left h-100 d-flex align-items-center o-hidden')}>
|
||||
<GoBack />
|
||||
<div className="file-name text-truncate">{fileName}</div>
|
||||
</div>
|
||||
<div className="sdoc-revision-header-right h-100 d-flex align-items-center">
|
||||
{(!isLoading && !errorMessage) && (
|
||||
<>
|
||||
<Button color="success" className="mr-4" onClick={this.edit}>{gettext('Edit')}</Button>
|
||||
<Button color="success" onClick={this.publishRevision}>{gettext('Publish')}</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sdoc-revision-content f-flex">
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<SdocRevision />, document.getElementById('wrapper'));
|
122
frontend/src/pages/sdoc-revisions/index.js
Normal file
122
frontend/src/pages/sdoc-revisions/index.js
Normal file
@ -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 (<span className={classnames('', className)}>{perPageCount}</span>);
|
||||
}
|
||||
return (
|
||||
<a href={`?per_page=${perPageCount}${extraHref}`} className={classnames('per-page', className)}>{perPageCount}</a>
|
||||
);
|
||||
}
|
||||
|
||||
renderRevisions = () => {
|
||||
if (!Array.isArray(validRevisions) || validRevisions.length === 0) {
|
||||
return (
|
||||
<div className="empty-tips">
|
||||
<h2 className="alc">{gettext('This file has not revisions yet')}</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<table className="file-audit-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="25%" className="user">{gettext('User')}</th>
|
||||
<th width="50%">{gettext('File_name')}</th>
|
||||
<th width="25%">{gettext('Ctime')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{validRevisions.map(revision => {
|
||||
return (
|
||||
<tr key={revision.doc_uuid} className="sdoc-revision">
|
||||
<td width="25%" className="user">{revision.nickname}</td>
|
||||
<td width="50%">
|
||||
<a href={`${siteRoot}lib/${repo['id']}/file${revision.file_path}`}>
|
||||
{revision.filename}
|
||||
</a>
|
||||
</td>
|
||||
<td width="25%">{moment(revision.created_at).format('YYYY-MM-DD HH:MM')}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
renderFooter = () => {
|
||||
return (
|
||||
<div id="paginator">
|
||||
{currentPage !== 1 && (
|
||||
<a href={`?page=${prevPage}&per_page=${perPage}${extraHref}`} className="mr-1">{gettext('Previous')}</a>
|
||||
)}
|
||||
{pageNext && (
|
||||
<a href={`?page=${nextPage}&per_page=${perPage}${extraHref}`} className="mr-1">{gettext('Next')}</a>
|
||||
)}
|
||||
{(currentPage !== 1 || pageNext) && (<span className="mr-1">{'|'}</span>)}
|
||||
<span className="mr-1">{gettext('Per page: ')}</span>
|
||||
{this.renderPerPage(25, 'mr-1')}
|
||||
{this.renderPerPage(50, 'mr-1')}
|
||||
{this.renderPerPage(100)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div id="header" className="d-flex">
|
||||
<a href={siteRoot} id="logo">
|
||||
<img src={`${mediaUrl}${logoPath}`} title={siteTitle} alt="logo" width={logoWidth} height={logoHeight} />
|
||||
</a>
|
||||
</div>
|
||||
<div id="main" className="container-fluid w100 flex-auto ov-auto sdoc-revisions">
|
||||
<div id="wide-panel-noframe" className="row">
|
||||
<div className="col-md-10 col-md-offset-1">
|
||||
<h2 className="file-access-hd">
|
||||
<span className="op-target mr-1">{filename}</span>
|
||||
{gettext('Revisions')}
|
||||
</h2>
|
||||
<div className="file-audit-list-topbar">
|
||||
<p className="path">
|
||||
<span className="mr-1">{gettext('Current Path:')}</span>
|
||||
{validZipped.map((item, index) => {
|
||||
if (forloopLast) {
|
||||
return (<a key={index} href={`${viewLibFile.slice(0, -1)}${item[1]}`} target="_blank" rel="noreferrer">{item[0]}</a>);
|
||||
}
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<a href={`${viewLibFile.slice(0, -1)}${item[1]}`} target="_blank" rel="noreferrer">{item[0]}</a>
|
||||
{index !== validZipped.length - 1 && (
|
||||
<span className="mr-1 ml-1">{'/'}</span>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{this.renderRevisions()}
|
||||
{this.renderFooter()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDom.render(<SdocRevisions />, document.getElementById('wrapper'));
|
@ -18,6 +18,8 @@ const TextTranslation = {
|
||||
'UNLOCK' : {key : 'Unlock', value : gettext('Unlock')},
|
||||
'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')},
|
||||
|
@ -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,
|
||||
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);
|
||||
|
||||
@ -600,6 +600,8 @@ export const Utils = {
|
||||
} else {
|
||||
list.push(MASK_AS_DRAFT);
|
||||
}
|
||||
list.push(START_REVISE);
|
||||
list.push(LIST_REVISIONS);
|
||||
}
|
||||
if (enableFileComment) {
|
||||
list.push(COMMENT);
|
||||
@ -1542,6 +1544,17 @@ export const Utils = {
|
||||
generateHistoryURL: function(siteRoot, repoID, path) {
|
||||
if (!siteRoot || !repoID || !path) return '';
|
||||
return siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + this.encodePath(path);
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -10,7 +10,8 @@ const { serviceURL, avatarURL, siteRoot } = window.app.config;
|
||||
const { username, name } = window.app.userInfo;
|
||||
const {
|
||||
repoID, repoName, parentDir, filePerm,
|
||||
docPath, docName, docUuid, seadocAccessToken, seadocServerUrl, assetsUrl
|
||||
docPath, docName, docUuid, seadocAccessToken, seadocServerUrl, assetsUrl,
|
||||
isSdocRevision, isPublished, originFilename, revisionCreatedAt,
|
||||
} = window.app.pageOptions;
|
||||
|
||||
window.seafile = {
|
||||
@ -31,7 +32,12 @@ window.seafile = {
|
||||
parentFolderURL: `${siteRoot}library/${repoID}/${Utils.encodePath(repoName + parentDir)}`,
|
||||
assetsUrl,
|
||||
isShowInternalLink: true,
|
||||
isStarIconShown: true // for star/unstar
|
||||
isStarIconShown: true, // for star/unstar
|
||||
isSdocRevision,
|
||||
isPublished,
|
||||
revisionURL: Utils.generateRevisionURL(siteRoot, repoID, docPath),
|
||||
originFilename,
|
||||
revisionCreatedAt,
|
||||
};
|
||||
|
||||
ReactDom.render(
|
||||
|
@ -185,3 +185,28 @@ class RepoAPITokenAuthentication(BaseAuthentication):
|
||||
request.repo_api_token_obj = rat
|
||||
|
||||
return AnonymousUser(), auth[1]
|
||||
|
||||
|
||||
class SdocJWTTokenAuthentication(BaseAuthentication):
|
||||
|
||||
def authenticate(self, request):
|
||||
""" sdoc jwt token
|
||||
"""
|
||||
from seahub.seadoc.utils import is_valid_seadoc_access_token
|
||||
file_uuid = request.parser_context['kwargs'].get('file_uuid')
|
||||
auth = request.headers.get('authorization', '').split()
|
||||
is_valid, payload = is_valid_seadoc_access_token(auth, file_uuid, return_payload=True)
|
||||
if not is_valid:
|
||||
return None
|
||||
|
||||
username = payload.get('username')
|
||||
if not username:
|
||||
return None
|
||||
try:
|
||||
user = User.objects.get(email=username)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
if not user or not user.is_active:
|
||||
return None
|
||||
|
||||
return user, auth[1]
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
import logging
|
||||
import requests
|
||||
import posixpath
|
||||
@ -18,7 +19,7 @@ from django.utils import timezone
|
||||
from seaserv import seafile_api, check_quota
|
||||
|
||||
from seahub.views import check_folder_permission
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.authentication import TokenAuthentication, SdocJWTTokenAuthentication
|
||||
from seahub.api2.utils import api_error, user_to_dict, to_python_boolean
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.seadoc.utils import is_valid_seadoc_access_token, get_seadoc_upload_link, \
|
||||
@ -32,12 +33,14 @@ from seahub.tags.models import FileUUIDMap
|
||||
from seahub.utils.error_msg import file_type_error_msg
|
||||
from seahub.utils.repo import parse_repo_perm
|
||||
from seahub.utils.file_revisions import get_file_revisions_within_limit
|
||||
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft
|
||||
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocRevision
|
||||
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname, \
|
||||
email2contact_email
|
||||
from seahub.utils.timeutils import utc_datetime_to_isoformat_timestr, timestamp_to_isoformat_timestr
|
||||
from seahub.base.models import FileComment
|
||||
from seahub.constants import PERMISSION_READ_WRITE
|
||||
from seahub.seadoc.sdoc_server_api import SdocServerAPI
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -514,13 +517,22 @@ class SeadocDrafts(APIView):
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def get(self, request, repo_id):
|
||||
def get(self, request):
|
||||
"""list drafts
|
||||
"""
|
||||
username = request.user.username
|
||||
# argument check
|
||||
owned = request.GET.get('owned')
|
||||
repo_id = request.GET.get('repo_id')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
per_page = int(request.GET.get('per_page', '25'))
|
||||
except ValueError:
|
||||
page = 1
|
||||
per_page = 25
|
||||
start = (page - 1) * per_page
|
||||
limit = per_page + 1
|
||||
|
||||
if repo_id:
|
||||
# resource check
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
@ -533,17 +545,16 @@ class SeadocDrafts(APIView):
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
#
|
||||
if owned:
|
||||
draft_queryset = SeadocDraft.objects.filter(
|
||||
repo_id=repo_id, username=username)
|
||||
# all
|
||||
draft_queryset = SeadocDraft.objects.list_by_repo_id(repo_id, start, limit)
|
||||
count = SeadocDraft.objects.filter(repo_id=repo_id).count()
|
||||
else:
|
||||
draft_queryset = SeadocDraft.objects.list_by_repo_id(repo_id)
|
||||
# owned
|
||||
draft_queryset = SeadocDraft.objects.list_by_username(username, start, limit)
|
||||
count = SeadocDraft.objects.filter(username=username).count()
|
||||
|
||||
drafts = [draft.to_dict() for draft in draft_queryset]
|
||||
|
||||
return Response({'drafts': drafts})
|
||||
return Response({'drafts': drafts, 'count': count})
|
||||
|
||||
|
||||
class SeadocMaskAsDraft(APIView):
|
||||
@ -810,3 +821,271 @@ class SeadocCommentView(APIView):
|
||||
comment = file_comment.to_dict()
|
||||
comment.update(user_to_dict(file_comment.author, request=request, avatar_size=avatar_size))
|
||||
return Response(comment)
|
||||
|
||||
|
||||
class SeadocRevisions(APIView):
|
||||
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def get(self, request):
|
||||
"""list
|
||||
"""
|
||||
username = request.user.username
|
||||
# argument check
|
||||
origin_file_uuid = request.GET.get('doc_uuid')
|
||||
repo_id = request.GET.get('repo_id')
|
||||
try:
|
||||
page = int(request.GET.get('page', '1'))
|
||||
per_page = int(request.GET.get('per_page', '25'))
|
||||
except ValueError:
|
||||
page = 1
|
||||
per_page = 25
|
||||
start = (page - 1) * per_page
|
||||
limit = per_page + 1
|
||||
|
||||
if origin_file_uuid:
|
||||
origin_uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(origin_file_uuid)
|
||||
if not origin_uuid_map:
|
||||
error_msg = 'seadoc uuid %s not found.' % origin_file_uuid
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
repo_id = origin_uuid_map.repo_id
|
||||
username = request.user.username
|
||||
path = posixpath.join(origin_uuid_map.parent_path, origin_uuid_map.filename)
|
||||
# permission check
|
||||
if not check_folder_permission(request, repo_id, path):
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
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()
|
||||
elif repo_id:
|
||||
# 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)
|
||||
# permission check
|
||||
permission = check_folder_permission(request, repo_id, '/')
|
||||
if not permission:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
revision_queryset = SeadocRevision.objects.list_by_repo_id(repo_id, start, limit)
|
||||
count = SeadocRevision.objects.filter(repo_id=repo_id).count()
|
||||
else:
|
||||
# owned
|
||||
revision_queryset = SeadocRevision.objects.list_by_username(username, start, limit)
|
||||
count = SeadocRevision.objects.filter(username=username).count()
|
||||
|
||||
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]
|
||||
|
||||
return Response({'revisions': revisions, 'count': count})
|
||||
|
||||
def post(self, request):
|
||||
"""create
|
||||
"""
|
||||
username = request.user.username
|
||||
# argument check
|
||||
path = request.data.get('p', None)
|
||||
if not path:
|
||||
error_msg = 'p invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
repo_id = request.data.get('repo_id')
|
||||
if not repo_id:
|
||||
error_msg = 'repo_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
path = normalize_file_path(path)
|
||||
parent_dir = os.path.dirname(path)
|
||||
filename = os.path.basename(path)
|
||||
|
||||
filetype, fileext = get_file_type_and_ext(filename)
|
||||
if filetype != SEADOC:
|
||||
error_msg = 'seadoc file type %s invalid.' % filetype
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, 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)
|
||||
|
||||
# permission check
|
||||
permission = check_folder_permission(request, repo_id, '/')
|
||||
if not permission:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
# create revision dir if does not exist
|
||||
revision_dir_id = seafile_api.get_dir_id_by_path(repo_id, '/Revisions')
|
||||
if revision_dir_id is None:
|
||||
seafile_api.post_dir(repo_id, '/', 'Revisions', username)
|
||||
|
||||
#
|
||||
origin_file_uuid = get_seadoc_file_uuid(repo, path)
|
||||
if SeadocRevision.objects.get_by_doc_uuid(origin_file_uuid):
|
||||
error_msg = 'seadoc %s is already a revision.' % filename
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
origin_file_id = seafile_api.get_file_id_by_path(repo_id, path)
|
||||
revision_file_uuid = str(uuid.uuid4())
|
||||
revision_filename = revision_file_uuid + '.sdoc'
|
||||
|
||||
# copy file to revision dir
|
||||
seafile_api.copy_file(
|
||||
repo_id, parent_dir,
|
||||
json.dumps([filename]),
|
||||
repo_id, '/Revisions',
|
||||
json.dumps([revision_filename]),
|
||||
username=username, need_progress=0, synchronous=1,
|
||||
)
|
||||
|
||||
revision_uuid_map = FileUUIDMap(
|
||||
uuid=revision_file_uuid,
|
||||
repo_id=repo_id,
|
||||
parent_path='/Revisions',
|
||||
filename=revision_filename,
|
||||
is_dir=False,
|
||||
)
|
||||
revision_uuid_map.save()
|
||||
|
||||
revision = SeadocRevision.objects.create(
|
||||
doc_uuid=revision_file_uuid,
|
||||
origin_doc_uuid=origin_file_uuid,
|
||||
repo_id=repo_id,
|
||||
origin_doc_path=path,
|
||||
username=username,
|
||||
origin_file_version=origin_file_id,
|
||||
)
|
||||
|
||||
# copy image files
|
||||
origin_image_parent_path = '/images/sdoc/' + origin_file_uuid + '/'
|
||||
dir_id = seafile_api.get_dir_id_by_path(repo_id, origin_image_parent_path)
|
||||
if dir_id:
|
||||
revision_image_parent_path = gen_seadoc_image_parent_path(
|
||||
revision_file_uuid, repo_id, username)
|
||||
dirents = seafile_api.list_dir_by_path(repo_id, origin_image_parent_path)
|
||||
for e in dirents:
|
||||
obj_name = e.obj_name
|
||||
seafile_api.copy_file(
|
||||
repo_id, origin_image_parent_path,
|
||||
json.dumps([obj_name]),
|
||||
repo_id, revision_image_parent_path,
|
||||
json.dumps([obj_name]),
|
||||
username=username, need_progress=0, synchronous=1
|
||||
)
|
||||
return Response(revision.to_dict())
|
||||
|
||||
|
||||
class SeadocPublishRevision(APIView):
|
||||
authentication_classes = (SdocJWTTokenAuthentication, TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated, )
|
||||
throttle_classes = (UserRateThrottle, )
|
||||
|
||||
def post(self, request, file_uuid):
|
||||
"""publish
|
||||
"""
|
||||
force = request.data.get('force') # used when origin file deleted
|
||||
|
||||
# resource check
|
||||
revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid)
|
||||
if not revision:
|
||||
error_msg = 'Revision %s not found.' % file_uuid
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
if revision.is_published:
|
||||
error_msg = 'Revision %s is already published.' % file_uuid
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
repo_id = revision.repo_id
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if not repo:
|
||||
error_msg = 'Library %s not found.' % repo_id
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
# permission check
|
||||
permission = check_folder_permission(request, repo_id, '/')
|
||||
if not permission:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
if permission != PERMISSION_READ_WRITE:
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
# get origin file info
|
||||
origin_file_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(
|
||||
revision.origin_doc_uuid)
|
||||
|
||||
if not origin_file_uuid and not force:
|
||||
error_msg = 'origin sdoc %s not found.' % file_uuid
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
if origin_file_uuid:
|
||||
origin_file_parent_path = origin_file_uuid.parent_path
|
||||
origin_file_filename = origin_file_uuid.filename
|
||||
else:
|
||||
origin_file_parent_path = os.path.dirname(revision.origin_doc_path)
|
||||
origin_file_filename = os.path.basename(revision.origin_doc_path)
|
||||
origin_file_path = posixpath.join(origin_file_parent_path, origin_file_filename)
|
||||
|
||||
# check if origin file's parent folder exists
|
||||
if not seafile_api.get_dir_id_by_path(repo_id, origin_file_parent_path):
|
||||
dst_parent_path = '/'
|
||||
else:
|
||||
dst_parent_path = origin_file_parent_path
|
||||
|
||||
# get revision file info
|
||||
revision_file_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
|
||||
revision_parent_path = revision_file_uuid.parent_path
|
||||
revision_filename = revision_file_uuid.filename
|
||||
|
||||
# move revision file
|
||||
username = request.user.username
|
||||
seafile_api.move_file(
|
||||
repo_id, revision_parent_path,
|
||||
json.dumps([revision_filename]),
|
||||
repo_id, dst_parent_path,
|
||||
json.dumps([origin_file_filename]),
|
||||
replace=1, username=username,
|
||||
need_progress=0, synchronous=1,
|
||||
)
|
||||
|
||||
dst_file_id = seafile_api.get_file_id_by_path(repo_id, origin_file_path)
|
||||
SeadocRevision.objects.publish(file_uuid, username, dst_file_id)
|
||||
|
||||
# refresh docs
|
||||
doc_uuids = [revision.origin_doc_uuid, revision.doc_uuid]
|
||||
sdoc_server_api = SdocServerAPI(
|
||||
revision.origin_doc_uuid, origin_file_filename, username)
|
||||
sdoc_server_api.internal_refresh_docs(doc_uuids)
|
||||
|
||||
# move image files
|
||||
revision_image_parent_path = '/images/sdoc/' + str(revision_file_uuid.uuid) + '/'
|
||||
dir_id = seafile_api.get_dir_id_by_path(repo_id, revision_image_parent_path)
|
||||
if dir_id:
|
||||
origin_image_parent_path = gen_seadoc_image_parent_path(
|
||||
str(origin_file_uuid.uuid), repo_id, username)
|
||||
dirents = seafile_api.list_dir_by_path(repo_id, revision_image_parent_path)
|
||||
for e in dirents:
|
||||
obj_name = e.obj_name
|
||||
seafile_api.move_file(
|
||||
repo_id, revision_image_parent_path,
|
||||
json.dumps([obj_name]),
|
||||
repo_id, origin_image_parent_path,
|
||||
json.dumps([obj_name]),
|
||||
replace=1, username=username,
|
||||
need_progress=0, synchronous=1,
|
||||
)
|
||||
seafile_api.del_file(
|
||||
repo_id, '/images/sdoc/', json.dumps([str(revision_file_uuid.uuid)]), username)
|
||||
|
||||
revision = SeadocRevision.objects.get_by_doc_uuid(file_uuid)
|
||||
return Response(revision.to_dict())
|
||||
|
@ -1,6 +1,10 @@
|
||||
import os
|
||||
import posixpath
|
||||
|
||||
from django.db import models
|
||||
|
||||
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
|
||||
|
||||
class SeadocHistoryNameManager(models.Manager):
|
||||
@ -28,7 +32,6 @@ class SeadocHistoryName(models.Model):
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.pk,
|
||||
'doc_uuid': self.doc_uuid,
|
||||
'obj_id': self.obj_id,
|
||||
'name': self.name,
|
||||
@ -50,11 +53,11 @@ class SeadocDraftManager(models.Manager):
|
||||
def list_by_doc_uuids(self, doc_uuid_list):
|
||||
return self.filter(doc_uuid__in=doc_uuid_list)
|
||||
|
||||
def list_by_username(self, username):
|
||||
return self.filter(username=username)
|
||||
def list_by_username(self, username, start, limit):
|
||||
return self.filter(username=username).order_by('-id')[start:limit]
|
||||
|
||||
def list_by_repo_id(self, repo_id):
|
||||
return self.filter(repo_id=repo_id)
|
||||
def list_by_repo_id(self, repo_id, start, limit):
|
||||
return self.filter(repo_id=repo_id).order_by('-id')[start:limit]
|
||||
|
||||
|
||||
class SeadocDraft(models.Model):
|
||||
@ -75,3 +78,98 @@ class SeadocDraft(models.Model):
|
||||
'username': self.username,
|
||||
'created_at': datetime_to_isoformat_timestr(self.created_at),
|
||||
}
|
||||
|
||||
class SeadocRevisionManager(models.Manager):
|
||||
|
||||
def get_by_doc_uuid(self, doc_uuid):
|
||||
return self.filter(doc_uuid=doc_uuid).first()
|
||||
|
||||
def list_by_doc_uuids(self, doc_uuid_list):
|
||||
return self.filter(doc_uuid__in=doc_uuid_list)
|
||||
|
||||
def list_by_origin_doc_uuid(self, origin_doc_uuid, start, limit):
|
||||
return self.filter(
|
||||
origin_doc_uuid=origin_doc_uuid, is_published=False).order_by('-id')[start:limit]
|
||||
|
||||
def list_by_username(self, username, start, limit):
|
||||
return self.filter(
|
||||
username=username, is_published=False).order_by('-id')[start:limit]
|
||||
|
||||
def list_by_repo_id(self, repo_id, start, limit):
|
||||
return self.filter(
|
||||
repo_id=repo_id, is_published=False).order_by('-id')[start:limit]
|
||||
|
||||
def publish(self, doc_uuid, publisher, publish_file_version):
|
||||
return self.filter(doc_uuid=doc_uuid).update(
|
||||
publisher=publisher,
|
||||
publish_file_version=publish_file_version,
|
||||
is_published=True,
|
||||
)
|
||||
|
||||
|
||||
class SeadocRevision(models.Model):
|
||||
"""
|
||||
"""
|
||||
doc_uuid = models.CharField(max_length=36, unique=True)
|
||||
origin_doc_uuid = models.CharField(max_length=36, db_index=True)
|
||||
repo_id = models.CharField(max_length=36, db_index=True)
|
||||
origin_doc_path = models.TextField() # used when origin file deleted
|
||||
username = models.CharField(max_length=255, db_index=True)
|
||||
origin_file_version = models.CharField(max_length=100)
|
||||
publish_file_version = models.CharField(max_length=100, null=True)
|
||||
publisher = models.CharField(max_length=255, null=True)
|
||||
is_published = models.BooleanField(default=False, db_index=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
objects = SeadocRevisionManager()
|
||||
|
||||
class Meta:
|
||||
db_table = 'sdoc_revision'
|
||||
|
||||
def to_dict(self, fileuuidmap_queryset=None):
|
||||
from seahub.tags.models import FileUUIDMap
|
||||
if fileuuidmap_queryset:
|
||||
origin_doc_uuid = fileuuidmap_queryset.filter(uuid=self.origin_doc_uuid).first()
|
||||
else:
|
||||
origin_doc_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(self.origin_doc_uuid)
|
||||
if origin_doc_uuid:
|
||||
origin_parent_path = origin_doc_uuid.parent_path
|
||||
origin_filename = origin_doc_uuid.filename
|
||||
else:
|
||||
origin_parent_path = os.path.dirname(self.origin_doc_path)
|
||||
origin_filename = os.path.basename(self.origin_doc_path)
|
||||
origin_file_path = posixpath.join(origin_parent_path, origin_filename)
|
||||
|
||||
if fileuuidmap_queryset:
|
||||
doc_uuid = fileuuidmap_queryset.filter(uuid=self.doc_uuid).first()
|
||||
else:
|
||||
doc_uuid = FileUUIDMap.objects.get_fileuuidmap_by_uuid(self.doc_uuid)
|
||||
if doc_uuid:
|
||||
parent_path = doc_uuid.parent_path
|
||||
filename = doc_uuid.filename
|
||||
else:
|
||||
parent_path = '/Revisions'
|
||||
filename = self.doc_uuid + '.sdoc'
|
||||
file_path = posixpath.join(parent_path, filename)
|
||||
|
||||
return {
|
||||
'username': self.username,
|
||||
'nickname': email2nickname(self.username),
|
||||
'repo_id': self.repo_id,
|
||||
'doc_uuid': self.doc_uuid,
|
||||
'parent_path': parent_path,
|
||||
'filename': filename,
|
||||
'file_path': file_path,
|
||||
'origin_doc_uuid': self.origin_doc_uuid,
|
||||
'origin_parent_path': origin_parent_path,
|
||||
'origin_filename': origin_filename,
|
||||
'origin_file_path': origin_file_path,
|
||||
'origin_file_version': self.origin_file_version,
|
||||
'publish_file_version': self.publish_file_version,
|
||||
'publisher': self.publisher,
|
||||
'publisher_nickname': email2nickname(self.publisher),
|
||||
'is_published': self.is_published,
|
||||
'created_at': datetime_to_isoformat_timestr(self.created_at),
|
||||
'updated_at': datetime_to_isoformat_timestr(self.updated_at),
|
||||
}
|
||||
|
39
seahub/seadoc/sdoc_server_api.py
Normal file
39
seahub/seadoc/sdoc_server_api.py
Normal file
@ -0,0 +1,39 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
from seahub.settings import SEADOC_SERVER_URL
|
||||
from seahub.seadoc.utils import gen_seadoc_access_token
|
||||
|
||||
|
||||
def parse_response(response):
|
||||
if response.status_code >= 400:
|
||||
raise ConnectionError(response.status_code, response.text)
|
||||
else:
|
||||
try:
|
||||
data = json.loads(response.text)
|
||||
return data
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class SdocServerAPI(object):
|
||||
|
||||
def __init__(self, doc_uuid, filename, username):
|
||||
self.doc_uuid = doc_uuid
|
||||
self.filename = filename
|
||||
self.username = username
|
||||
self.headers = None
|
||||
self.sdoc_server_url = SEADOC_SERVER_URL.rstrip('/')
|
||||
self.timeout = 30
|
||||
self._init()
|
||||
|
||||
def _init(self):
|
||||
sdoc_server_access_token = gen_seadoc_access_token(
|
||||
self.doc_uuid, self.filename, self.username)
|
||||
self.headers = {'Authorization': 'Token ' + sdoc_server_access_token}
|
||||
|
||||
def internal_refresh_docs(self, doc_uuids):
|
||||
url = self.sdoc_server_url + '/api/v1/docs/' + self.doc_uuid + '/internal-refresh-docs/?from=seahub'
|
||||
data = {"doc_uuids" : doc_uuids}
|
||||
response = requests.post(url, json=data, headers=self.headers)
|
||||
return parse_response(response)
|
@ -1,7 +1,8 @@
|
||||
from django.urls import re_path
|
||||
from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \
|
||||
SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory, SeadocDrafts, SeadocMaskAsDraft, \
|
||||
SeadocCommentsView, SeadocCommentView
|
||||
SeadocCommentsView, SeadocCommentView, SeadocRevisions, SeadocPublishRevision
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^access-token/(?P<repo_id>[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
|
||||
@ -12,8 +13,10 @@ urlpatterns = [
|
||||
re_path(r'^download-image/(?P<file_uuid>[-0-9a-f]{36})/(?P<filename>.*)$', SeadocDownloadImage.as_view(), name='seadoc_download_image'),
|
||||
re_path(r'^copy-history-file/(?P<repo_id>[-0-9a-f]{36})/$', SeadocCopyHistoryFile.as_view(), name='seadoc_copy_history_file'),
|
||||
re_path(r'^history/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocHistory.as_view(), name='seadoc_history'),
|
||||
re_path(r'^drafts/(?P<repo_id>[-0-9a-f]{36})/$', SeadocDrafts.as_view(), name='seadoc_drafts'),
|
||||
re_path(r'^drafts/$', SeadocDrafts.as_view(), name='seadoc_drafts'),
|
||||
re_path(r'^mask-as-draft/(?P<repo_id>[-0-9a-f]{36})/$', SeadocMaskAsDraft.as_view(), name='seadoc_mask_as_draft'),
|
||||
re_path(r'^comments/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocCommentsView.as_view(), name='seadoc_comments'),
|
||||
re_path(r'^comment/(?P<file_uuid>[-0-9a-f]{36})/(?P<comment_id>\d+)/$', SeadocCommentView.as_view(), name='seadoc_comment'),
|
||||
re_path(r'^revisions/$', SeadocRevisions.as_view(), name='seadoc_revisions'),
|
||||
re_path(r'^publish-revision/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocPublishRevision.as_view(), name='seadoc_publish_revision'),
|
||||
]
|
||||
|
@ -15,6 +15,7 @@ from seahub.utils import normalize_file_path, gen_inner_file_get_url, gen_inner_
|
||||
from seahub.views import check_folder_permission
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
||||
from seahub.seadoc.models import SeadocRevision
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -172,3 +173,50 @@ def can_access_seadoc_asset(request, repo_id, path, file_uuid):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_seadoc_revision(doc_uuid):
|
||||
info = {}
|
||||
revision = SeadocRevision.objects.get_by_doc_uuid(doc_uuid)
|
||||
if revision:
|
||||
info = {'is_sdoc_revision': True}
|
||||
revision_info = revision.to_dict()
|
||||
info['origin_doc_uuid'] = revision_info['origin_doc_uuid']
|
||||
info['origin_parent_path'] = revision_info['origin_parent_path']
|
||||
info['origin_filename'] = revision_info['origin_filename']
|
||||
info['origin_file_path'] = revision_info['origin_file_path']
|
||||
info['origin_file_version'] = revision_info['origin_file_version']
|
||||
info['publish_file_version'] = revision_info['publish_file_version']
|
||||
info['publisher'] = revision_info['publisher']
|
||||
info['publisher_nickname'] = revision_info['publisher_nickname']
|
||||
info['is_published'] = revision_info['is_published']
|
||||
info['revision_created_at'] = revision_info['created_at']
|
||||
info['revision_updated_at'] = revision_info['updated_at']
|
||||
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
|
||||
|
||||
|
139
seahub/seadoc/views.py
Normal file
139
seahub/seadoc/views.py
Normal file
@ -0,0 +1,139 @@
|
||||
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, gen_path_link
|
||||
|
||||
|
||||
@login_required
|
||||
def sdoc_revision(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]
|
||||
|
||||
u_filename = os.path.basename(path)
|
||||
|
||||
# Check whether user is repo owner
|
||||
if validate_owner(request, repo_id):
|
||||
is_owner = True
|
||||
else:
|
||||
is_owner = False
|
||||
|
||||
file_uuid = get_seadoc_file_uuid(repo, path)
|
||||
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
|
||||
return_dict = {
|
||||
'repo': repo,
|
||||
'path': path,
|
||||
'u_filename': u_filename,
|
||||
'file_uuid': file_uuid,
|
||||
'is_owner': is_owner,
|
||||
'can_compare': True,
|
||||
'assets_url': '/api/v2.1/seadoc/download-image/' + file_uuid,
|
||||
'file_download_link': get_seadoc_download_link(uuid_map)
|
||||
}
|
||||
|
||||
revision_info = is_seadoc_revision(file_uuid)
|
||||
return_dict.update(revision_info)
|
||||
|
||||
origin_doc_uuid = return_dict.get('origin_doc_uuid', '')
|
||||
is_published = return_dict.get('is_published', False)
|
||||
if (origin_doc_uuid and not is_published):
|
||||
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(origin_doc_uuid)
|
||||
return_dict['origin_file_download_link'] = get_seadoc_download_link(uuid_map)
|
||||
|
||||
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,
|
||||
})
|
@ -14,6 +14,18 @@ docUuid: '{{ file_uuid }}',
|
||||
assetsUrl: '{{ assets_url }}',
|
||||
seadocAccessToken: '{{ seadoc_access_token }}',
|
||||
seadocServerUrl: '{{ seadoc_server_url }}',
|
||||
isSdocRevision: {% if is_sdoc_revision %}true{% else %}false{% endif %},
|
||||
originDocUuid: '{{ origin_doc_uuid }}',
|
||||
originParentPath: '{{ origin_parent_path }}',
|
||||
originFilename: '{{ origin_filename }}',
|
||||
originFilePath: '{{ origin_file_path }}',
|
||||
originFileVersion: '{{ origin_file_version }}',
|
||||
publishFileVersion: '{{ publish_file_version }}',
|
||||
publisher: '{{ publisher }}',
|
||||
publisherNickname: '{{ publisher_nickname }}',
|
||||
isPublished: {% if is_published %}true{% else %}false{% endif %},
|
||||
revisionCreatedAt: '{{ revision_created_at }}',
|
||||
revisionUpdatedAt: '{{ revision_updated_at }}',
|
||||
{% endblock %}
|
||||
|
||||
{% block render_bundle %}
|
||||
|
37
seahub/templates/sdoc_revision.html
Normal file
37
seahub/templates/sdoc_revision.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends "base_for_react.html" %}
|
||||
{% load render_bundle from webpack_loader %}
|
||||
|
||||
{% block extra_style %}
|
||||
{% render_bundle 'sdocRevision' 'css'%}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
window.app.config.lang = '{{LANGUAGE_CODE}}';
|
||||
window.sdocRevision = {
|
||||
repoID: '{{ repo.id }}',
|
||||
repoName: '{{ repo.name }}',
|
||||
filePath: '{{ path|escapejs }}',
|
||||
fileName: '{{ u_filename|escapejs }}',
|
||||
docUuid: '{{ file_uuid }}',
|
||||
domain: '{{ domain }}',
|
||||
protocol: '{{ protocol }}',
|
||||
assetsUrl: '{{ assets_url }}',
|
||||
fileDownloadLink: '{{ file_download_link }}',
|
||||
isSdocRevision: {% if is_sdoc_revision %}true{% else %}false{% endif %},
|
||||
originDocUuid: '{{ origin_doc_uuid }}',
|
||||
originFileDownloadLink: '{{ origin_file_download_link }}',
|
||||
originParentPath: '{{ origin_parent_path }}',
|
||||
originFilename: '{{ origin_filename }}',
|
||||
originFilePath: '{{ origin_file_path }}',
|
||||
originFileVersion: '{{ origin_file_version }}',
|
||||
publishFileVersion: '{{ publish_file_version }}',
|
||||
publisher: '{{ publisher }}',
|
||||
publisherNickname: '{{ publisher_nickname }}',
|
||||
isPublished: {% if is_published %}true{% else %}false{% endif %},
|
||||
revisionCreatedAt: '{{ revision_created_at }}',
|
||||
revisionUpdatedAt: '{{ revision_updated_at }}',
|
||||
}
|
||||
</script>
|
||||
{% render_bundle 'sdocRevision' 'js'%}
|
||||
{% endblock %}
|
43
seahub/templates/sdoc_revisions.html
Normal file
43
seahub/templates/sdoc_revisions.html
Normal file
@ -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 %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "css/bootstrap.min.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/seahub.css?t=1398068110" />
|
||||
{% render_bundle 'sdocRevisions' 'css'%}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
window.app.config.lang = '{{LANGUAGE_CODE}}';
|
||||
console.log('{{page_next}}')
|
||||
window.sdocRevisions = {
|
||||
repo: { "id": "{{ repo.id }}", "name": "{{ repo.name }}" },
|
||||
revisions: '{{ revisions|escapejs }}',
|
||||
count: '{{ count }}',
|
||||
filePath: '{{ path|escapejs }}',
|
||||
docUuid: '{{ docUuid }}',
|
||||
perPage: '{{ per_page }}',
|
||||
filename: '{{ filename }}',
|
||||
zipped: '{{ zipped|escapejs }}',
|
||||
currentPage: Number('{{ current_page }}'),
|
||||
prevPage: Number('{{ prev_page }}'),
|
||||
nextPage: Number('{{ next_page }}'),
|
||||
perPage: Number('{{ per_page }}'),
|
||||
pageNext: '{{ page_next }}' === 'False' ? false : true,
|
||||
extraHref: '{{ extra_href|escapejs }}',
|
||||
|
||||
}
|
||||
{% if not forloop.last %}
|
||||
window.sdocRevisions['forloopLast'] = false;
|
||||
window.sdocRevisions['viewLibFile'] = '{% url 'lib_view' repo.id repo.name '' %}';
|
||||
{% else %}
|
||||
window.sdocRevisions['forloopLast'] = true;
|
||||
window.sdocRevisions['viewLibFile'] = '{% url 'view_lib_file' repo.id '' %}';
|
||||
{% endif %}
|
||||
|
||||
|
||||
</script>
|
||||
{% render_bundle 'sdocRevisions' 'js'%}
|
||||
{% endblock %}
|
@ -194,6 +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, sdoc_revisions
|
||||
|
||||
from seahub.ocm.settings import OCM_ENDPOINT
|
||||
|
||||
@ -220,6 +221,8 @@ urlpatterns = [
|
||||
path('repo/upload_check/', validate_filename),
|
||||
re_path(r'^repo/download_dir/(?P<repo_id>[-0-9a-f]{36})/$', repo_download_dir, name='repo_download_dir'),
|
||||
re_path(r'^repo/file_revisions/(?P<repo_id>[-0-9a-f]{36})/$', file_revisions, name='file_revisions'),
|
||||
re_path(r'^repo/sdoc_revision/(?P<repo_id>[-0-9a-f]{36})/$', sdoc_revision, name='sdoc_revision'),
|
||||
re_path(r'^repo/sdoc_revisions/(?P<repo_id>[-0-9a-f]{36})/$', sdoc_revisions, name='sdoc_revisions'),
|
||||
re_path(r'^repo/file-access/(?P<repo_id>[-0-9a-f]{36})/$', file_access, name='file_access'),
|
||||
re_path(r'^repo/text_diff/(?P<repo_id>[-0-9a-f]{36})/$', text_diff, name='text_diff'),
|
||||
re_path(r'^repo/history/(?P<repo_id>[-0-9a-f]{36})/$', repo_history, name='repo_history'),
|
||||
|
@ -75,7 +75,7 @@ from seahub.thumbnail.utils import extract_xmind_image, get_thumbnail_src, \
|
||||
XMIND_IMAGE_SIZE, get_share_link_thumbnail_src, get_thumbnail_image_path
|
||||
from seahub.drafts.utils import get_file_draft, \
|
||||
is_draft_file, has_draft_file
|
||||
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token
|
||||
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token, is_seadoc_revision
|
||||
|
||||
if HAS_OFFICE_CONVERTER:
|
||||
from seahub.utils import (
|
||||
@ -666,6 +666,10 @@ def view_lib_file(request, repo_id, path):
|
||||
return_dict['can_edit_file'] = can_edit_file
|
||||
return_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
|
||||
|
||||
# revision
|
||||
revision_info = is_seadoc_revision(file_uuid)
|
||||
return_dict.update(revision_info)
|
||||
|
||||
send_file_access_msg(request, repo, path, 'web')
|
||||
return render(request, template, return_dict)
|
||||
|
||||
|
@ -1397,3 +1397,24 @@ CREATE TABLE `sdoc_draft` (
|
||||
KEY `sdoc_draft_repo_id` (`repo_id`),
|
||||
KEY `sdoc_draft_username` (`username`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `sdoc_revision` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`repo_id` varchar(36) NOT NULL,
|
||||
`doc_uuid` varchar(36) NOT NULL,
|
||||
`origin_doc_uuid` varchar(36) NOT NULL,
|
||||
`origin_doc_path` longtext NOT NULL,
|
||||
`origin_file_version` varchar(100) NOT NULL,
|
||||
`publish_file_version` varchar(100) DEFAULT NULL,
|
||||
`username` varchar(255) NOT NULL,
|
||||
`publisher` varchar(255) DEFAULT NULL,
|
||||
`is_published` tinyint(1) NOT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `sdoc_revise_doc_uuid` (`doc_uuid`),
|
||||
KEY `sdoc_revision_repo_id` (`repo_id`),
|
||||
KEY `sdoc_revision_origin_doc_uuid` (`origin_doc_uuid`),
|
||||
KEY `sdoc_revision_username` (`username`),
|
||||
KEY `sdoc_revision_is_published` (`is_published`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
Loading…
Reference in New Issue
Block a user