1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 07:27:04 +00:00

View repo content in list mode (#2579)

This commit is contained in:
杨顺强
2018-11-29 17:55:14 +08:00
committed by Daniel Pan
parent 4f38e393d5
commit c00d4f30bd
23 changed files with 773 additions and 107 deletions

View File

@@ -124,7 +124,7 @@ class CopyDirent extends React.Component {
return;
}
this.props.onItemCopy(repo, direntPath, selectedPath);
this.props.onItemCopy(repo, this.props.dirent, selectedPath);
this.toggle();
}

View File

@@ -124,7 +124,7 @@ class MoveDirent extends React.Component {
return;
}
this.props.onItemMove(repo, direntPath, selectedPath);
this.props.onItemMove(repo, this.props.dirent, selectedPath);
this.toggle();
}

View File

@@ -0,0 +1,208 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import cookie from 'react-cookies';
import { gettext, siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import CommonToolbar from '../toolbar/common-toolbar';
import ViewModeToolbar from '../toolbar/view-mode-toolbar';
import DirOperationToolBar from '../toolbar/dir-operation-toolbar';
import MutipleDirOperationToolbar from '../toolbar/mutilple-dir-operation-toolbar';
import CurDirPath from '../cur-dir-path';
import DirentListView from '../dirent-list-view/dirent-list-view';
import DirentDetail from '../dirent-detail/dirent-details';
import FileUploader from '../file-uploader/file-uploader';
const propTypes = {
currentRepo: PropTypes.object,
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
repoName: PropTypes.string.isRequired,
pathExist: PropTypes.bool.isRequired,
permission: PropTypes.bool.isRequired,
isDirentListLoading: PropTypes.bool.isRequired,
isDirentSelected: PropTypes.bool.isRequired,
isAllDirentSelected: PropTypes.bool.isRequired,
direntList: PropTypes.array.isRequired,
selectedDirentList: PropTypes.array.isRequired,
onItemClick: PropTypes.func.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired,
onItemRename: PropTypes.func.isRequired,
onItemDelete: PropTypes.func.isRequired,
onItemSelected: PropTypes.func.isRequired,
onItemsCopy: PropTypes.func.isRequired,
onItemsMove: PropTypes.func.isRequired,
onItemsDelete: PropTypes.func.isRequired,
onAllItemSelected: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
onMenuClick: PropTypes.func.isRequired,
onPathClick: PropTypes.func.isRequired,
updateDirent: PropTypes.func.isRequired,
onSearchedClick: PropTypes.func.isRequired,
onFileUploadSuccess: PropTypes.func.isRequired,
};
class DirPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
currentDirent: null,
currentMode: 'list',
isDirentDetailShow: false,
isRepoOwner: true,
};
}
componentDidMount() {
let currentRepo = this.props.currentRepo;
if (currentRepo) {
seafileAPI.getAccountInfo().then(res => {
let user_email = res.data.email;
let isRepoOwner = currentRepo.owner_email === user_email;
this.setState({isRepoOwner: isRepoOwner});
});
}
}
onItemDetails = (dirent) => {
this.setState({
currentDirent: dirent,
isDirentDetailShow: true,
});
}
onItemDetailsClose = () => {
this.setState({isDirentDetailShow: false});
}
onUploadFile = (e) => {
e.nativeEvent.stopImmediatePropagation();
this.uploader.onFileUpload();
}
onUploadFolder = (e) => {
e.nativeEvent.stopImmediatePropagation();
this.uploader.onFolderUpload();
}
switchViewMode = (mode) => {
let { path, repoID } = this.props;
if (mode === this.state.currentMode) {
return;
}
if (mode === 'wiki') {
var url = siteRoot + 'wiki/lib/' + repoID + path;
window.location = url;
}
cookie.save('view_mode', mode, { path: '/' });
this.setState({currentMode: mode});
}
render() {
const ErrMessage = (<div className="message empty-tip err-message"><h2>{gettext('Folder does not exist.')}</h2></div>);
return (
<div className="main-panel wiki-main-panel o-hidden">
<div className="main-panel-north">
<div className="cur-view-toolbar border-left-show">
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.props.onMenuClick}></span>
<div className="dir-operation">
{this.props.isDirentSelected ?
<MutipleDirOperationToolbar
path={this.props.path}
repoID={this.props.repoID}
selectedDirentList={this.props.selectedDirentList}
onItemsMove={this.props.onItemsMove}
onItemsCopy={this.props.onItemsCopy}
onItemsDelete={this.props.onItemsDelete}
/> :
<DirOperationToolBar
path={this.props.path}
repoID={this.props.repoID}
onAddFile={this.props.onAddFile}
onAddFolder={this.props.onAddFolder}
onUploadFile={this.onUploadFile}
onUploadFolder={this.onUploadFolder}
/>
}
</div>
<ViewModeToolbar
currentMode={this.state.currentMode}
switchViewMode={this.switchViewMode}
/>
</div>
<CommonToolbar
repoID={this.props.repoID}
onSearchedClick={this.props.onSearchedClick}
searchPlaceholder={'Search files in this library'}
/>
</div>
<div className="main-panel-center flex-direction-row">
<div className="cur-view-container">
<div className="cur-view-path">
<CurDirPath
repoID={this.props.repoID}
repoName={this.props.repoName}
currentPath={this.props.path}
permission={this.props.permission}
onPathClick={this.props.onPathClick}
/>
</div>
<div className="cur-view-content">
{!this.props.pathExist ?
ErrMessage :
<Fragment>
<DirentListView
path={this.props.path}
repoID={this.props.repoID}
direntList={this.props.direntList}
currentRepo={this.props.currentRepo}
isDirentListLoading={this.props.isDirentListLoading}
isAllItemSelected={this.props.isAllDirentSelected}
isRepoOwner={this.state.isRepoOwner}
onItemDetails={this.onItemDetails}
onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy}
onItemClick={this.props.onItemClick}
onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onItemRename}
onItemSelected={this.props.onItemSelected}
onAllItemSelected={this.props.onAllItemSelected}
updateDirent={this.props.updateDirent}
/>
<FileUploader
dragAndDrop={true}
ref={uploader => this.uploader = uploader}
path={this.props.path}
repoID={this.props.repoID}
direntList={this.props.direntList}
onFileUploadSuccess={this.props.onFileUploadSuccess}
/>
</Fragment>
}
</div>
</div>
{this.state.isDirentDetailShow && (
<div className="cur-view-detail">
<DirentDetail
path={this.props.path}
repoID={this.props.repoID}
dirent={this.state.currentDirent}
onFileTagChanged={this.props.onFileTagChanged}
onItemDetailsClose={this.onItemDetailsClose}
/>
</div>
)}
</div>
</div>
);
}
}
DirPanel.propTypes = propTypes;
export default DirPanel;

