mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-05 08:53:14 +00:00
rewrite wikis by react
This commit is contained in:
committed by
shanshuirenjia
parent
8f1b006e78
commit
ac77266ea8
@@ -20,6 +20,7 @@ import MyLibraries from './pages/my-libs/my-libs';
|
|||||||
import DirView from './components/dir-view/dir-view';
|
import DirView from './components/dir-view/dir-view';
|
||||||
import Group from './pages/groups/group-view';
|
import Group from './pages/groups/group-view';
|
||||||
import Groups from './pages/groups/groups-view';
|
import Groups from './pages/groups/groups-view';
|
||||||
|
import Wikis from './pages/wikis/wikis';
|
||||||
import MainContentWrapper from './components/main-content-wrapper';
|
import MainContentWrapper from './components/main-content-wrapper';
|
||||||
|
|
||||||
import './assets/css/fa-solid.css';
|
import './assets/css/fa-solid.css';
|
||||||
@@ -38,6 +39,7 @@ const ShareAdminLibrariesWrapper = MainContentWrapper(ShareAdminLibraries);
|
|||||||
const ShareAdminFoldersWrapper = MainContentWrapper(ShareAdminFolders);
|
const ShareAdminFoldersWrapper = MainContentWrapper(ShareAdminFolders);
|
||||||
const ShareAdminShareLinksWrapper = MainContentWrapper(ShareAdminShareLinks);
|
const ShareAdminShareLinksWrapper = MainContentWrapper(ShareAdminShareLinks);
|
||||||
const ShareAdminUploadLinksWrapper = MainContentWrapper(ShareAdminUploadLinks);
|
const ShareAdminUploadLinksWrapper = MainContentWrapper(ShareAdminUploadLinks);
|
||||||
|
const Wikiswrapper = MainContentWrapper(Wikis);
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
|
|
||||||
@@ -142,6 +144,7 @@ class App extends Component {
|
|||||||
<DirView path={siteRoot + 'library/:repoID/*'} onMenuClick={this.onShowSidePanel} updateCurrentTab={this.updateCurrentTab}/>
|
<DirView path={siteRoot + 'library/:repoID/*'} onMenuClick={this.onShowSidePanel} updateCurrentTab={this.updateCurrentTab}/>
|
||||||
<Groups path={siteRoot + 'groups'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
|
<Groups path={siteRoot + 'groups'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
|
||||||
<Group path={siteRoot + 'group/:groupID'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
|
<Group path={siteRoot + 'group/:groupID'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
|
||||||
|
<Wikiswrapper path={siteRoot + 'wikis'} onShowSidePanel={this.onShowSidePanel} onSearchedClick={this.onSearchedClick}/>
|
||||||
</Router>
|
</Router>
|
||||||
</MainPanel>
|
</MainPanel>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -170,6 +170,12 @@ class MainSideNav extends React.Component {
|
|||||||
<span className="nav-text">{gettext('Acitivities')}</span>
|
<span className="nav-text">{gettext('Acitivities')}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li className='nav-item'>
|
||||||
|
<Link className={`nav-link ellipsis ${this.getActiveClass('wikis')}`} to={siteRoot + 'wikis/'} title={gettext('Wikis')} onClick={() => this.tabItemClick('wikis')}>
|
||||||
|
<span className="sf2-icon-wiki-view" aria-hidden="true"></span>
|
||||||
|
{gettext('Wikis')}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<Link className={`nav-link ellipsis ${this.getActiveClass('linked-devices')}`} to={siteRoot + 'linked-devices/'} title={gettext('Linked Devices')} onClick={() => this.tabItemClick('linked-devices')}>
|
<Link className={`nav-link ellipsis ${this.getActiveClass('linked-devices')}`} to={siteRoot + 'linked-devices/'} title={gettext('Linked Devices')} onClick={() => this.tabItemClick('linked-devices')}>
|
||||||
<span className="sf2-icon-monitor" aria-hidden="true"></span>
|
<span className="sf2-icon-monitor" aria-hidden="true"></span>
|
||||||
|
31
frontend/src/pages/wikis/wiki-add.js
Normal file
31
frontend/src/pages/wikis/wiki-add.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
isShowWikiAdd: PropTypes.bool.isRequired,
|
||||||
|
addPosition: PropTypes.object.isRequired,
|
||||||
|
onSelectToggle: PropTypes.func.isRequired,
|
||||||
|
onCreateToggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikiAdd extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let style = {};
|
||||||
|
let {isShowWikiAdd, addPosition} = this.props;
|
||||||
|
if (isShowWikiAdd) {
|
||||||
|
style = {position: 'fixed', top: addPosition.top, left: addPosition.left, display: 'block'};
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu" style={style}>
|
||||||
|
<li className="dropdown-item" onClick={this.props.onCreateToggle}>{gettext('New Wiki')}</li>
|
||||||
|
<li className="dropdown-item" onClick={this.props.onSelectToggle}>{gettext('Choose a library as Wiki')}</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiAdd.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiAdd;
|
69
frontend/src/pages/wikis/wiki-create.js
Normal file
69
frontend/src/pages/wikis/wiki-create.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input } from 'reactstrap';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
toggleCancel: PropTypes.func.isRequired,
|
||||||
|
addWiki: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikiDelete extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isExist: false,
|
||||||
|
name: "",
|
||||||
|
repoID: "",
|
||||||
|
};
|
||||||
|
this.newName = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.newName.focus();
|
||||||
|
this.newName.setSelectionRange(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputNewName = (e) => {
|
||||||
|
this.setState({
|
||||||
|
name: e.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyPress = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.handleSubmit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
let { isExist, name, repoID } = this.state;
|
||||||
|
this.props.addWiki(isExist, name, repoID);
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true}>
|
||||||
|
<ModalHeader toggle={this.toggle}>{gettext('New Wiki')}</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<label className="form-label">{gettext('Name')}</label>
|
||||||
|
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newName = input}} value={this.state.name} onChange={this.inputNewName}/>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button outline color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button outline color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiDelete.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiDelete;
|
35
frontend/src/pages/wikis/wiki-delete.js
Normal file
35
frontend/src/pages/wikis/wiki-delete.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
toggleCancel: PropTypes.func.isRequired,
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikiDelete extends React.Component {
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true}>
|
||||||
|
<ModalHeader toggle={this.toggle}>{gettext('Delete Wiki')}</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<p>{gettext('Are you sure you want to delete this wiki?')}</p>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button outline color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button outline color="danger" onClick={this.props.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiDelete.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiDelete;
|
28
frontend/src/pages/wikis/wiki-menu.js
Normal file
28
frontend/src/pages/wikis/wiki-menu.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
menuPosition: PropTypes.object.isRequired,
|
||||||
|
onRenameToggle: PropTypes.func.isRequired,
|
||||||
|
onDeleteToggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikiMenu extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let menuPosition = this.props.menuPosition;
|
||||||
|
let style = {position: 'fixed', top: menuPosition.top, left: menuPosition.left, display: 'block'};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="dropdown-menu" style={style}>
|
||||||
|
<li className="dropdown-item" onClick={this.props.onRenameToggle}>{gettext('Rename')}</li>
|
||||||
|
<li className="dropdown-item" onClick={this.props.onDeleteToggle}>{gettext('Delete')}</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiMenu.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiMenu;
|
69
frontend/src/pages/wikis/wiki-rename.js
Normal file
69
frontend/src/pages/wikis/wiki-rename.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
wiki: PropTypes.object.isRequired,
|
||||||
|
onRenameConfirm: PropTypes.func.isRequired,
|
||||||
|
onRenameCancel: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
class WikiRename extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
name: props.wiki.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.refs.renameInput.focus();
|
||||||
|
this.refs.renameInput.setSelectionRange(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange = (e) => {
|
||||||
|
this.setState({name: e.target.value});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.onRenameConfirm(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameConfirm = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.onRenameConfirm(this.state.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameCancel = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.onRenameCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="rename-container">
|
||||||
|
<input
|
||||||
|
ref="renameInput"
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.onChange}
|
||||||
|
onKeyPress={this.onKeyPress}
|
||||||
|
onClick={this.onClick}
|
||||||
|
/>
|
||||||
|
<button className="btn btn-secondary sf2-icon-confirm confirm" onClick={this.onRenameConfirm}></button>
|
||||||
|
<button className="btn btn-secondary sf2-icon-cancel cancel" onClick={this.onRenameCancel}></button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiRename.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiRename;
|
89
frontend/src/pages/wikis/wiki-select.js
Normal file
89
frontend/src/pages/wikis/wiki-select.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext, siteRoot } from '../../utils/constants';
|
||||||
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
toggleCancel: PropTypes.func.isRequired,
|
||||||
|
addWiki: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikiSelect extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
repos: [],
|
||||||
|
isExist: true,
|
||||||
|
name: "",
|
||||||
|
repoID: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
seafileAPI.listRepos().then(res => {
|
||||||
|
this.setState({
|
||||||
|
repos: res.data.repos,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange = (repo) => {
|
||||||
|
this.setState({
|
||||||
|
name: repo.repo_name,
|
||||||
|
repoID: repo.repo_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
let { isExist, name, repoID } = this.state;
|
||||||
|
this.props.addWiki(isExist, name, repoID);
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
this.props.toggleCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={true}>
|
||||||
|
<ModalHeader toggle={this.toggle}>{gettext('Choose a library as Wiki')}</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width='12%'>{/* select */}</th>
|
||||||
|
<th width='13%'>{/* icon */}</th>
|
||||||
|
<th width='50%'>{gettext('Name')}</th>
|
||||||
|
<th width='25%'>{gettext('Last Update')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.state.repos.map((repo, index) => {
|
||||||
|
return (
|
||||||
|
<tr key={index}>
|
||||||
|
<td className="select"><input type="radio" className="vam" name="repo" value={repo.repo_id} onChange={this.onChange.bind(this, repo)} /></td>
|
||||||
|
<td><img src={siteRoot + 'media/img/lib/48/lib.png'} width="24" /></td>
|
||||||
|
<td>{gettext(repo.repo_name)}</td>
|
||||||
|
<td>{moment(repo.last_modified).fromNow()}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button outline color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button outline color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikiSelect.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default WikiSelect;
|
410
frontend/src/pages/wikis/wikis.js
Normal file
410
frontend/src/pages/wikis/wikis.js
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
import { gettext, siteRoot, loginUrl } from '../../utils/constants';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Button } from 'reactstrap';
|
||||||
|
import Toast from '../../components/toast';
|
||||||
|
import MenuControl from '../../components/menu-control';
|
||||||
|
import WikiAdd from './wiki-add';
|
||||||
|
import WikiMenu from './wiki-menu';
|
||||||
|
import WikiRename from './wiki-rename';
|
||||||
|
import WikiDelete from './wiki-delete';
|
||||||
|
import WikiSelect from './wiki-select';
|
||||||
|
import WikiCreate from './wiki-create';
|
||||||
|
|
||||||
|
|
||||||
|
const itempropTypes = {
|
||||||
|
wiki: PropTypes.object.isRequired,
|
||||||
|
deleteWiki: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Item extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isShowWikiMenu: false,
|
||||||
|
menuPosition: {top:'', left: ''},
|
||||||
|
isItemFreezed: false,
|
||||||
|
isShowDeleteDialog: false,
|
||||||
|
isShowMenuControl: false,
|
||||||
|
isRenameing: false,
|
||||||
|
highlight: '',
|
||||||
|
wiki: this.props.wiki,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('click', this.onHideWikiMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('click', this.onHideWikiMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMenuToggle = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (this.state.isShowWikiMenu) {
|
||||||
|
this.onHideWikiMenu();
|
||||||
|
} else {
|
||||||
|
this.onShowWikiMenu(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowWikiMenu = (e) => {
|
||||||
|
let left = e.clientX - 8*16;
|
||||||
|
let top = e.clientY + 12;
|
||||||
|
let position = {top: top, left: left};
|
||||||
|
this.setState({
|
||||||
|
isShowWikiMenu: true,
|
||||||
|
menuPosition: position,
|
||||||
|
isItemFreezed: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHideWikiMenu = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowWikiMenu: false,
|
||||||
|
isItemFreezed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseEnter = () => {
|
||||||
|
if (!this.state.isItemFreezed) {
|
||||||
|
this.setState({
|
||||||
|
isShowMenuControl: true,
|
||||||
|
highlight: 'tr-highlight',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseLeave = () => {
|
||||||
|
if (!this.state.isItemFreezed) {
|
||||||
|
this.setState({
|
||||||
|
isShowMenuControl: false,
|
||||||
|
highlight: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameToggle = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowWikiMenu: false,
|
||||||
|
isItemFreezed: true,
|
||||||
|
isRenameing: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameConfirm = (newName) => {
|
||||||
|
let wiki = this.state.wiki;
|
||||||
|
|
||||||
|
if (newName === wiki.name) {
|
||||||
|
this.onRenameCancel();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!newName) {
|
||||||
|
let errMessage = 'Name is required.';
|
||||||
|
Toast.error(gettext(errMessage));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (newName.indexOf('/') > -1) {
|
||||||
|
let errMessage = 'Name should not include ' + '\'/\'' + '.';
|
||||||
|
Toast.error(gettext(errMessage));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.renameWiki(newName);
|
||||||
|
this.onRenameCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRenameCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
isRenameing: false,
|
||||||
|
isItemFreezed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteToggle = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowDeleteDialog: !this.state.isShowDeleteDialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renameWiki = (newName) => {
|
||||||
|
let wiki = this.state.wiki;
|
||||||
|
seafileAPI.renameWiki(wiki.slug, newName).then((res) => {
|
||||||
|
this.setState({wiki: res.data});
|
||||||
|
}).catch((error) => {
|
||||||
|
if(error.response) {
|
||||||
|
let errorMsg = error.response.data.error_msg;
|
||||||
|
Toast.error(errorMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteWiki = () => {
|
||||||
|
let wiki = this.props.wiki;
|
||||||
|
this.props.deleteWiki(wiki);
|
||||||
|
this.setState({
|
||||||
|
isShowDeleteDialog: !this.state.isShowDeleteDialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let wiki = this.state.wiki;
|
||||||
|
let userProfileURL = `${siteRoot}profile/${encodeURIComponent(wiki.owner)}/`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
|
<td>
|
||||||
|
{this.state.isRenameing ?
|
||||||
|
<WikiRename wiki={wiki} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel}/> :
|
||||||
|
<a href={wiki.link}>{gettext(wiki.name)}</a>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td><a href={userProfileURL} target='_blank'>{gettext(wiki.owner_nickname)}</a></td>
|
||||||
|
<td>{moment(wiki.updated_at).fromNow()}</td>
|
||||||
|
<td className="menu-toggle" onClick={this.onMenuToggle}>
|
||||||
|
<MenuControl
|
||||||
|
isShow={this.state.isShowMenuControl}
|
||||||
|
onClick={this.onMenuToggle}
|
||||||
|
/>
|
||||||
|
{this.state.isShowWikiMenu &&
|
||||||
|
<WikiMenu
|
||||||
|
menuPosition={this.state.menuPosition}
|
||||||
|
onRenameToggle={this.onRenameToggle}
|
||||||
|
onDeleteToggle={this.onDeleteToggle}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{this.state.isShowDeleteDialog &&
|
||||||
|
<WikiDelete
|
||||||
|
toggleCancel={this.onDeleteToggle}
|
||||||
|
handleSubmit={this.deleteWiki}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item.propTypes = itempropTypes;
|
||||||
|
|
||||||
|
|
||||||
|
const contentpropTypes = {
|
||||||
|
data: PropTypes.object.isRequired,
|
||||||
|
deleteWiki: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class WikisContent extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {loading, errorMsg, wikis} = this.props.data;
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <span className="loading-icon loading-tip"></span>;
|
||||||
|
} else if (errorMsg) {
|
||||||
|
return <p className="error text-center">{errorMsg}</p>;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="50%">{gettext('Name')}</th>
|
||||||
|
<th width="20%">{gettext('Owner')}</th>
|
||||||
|
<th width="20%">{gettext('Last Update')}</th>
|
||||||
|
<th width="10%">{/* operation */}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{wikis.map((wiki, index) => {
|
||||||
|
return(<Item key={index} wiki={wiki} deleteWiki={this.props.deleteWiki} />);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WikisContent.propTypes = contentpropTypes;
|
||||||
|
|
||||||
|
|
||||||
|
class Wikis extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
errorMsg: '',
|
||||||
|
wikis: [],
|
||||||
|
isShowWikiAdd: false,
|
||||||
|
addPosition: {top:'', left: ''},
|
||||||
|
isShowSelectDialog: false,
|
||||||
|
isShowCreateDialog: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('click', this.onHideWikiAdd);
|
||||||
|
this.getWikis();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps() {
|
||||||
|
this.getWikis();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('click', this.onHideWikiAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWikis = () => {
|
||||||
|
seafileAPI.listWikis().then(res => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
wikis: res.data.data,
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
if (error.response) {
|
||||||
|
if (error.response.status == 403) {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
errorMsg: gettext('Permission denied')
|
||||||
|
});
|
||||||
|
location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`;
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
errorMsg: gettext('Error')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
errorMsg: gettext('Please check the network.')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddMenuToggle = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.nativeEvent.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (this.state.isShowWikiAdd) {
|
||||||
|
this.onHideWikiAdd();
|
||||||
|
} else {
|
||||||
|
this.onShowWikiAdd(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowWikiAdd = (e) => {
|
||||||
|
let left = e.clientX - 10*20;
|
||||||
|
let top = e.clientY + 12;
|
||||||
|
let position = {top: top, left: left};
|
||||||
|
this.setState({
|
||||||
|
isShowWikiAdd: true,
|
||||||
|
addPosition: position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onHideWikiAdd = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowWikiAdd: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectToggle = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowSelectDialog: !this.state.isShowSelectDialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreateToggle = () => {
|
||||||
|
this.setState({
|
||||||
|
isShowCreateDialog: !this.state.isShowCreateDialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addWiki = (isExist, name, repoID) => {
|
||||||
|
seafileAPI.addWiki(isExist, name, repoID).then((res) => {
|
||||||
|
this.state.wikis.push(res.data);
|
||||||
|
this.setState({
|
||||||
|
wikis: this.state.wikis
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
if(error.response) {
|
||||||
|
let errorMsg = error.response.data.error_msg;
|
||||||
|
Toast.error(errorMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteWiki = (wiki) => {
|
||||||
|
seafileAPI.deleteWiki(wiki.slug).then(() => {
|
||||||
|
this.setState({
|
||||||
|
wikis: this.state.wikis.filter(item => {
|
||||||
|
return item.name !== wiki.name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
if(error.response) {
|
||||||
|
let errorMsg = error.response.data.error_msg;
|
||||||
|
Toast.error(errorMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="main-panel-center">
|
||||||
|
<div className="cur-view-container" id="wikis">
|
||||||
|
<div className="cur-view-path">
|
||||||
|
<h3 className="sf-heading">{gettext('Wikis')}</h3>
|
||||||
|
<div style={{float:'right'}}>
|
||||||
|
<Button className="fa fa-plus-square" onClick={this.onAddMenuToggle}>
|
||||||
|
{gettext('Add Wiki')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{this.state.isShowWikiAdd &&
|
||||||
|
<WikiAdd
|
||||||
|
isShowWikiAdd={this.state.isShowWikiAdd}
|
||||||
|
addPosition={this.state.addPosition}
|
||||||
|
onSelectToggle={this.onSelectToggle}
|
||||||
|
onCreateToggle={this.onCreateToggle}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{this.state.isShowCreateDialog &&
|
||||||
|
<WikiCreate
|
||||||
|
toggleCancel={this.onCreateToggle}
|
||||||
|
addWiki={this.addWiki}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{this.state.isShowSelectDialog &&
|
||||||
|
<WikiSelect
|
||||||
|
toggleCancel={this.onSelectToggle}
|
||||||
|
addWiki={this.addWiki}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="cur-view-content">
|
||||||
|
{(this.state.loading || this.state.wikis.length !== 0) &&
|
||||||
|
<WikisContent
|
||||||
|
data={this.state}
|
||||||
|
deleteWiki={this.deleteWiki}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{(!this.state.loading && this.state.wikis.length === 0) &&
|
||||||
|
<div className="message empty-tip">
|
||||||
|
<h2>{gettext('You do not have any Wiki.')}</h2>
|
||||||
|
<p>{gettext('Seafile Wiki enables you to organize your knowledge in a simple way. The contents of wiki is stored in a normal library with pre-defined file/folder structure. This enables you to edit your wiki in your desktop and then sync back to the server.')}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Wikis;
|
@@ -34,7 +34,7 @@ def wiki_list(request):
|
|||||||
if joined_groups:
|
if joined_groups:
|
||||||
joined_groups.sort(lambda x, y: cmp(x.group_name.lower(), y.group_name.lower()))
|
joined_groups.sort(lambda x, y: cmp(x.group_name.lower(), y.group_name.lower()))
|
||||||
|
|
||||||
return render(request, "wiki/wiki_list.html", {
|
return render(request, "react_app.html", {
|
||||||
"grps": joined_groups,
|
"grps": joined_groups,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user