mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-26 15:26:19 +00:00
renameSdocHistory (#5500)
* renameSdocHistory * sdoc NewFileHistoryView * add SeadocHistoryName
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
|
import moment from 'moment';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';
|
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';
|
||||||
import { gettext, filePath } from '../../utils/constants';
|
import { gettext, filePath } from '../../utils/constants';
|
||||||
import URLDecorator from '../../utils/url-decorator';
|
import URLDecorator from '../../utils/url-decorator';
|
||||||
|
import Rename from '../../components/rename';
|
||||||
|
|
||||||
import '../../css/history-record-item.css';
|
import '../../css/history-record-item.css';
|
||||||
|
|
||||||
|
moment.locale(window.app.config.lang);
|
||||||
|
|
||||||
class HistoryVersion extends React.Component {
|
class HistoryVersion extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -13,18 +17,19 @@ class HistoryVersion extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isShowOperationIcon: false,
|
isShowOperationIcon: false,
|
||||||
isMenuShow: false,
|
isMenuShow: false,
|
||||||
|
isRenameShow: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
const { currentVersion, historyVersion } = this.props;
|
const { currentVersion, historyVersion } = this.props;
|
||||||
if (currentVersion.commitId === historyVersion.commitId) return;
|
if (currentVersion.commit_id === historyVersion.commit_id) return;
|
||||||
this.setState({ isShowOperationIcon: true });
|
this.setState({ isShowOperationIcon: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseLeave = () => {
|
onMouseLeave = () => {
|
||||||
const { currentVersion, historyVersion } = this.props;
|
const { currentVersion, historyVersion } = this.props;
|
||||||
if (currentVersion.commitId === historyVersion.commitId) return;
|
if (currentVersion.commit_id === historyVersion.commit_id) return;
|
||||||
this.setState({ isShowOperationIcon: false });
|
this.setState({ isShowOperationIcon: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +40,7 @@ class HistoryVersion extends React.Component {
|
|||||||
onClick = () => {
|
onClick = () => {
|
||||||
this.setState({ isShowOperationIcon: false });
|
this.setState({ isShowOperationIcon: false });
|
||||||
const { currentVersion, historyVersion } = this.props;
|
const { currentVersion, historyVersion } = this.props;
|
||||||
if (currentVersion.commitId === historyVersion.commitId) return;
|
if (currentVersion.commit_id === historyVersion.commit_id) return;
|
||||||
this.props.onSelectHistoryVersion(historyVersion);
|
this.props.onSelectHistoryVersion(historyVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,15 +55,30 @@ class HistoryVersion extends React.Component {
|
|||||||
|
|
||||||
onItemCopy = () => {
|
onItemCopy = () => {
|
||||||
const { historyVersion } = this.props;
|
const { historyVersion } = this.props;
|
||||||
|
historyVersion.ctime_format = moment(historyVersion.ctime).format('YYYY-MM-DD HH:mm');
|
||||||
this.props.onCopy(historyVersion);
|
this.props.onCopy(historyVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleRename = () => {
|
||||||
|
this.setState({isRenameShow: !this.state.isRenameShow});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameConfirm = (newName) => {
|
||||||
|
const { obj_id } = this.props.historyVersion;
|
||||||
|
this.props.renameHistoryVersion(obj_id, newName);
|
||||||
|
this.toggleRename();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameCancel = () => {
|
||||||
|
this.toggleRename();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { currentVersion, historyVersion } = this.props;
|
const { currentVersion, historyVersion } = this.props;
|
||||||
if (!currentVersion || !historyVersion) return null;
|
if (!currentVersion || !historyVersion) return null;
|
||||||
const { ctime, commitId, creatorName, revFileId } = historyVersion;
|
const { ctime, commit_id, creator_name, obj_id, name} = historyVersion;
|
||||||
const isHighlightItem = commitId === currentVersion.commitId;
|
const isHighlightItem = commit_id === currentVersion.commit_id;
|
||||||
const url = URLDecorator.getUrl({ type: 'download_historic_file', filePath: filePath, objID: revFileId });
|
const url = URLDecorator.getUrl({ type: 'download_historic_file', filePath: filePath, objID: obj_id });
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={`history-list-item ${isHighlightItem ? 'item-active' : ''}`}
|
className={`history-list-item ${isHighlightItem ? 'item-active' : ''}`}
|
||||||
@@ -67,10 +87,14 @@ class HistoryVersion extends React.Component {
|
|||||||
onClick={this.onClick}
|
onClick={this.onClick}
|
||||||
>
|
>
|
||||||
<div className="history-info">
|
<div className="history-info">
|
||||||
<div className="time">{ctime}</div>
|
{this.state.isRenameShow ?
|
||||||
|
<Rename name={name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel}/>
|
||||||
|
:<div className="name">{name}</div>
|
||||||
|
}
|
||||||
|
<div className="time">{moment(ctime).format('YYYY-MM-DD HH:mm')}</div>
|
||||||
<div className="owner">
|
<div className="owner">
|
||||||
<span className="squire-icon"></span>
|
<span className="squire-icon"></span>
|
||||||
<span>{creatorName}</span>
|
<span>{creator_name}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="history-operation">
|
<div className="history-operation">
|
||||||
@@ -86,6 +110,7 @@ class HistoryVersion extends React.Component {
|
|||||||
{(this.props.index !== 0) && <DropdownItem onClick={this.onItemRestore}>{gettext('Restore')}</DropdownItem>}
|
{(this.props.index !== 0) && <DropdownItem onClick={this.onItemRestore}>{gettext('Restore')}</DropdownItem>}
|
||||||
<DropdownItem tag='a' href={url} onClick={this.onItemDownLoad}>{gettext('Download')}</DropdownItem>
|
<DropdownItem tag='a' href={url} onClick={this.onItemDownLoad}>{gettext('Download')}</DropdownItem>
|
||||||
{(this.props.index !== 0) && <DropdownItem onClick={this.onItemCopy}>{gettext('Copy')}</DropdownItem>}
|
{(this.props.index !== 0) && <DropdownItem onClick={this.onItemCopy}>{gettext('Copy')}</DropdownItem>}
|
||||||
|
<DropdownItem onClick={this.toggleRename}>{gettext('Rename')}</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,6 +126,7 @@ HistoryVersion.propTypes = {
|
|||||||
onSelectHistoryVersion: PropTypes.func.isRequired,
|
onSelectHistoryVersion: PropTypes.func.isRequired,
|
||||||
onRestore: PropTypes.func.isRequired,
|
onRestore: PropTypes.func.isRequired,
|
||||||
onCopy: PropTypes.func.isRequired,
|
onCopy: PropTypes.func.isRequired,
|
||||||
|
renameHistoryVersion: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default HistoryVersion;
|
export default HistoryVersion;
|
||||||
|
@@ -50,12 +50,12 @@ class SdocFileHistory extends React.Component {
|
|||||||
|
|
||||||
onSelectHistoryVersion = (currentVersion, lastVersion) => {
|
onSelectHistoryVersion = (currentVersion, lastVersion) => {
|
||||||
this.setState({ isLoading: true, currentVersion });
|
this.setState({ isLoading: true, currentVersion });
|
||||||
seafileAPI.getFileRevision(historyRepoID, currentVersion.commitId, currentVersion.path).then(res => {
|
seafileAPI.getFileRevision(historyRepoID, currentVersion.commit_id, currentVersion.path).then(res => {
|
||||||
return seafileAPI.getFileContent(res.data);
|
return seafileAPI.getFileContent(res.data);
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
const currentVersionContent = res.data;
|
const currentVersionContent = res.data;
|
||||||
if (lastVersion) {
|
if (lastVersion) {
|
||||||
seafileAPI.getFileRevision(historyRepoID, lastVersion.commitId, lastVersion.path).then(res => {
|
seafileAPI.getFileRevision(historyRepoID, lastVersion.commit_id, lastVersion.path).then(res => {
|
||||||
return seafileAPI.getFileContent(res.data);
|
return seafileAPI.getFileContent(res.data);
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
const lastVersionContent = res.data;
|
const lastVersionContent = res.data;
|
||||||
@@ -84,7 +84,7 @@ class SdocFileHistory extends React.Component {
|
|||||||
const { currentVersionContent } = this.state;
|
const { currentVersionContent } = this.state;
|
||||||
this.setState({ isLoading: true }, () => {
|
this.setState({ isLoading: true }, () => {
|
||||||
localStorage.setItem('seahub-sdoc-history-show-changes', isShowChanges + '');
|
localStorage.setItem('seahub-sdoc-history-show-changes', isShowChanges + '');
|
||||||
seafileAPI.getFileRevision(historyRepoID, lastVersion.commitId, lastVersion.path).then(res => {
|
seafileAPI.getFileRevision(historyRepoID, lastVersion.commit_id, lastVersion.path).then(res => {
|
||||||
return seafileAPI.getFileContent(res.data);
|
return seafileAPI.getFileContent(res.data);
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
const lastVersionContent = res.data;
|
const lastVersionContent = res.data;
|
||||||
|
@@ -2,15 +2,16 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Loading from '../../components/loading';
|
import Loading from '../../components/loading';
|
||||||
import { gettext, filePath, historyRepoID } from '../../utils/constants';
|
import { gettext, historyRepoID, PER_PAGE } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import FileHistory from '../../models/file-history';
|
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import editUtilities from '../../utils/editor-utilities';
|
import editUtilities from '../../utils/editor-utilities';
|
||||||
import toaster from '../../components/toast';
|
import toaster from '../../components/toast';
|
||||||
import HistoryVersion from './history-version';
|
import HistoryVersion from './history-version';
|
||||||
import Switch from '../../components/common/switch';
|
import Switch from '../../components/common/switch';
|
||||||
|
|
||||||
|
const { docUuid } = window.fileHistory.pageOptions;
|
||||||
|
|
||||||
class SidePanel extends Component {
|
class SidePanel extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -19,85 +20,104 @@ class SidePanel extends Component {
|
|||||||
isLoading: true,
|
isLoading: true,
|
||||||
historyVersions: [],
|
historyVersions: [],
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
|
currentPage: 1,
|
||||||
|
hasMore: false,
|
||||||
|
fileOwner: '',
|
||||||
|
isReloadingData: false,
|
||||||
};
|
};
|
||||||
this.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.listHistoryVersions(historyRepoID, filePath, this.nextCommit, (historyVersion, lastHistoryVersion) => {
|
seafileAPI.listSdocHistory(docUuid, 1, PER_PAGE).then(res => {
|
||||||
this.props.onSelectHistoryVersion(historyVersion, lastHistoryVersion);
|
let historyList = res.data;
|
||||||
|
if (historyList.length === 0) {
|
||||||
|
this.setState({isLoading: false});
|
||||||
|
throw Error('there has an error in server');
|
||||||
|
}
|
||||||
|
this.initResultState(res.data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init = () => {
|
refershFileList() {
|
||||||
this.hasMore = true;
|
seafileAPI.listSdocHistory(docUuid, 1, PER_PAGE).then(res => {
|
||||||
this.nextCommit = '';
|
this.initResultState(res.data);
|
||||||
this.filePath = '';
|
});
|
||||||
this.oldFilePath = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listHistoryVersions = (repoID, filePath, commit, callback) => {
|
initResultState(result) {
|
||||||
seafileAPI.listOldFileHistoryRecords(repoID, filePath, commit).then((res) => {
|
if (result.histories.length) {
|
||||||
let historyData = res.data;
|
this.setState({
|
||||||
if (!historyData) {
|
historyVersions: result.histories,
|
||||||
this.setState({ isLoading: false, errorMessage: 'There is an error in server.' });
|
currentPage: result.page,
|
||||||
return;
|
hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
|
||||||
|
isLoading: false,
|
||||||
|
fileOwner: result.histories[0].creator_email,
|
||||||
|
});
|
||||||
|
this.props.onSelectHistoryVersion(result.histories[0], result.histories[1]);
|
||||||
}
|
}
|
||||||
this.updateHistoryVersions(historyData, callback);
|
}
|
||||||
|
|
||||||
|
updateResultState(result) {
|
||||||
|
if (result.histories.length) {
|
||||||
|
this.setState({
|
||||||
|
historyVersions: [...this.state.historyVersions, ...result.histories],
|
||||||
|
currentPage: result.page,
|
||||||
|
hasMore: result.total_count > (PER_PAGE * this.state.currentPage),
|
||||||
|
isLoading: false,
|
||||||
|
fileOwner: result.histories[0].creator_email
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore = () => {
|
||||||
|
if (!this.state.isReloadingData) {
|
||||||
|
let currentPage = this.state.currentPage + 1;
|
||||||
|
this.setState({
|
||||||
|
currentPage: currentPage,
|
||||||
|
isReloadingData: true,
|
||||||
|
});
|
||||||
|
seafileAPI.listSdocHistory(docUuid, currentPage, PER_PAGE).then(res => {
|
||||||
|
this.updateResultState(res.data);
|
||||||
|
this.setState({isReloadingData: false});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renameHistoryVersion = (objID, newName) => {
|
||||||
|
seafileAPI.renameSdocHistory(docUuid, objID, newName).then((res) => {
|
||||||
|
this.setState({
|
||||||
|
historyVersions: this.state.historyVersions.map(item => {
|
||||||
|
if (item.obj_id == objID) {
|
||||||
|
item.name = newName;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
const errorMessage = Utils.getErrorMsg(error, true);
|
const errorMessage = Utils.getErrorMsg(error, true);
|
||||||
this.setState({ isLoading: false, errorMessage: errorMessage });
|
this.setState({ isLoading: false, errorMessage: errorMessage });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHistoryVersions(result, callback) {
|
|
||||||
const dataCount = result.data ? result.data.length : 0;
|
|
||||||
this.nextCommit = result.next_start_commit || '';
|
|
||||||
if (dataCount) {
|
|
||||||
const addedHistoryVersions = result.data.map(item => new FileHistory(item));
|
|
||||||
this.filePath = addedHistoryVersions[dataCount - 1].path;
|
|
||||||
this.oldFilePath = addedHistoryVersions[dataCount - 1].revRenamedOldPath;
|
|
||||||
const historyVersions = [ ...this.state.historyVersions, ...addedHistoryVersions ];
|
|
||||||
this.setState({ historyVersions: historyVersions, isLoading: false, errorMessage: '' }, () => {
|
|
||||||
callback && callback(historyVersions[0], historyVersions[1]);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.nextCommit) {
|
|
||||||
this.listHistoryVersions(historyRepoID, filePath, this.nextCommit);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hasMore = false;
|
|
||||||
this.setState({ isLoading: false, errorMessage: '' });
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrollHandler = (event) => {
|
onScrollHandler = (event) => {
|
||||||
const clientHeight = event.target.clientHeight;
|
const clientHeight = event.target.clientHeight;
|
||||||
const scrollHeight = event.target.scrollHeight;
|
const scrollHeight = event.target.scrollHeight;
|
||||||
const scrollTop = event.target.scrollTop;
|
const scrollTop = event.target.scrollTop;
|
||||||
const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
|
const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
|
||||||
if (isBottom && this.hasMore && this.nextCommit) {
|
if (isBottom && this.state.hasMore) {
|
||||||
this.loadMore();
|
this.loadMore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMore = () => {
|
restoreVersion = (currentItem) => {
|
||||||
if (this.state.isLoading) return;
|
const { commit_id, path } = currentItem;
|
||||||
this.setState({ isLoading: true }, () => {
|
editUtilities.revertFile(path, commit_id).then(res => {
|
||||||
const currentFilePath = this.oldFilePath || this.filePath;
|
|
||||||
this.listHistoryVersions(historyRepoID, currentFilePath, this.nextCommit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreVersion = (historyVersion) => {
|
|
||||||
const { commitId, path } = historyVersion;
|
|
||||||
editUtilities.revertFile(path, commitId).then(res => {
|
|
||||||
if (res.data.success) {
|
if (res.data.success) {
|
||||||
this.init();
|
this.setState({isLoading: true});
|
||||||
this.setState({ isLoading: true, historyVersions: [], errorMessage: '' } , () => {
|
this.refershFileList();
|
||||||
this.listHistoryVersions(historyRepoID, filePath);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
let message = gettext('Successfully restored.');
|
||||||
|
toaster.success(message);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
const errorMessage = Utils.getErrorMsg(error, true);
|
const errorMessage = Utils.getErrorMsg(error, true);
|
||||||
toaster.danger(gettext(errorMessage));
|
toaster.danger(gettext(errorMessage));
|
||||||
@@ -111,13 +131,13 @@ class SidePanel extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { historyVersions } = this.state;
|
const { historyVersions } = this.state;
|
||||||
const historyVersionIndex = historyVersions.findIndex(item => item.commitId === historyVersion.commitId);
|
const historyVersionIndex = historyVersions.findIndex(item => item.commit_id === historyVersion.commit_id);
|
||||||
this.props.onSelectHistoryVersion(historyVersion, historyVersions[historyVersionIndex + 1]);
|
this.props.onSelectHistoryVersion(historyVersion, historyVersions[historyVersionIndex + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
copyHistoryFile = (historyVersion) => {
|
copyHistoryFile = (historyVersion) => {
|
||||||
const { path, revFileId, ctime } = historyVersion;
|
const { path, obj_id, ctime_format } = historyVersion;
|
||||||
seafileAPI.sdocCopyHistoryFile(historyRepoID, path, revFileId, ctime).then(res => {
|
seafileAPI.sdocCopyHistoryFile(historyRepoID, path, obj_id, ctime_format).then(res => {
|
||||||
let message = gettext('Successfully copied %(name)s.');
|
let message = gettext('Successfully copied %(name)s.');
|
||||||
let filename = res.data.file_name;
|
let filename = res.data.file_name;
|
||||||
message = message.replace('%(name)s', filename);
|
message = message.replace('%(name)s', filename);
|
||||||
@@ -157,13 +177,14 @@ class SidePanel extends Component {
|
|||||||
{historyVersions.map((historyVersion, index) => {
|
{historyVersions.map((historyVersion, index) => {
|
||||||
return (
|
return (
|
||||||
<HistoryVersion
|
<HistoryVersion
|
||||||
key={historyVersion.commitId}
|
key={historyVersion.commit_id}
|
||||||
index={index}
|
index={index}
|
||||||
currentVersion={this.props.currentVersion}
|
currentVersion={this.props.currentVersion}
|
||||||
historyVersion={historyVersion}
|
historyVersion={historyVersion}
|
||||||
onSelectHistoryVersion={this.onSelectHistoryVersion}
|
onSelectHistoryVersion={this.onSelectHistoryVersion}
|
||||||
onRestore={this.restoreVersion}
|
onRestore={this.restoreVersion}
|
||||||
onCopy={this.copyHistoryFile}
|
onCopy={this.copyHistoryFile}
|
||||||
|
renameHistoryVersion={this.renameHistoryVersion}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -179,7 +200,7 @@ class SidePanel extends Component {
|
|||||||
onShowChanges = () => {
|
onShowChanges = () => {
|
||||||
const { isShowChanges, currentVersion } = this.props;
|
const { isShowChanges, currentVersion } = this.props;
|
||||||
const { historyVersions } = this.state;
|
const { historyVersions } = this.state;
|
||||||
const historyVersionIndex = historyVersions.findIndex(item => item.commitId === currentVersion.commitId);
|
const historyVersionIndex = historyVersions.findIndex(item => item.commit_id === currentVersion.commit_id);
|
||||||
const lastVersion = historyVersions[historyVersionIndex + 1];
|
const lastVersion = historyVersions[historyVersionIndex + 1];
|
||||||
this.props.onShowChanges(!isShowChanges, lastVersion);
|
this.props.onShowChanges(!isShowChanges, lastVersion);
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import posixpath
|
import posixpath
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@@ -24,11 +25,17 @@ from seahub.seadoc.utils import is_valid_seadoc_access_token, get_seadoc_upload_
|
|||||||
gen_seadoc_image_parent_path, get_seadoc_asset_upload_link, get_seadoc_asset_download_link, \
|
gen_seadoc_image_parent_path, get_seadoc_asset_upload_link, get_seadoc_asset_download_link, \
|
||||||
can_access_seadoc_asset
|
can_access_seadoc_asset
|
||||||
from seahub.utils.file_types import SEADOC, IMAGE
|
from seahub.utils.file_types import SEADOC, IMAGE
|
||||||
from seahub.utils import get_file_type_and_ext, normalize_file_path, PREVIEW_FILEEXT, \
|
from seahub.utils import get_file_type_and_ext, normalize_file_path, PREVIEW_FILEEXT, get_file_history, \
|
||||||
gen_inner_file_get_url, gen_inner_file_upload_url
|
gen_inner_file_get_url, gen_inner_file_upload_url
|
||||||
from seahub.tags.models import FileUUIDMap
|
from seahub.tags.models import FileUUIDMap
|
||||||
from seahub.utils.error_msg import file_type_error_msg
|
from seahub.utils.error_msg import file_type_error_msg
|
||||||
from seahub.utils.repo import parse_repo_perm
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -353,3 +360,147 @@ class SeadocCopyHistoryFile(APIView):
|
|||||||
'file_name': new_file_name,
|
'file_name': new_file_name,
|
||||||
'file_path': new_file_path,
|
'file_path': new_file_path,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class SeadocHistory(APIView):
|
||||||
|
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def _get_new_file_history_info(self, ent, avatar_size, name_dict):
|
||||||
|
info = {}
|
||||||
|
creator_name = ent.op_user
|
||||||
|
url, is_default, date_uploaded = api_avatar_url(creator_name, avatar_size)
|
||||||
|
info['creator_avatar_url'] = url
|
||||||
|
info['creator_email'] = creator_name
|
||||||
|
info['creator_name'] = email2nickname(creator_name)
|
||||||
|
info['creator_contact_email'] = email2contact_email(creator_name)
|
||||||
|
info['ctime'] = utc_datetime_to_isoformat_timestr(ent.timestamp)
|
||||||
|
info['size'] = ent.size
|
||||||
|
info['obj_id'] = ent.file_id
|
||||||
|
info['commit_id'] = ent.commit_id
|
||||||
|
info['old_path'] = ent.old_path if hasattr(ent, 'old_path') else ''
|
||||||
|
info['path'] = ent.path
|
||||||
|
info['name'] = name_dict.get(ent.file_id, '')
|
||||||
|
return info
|
||||||
|
|
||||||
|
def get(self, request, file_uuid):
|
||||||
|
"""list history, same as NewFileHistoryView
|
||||||
|
"""
|
||||||
|
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
|
||||||
|
if not uuid_map:
|
||||||
|
error_msg = 'seadoc uuid %s not found.' % file_uuid
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_id = uuid_map.repo_id
|
||||||
|
username = request.user.username
|
||||||
|
path = posixpath.join(uuid_map.parent_path, 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
commit_id = repo.head_cmmt_id
|
||||||
|
|
||||||
|
try:
|
||||||
|
avatar_size = int(request.GET.get('avatar_size', 32))
|
||||||
|
page = int(request.GET.get('page', 1))
|
||||||
|
per_page = int(request.GET.get('per_page', 25))
|
||||||
|
except ValueError:
|
||||||
|
avatar_size = 32
|
||||||
|
page = 1
|
||||||
|
per_page = 25
|
||||||
|
|
||||||
|
# Don't use seafile_api.get_file_id_by_path()
|
||||||
|
# if path parameter is `rev_renamed_old_path`.
|
||||||
|
# seafile_api.get_file_id_by_path() will return None.
|
||||||
|
file_id = seafile_api.get_file_id_by_commit_and_path(repo_id,
|
||||||
|
commit_id, path)
|
||||||
|
if not file_id:
|
||||||
|
error_msg = 'File %s not found.' % path
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# get repo history limit
|
||||||
|
try:
|
||||||
|
history_limit = seafile_api.get_repo_history_limit(repo_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
start = (page - 1) * per_page
|
||||||
|
count = per_page
|
||||||
|
try:
|
||||||
|
file_revisions, total_count = get_file_history(repo_id, path, start, count, history_limit)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
name_dict = {}
|
||||||
|
obj_id_list = [commit.file_id for commit in file_revisions]
|
||||||
|
if obj_id_list:
|
||||||
|
name_queryset = SeadocHistoryName.objects.list_by_obj_ids(
|
||||||
|
doc_uuid=file_uuid, obj_id_list=obj_id_list)
|
||||||
|
name_dict = {item.obj_id: item.name for item in name_queryset}
|
||||||
|
data = [self._get_new_file_history_info(ent, avatar_size, name_dict) for ent in file_revisions]
|
||||||
|
result = {
|
||||||
|
"histories": data,
|
||||||
|
"page": page,
|
||||||
|
"total_count": total_count
|
||||||
|
}
|
||||||
|
return Response(result)
|
||||||
|
|
||||||
|
def post(self, request, file_uuid):
|
||||||
|
"""rename history
|
||||||
|
"""
|
||||||
|
username = request.user.username
|
||||||
|
obj_id = request.data.get('obj_id', '')
|
||||||
|
new_name = request.data.get('new_name', '')
|
||||||
|
if not obj_id:
|
||||||
|
error_msg = 'obj_id invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
if not new_name:
|
||||||
|
error_msg = 'new_name invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(file_uuid)
|
||||||
|
if not uuid_map:
|
||||||
|
error_msg = 'seadoc uuid %s not found.' % file_uuid
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_id = uuid_map.repo_id
|
||||||
|
username = request.user.username
|
||||||
|
path = posixpath.join(uuid_map.parent_path, 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)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
token = seafile_api.get_fileserver_access_token(repo_id,
|
||||||
|
obj_id, 'download', username)
|
||||||
|
if not token:
|
||||||
|
error_msg = 'history %s not found.' % obj_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# main
|
||||||
|
SeadocHistoryName.objects.update_name(file_uuid, obj_id, new_name)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'obj_id': obj_id,
|
||||||
|
'name': new_name,
|
||||||
|
})
|
||||||
|
33
seahub/seadoc/models.py
Normal file
33
seahub/seadoc/models.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class SeadocHistoryNameManager(models.Manager):
|
||||||
|
def update_name(self, doc_uuid, obj_id, name):
|
||||||
|
if self.filter(doc_uuid=doc_uuid, obj_id=obj_id).exists():
|
||||||
|
obj = self.filter(doc_uuid=doc_uuid, obj_id=obj_id).update(name=name)
|
||||||
|
else:
|
||||||
|
obj = self.create(doc_uuid=doc_uuid, obj_id=obj_id, name=name)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def list_by_obj_ids(self, doc_uuid, obj_id_list):
|
||||||
|
return self.filter(doc_uuid=doc_uuid, obj_id__in=obj_id_list)
|
||||||
|
|
||||||
|
|
||||||
|
class SeadocHistoryName(models.Model):
|
||||||
|
doc_uuid = models.CharField(max_length=36, db_index=True)
|
||||||
|
obj_id = models.CharField(max_length=40)
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
objects = SeadocHistoryNameManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'history_name'
|
||||||
|
unique_together = ('doc_uuid', 'obj_id')
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
'id': self.pk,
|
||||||
|
'doc_uuid': self.doc_uuid,
|
||||||
|
'obj_id': self.obj_id,
|
||||||
|
'name': self.name,
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
from django.urls import re_path
|
from django.urls import re_path
|
||||||
from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \
|
from .apis import SeadocAccessToken, SeadocUploadLink, SeadocDownloadLink, SeadocUploadFile, \
|
||||||
SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile
|
SeadocUploadImage, SeadocDownloadImage, SeadocCopyHistoryFile, SeadocHistory
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r'^access-token/(?P<repo_id>[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
|
re_path(r'^access-token/(?P<repo_id>[-0-9a-f]{36})/$', SeadocAccessToken.as_view(), name='seadoc_access_token'),
|
||||||
@@ -10,4 +10,5 @@ urlpatterns = [
|
|||||||
re_path(r'^upload-image/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocUploadImage.as_view(), name='seadoc_upload_image'),
|
re_path(r'^upload-image/(?P<file_uuid>[-0-9a-f]{36})/$', SeadocUploadImage.as_view(), name='seadoc_upload_image'),
|
||||||
re_path(r'^download-image/(?P<file_uuid>[-0-9a-f]{36})/(?P<filename>.*)$', SeadocDownloadImage.as_view(), name='seadoc_download_image'),
|
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'^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'),
|
||||||
]
|
]
|
||||||
|
@@ -275,6 +275,7 @@ INSTALLED_APPS = [
|
|||||||
'seahub.organizations',
|
'seahub.organizations',
|
||||||
'seahub.krb5_auth',
|
'seahub.krb5_auth',
|
||||||
'seahub.django_cas_ng',
|
'seahub.django_cas_ng',
|
||||||
|
'seahub.seadoc',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1375,3 +1375,13 @@ CREATE TABLE `organizations_orgadminsettings` (
|
|||||||
UNIQUE KEY `organizations_orgadminsettings_org_id_key_a01cc7de_uniq` (`org_id`,`key`),
|
UNIQUE KEY `organizations_orgadminsettings_org_id_key_a01cc7de_uniq` (`org_id`,`key`),
|
||||||
KEY `organizations_orgadminsettings_org_id_4f70d186` (`org_id`)
|
KEY `organizations_orgadminsettings_org_id_4f70d186` (`org_id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
CREATE TABLE `history_name` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`doc_uuid` varchar(36) NOT NULL,
|
||||||
|
`obj_id` varchar(40) NOT NULL,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `history_name_doc_uuid` (`doc_uuid`),
|
||||||
|
UNIQUE KEY `history_name_doc_uuid_obj_id` (`doc_uuid`, `obj_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
Reference in New Issue
Block a user