mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-02 15:38:15 +00:00
Wiki optimized third (#2406)
This commit is contained in:
committed by
Daniel Pan
parent
5cffd4cb69
commit
3ab4cbff4f
@@ -10,6 +10,7 @@ export const logoWidth = window.app.config.logoWidth;
|
|||||||
export const logoHeight = window.app.config.logoHeight;
|
export const logoHeight = window.app.config.logoHeight;
|
||||||
export const isPro = window.app.config.isPro === "True";
|
export const isPro = window.app.config.isPro === "True";
|
||||||
export const lang = window.app.config.lang;
|
export const lang = window.app.config.lang;
|
||||||
|
export const fileServerRoot = window.app.config.fileServerRoot;
|
||||||
|
|
||||||
// wiki
|
// wiki
|
||||||
export const slug = window.wiki ? window.wiki.config.slug : '';
|
export const slug = window.wiki ? window.wiki.config.slug : '';
|
||||||
|
22
frontend/src/components/dialog/zip-download-dialog.js
Normal file
22
frontend/src/components/dialog/zip-download-dialog.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||||
|
|
||||||
|
class ZipDownloadDialog extends React.Component {
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.onCancelDownload();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true} toggle={this.toggle}>
|
||||||
|
<ModalHeader toggle={this.toggle}></ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<div>{this.props.progress}</div>
|
||||||
|
</ModalBody>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ZipDownloadDialog;
|
101
frontend/src/components/dirent-operation/operation-group.js
Normal file
101
frontend/src/components/dirent-operation/operation-group.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { gettext } from '../constants';
|
||||||
|
import OperationMenu from './operation-menu';
|
||||||
|
|
||||||
|
class OperationGroup extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isItemMenuShow: false,
|
||||||
|
menuPosition: {top: 0, left: 0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('click', this.onItemMenuHide);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('click', this.onItemMenuHide);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownload = (e) => {
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.onDownload();
|
||||||
|
}
|
||||||
|
|
||||||
|
onShare = (e) => {
|
||||||
|
//todos::
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete = (e) => {
|
||||||
|
e.nativeEvent.stopImmediatePropagation(); //for document event
|
||||||
|
this.props.onDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuShow = (e) => {
|
||||||
|
if (!this.state.isItemMenuShow) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
|
||||||
|
let left = e.clientX - 8*16;
|
||||||
|
let top = e.clientY + 15;
|
||||||
|
let position = Object.assign({},this.state.menuPosition, {left: left, top: top});
|
||||||
|
this.setState({
|
||||||
|
menuPosition: position,
|
||||||
|
isItemMenuShow: true,
|
||||||
|
});
|
||||||
|
this.props.onItemMenuShow();
|
||||||
|
} else {
|
||||||
|
this.onItemMenuHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuHide = () => {
|
||||||
|
this.setState({
|
||||||
|
isItemMenuShow: false,
|
||||||
|
});
|
||||||
|
this.props.onItemMenuHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRename = () => {
|
||||||
|
//todos:
|
||||||
|
}
|
||||||
|
|
||||||
|
onCopy = () => {
|
||||||
|
//todos
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="operations">
|
||||||
|
<ul className="operation-group">
|
||||||
|
<li className="operation-group-item">
|
||||||
|
<i className="sf2-icon-download" title={gettext('Download')} onClick={this.onDownload}></i>
|
||||||
|
</li>
|
||||||
|
<li className="operation-group-item">
|
||||||
|
<i className="sf2-icon-share" title={gettext('Share')} onClick={this.onShare}></i>
|
||||||
|
</li>
|
||||||
|
<li className="operation-group-item">
|
||||||
|
<i className="sf2-icon-delete" title={gettext('Delete')} onClick={this.onDelete}></i>
|
||||||
|
</li>
|
||||||
|
<li className="operation-group-item">
|
||||||
|
<i className="sf2-icon-caret-down sf-dropdown-toggle" title={gettext('More Operation')} onClick={this.onItemMenuShow}></i>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{
|
||||||
|
this.state.isItemMenuShow &&
|
||||||
|
<OperationMenu
|
||||||
|
currentItem={this.props.item}
|
||||||
|
menuPosition={this.state.menuPosition}
|
||||||
|
onRename={this.onRename}
|
||||||
|
onCopy={this.onCopy}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OperationGroup;
|
142
frontend/src/components/dirent-operation/operation-menu.js
Normal file
142
frontend/src/components/dirent-operation/operation-menu.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { gettext } from '../constants';
|
||||||
|
|
||||||
|
class OperationMenu extends React.Component {
|
||||||
|
|
||||||
|
getItemType() {
|
||||||
|
return this.props.currentItem.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDirentDirMenu() {
|
||||||
|
let position = this.props.menuPosition;
|
||||||
|
let style = {position: 'fixed', left: position.left, top: position.top, display: 'block'};
|
||||||
|
if (this.props.currentItem.permission === 'rw') {
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu operation-menu" style={style}>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Rename')} aria-label={gettext('Rename')}>{gettext('Rename')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Move')} aria-label={gettext('Move')}>{gettext('Move')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Copy')} aria-label={gettext('Copy')}>{gettext('Copy')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item menu-inner-divider"></li>
|
||||||
|
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Permission')} aria-label={gettext('Permission')}>{gettext('Permission')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Details')} aria-label={gettext('Details')}>{gettext('Details')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item menu-inner-divider"></li>
|
||||||
|
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Open via Client')} aria-label={gettext('Open via Client')}>{gettext('Open via Client')}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.currentItem.permission === 'r') {
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu operation-menu" style={style}>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Copy')} aria-label={gettext('Copy')}>{gettext('Copy')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Details')} aria-label={gettext('Details')}>{gettext('Details')}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDirentFileMenu() {
|
||||||
|
let position = this.props.menuPosition;
|
||||||
|
let style = {position: 'fixed', left: position.left, top: position.top, display: 'block'};
|
||||||
|
if (this.props.currentItem.permission === 'rw') {
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu operation-menu" style={style}>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Rename')} aria-label={gettext('Rename')}>{gettext('Rename')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Move')} aria-label={gettext('Move')}>{gettext('Move')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Copy')} aria-label={gettext('Copy')}>{gettext('Copy')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Lock')} aria-label={gettext('Lock')}>{gettext('Lock')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Unlock')} aria-label={gettext('Unlock')}>{gettext('Unlock')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('New Draft')} aria-label={gettext('New Draft')}>{gettext('New Draft')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item menu-inner-divider"></li>
|
||||||
|
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Comment')} aria-label={gettext('Comment')}>{gettext('Comment')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="" title={gettext('History')} aria-label={gettext('History')}>{gettext('History')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Access Log')} aria-label={gettext('Access Log')}>{gettext('Access Log')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Details')} aria-label={gettext('Details')}>{gettext('Details')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item menu-inner-divider"></li>
|
||||||
|
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Open via Client')} aria-label={gettext('Open via Client')}>{gettext('Open via Client')}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.currentItem.permission === "r") {
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu operation-menu" style={style}>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Copy')} aria-label={gettext('Copy')}>{gettext('Copy')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Comment')} aria-label={gettext('Comment')}>{gettext('Comment')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('History')} aria-label={gettext('History')}>{gettext('History')}</span>
|
||||||
|
</li>
|
||||||
|
<li className="dropdown-item operation-menu-item">
|
||||||
|
<span className="user-select-none" title={gettext('Details')} aria-label={gettext('Details')}>{gettext('Details')}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let type = this.getItemType();
|
||||||
|
let menu = null;
|
||||||
|
switch(type) {
|
||||||
|
case 'file':
|
||||||
|
menu = this.renderDirentFileMenu();
|
||||||
|
break;
|
||||||
|
case 'dir':
|
||||||
|
menu = this.renderDirentDirMenu();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OperationMenu;
|
@@ -62,11 +62,11 @@ class DraftListItem extends React.Component {
|
|||||||
localTime = moment(localTime).fromNow();
|
localTime = moment(localTime).fromNow();
|
||||||
return (
|
return (
|
||||||
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<td className="icon" style={{width: "4%"}}><img src={siteRoot + "media/img/file/192/txt.png"} /></td>
|
<td className="icon"><img src={siteRoot + "media/img/file/192/txt.png"} /></td>
|
||||||
<td className="name a-simulate" style={{width: "46%"}} onClick={this.onDraftEditClick}>{fileName}</td>
|
<td className="name a-simulate" onClick={this.onDraftEditClick}>{fileName}</td>
|
||||||
<td className="owner" style={{width: "20%"}}>{draft.owner}</td>
|
<td className="owner">{draft.owner}</td>
|
||||||
<td className="update" style={{width: "20%"}}>{localTime}</td>
|
<td className="update">{localTime}</td>
|
||||||
<td className="menu-toggle" style={{width: "10%"}}>
|
<td className="menu-toggle">
|
||||||
<NodeMenuControl
|
<NodeMenuControl
|
||||||
isShow={this.state.isMenuControlShow}
|
isShow={this.state.isMenuControlShow}
|
||||||
onClick={this.onMenuToggleClick}
|
onClick={this.onMenuToggleClick}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { gettext, repoID, serviceUrl } from '../constants';
|
import { gettext, repoID, siteRoot } from '../constants';
|
||||||
import SearchResultItem from './search-result-item';
|
import SearchResultItem from './search-result-item';
|
||||||
import editorUtilities from '../../utils/editor-utilties';
|
import editorUtilities from '../../utils/editor-utilties';
|
||||||
import More from '../more';
|
import More from '../more';
|
||||||
@@ -169,7 +169,7 @@ class Search extends Component {
|
|||||||
for (let key in queryData) {
|
for (let key in queryData) {
|
||||||
params += key + '=' + queryData[key] + '&';
|
params += key + '=' + queryData[key] + '&';
|
||||||
}
|
}
|
||||||
window.location = serviceUrl + '/search/?' + params.slice(0, params.length - 1);
|
window.location = siteRoot + '/search/?' + params.slice(0, params.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSearchResult() {
|
renderSearchResult() {
|
||||||
|
@@ -1,29 +1,51 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { gettext, repoID, permission, siteRoot, initialFilePath } from '../constants';
|
import { gettext, repoID, slug, permission, siteRoot } from '../constants';
|
||||||
|
import { encodePath } from '../utils';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
function PathToolbar() {
|
const propTypes = {
|
||||||
|
filePath: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
let trashUrl = '';
|
class PathToolbar extends React.Component {
|
||||||
let historyUrl = '';
|
|
||||||
if (initialFilePath === '/' && permission) {
|
isMarkdownFile(filePath) {
|
||||||
trashUrl = siteRoot + 'repo/recycle/' + repoID + '/?referer=' + encodeURIComponent(location.href);
|
let lastIndex = filePath.lastIndexOf('/');
|
||||||
historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href);
|
let name = filePath.slice(lastIndex + 1);
|
||||||
|
return name.indexOf('.md') > -1 ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let isFile = this.isMarkdownFile(this.props.filePath);
|
||||||
|
let index = this.props.filePath.lastIndexOf('/');
|
||||||
|
let name = this.props.filePath.slice(index + 1);
|
||||||
|
let trashUrl = siteRoot + 'repo/recycle/' + repoID + '/?referer=' + encodeURIComponent(location.href);
|
||||||
|
let historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href);
|
||||||
|
if ( (name === slug || name === '') && !isFile && permission) {
|
||||||
return (
|
return (
|
||||||
<ul className="path-toolbar">
|
<ul className="path-toolbar">
|
||||||
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
|
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
|
||||||
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
|
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
} else if (permission) {
|
} else if ( !isFile && permission) {
|
||||||
trashUrl = siteRoot + 'dir/recycle/' + repoID + '/?dir_path=' + encodeURIComponent(initialFilePath);
|
|
||||||
return (
|
return (
|
||||||
<ul className="path-toolbar">
|
<ul className="path-toolbar">
|
||||||
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
|
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
} else if (permission) {
|
||||||
|
historyUrl = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + encodePath(this.props.filePath) + '&referer=' + encodeURIComponent(location.href);
|
||||||
|
return (
|
||||||
|
<ul className="path-toolbar">
|
||||||
|
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PathToolbar.propTypes = propTypes;
|
||||||
|
|
||||||
export default PathToolbar;
|
export default PathToolbar;
|
||||||
|
@@ -1,41 +1,93 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { serviceUrl } from '../constants';
|
import { serviceUrl } from '../constants';
|
||||||
|
import OperationGroup from '../dirent-operation/operation-group';
|
||||||
|
|
||||||
class TreeDirList extends React.Component {
|
class TreeDirList extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isMourseEnter: false,
|
highlight: false,
|
||||||
highlight: '',
|
isOperationShow: false,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
|
if (!this.props.isItemFreezed) {
|
||||||
this.setState({
|
this.setState({
|
||||||
highlight: 'tr-highlight'
|
highlight: true,
|
||||||
|
isOperationShow: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOver = () => {
|
||||||
|
if (!this.props.isItemFreezed) {
|
||||||
|
this.setState({
|
||||||
|
highlight: true,
|
||||||
|
isOperationShow: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMouseLeave = () => {
|
onMouseLeave = () => {
|
||||||
|
if (!this.props.isItemFreezed) {
|
||||||
this.setState({
|
this.setState({
|
||||||
highlight: '',
|
highlight: false,
|
||||||
|
isOperationShow: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuShow = () => {
|
||||||
|
this.props.onItemMenuShow();
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuHide = () => {
|
||||||
|
this.setState({
|
||||||
|
isOperationShow: false,
|
||||||
|
highlight: ''
|
||||||
|
});
|
||||||
|
this.props.onItemMenuHide();
|
||||||
|
}
|
||||||
|
|
||||||
onMainNodeClick = () => {
|
onMainNodeClick = () => {
|
||||||
this.props.onMainNodeClick(this.props.node);
|
this.props.onMainNodeClick(this.props.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDownload = () => {
|
||||||
|
this.props.onDownload(this.props.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelete = () => {
|
||||||
|
this.props.onDelete(this.props.node);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let node = this.props.node;
|
let node = this.props.node;
|
||||||
return (
|
return (
|
||||||
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<tr className={this.state.highlight ? "tr-highlight" : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onMouseOver={this.onMouseOver}>
|
||||||
<td className="icon" style={{width: "4%"}}>
|
<td className="icon">
|
||||||
<img src={node.type === "dir" ? serviceUrl + "/media/img/folder-192.png" : serviceUrl + "/media/img/file/192/txt.png"}></img>
|
<img src={node.type === "dir" ? serviceUrl + "/media/img/folder-192.png" : serviceUrl + "/media/img/file/192/txt.png"}></img>
|
||||||
</td>
|
</td>
|
||||||
<td className="name a-simulate" style={{width: "60%"}} onClick={this.onMainNodeClick}>{node.name}</td>
|
<td className="name a-simulate" onClick={this.onMainNodeClick}>{node.name}</td>
|
||||||
<td style={{width: "16%"}}>{node.size}</td>
|
{
|
||||||
<td style={{width: "20%"}} title={node.last_update_time}>{node.last_update_time}</td>
|
this.props.needOperationGroup &&
|
||||||
|
<td>
|
||||||
|
{
|
||||||
|
this.state.isOperationShow &&
|
||||||
|
<OperationGroup
|
||||||
|
item={node}
|
||||||
|
onItemMenuShow={this.onItemMenuShow}
|
||||||
|
onItemMenuHide={this.onItemMenuHide}
|
||||||
|
onDownload={this.onDownload}
|
||||||
|
onDelete={this.onDelete}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
<td>{node.size}</td>
|
||||||
|
<td title={node.last_update_time}>{node.last_update_time}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,81 @@
|
|||||||
import React, { Component } from "react";
|
import React from "react";
|
||||||
|
import { gettext, repoID } from '../constants';
|
||||||
|
import editorUtilities from '../../utils/editor-utilties';
|
||||||
|
import URLDecorator from '../../utils/url-decorator';
|
||||||
|
import ZipDownloadDialog from '../dialog/zip-download-dialog';
|
||||||
import TreeDirList from './tree-dir-list'
|
import TreeDirList from './tree-dir-list'
|
||||||
import "../../css/common.css";
|
import "../../css/common.css";
|
||||||
const gettext = window.gettext;
|
|
||||||
|
|
||||||
class TreeDirView extends React.Component {
|
class TreeDirView extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isProgressDialogShow: false,
|
||||||
|
progress: '0%',
|
||||||
|
isItemFreezed: false
|
||||||
|
};
|
||||||
|
this.zip_token = null;
|
||||||
|
this.interval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownload = (item) => {
|
||||||
|
if (item.isDir()) {
|
||||||
|
this.setState({isProgressDialogShow: true, progress: '0%'});
|
||||||
|
editorUtilities.zipDownload(item.parent_path, item.name).then(res => {
|
||||||
|
this.zip_token = res.data['zip_token'];
|
||||||
|
this.addDownloadAnimation();
|
||||||
|
this.interval = setInterval(this.addDownloadAnimation, 1000);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let url = URLDecorator.getUrl({type:'download_file_url', repoID: repoID, filePath: item.path});
|
||||||
|
location.href = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDownloadAnimation = () => {
|
||||||
|
let _this = this;
|
||||||
|
let token = this.zip_token;
|
||||||
|
editorUtilities.queryZipProgress(token).then(res => {
|
||||||
|
let data = res.data;
|
||||||
|
let progress = data.total === 0 ? '100%' : (data.zipped / data.total * 100).toFixed(0) + '%';
|
||||||
|
this.setState({progress: progress});
|
||||||
|
|
||||||
|
if (data['total'] === data['zipped']) {
|
||||||
|
this.setState({
|
||||||
|
progress: '100%'
|
||||||
|
});
|
||||||
|
clearInterval(this.interval);
|
||||||
|
location.href = URLDecorator.getUrl({type: 'download_dir_zip_url', token: token});
|
||||||
|
setTimeout(function() {
|
||||||
|
_this.setState({isProgressDialogShow: false});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancelDownload = () => {
|
||||||
|
let zip_token = this.zip_token;
|
||||||
|
editorUtilities.cancelZipTask(zip_token).then(res => {
|
||||||
|
this.setState({
|
||||||
|
isProgressDialogShow: false,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuShow = () => {
|
||||||
|
this.setState({
|
||||||
|
isItemFreezed: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemMenuHide = () => {
|
||||||
|
this.setState({
|
||||||
|
isItemFreezed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let node = this.props.node;
|
let node = this.props.node;
|
||||||
let children = node.hasChildren() ? node.children : null;
|
let children = node.hasChildren() ? node.children : null;
|
||||||
@@ -12,21 +84,46 @@ class TreeDirView extends React.Component {
|
|||||||
<div className="table-container">
|
<div className="table-container">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
{
|
||||||
|
this.props.needOperationGroup ?
|
||||||
<tr>
|
<tr>
|
||||||
<th style={{width: "4%"}}></th>
|
<th style={{width: "4%"}}></th>
|
||||||
<th style={{width: "60%"}}>{gettext('Name')}</th>
|
<th style={{width: "46%"}}>{gettext('Name')}</th>
|
||||||
<th style={{width: "16%"}}>{gettext('Size')}</th>
|
<th style={{width: "20%"}}></th>
|
||||||
<th style={{width: "20%"}}>{gettext('Last Update')}</th>
|
<th style={{width: "15%"}}>{gettext('Size')}</th>
|
||||||
|
<th style={{width: "15%"}}>{gettext('Last Update')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
:
|
||||||
|
<tr>
|
||||||
|
<th style={{width: "4%"}}></th>
|
||||||
|
<th style={{width: "66%"}}>{gettext('Name')}</th>
|
||||||
|
<th style={{width: "15%"}}>{gettext('Size')}</th>
|
||||||
|
<th style={{width: "15%"}}>{gettext('Last Update')}</th>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{children && children.map((node, index) => {
|
{children && children.map((node, index) => {
|
||||||
return (
|
return (
|
||||||
<TreeDirList key={index} node={node} onMainNodeClick={this.props.onMainNodeClick}></TreeDirList>
|
<TreeDirList
|
||||||
|
key={index}
|
||||||
|
node={node}
|
||||||
|
isItemFreezed={this.state.isItemFreezed}
|
||||||
|
onMainNodeClick={this.props.onMainNodeClick}
|
||||||
|
onItemMenuShow={this.onItemMenuShow}
|
||||||
|
onItemMenuHide={this.onItemMenuHide}
|
||||||
|
onDownload={this.onDownload}
|
||||||
|
onDelete={this.props.onDeleteItem}
|
||||||
|
needOperationGroup={this.props.needOperationGroup}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{
|
||||||
|
this.state.isProgressDialogShow &&
|
||||||
|
<ZipDownloadDialog progress={this.state.progress} onCancleDownload={this.onCancelDownload}/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
class Node {
|
class Node {
|
||||||
|
|
||||||
static deserializefromJson(object) {
|
static deserializefromJson(object) {
|
||||||
const {name, type, size, last_update_time, isExpanded = true, children = []} = object;
|
const {name, type, size, last_update_time, permission, parent_path, isExpanded = true, children = []} = object;
|
||||||
|
|
||||||
const node = new Node({
|
const node = new Node({
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
size,
|
size,
|
||||||
last_update_time,
|
last_update_time,
|
||||||
|
permission,
|
||||||
|
parent_path,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
children: children.map(item => Node.deserializefromJson(item)),
|
children: children.map(item => Node.deserializefromJson(item)),
|
||||||
});
|
});
|
||||||
@@ -15,11 +17,13 @@ class Node {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor({name, type, size, last_update_time, isExpanded, children}) {
|
constructor({name, type, size, last_update_time, permission, parent_path, isExpanded, children}) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.last_update_time = last_update_time;
|
this.last_update_time = last_update_time;
|
||||||
|
this.permission = permission;
|
||||||
|
this.parent_path = parent_path;
|
||||||
this.isExpanded = isExpanded !== undefined ? isExpanded : true;
|
this.isExpanded = isExpanded !== undefined ? isExpanded : true;
|
||||||
this.children = children ? children : [];
|
this.children = children ? children : [];
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
@@ -31,6 +35,8 @@ class Node {
|
|||||||
type: this.type,
|
type: this.type,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
last_update_time: this.last_update_time,
|
last_update_time: this.last_update_time,
|
||||||
|
permission: this.permission,
|
||||||
|
parent_path: this.parent_path,
|
||||||
isExpanded: this.isExpanded
|
isExpanded: this.isExpanded
|
||||||
});
|
});
|
||||||
n.children = this.children.map(child => {
|
n.children = this.children.map(child => {
|
||||||
@@ -101,6 +107,8 @@ class Node {
|
|||||||
type: this.type,
|
type: this.type,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
last_update_time: this.last_update_time,
|
last_update_time: this.last_update_time,
|
||||||
|
permission: this.permission,
|
||||||
|
parent_path: this.parent_path,
|
||||||
isExpanded: this.isExpanded,
|
isExpanded: this.isExpanded,
|
||||||
children: children
|
children: children
|
||||||
}
|
}
|
||||||
|
@@ -186,6 +186,8 @@ class Tree {
|
|||||||
type: model.type,
|
type: model.type,
|
||||||
size: bytesToSize(model.size),
|
size: bytesToSize(model.size),
|
||||||
last_update_time: moment.unix(model.last_update_time).fromNow(),
|
last_update_time: moment.unix(model.last_update_time).fromNow(),
|
||||||
|
permission: model.permission,
|
||||||
|
parent_path: model.parent_path,
|
||||||
isExpanded: false
|
isExpanded: false
|
||||||
});
|
});
|
||||||
if (model.children instanceof Array) {
|
if (model.children instanceof Array) {
|
||||||
@@ -214,6 +216,8 @@ class Tree {
|
|||||||
type: nodeObj.type,
|
type: nodeObj.type,
|
||||||
size: bytesToSize(nodeObj.size),
|
size: bytesToSize(nodeObj.size),
|
||||||
last_update_time: moment.unix(nodeObj.last_update_time).fromNow(),
|
last_update_time: moment.unix(nodeObj.last_update_time).fromNow(),
|
||||||
|
permission: nodeObj.permission,
|
||||||
|
parent_path: nodeObj.parent_path,
|
||||||
isExpanded: false
|
isExpanded: false
|
||||||
});
|
});
|
||||||
node.parent_path = nodeObj.parent_path;
|
node.parent_path = nodeObj.parent_path;
|
||||||
@@ -240,6 +244,8 @@ class Tree {
|
|||||||
type: node.type,
|
type: node.type,
|
||||||
size: bytesToSize(node.size),
|
size: bytesToSize(node.size),
|
||||||
last_update_time: moment.unix(node.last_update_time).fromNow(),
|
last_update_time: moment.unix(node.last_update_time).fromNow(),
|
||||||
|
permission: node.permission,
|
||||||
|
parent_path: node.parent_path,
|
||||||
isExpanded: false
|
isExpanded: false
|
||||||
});
|
});
|
||||||
if (node.children instanceof Array) {
|
if (node.children instanceof Array) {
|
||||||
|
@@ -18,3 +18,12 @@ export function bytesToSize(bytes) {
|
|||||||
if (i === 0) return bytes + ' ' + sizes[i];
|
if (i === 0) return bytes + ' ' + sizes[i];
|
||||||
return (bytes / (1000 ** i)).toFixed(1) + ' ' + sizes[i];
|
return (bytes / (1000 ** i)).toFixed(1) + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function encodePath(path) {
|
||||||
|
let path_arr = path.split('/');
|
||||||
|
let path_arr_ = [];
|
||||||
|
for (let i = 0, len = path_arr.length; i < len; i++) {
|
||||||
|
path_arr_.push(encodeURIComponent(path_arr[i]));
|
||||||
|
}
|
||||||
|
return path_arr_.join('/');
|
||||||
|
}
|
@@ -99,17 +99,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-container table .icon {
|
.table-container table .icon {
|
||||||
position: relative;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container table .icon img {
|
.table-container table .icon img {
|
||||||
position: absolute;
|
width: 1.5rem;
|
||||||
display: block;
|
height: 1.5rem;
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
}
|
||||||
/* specific handler */
|
/* specific handler */
|
||||||
.table-container table .menu-toggle {
|
.table-container table .menu-toggle {
|
||||||
@@ -131,6 +126,12 @@
|
|||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-item.menu-inner-divider {
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
/* end dropdown-menu style */
|
/* end dropdown-menu style */
|
||||||
|
|
||||||
/* begin tip */
|
/* begin tip */
|
||||||
@@ -172,3 +173,39 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
/* end more component */
|
/* end more component */
|
||||||
|
|
||||||
|
/* begin operation menu */
|
||||||
|
.operation {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation .operation-group {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-group .operation-group-item {
|
||||||
|
display: inline-block;
|
||||||
|
color: #f89a68;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-group-item i {
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.operation-group-item i:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-group-item .sf-dropdown-toggle {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-group-item .sf-dropdown-toggle:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
/* end operaton menu */
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
export const gettext = window.gettext;
|
|
||||||
export const siteRoot = window.app.config.siteRoot;
|
|
||||||
export const lang = window.app.config.lang;
|
|
||||||
|
|
||||||
export const getUrl = (options) => {
|
|
||||||
switch (options.name) {
|
|
||||||
case 'user_profile': return siteRoot + 'profile/' + options.username + '/';
|
|
||||||
case 'common_lib': return siteRoot + '#common/lib/' + options.repoID + options.path;
|
|
||||||
case 'view_lib_file': return `${siteRoot}lib/${options.repoID}/file${options.filePath}`;
|
|
||||||
case 'download_historic_file': return `${siteRoot}repo/${options.repoID}/${options.objID}/download/?p=${options.filePath}`;
|
|
||||||
case 'view_historic_file': return `${siteRoot}repo/${options.repoID}/history/files/?obj_id=${options.objID}&commit_id=${options.commitID}&p=${options.filePath}`;
|
|
||||||
case 'diff_historic_file': return `${siteRoot}repo/text_diff/${options.repoID}/?commit=${options.commitID}&p=${options.filePath}`;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@@ -10,8 +10,9 @@ class MainPanel extends Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isWikiMode: true
|
isWikiMode: true,
|
||||||
}
|
needOperationGroup: true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuClick = () => {
|
onMenuClick = () => {
|
||||||
@@ -90,7 +91,7 @@ class MainPanel extends Component {
|
|||||||
<a href={siteRoot + 'wiki/lib/' + repoID + '/'} className="normal">{slug}</a>
|
<a href={siteRoot + 'wiki/lib/' + repoID + '/'} className="normal">{slug}</a>
|
||||||
{pathElem}
|
{pathElem}
|
||||||
</div>
|
</div>
|
||||||
<PathToolbar />
|
<PathToolbar filePath={this.props.filePath}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="cur-view-content">
|
<div className="cur-view-content">
|
||||||
{ this.props.isViewFileState &&
|
{ this.props.isViewFileState &&
|
||||||
@@ -106,6 +107,9 @@ class MainPanel extends Component {
|
|||||||
<TreeDirView
|
<TreeDirView
|
||||||
node={this.props.changedNode}
|
node={this.props.changedNode}
|
||||||
onMainNodeClick={this.props.onMainNodeClick}
|
onMainNodeClick={this.props.onMainNodeClick}
|
||||||
|
onDeleteItem={this.props.onDeleteNode}
|
||||||
|
onRenameItem={this.props.onRenameNode}
|
||||||
|
needOperationGroup={this.state.needOperationGroup}
|
||||||
>
|
>
|
||||||
</TreeDirView>
|
</TreeDirView>
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,13 @@ import TreeDirView from '../../components/tree-dir-view/tree-dir-view';
|
|||||||
|
|
||||||
class MainPanel extends Component {
|
class MainPanel extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
needOperationGroupo: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
onMenuClick = () => {
|
onMenuClick = () => {
|
||||||
this.props.onMenuClick();
|
this.props.onMenuClick();
|
||||||
}
|
}
|
||||||
@@ -82,7 +89,10 @@ class MainPanel extends Component {
|
|||||||
{ !this.props.isViewFileState &&
|
{ !this.props.isViewFileState &&
|
||||||
<TreeDirView
|
<TreeDirView
|
||||||
node={this.props.changedNode}
|
node={this.props.changedNode}
|
||||||
|
needOperationGroupo={this.state.needOperationGroupo}
|
||||||
onMainNodeClick={this.props.onMainNodeClick}
|
onMainNodeClick={this.props.onMainNodeClick}
|
||||||
|
onDeleteItem={this.props.onDeleteNode}
|
||||||
|
onRenameItem={this.props.onRenameNode}
|
||||||
>
|
>
|
||||||
</TreeDirView>
|
</TreeDirView>
|
||||||
}
|
}
|
||||||
|
@@ -352,14 +352,13 @@ class Wiki extends Component {
|
|||||||
|
|
||||||
onDeleteNode = (node) => {
|
onDeleteNode = (node) => {
|
||||||
let filePath = node.path;
|
let filePath = node.path;
|
||||||
if (node.isMarkdown()) {
|
if (node.isDir()) {
|
||||||
editorUtilities.deleteFile(filePath);
|
|
||||||
} else if (node.isDir()) {
|
|
||||||
editorUtilities.deleteDir(filePath);
|
editorUtilities.deleteDir(filePath);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
editorUtilities.deleteFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let isCurrentFile = false;
|
let isCurrentFile = false;
|
||||||
if (node.isDir()) {
|
if (node.isDir()) {
|
||||||
isCurrentFile = this.isModifyContainsCurrentFile(node);
|
isCurrentFile = this.isModifyContainsCurrentFile(node);
|
||||||
@@ -533,9 +532,11 @@ class Wiki extends Component {
|
|||||||
onMainNavBarClick={this.onMainNavBarClick}
|
onMainNavBarClick={this.onMainNavBarClick}
|
||||||
onMainNodeClick={this.onMainNodeClick}
|
onMainNodeClick={this.onMainNodeClick}
|
||||||
switchViewMode={this.switchViewMode}
|
switchViewMode={this.switchViewMode}
|
||||||
|
onDeleteNode={this.onDeleteNode}
|
||||||
|
onRenameNode={this.onRenameNode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@ class EditorUtilities {
|
|||||||
isExpanded: item.type === 'dir' ? true : false,
|
isExpanded: item.type === 'dir' ? true : false,
|
||||||
parent_path: item.parent_dir,
|
parent_path: item.parent_dir,
|
||||||
last_update_time: item.last_update_time,
|
last_update_time: item.last_update_time,
|
||||||
|
permission: item.permission,
|
||||||
size: item.size
|
size: item.size
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -28,6 +29,7 @@ class EditorUtilities {
|
|||||||
isExpanded: item.type === 'dir' ? true : false,
|
isExpanded: item.type === 'dir' ? true : false,
|
||||||
parent_path: item.parent_dir,
|
parent_path: item.parent_dir,
|
||||||
last_update_time: item.mtime,
|
last_update_time: item.mtime,
|
||||||
|
permission: item.permission,
|
||||||
size: item.size
|
size: item.size
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -104,6 +106,19 @@ class EditorUtilities {
|
|||||||
publishDraft(id) {
|
publishDraft(id) {
|
||||||
return seafileAPI.publishDraft(id);
|
return seafileAPI.publishDraft(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zipDownload(parent_dir, dirents) {
|
||||||
|
return seafileAPI.zipDownload(repoID, parent_dir, dirents);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryZipProgress(zip_token) {
|
||||||
|
return seafileAPI.queryZipProgress(zip_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelZipTask(zip_token) {
|
||||||
|
return seafileAPI.cancelZipTask(zip_token)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorUtilities = new EditorUtilities();
|
const editorUtilities = new EditorUtilities();
|
||||||
|
@@ -1,32 +1,20 @@
|
|||||||
const siteRoot = window.app.config.siteRoot;
|
import {siteRoot, historyRepoID, fileServerRoot } from '../components/constants';
|
||||||
const repoID = window.fileHistory.pageOptions.repoID;
|
import { encodePath } from '../components/utils';
|
||||||
|
|
||||||
class URLDecorator {
|
class URLDecorator {
|
||||||
|
|
||||||
static getUrl(options) {
|
static getUrl(options) {
|
||||||
let url = '';
|
let url = '';
|
||||||
let params = '';
|
let params = '';
|
||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
case 'user_profile':
|
|
||||||
url = siteRoot + 'profile/' + options.username + '/';
|
|
||||||
break;
|
|
||||||
case 'common_lib':
|
|
||||||
url = siteRoot + '#common/lib/' + repoID + options.path;
|
|
||||||
break;
|
|
||||||
case 'view_lib_file':
|
|
||||||
url = siteRoot + 'lib/' + repoID + '/file' + options.filePath;
|
|
||||||
break;
|
|
||||||
case 'download_historic_file':
|
case 'download_historic_file':
|
||||||
params = 'p=' + options.filePath;
|
params = 'p=' + options.filePath;
|
||||||
url = siteRoot + 'repo/' + repoID + '/' + options.objID + '/download?' + params;
|
url = siteRoot + 'repo/' + historyRepoID + '/' + options.objID + '/download?' + params;
|
||||||
break;
|
break;
|
||||||
case 'view_historic_file':
|
case 'download_dir_zip_url':
|
||||||
params = 'obj_id=' + options.objID + '&commit_id=' + options.commitID + '&p=' + options.filePath;
|
url = fileServerRoot + 'zip/' + options.token;
|
||||||
url = siteRoot + 'repo/' + options.repoID + 'history/files/?' + params;
|
|
||||||
break;
|
break;
|
||||||
case 'diff_historic_file':
|
case 'download_file_url':
|
||||||
params = 'commit_id=' + options.commitID + '&p=' + options.filePath;
|
url = siteRoot + 'lib/' + options.repoID + "/file" + encodePath(options.filePath) + "?dl=1";
|
||||||
url = siteRoot + 'repo/text_diff/' + repoID + '/?' + params;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
url = '';
|
url = '';
|
||||||
|
@@ -314,14 +314,13 @@ class Wiki extends Component {
|
|||||||
|
|
||||||
onDeleteNode = (node) => {
|
onDeleteNode = (node) => {
|
||||||
let filePath = node.path;
|
let filePath = node.path;
|
||||||
if (node.isMarkdown()) {
|
if (node.isDir()) {
|
||||||
editorUtilities.deleteFile(filePath);
|
|
||||||
} else if (node.isDir()) {
|
|
||||||
editorUtilities.deleteDir(filePath);
|
editorUtilities.deleteDir(filePath);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
editorUtilities.deleteFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let isCurrentFile = false;
|
let isCurrentFile = false;
|
||||||
if (node.isDir()) {
|
if (node.isDir()) {
|
||||||
isCurrentFile = this.isModifyContainsCurrentFile(node);
|
isCurrentFile = this.isModifyContainsCurrentFile(node);
|
||||||
@@ -494,6 +493,8 @@ class Wiki extends Component {
|
|||||||
onSearchedClick={this.onSearchedClick}
|
onSearchedClick={this.onSearchedClick}
|
||||||
onMainNavBarClick={this.onMainNavBarClick}
|
onMainNavBarClick={this.onMainNavBarClick}
|
||||||
onMainNodeClick={this.onMainNodeClick}
|
onMainNodeClick={this.onMainNodeClick}
|
||||||
|
onDeleteNode={this.onDeleteNode}
|
||||||
|
onRenameNode={this.onRenameNode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -65,6 +65,9 @@
|
|||||||
.sf2-icon-edit:before { content:"\e018"; }
|
.sf2-icon-edit:before { content:"\e018"; }
|
||||||
.sf2-icon-history:before { content:"\e014"; }
|
.sf2-icon-history:before { content:"\e014"; }
|
||||||
.sf2-icon-trash:before { content:"\e016"; }
|
.sf2-icon-trash:before { content:"\e016"; }
|
||||||
|
.sf2-icon-download:before { content:"\e008"; }
|
||||||
|
.sf2-icon-delete:before { content:"\e006"; }
|
||||||
|
.sf2-icon-caret-down:before { content:"\e01a"; }
|
||||||
|
|
||||||
/* common class and element style*/
|
/* common class and element style*/
|
||||||
a { color:#eb8205; }
|
a { color:#eb8205; }
|
||||||
|
@@ -30,7 +30,8 @@
|
|||||||
siteTitle: '{{ site_title }}',
|
siteTitle: '{{ site_title }}',
|
||||||
siteRoot: '{{ SITE_ROOT }}',
|
siteRoot: '{{ SITE_ROOT }}',
|
||||||
isPro: '{{ is_pro }}',
|
isPro: '{{ is_pro }}',
|
||||||
lang: '{{ LANGUAGE_CODE }}'
|
lang: '{{ LANGUAGE_CODE }}',
|
||||||
|
fileServerRoot: '{{ FILE_SERVER_ROOT }}'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user