View File

@@ -0,0 +1,465 @@
import React from 'react';
import PropTypes from 'prop-types';
import { siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import { gettext } from '../../utils/constants';
import Toast from '../toast';
import DirPanel from './dir-panel';
import Dirent from '../../models/dirent';
import FileTag from '../../models/file-tag';
import Repo from '../../models/repo';
const propTypes = {
onMenuClick: PropTypes.func.isRequired,
};
class DirView extends React.Component {
constructor(props) {
super(props);
this.state = {
path: '/',
pathExit: true,
repoName: '',
repoID: '',
permission: true,
isDirentSelected: false,
isAllDirentSelected: false,
isDirentListLoading: true,
currentRepo: null,
direntList: [],
selectedDirentList: [],
};
window.onpopstate = this.onpopstate;
}
onpopstate = (event) => {
if (event.state && event.state.path) {
this.updateDirentList(event.state.path);
this.setState({path: event.state.path});
}
}
componentDidMount() {
let repoID = this.props.repoID;
let index = location.href.indexOf(repoID) + repoID.length;
let path = decodeURIComponent(location.href.slice(index));
seafileAPI.getRepoInfo(repoID).then(res => {
let repo = new Repo(res.data);
this.setState({
repoID: repo.repo_id,
repoName: repo.repo_name,
permission: repo.permission === 'rw',
currentRepo: repo,
});
this.updateDirentList(path);
this.setState({path: path});
});
}
updateDirentList = (filePath) => {
let repoID = this.state.repoID;
this.setState({isDirentListLoading: true});
seafileAPI.listDir(repoID, filePath).then(res => {
let direntList = res.data.map(item => {
return new Dirent(item);
});
this.setState({
isDirentListLoading: false,
direntList: direntList,
});
}).catch(() => {
this.setState({pathExist: false});
});
}
onItemClick = (dirent) => {
this.resetSelected();
let direntPath = Utils.joinPath(this.state.path, dirent.name);
if (dirent.isDir()) {
this.updateDirentList(direntPath);
this.setState({path: direntPath});
let fileUrl = siteRoot + 'librarys/' + this.state.repoID + direntPath;
window.history.pushState({url: fileUrl, path: direntPath}, direntPath, fileUrl);
} else {
const w=window.open('about:blank');
const url = siteRoot + 'lib/' + this.state.repoID + '/file' + direntPath;
w.location.href = url;
}
}
onAddFolder = (dirPath) => {
let repoID = this.state.repoID;
seafileAPI.createDir(repoID, dirPath).then(() => {
let name = Utils.getFileName(dirPath);
let dirent = this.createDirent(name, 'dir');
let direntList = this.addItem(dirent, 'dir');
this.setState({direntList: direntList});
});
}
onAddFile = (filePath, isDraft) => {
let repoID = this.state.repoID;
seafileAPI.createDir(repoID, filePath).then(() => {
let name = Utils.getFileName(filePath);
let dirent = this.createDirent(name, 'file');
let direntList = this.addItem(dirent, 'file');
this.setState({direntList: direntList});
});
}
onItemDelete = (dirent) => {
let repoID = this.state.repoID;
let direntPath = Utils.joinPath(this.state.path, dirent.name);
if (dirent.isDir()) {
seafileAPI.deleteDir(repoID, direntPath).then(() => {
let direntList = this.deleteItem(dirent);
this.setState({direntList: direntList});
}).catch(() => {
// todo
})
} else {
seafileAPI.deleteFile(repoID, direntPath).then(() => {
let direntList = this.deleteItem(dirent);
this.setState({direntList: direntList});
}).catch(() => {
// todo
})
}
}
onItemRename = (dirent, newName) => {
let repoID = this.state.repoID;
let direntPath = Utils.joinPath(this.state.path, dirent.name);
if (dirent.isDir()) {
seafileAPI.renameDir(repoID, direntPath, newName).then(() => {
let direntList = this.renameItem(dirent, newName);
this.setState({direntList: direntList});
}).catch(() => {
//todo
});
} else {
seafileAPI.renameFile(repoID, direntPath, newName).then(() => {
let direntList = this.renameItem(dirent, newName);
this.setState({direntList: direntList});
}).catch(() => {
//todo
});
}
}
onItemMove = (destRepo, dirent, moveToDirentPath) => {
let dirName = dirent.name;
let repoID = this.state.repoID;
seafileAPI.moveDir(repoID, destRepo.repo_id, moveToDirentPath, this.state.path, dirName).then(() => {
let direntList = this.deleteItem(dirent);
this.setState(direntList);
let message = gettext('Successfully moved %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to move %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
});
}
onItemCopy = (destRepo, dirent, copyToDirentPath) => {
let dirName = dirent.name;
let repoID = this.state.repoID;
seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(() => {
let message = gettext('Successfully copied %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to copy %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
});
}
onItemSelected = (dirent) => {
let direntList = this.state.direntList.map(item => {
if (item.name === dirent.name) {
item.isSelected = !item.isSelected;
}
return item;
});
let selectedDirentList = direntList.filter(item => {
return item.isSelected;
});
if (selectedDirentList.length) {
this.setState({isDirentSelected: true});
if (selectedDirentList.length === direntList.length) {
this.setState({
isAllDirentSelected: true,
direntList: direntList,
selectedDirentList: selectedDirentList,
});
} else {
this.setState({
isAllDirentSelected: false,
direntList: direntList,
selectedDirentList: selectedDirentList
});
}
} else {
this.setState({
isDirentSelected: false,
isAllDirentSelected: false,
direntList: direntList,
selectedDirentList: []
});
}
}
onItemsMove = (destRepo, destDirentPath) => {
let dirNames = this.getSelectedDirentNames();
let repoID = this.state.repoID;
seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => {
let direntList = this.deleteItems(dirNames);
this.setState({direntList: direntList});
let message = gettext('Successfully moved %(name)s.');
message = message.replace('%(name)s', dirNames);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to move %(name)s');
message = message.replace('%(name)s', dirNames);
Toast.error(message);
});
}
onItemsCopy = (destRepo, destDirentPath) => {
let dirNames = this.getSelectedDirentNames();
let repoID = this.state.repoID;
seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => {
let message = gettext('Successfully copied %(name)s.');
message = message.replace('%(name)s', dirNames);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to copy %(name)s');
message = message.replace('%(name)s', dirNames);
Toast.error(message);
});
}
onItemsDelete = () => {
let dirNames = this.getSelectedDirentNames();
let repoID = this.state.repoID;
seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => {
let direntList = this.deleteItems(dirNames);
this.setState({direntList: direntList});
});
}
onAllItemSelected = () => {
if (this.state.isAllDirentSelected) {
let direntList = this.state.direntList.map(item => {
item.isSelected = false;
return item;
});
this.setState({
isDirentSelected: false,
isAllDirentSelected: false,
direntList: direntList,
selectedDirentList: [],
});
} else {
let direntList = this.state.direntList.map(item => {
item.isSelected = true;
return item;
});
this.setState({
isDirentSelected: true,
isAllDirentSelected: true,
direntList: direntList,
selectedDirentList: direntList,
});
}
}
onFileTagChanged = (dirent, direntPath) => {
let repoID = this.state.repoID;
seafileAPI.listFileTags(repoID, direntPath).then(res => {
let fileTags = res.data.file_tags.map(item => {
return new FileTag(item);
});
this.updateDirent(dirent, 'file_tags', fileTags);
});
}
onMenuClick = () => {
this.props.onMenuClick();
}
onPathClick = (path) => {
this.updateDirentList(path);
this.setState({path: path});
let fileUrl = siteRoot + 'librarys/' + this.state.repoID + path;
window.history.pushState({url: fileUrl, path: path}, path, fileUrl);
}
updateDirent = (dirent, paramKey, paramValue) => {
let newDirentList = this.state.direntList.map(item => {
if (item.name === dirent.name) {
item[paramKey] = paramValue;
}
return item;
});
this.setState({direntList: newDirentList});
}
onFileUploadSuccess = () => {
// todo update upload file to direntList
}
onSearchedClick = () => {
// todo
}
resetSelected = () => {
this.setState({isDirentSelected: false, isAllDirentSelected: false});
}
addItem = (dirent, type) => {
let direntList = this.state.direntList.map(item => {return item}); //clone
if (type === 'dir') {
direntList.unshift(dirent);
return direntList;
}
direntList.push(dirent);
return direntList;
}
deleteItem = (dirent) => {
return this.state.direntList.filter(item => {
return item.name !== dirent.name;
});
}
renameItem = (dirent, newName) => {
return this.state.direntList.map(item => {
if (item.name === dirent.name) {
item.name = newName;
}
return item;
});
}
deleteItems = (dirNames) => {
let direntList = this.state.direntList.map(item => {return item}); //clone
while (dirNames.length) {
for (let i = 0; i < direntList.length; i++) {
if (direntList[i].name === dirNames[0]) {
direntList.splice(i, 1);
break;
}
}
dirNames.shift();
}
return direntList;
}
createDirent(name, type) {
let data = new Date().getTime()/1000;
let dirent = null;
if (type === 'dir') {
dirent = new Dirent({
id: '000000000000000000',
name: name,
type: type,
mtime: data,
permission: 'rw',
});
} else {
dirent = new Dirent({
id: '000000000000000000',
name: name,
type: type,
mtime: data,
permission: 'rw',
size: 0,
starred: false,
is_locked: false,
lock_time: '',
lock_owner: null,
locked_by_me: false,
modifier_name: '',
modifier_email: '',
modifier_contact_email: '',
file_tags: []
});
}
return dirent;
}
getSelectedDirentNames = () => {
let names = [];
this.state.selectedDirentList.forEach(selectedDirent => {
names.push(selectedDirent.name);
});
return names;
}
isMarkdownFile(filePath) {
let index = filePath.lastIndexOf('.');
if (index === -1) {
return false;
} else {
let type = filePath.substring(index).toLowerCase();
if (type === '.md' || type === '.markdown') {
return true;
} else {
return false;
}
}
}
render() {
return (
<DirPanel
currentRepo={this.state.currentRepo}
path={this.state.path}
pathExist={this.state.pathExit}
repoID={this.state.repoID}
repoName={this.state.repoName}
permission={this.state.permission}
isDirentListLoading={this.state.isDirentListLoading}
isDirentSelected={this.state.isDirentSelected}
isAllDirentSelected={this.state.isAllDirentSelected}
direntList={this.state.direntList}
selectedDirentList={this.state.direntList}
onItemClick={this.onItemClick}
onAddFile={this.onAddFile}
onAddFolder={this.onAddFolder}
onItemMove={this.onItemMove}
onItemCopy={this.onItemCopy}
onItemRename={this.onItemRename}
onItemDelete={this.onItemDelete}
onItemSelected={this.onItemSelected}
onItemsMove={this.onItemsMove}
onItemsCopy={this.onItemsCopy}
onItemsDelete={this.onItemsDelete}
onAllItemSelected={this.onAllItemSelected}
onFileTagChanged={this.onFileTagChanged}
onMenuClick={this.onMenuClick}
onPathClick={this.onPathClick}
updateDirent={this.updateDirent}
switchViewMode={this.switchViewMode}
onSearchedClick={this.onSearchedClick}
onFileUploadSuccess={this.onFileUploadSuccess}
/>
);
}
}
DirView.propTypes = propTypes;
export default DirView;

View File

@@ -11,7 +11,7 @@ const propTypes = {
dirent: PropTypes.object.isRequired,
direntType: PropTypes.string.isRequired,
direntDetail: PropTypes.object.isRequired,
direntPath: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
fileTagList: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
};
@@ -26,7 +26,8 @@ class DetailListView extends React.Component {
}
getDirentPostion = () => {
let { repo, direntPath } = this.props;
let { repo } = this.props;
let direntPath = this.getDirentPath();
let position = repo.repo_name;
if (direntPath !== '/') {
let index = direntPath.lastIndexOf('/');
@@ -43,12 +44,19 @@ class DetailListView extends React.Component {
}
onFileTagChanged = () => {
this.props.onFileTagChanged(this.props.dirent, this.props.direntPath);
let direntPath = this.getDirentPath();
this.props.onFileTagChanged(this.props.dirent, direntPath);
}
getDirentPath = () => {
let { dirent, path } = this.props;
return Utils.joinPath(path, dirent.name);
}
render() {
let { direntType, direntDetail, fileTagList } = this.props;
let position = this.getDirentPostion();
let direntPath = this.getDirentPath();
if (direntType === 'dir') {
return (
<table>
@@ -93,7 +101,7 @@ class DetailListView extends React.Component {
<EditFileTagDialog
repoID={this.props.repoID}
fileTagList={fileTagList}
filePath={this.props.direntPath}
filePath={direntPath}
toggleCancel={this.onEditFileTagToggle}
onFileTagChanged={this.onFileTagChanged}
/>

View File

@@ -1,5 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import { siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import DetailListView from './detail-list-view';
import Repo from '../../models/repo';
@@ -8,9 +10,8 @@ import '../../css/dirent-detail.css';
const propTypes = {
repoID: PropTypes.string.isRequired,
serviceUrl: PropTypes.string.isRequired,
dirent: PropTypes.object.isRequired,
direntPath: PropTypes.string.isRequired,
path: PropTypes.string.isRequired,
onItemDetailsClose: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
};
@@ -28,7 +29,8 @@ class DirentDetail extends React.Component {
}
componentDidMount() {
let { dirent, direntPath, repoID } = this.props;
let { dirent, path, repoID } = this.props;
let direntPath = Utils.joinPath(path, dirent.name);
seafileAPI.getRepoInfo(repoID).then(res => {
let repo = new Repo(res.data);
this.setState({repo: repo});
@@ -37,7 +39,9 @@ class DirentDetail extends React.Component {
}
componentWillReceiveProps(nextProps) {
this.updateDetailView(nextProps.dirent, nextProps.direntPath);
let { dirent, path } = nextProps;
let direntPath = Utils.joinPath(path, dirent.name);
this.updateDetailView(dirent, direntPath);
}
updateDetailView = (dirent, direntPath) => {
@@ -68,27 +72,27 @@ class DirentDetail extends React.Component {
}
render() {
let { dirent, serviceUrl } = this.props;
let { dirent } = this.props;
return (
<div className="detail-container">
<div className="detail-header">
<div className="detail-control sf2-icon-x1" onClick={this.props.onItemDetailsClose}></div>
<div className="detail-title dirent-title">
<img src={dirent.type === 'dir' ? serviceUrl + '/media/img/folder-192.png' : serviceUrl + '/media/img/file/192/txt.png'} alt="icon"></img>
<img src={dirent.type === 'dir' ? siteRoot + 'media/img/folder-192.png' : siteRoot + 'media/img/file/192/txt.png'} alt="icon"></img>
<span className="name">{dirent.name}</span>
</div>
</div>
<div className="detail-body dirent-info">
<div className="img">
<img src={dirent.type === 'dir' ? serviceUrl + '/media/img/folder-192.png' : serviceUrl + '/media/img/file/192/txt.png'} alt="icon"></img>
<img src={dirent.type === 'dir' ? siteRoot + 'media/img/folder-192.png' : siteRoot + 'media/img/file/192/txt.png'} alt="icon"></img>
</div>
{this.state.direntDetail &&
<div className="dirent-table-container">
<DetailListView
repo={this.state.repo}
path={this.props.path}
repoID={this.props.repoID}
dirent={this.props.dirent}
direntPath={this.props.direntPath}
direntType={this.state.direntType}
direntDetail={this.state.direntDetail}
fileTagList={this.state.fileTagList}

View File

@@ -1,6 +1,6 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { gettext, siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import URLDecorator from '../../utils/url-decorator';
import Toast from '../toast';
@@ -14,7 +14,6 @@ import CopyDirentDialog from '../dialog/copy-dirent-dialog';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
serviceUrl: PropTypes.string.isRequired,
isItemFreezed: PropTypes.bool.isRequired,
dirent: PropTypes.object.isRequired,
onItemClick: PropTypes.func.isRequired,
@@ -139,8 +138,7 @@ class DirentListItem extends React.Component {
}
onItemClick = () => {
let direntPath = this.getDirentPath(this.props.dirent);
this.props.onItemClick(direntPath);
this.props.onItemClick(this.props.dirent);
}
onItemDelete = (e) => {
@@ -244,8 +242,7 @@ class DirentListItem extends React.Component {
}
onDetailsItem = () => {
let direntPath = this.getDirentPath(this.props.dirent);
this.props.onItemDetails(this.props.dirent, direntPath);
this.props.onItemDetails(this.props.dirent);
this.onItemMenuHide();
}
@@ -368,7 +365,7 @@ class DirentListItem extends React.Component {
}
render() {
let { dirent, serviceUrl } = this.props;
let { dirent } = this.props;
return (
<Fragment>
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseOver={this.onMouseOver} onMouseLeave={this.onMouseLeave}>
@@ -381,8 +378,8 @@ class DirentListItem extends React.Component {
</td>
<td className="icon">
<div className="dir-icon">
<img src={dirent.type === 'dir' ? serviceUrl + '/media/img/folder-192.png' : serviceUrl + '/media/img/file/192/txt.png'} alt={gettext('file icon')}></img>
{dirent.is_locked && <img className="locked" src={serviceUrl + '/media/img/file-locked-32.png'} alt={gettext('locked')}></img>}
<img src={dirent.type === 'dir' ? siteRoot + 'media/img/folder-192.png' : siteRoot + 'media/img/file/192/txt.png'} alt={gettext('file icon')}></img>
{dirent.is_locked && <img className="locked" src={siteRoot + 'media/img/file-locked-32.png'} alt={gettext('locked')}></img>}
</div>
</td>
<td className="name a-simulate">

View File

@@ -7,7 +7,6 @@ import DirentListItem from './dirent-list-item';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
serviceUrl: PropTypes.string.isRequired,
isRepoOwner: PropTypes.bool,
currentRepo: PropTypes.object,
isAllItemSelected: PropTypes.bool.isRequired,
@@ -45,8 +44,8 @@ class DirentListView extends React.Component {
this.onFreezedItem();
}
onItemDetails = (dirent, direntPath) => {
this.props.onItemDetails(dirent, direntPath);
onItemDetails = (dirent) => {
this.props.onItemDetails(dirent);
}
render() {
@@ -81,7 +80,6 @@ class DirentListView extends React.Component {
dirent={dirent}
path={this.props.path}
repoID={this.props.repoID}
serviceUrl={this.props.serviceUrl}
currentRepo={this.props.currentRepo}
isRepoOwner={this.props.isRepoOwner}
onItemClick={this.props.onItemClick}

View File

@@ -26,7 +26,7 @@ const propTypes = {
fileTypeErrorCallback: PropTypes.func,
dragAndDrop: PropTypes.bool.isRequired,
path: PropTypes.string.isRequired,
onFileSuccess: PropTypes.func.isRequired,
onFileUploadSuccess: PropTypes.func.isRequired,
};
class FileUploader extends React.Component {
@@ -97,7 +97,7 @@ class FileUploader extends React.Component {
this.resumable.on('fileAdded', this.onFileAdded);
this.resumable.on('filesAddedComplete', this.filesAddedComplete);
this.resumable.on('fileProgress', this.onFileProgress);
this.resumable.on('fileSuccess', this.onFileSuccess);
this.resumable.on('fileSuccess', this.onFileUploadSuccess);
this.resumable.on('progress', this.onProgress);
this.resumable.on('complete', this.onComplete);
this.resumable.on('pause', this.onPause);
@@ -205,7 +205,7 @@ class FileUploader extends React.Component {
this.setState({uploadFileList: uploadFileList});
}
onFileSuccess = (file) => {
onFileUploadSuccess = (file) => {
// todos, update uploadList or updateList;
}

View File

@@ -179,7 +179,7 @@ class Search extends Component {
for (let key in queryData) {
params += key + '=' + queryData[key] + '&';
}
window.location = siteRoot + '/search/?' + params.slice(0, params.length - 1);
window.location = siteRoot + 'search/?' + params.slice(0, params.length - 1);
}
renderSearchResult() {

View File

@@ -1,17 +1,16 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import { gettext } from '../../utils/constants';
import { gettext, siteRoot } from '../../utils/constants';
import ModalPortal from '../modal-portal';
import CreateFolder from '../../components/dialog/create-folder-dialog';
import CreateFile from '../../components/dialog/create-file-dialog';
const propTypes = {
isViewFile: PropTypes.bool,
isViewFile: PropTypes.bool, // just for view file,
permission: PropTypes.string, //just for view file and premission is file permission
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
serviceUrl: PropTypes.string.isRequired,
permission: PropTypes.string.isRequired,
onAddFile: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired,
onUploadFile: PropTypes.func.isRequired,
@@ -58,8 +57,8 @@ class DirOperationToolbar extends React.Component {
onEditClick = (e) => {
e.preventDefault();
let { path, repoID, serviceUrl } = this.props;
window.location.href= serviceUrl + '/lib/' + repoID + '/file' + path + '?mode=edit';
let { path, repoID } = this.props;
window.location.href= siteRoot + 'lib/' + repoID + '/file' + path + '?mode=edit';
}
onUploadClick = (e) => {

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { serviceUrl } from '../../utils/constants';
import { siteRoot } from '../../utils/constants';
const propTypes = {
node: PropTypes.object.isRequired,
@@ -35,7 +35,7 @@ class TreeDirList extends React.Component {
return (
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td className="icon">
<img src={node.type === 'dir' ? serviceUrl + '/media/img/folder-192.png' : serviceUrl + '/media/img/file/192/txt.png'} alt='icon'></img>
<img src={node.type === 'dir' ? siteRoot + 'media/img/folder-192.png' : siteRoot + 'media/img/file/192/txt.png'} alt='icon'></img>
</td>
<td className="name a-simulate" onClick={this.onMainNodeClick}>{node.name}</td>
<td>{node.size}</td>

View File

@@ -65,6 +65,9 @@ class Node {
}
isMarkdown() {
if (this.isDir()) {
return false;
}
let index = this.name.lastIndexOf('.');
if (index == -1) {
return false;

View File

@@ -121,6 +121,7 @@
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
}
.header {

View File

@@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot } from '../../utils/constants';
@@ -17,7 +17,7 @@ class FileActivitiesContent extends Component {
return <p className="error text-center">{error_msg}</p>;
} else {
return (
<div className="activity-table-container">
<Fragment>
<table className="table table-hover table-vcenter">
<thead>
<tr>
@@ -32,7 +32,7 @@ class FileActivitiesContent extends Component {
</table>
{has_more ? <span className="loading-icon loading-tip"></span> : ''}
{error_msg ? <p className="error text-center">{error_msg}</p> : ''}
</div>
</Fragment>
);
}
}

View File

@@ -1,7 +1,7 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import cookie from 'react-cookies';
import { gettext, repoID, serviceUrl, slug, permission } from '../../utils/constants';
import { gettext, repoID, siteRoot, slug, permission } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import Repo from '../../models/repo';
@@ -81,7 +81,7 @@ class MainPanel extends Component {
switchViewMode = (mode) => {
cookie.save('view_mode', mode, { path: '/' });
let dirPath = this.props.isViewFile ? Utils.getDirName(this.props.path) : this.props.path;
window.location.href = serviceUrl + '/#common/lib/' + repoID + dirPath;
window.location.href = siteRoot + '#common/lib/' + repoID + dirPath;
}
onSideNavMenuClick = () => {
@@ -92,10 +92,9 @@ class MainPanel extends Component {
this.props.onMainNavBarClick(path);
}
onItemDetails = (dirent, direntPath) => {
onItemDetails = (dirent) => {
this.setState({
currentDirent: dirent,
direntPath: direntPath,
isDirentDetailShow: true,
});
}
@@ -118,14 +117,16 @@ class MainPanel extends Component {
this.uploader.onFolderUpload();
}
onFileSuccess = (file) => {
onFileUploadSuccess = (file) => {
// todo
}
render() {
const ErrMessage = (<div className="message empty-tip err-message"><h2>{gettext('Folder does not exist.')}</h2></div>);
return (
<div className="main-panel wiki-main-panel o-hidden">
<div className="main-panel-top panel-top">
<div className="main-panel-north">
<div className="cur-view-toolbar border-left-show">
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.onSideNavMenuClick}></span>
<div className="dir-operation">
@@ -141,7 +142,6 @@ class MainPanel extends Component {
<DirOperationToolBar
path={this.props.path}
repoID={repoID}
serviceUrl={serviceUrl}
permission={this.props.permission}
isViewFile={this.props.isViewFile}
onAddFile={this.props.onAddFile}
@@ -167,8 +167,8 @@ class MainPanel extends Component {
/>
</div>
<div className="cur-view-content">
{ !this.props.pathExist ?
<div className="message empty-tip err-message"><h2>{gettext('Folder does not exist.')}</h2></div> :
{!this.props.pathExist ?
ErrMessage :
<Fragment>
{ this.props.isViewFile ?
<MarkdownViewer
@@ -182,7 +182,6 @@ class MainPanel extends Component {
<DirentListView
path={this.props.path}
repoID={repoID}
serviceUrl={serviceUrl}
direntList={this.props.direntList}
onItemClick={this.props.onItemClick}
onItemDelete={this.props.onItemDelete}
@@ -203,7 +202,7 @@ class MainPanel extends Component {
dragAndDrop={true}
path={this.props.path}
repoID={repoID}
onFileSuccess={this.onFileSuccess}
onFileUploadSuccess={this.onFileUploadSuccess}
direntList={this.props.direntList}
/>
</Fragment>
@@ -212,18 +211,18 @@ class MainPanel extends Component {
}
</div>
</div>
{ this.state.isDirentDetailShow &&
{this.state.isDirentDetailShow && (
<div className="cur-view-detail">
<DirentDetail
repoID={repoID}
serviceUrl={serviceUrl}
path={this.props.path}
dirent={this.state.currentDirent}
direntPath={this.state.direntPath}
onItemDetailsClose={this.onItemDetailsClose}
onFileTagChanged={this.onFileTagChanged}
/>
</div>
}
)}
</div>
</div>
);

View File

@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { gettext, repoID, serviceUrl, slug, siteRoot } from '../../utils/constants';
import { gettext, repoID, slug, siteRoot } from '../../utils/constants';
import CommonToolbar from '../../components/toolbar/common-toolbar';
import MarkdownViewer from '../../components/markdown-viewer';
import TreeDirView from '../../components/tree-dir-view/tree-dir-view';
@@ -39,7 +39,7 @@ class MainPanel extends Component {
onEditClick = (e) => {
// const w=window.open('about:blank')
e.preventDefault();
window.location.href= serviceUrl + '/lib/' + repoID + '/file' + this.props.filePath + '?mode=edit';
window.location.href= siteRoot + 'lib/' + repoID + '/file' + this.props.filePath + '?mode=edit';
}
onMainNavBarClick = (e) => {

View File

@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import moment from 'moment';
import { gettext, repoID, serviceUrl, initialPath, isDir } from './utils/constants';
import { gettext, repoID, siteRoot, initialPath, isDir } from './utils/constants';
import { seafileAPI } from './utils/seafile-api';
import { Utils } from './utils/utils';
import SidePanel from './pages/repo-wiki-mode/side-panel';
@@ -133,14 +133,13 @@ class Wiki extends Component {
});
}
onMoveItem = (repo, direntPath, moveToDirentPath) => {
onMoveItem = (destRepo, dirent, moveToDirentPath) => {
//just for view list state
let index = direntPath.lastIndexOf('/');
let dirPath = direntPath.slice(0, index + 1);
let dirName = direntPath.slice(index + 1);
seafileAPI.moveDir(repoID, repo.repo_id,moveToDirentPath, dirPath, dirName).then(() => {
let dirName = dirent.name
let direntPath = Utils.joinPath(this.state.path, dirName);
seafileAPI.moveDir(repoID, destRepo.repo_id,moveToDirentPath, this.state.path, dirName).then(() => {
this.moveTreeNode(direntPath, moveToDirentPath, repo);
this.moveTreeNode(direntPath, moveToDirentPath, destRepo);
this.moveDirent(direntPath);
let message = gettext('Successfully moved %(name)s.');
@@ -153,13 +152,12 @@ class Wiki extends Component {
});
}
onCopyItem = (repo, direntPath, copyToDirentPath) => {
onCopyItem = (destRepo, dirent, copyToDirentPath) => {
//just for view list state
let index = direntPath.lastIndexOf('/');
let dirPath = direntPath.slice(0, index + 1);
let dirName = direntPath.slice(index + 1);
seafileAPI.copyDir(repoID, repo.repo_id, copyToDirentPath, dirPath, dirName).then(() => {
this.copyTreeNode(direntPath, copyToDirentPath, repo);
let dirName = dirent.name;
let direntPath = Utils.joinPath(this.state.path, dirName);
seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(() => {
this.copyTreeNode(direntPath, copyToDirentPath, destRepo);
let message = gettext('Successfully copied %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
@@ -224,7 +222,7 @@ class Wiki extends Component {
});
});
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + filePath;
let fileUrl = siteRoot + 'wiki/lib/' + repoID + filePath;
window.history.pushState({url: fileUrl, path: filePath}, filePath, fileUrl);
}
@@ -236,7 +234,7 @@ class Wiki extends Component {
});
// update location url
let url = serviceUrl + '/wiki/lib/' + repoID + path;
let url = siteRoot + 'wiki/lib/' + repoID + path;
window.history.pushState({ url: url, path: path}, path, url);
}
@@ -312,8 +310,9 @@ class Wiki extends Component {
this.showDir(node.path);
}
onDirentClick = (direntPath) => {
onDirentClick = (dirent) => {
this.resetSelected();
let direntPath = Utils.joinPath(this.state.path, dirent.name);
let tree = this.state.treeData.clone();
let node = tree.getNodeByPath(direntPath);
let parentNode = tree.findNodeParentFromTree(node);
@@ -327,7 +326,7 @@ class Wiki extends Component {
this.showDir(node.path);
} else {
const w=window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
w.location.href = url;
}
}
@@ -552,7 +551,7 @@ class Wiki extends Component {
}
} else {
const w = window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
w.location.href = url;
}
}
@@ -688,24 +687,24 @@ class Wiki extends Component {
}
isInternalMarkdownLink(url) {
var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '.*\.md$');
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$');
return re.test(url);
}
isInternalDirLink(url) {
var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '/.*');
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*');
return re.test(url);
}
getPathFromInternalMarkdownLink(url) {
var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '(.*\.md)');
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
return path;
}
getPathFromInternalDirLink(url) {
var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '(/.*)');
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);

View File

@@ -23,16 +23,12 @@ export const folderPermEnabled = window.app.pageOptions.folderPermEnabled === 'T
export const enableUploadFolder = window.app.pageOptions.enableUploadFolder === 'True';
export const enableResumableFileUpload = window.app.pageOptions.enableResumableFileUpload === 'True';
//main-page
export const mainServiceUrl = window.main ? window.main.pageOptions.serviceUrl : '';
// wiki
export const slug = window.wiki ? window.wiki.config.slug : '';
export const repoID = window.wiki ? window.wiki.config.repoId : '';
export const initialPath = window.wiki ? window.wiki.config.initial_path : '';
export const permission = window.wiki ? window.wiki.config.permission === 'True' : '';
export const isDir = window.wiki ? window.wiki.config.isDir : '';
export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : '';
// file history
export const PER_PAGE = 25;

View File

@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
import SidePanel from './pages/wiki/side-panel';
import MainPanel from './pages/wiki/main-panel';
import moment from 'moment';
import { slug, repoID, serviceUrl, initialFilePath } from './utils/constants';
import { slug, repoID, siteRoot, initialFilePath } from './utils/constants';
import editorUtilities from './utils/editor-utilties';
import Node from './components/tree-view/node';
import Tree from './components/tree-view/tree';
@@ -65,7 +65,7 @@ class Wiki extends Component {
});
});
const hash = window.location.hash;
let fileUrl = serviceUrl + '/wikis/' + slug + filePath + hash;
let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash;
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl);
}
}, () => {
@@ -90,7 +90,7 @@ class Wiki extends Component {
});
const hash = window.location.hash;
let fileUrl = serviceUrl + '/wikis/' + slug + filePath + hash;
let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash;
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl);
}
@@ -133,7 +133,7 @@ class Wiki extends Component {
this.exitViewFileState(tree, node);
// update location url
let fileUrl = serviceUrl + '/wikis/' + slug + node.path;
let fileUrl = siteRoot + 'wikis/' + slug + node.path;
window.history.pushState({urlPath: fileUrl, filePath: node.path},node.path, fileUrl);
}
@@ -147,7 +147,7 @@ class Wiki extends Component {
this.exitViewFileState(tree, node);
} else {
const w=window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
w.location.href = url;
}
}
@@ -169,7 +169,7 @@ class Wiki extends Component {
this.exitViewFileState(tree, node);
} else {
const w=window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
w.location.href = url;
}
}
@@ -371,7 +371,7 @@ class Wiki extends Component {
filePath: newNode.path,
isViewFileState: false
});
let fileUrl = serviceUrl + '/wikis/' + slug + newNode.path;
let fileUrl = siteRoot + 'wikis/' + slug + newNode.path;
window.history.pushState({urlPath: fileUrl, filePath: newNode.path}, newNode.path, fileUrl);
}
@@ -431,24 +431,24 @@ class Wiki extends Component {
}
isInternalMarkdownLink(url) {
var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '.*\.md$');
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$');
return re.test(url);
}
isInternalDirLink(url) {
var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '/.*');
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*');
return re.test(url);
}
getPathFromInternalMarkdownLink(url) {
var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '(.*\.md)');
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
return path;
}
getPathFromInternalDirLink(url) {
var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '(/.*)');
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);

View File

@@ -1017,14 +1017,6 @@ table .menu-toggle {
/* end file-tag */
/* begin activity page */
.activity-table-container {
flex: 1;
padding: 10px 16px 20px;
overflow: auto;
}
/* end activity page */
.dirent-item.tag-list {
display: flex;
align-items: center;

View File

@@ -1212,6 +1212,4 @@ def choose_register(request):
@login_required
def react_fake_view(request):
return render(request, "react_app.html", {
"service_url": get_service_url().rstrip('/')
})
return render(request, "react_app.html")

View File

@@ -76,7 +76,6 @@ def slug(request, slug, file_path="home.md"):
"repo_id": wiki.repo_id,
"search_repo_id": wiki.repo_id,
"search_wiki": True,
"service_url": get_service_url().rstrip('/')
})