1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-03 07:55:36 +00:00

refactor wiki (#3289)

* refactor wiki

* refactor code
This commit is contained in:
ilearnit
2019-04-17 10:34:43 +08:00
committed by Daniel Pan
parent ff6ffb0f4c
commit b83197e2b0
15 changed files with 96 additions and 127 deletions

View File

@@ -17,13 +17,13 @@ class WikiDeleteDialog extends React.Component {
render() { render() {
return ( return (
<Modal isOpen={true}> <Modal isOpen={true}>
<ModalHeader toggle={this.toggle}>{gettext('Delete Wiki')}</ModalHeader> <ModalHeader toggle={this.toggle}>{gettext('Unpublish Library')}</ModalHeader>
<ModalBody> <ModalBody>
<p>{gettext('Are you sure you want to delete this wiki?')}</p> <p>{gettext('Are you sure you want to unpublish this library?')}</p>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button> <Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
<Button color="danger" onClick={this.props.handleSubmit}>{gettext('Delete')}</Button> <Button color="danger" onClick={this.props.handleSubmit}>{gettext('Unpublish')}</Button>
</ModalFooter> </ModalFooter>
</Modal> </Modal>
); );

View File

@@ -18,8 +18,6 @@ class WikiSelectDialog extends React.Component {
super(props); super(props);
this.state = { this.state = {
repos: [], repos: [],
isExist: true,
name: '',
repoID: '', repoID: '',
}; };
} }
@@ -43,8 +41,8 @@ class WikiSelectDialog extends React.Component {
} }
handleSubmit = () => { handleSubmit = () => {
let { isExist, name, repoID } = this.state; let { repoID } = this.state;
this.props.addWiki(isExist, name, repoID); this.props.addWiki(repoID);
this.props.toggleCancel(); this.props.toggleCancel();
} }
@@ -55,7 +53,7 @@ class WikiSelectDialog extends React.Component {
render() { render() {
return ( return (
<Modal isOpen={true}> <Modal isOpen={true}>
<ModalHeader toggle={this.toggle}>{gettext('Choose a library as Wiki')}</ModalHeader> <ModalHeader toggle={this.toggle}>{gettext('Publish a library')}</ModalHeader>
<ModalBody className="dialog-list-container"> <ModalBody className="dialog-list-container">
<table> <table>
<thead> <thead>

View File

@@ -202,7 +202,7 @@ class MainSideNav extends React.Component {
<li className="nav-item"> <li className="nav-item">
<Link className={`nav-link ellipsis ${this.getActiveClass('wikis')}`} to={siteRoot + 'wikis/'} title={gettext('Wikis')} onClick={() => this.tabItemClick('wikis')}> <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> <span className="sf2-icon-wiki-view" aria-hidden="true"></span>
<span className="nav-text">{gettext('Wikis')}</span> <span className="nav-text">{gettext('Published Libraries')}</span>
</Link> </Link>
</li> </li>
} }

View File

@@ -153,15 +153,6 @@ class WikiListItem extends Component {
</td> </td>
<td><a href={userProfileURL} target='_blank'>{wiki.owner_nickname}</a></td> <td><a href={userProfileURL} target='_blank'>{wiki.owner_nickname}</a></td>
<td>{moment(wiki.updated_at).fromNow()}</td> <td>{moment(wiki.updated_at).fromNow()}</td>
<td>
<WikiPermissionEditor
isTextMode={true}
isEditIconShow={this.state.showOpIcon}
currentPermission={this.state.permission}
permissions={this.permissions}
onPermissionChanged={this.changePerm}
/>
</td>
<td className="text-center cursor-pointer"> <td className="text-center cursor-pointer">
{this.state.isShowMenuControl && ( {this.state.isShowMenuControl && (
<Dropdown isOpen={this.state.isShowWikiMenu} toggle={this.onMenuToggle}> <Dropdown isOpen={this.state.isShowWikiMenu} toggle={this.onMenuToggle}>
@@ -174,8 +165,7 @@ class WikiListItem extends Component {
onClick={this.clickMenuToggle} onClick={this.clickMenuToggle}
/> />
<DropdownMenu> <DropdownMenu>
<DropdownItem onClick={this.onRenameToggle}>{gettext('Rename')}</DropdownItem> <DropdownItem onClick={this.onDeleteToggle}>{gettext('Unpublish')}</DropdownItem>
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Delete')}</DropdownItem>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
)} )}

View File

@@ -38,10 +38,9 @@ class WikiListView extends Component {
<table> <table>
<thead> <thead>
<tr> <tr>
<th width="35%">{gettext('Name')}</th> <th width="40%">{gettext('Name')}</th>
<th width="20%">{gettext('Owner')}</th> <th width="25%">{gettext('Owner')}</th>
<th width="20%">{gettext('Last Update')}</th> <th width="25%">{gettext('Last Update')}</th>
<th width="15%">{gettext('Permission')}</th>
<th width="10%">{/* operation */}</th> <th width="10%">{/* operation */}</th>
</tr> </tr>
</thead> </thead>

View File

@@ -79,7 +79,7 @@ class MainPanel extends Component {
<div className="cur-view-toolbar"> <div className="cur-view-toolbar">
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title="Side Nav Menu" onClick={this.onMenuClick}></span> <span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title="Side Nav Menu" onClick={this.onMenuClick}></span>
{this.props.permission === 'rw' && ( {this.props.permission === 'rw' && (
<button className="btn btn-secondary operation-item" title="Edit File" onClick={this.onEditClick}>{gettext('Edit Page')}</button> <button className="btn btn-secondary operation-item" title="Edit" onClick={this.onEditClick}>{gettext('Edit')}</button>
)} )}
</div> </div>
<CommonToolbar <CommonToolbar
@@ -93,12 +93,6 @@ class MainPanel extends Component {
<div className="main-panel-center"> <div className="main-panel-center">
<div className="cur-view-path"> <div className="cur-view-path">
<div className="path-containter"> <div className="path-containter">
{username &&
<Fragment>
<a href={siteRoot + 'wikis/'} className="normal">{gettext('Wikis')}</a>
<span className="path-split">/</span>
</Fragment>
}
<a href={siteRoot + 'wikis/' + slug} className="normal">{slug}</a> <a href={siteRoot + 'wikis/' + slug} className="normal">{slug}</a>
{this.renderNavPath()} {this.renderNavPath()}
</div> </div>

View File

@@ -46,7 +46,7 @@ class SidePanel extends Component {
renderTreeView = () => { renderTreeView = () => {
return ( return (
<Fragment> <Fragment>
<h3 className="wiki-pages-heading">{gettext('Pages')}</h3> <h3 className="wiki-pages-heading">{gettext('Contents')}</h3>
<div className="wiki-pages-container"> <div className="wiki-pages-container">
{this.props.treeData && ( {this.props.treeData && (
<TreeView <TreeView

View File

@@ -1,5 +1,5 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import { Button } from 'reactstrap';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { gettext, loginUrl } from '../../utils/constants'; import { gettext, loginUrl } from '../../utils/constants';
import toaster from '../../components/toast'; import toaster from '../../components/toast';
@@ -73,8 +73,8 @@ class Wikis extends Component {
this.setState({isShowCreateDialog: !this.state.isShowCreateDialog}); this.setState({isShowCreateDialog: !this.state.isShowCreateDialog});
} }
addWiki = (isExist, name, repoID) => { addWiki = (repoID) => {
seafileAPI.addWiki(isExist, name, repoID).then((res) => { seafileAPI.addWiki(repoID).then((res) => {
this.state.wikis.push(res.data); this.state.wikis.push(res.data);
this.setState({wikis: this.state.wikis}); this.setState({wikis: this.state.wikis});
}).catch((error) => { }).catch((error) => {
@@ -123,15 +123,9 @@ class Wikis extends Component {
<div className="cur-view-toolbar"> <div className="cur-view-toolbar">
<span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu" onClick={this.props.onShowSidePanel}></span> <span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu" onClick={this.props.onShowSidePanel}></span>
<div className="operation"> <div className="operation">
<Dropdown tag="div" isOpen={this.state.isShowAddWikiMenu} toggle={this.onMenuToggle}> <Button className="btn btn-secondary operation-item" onClick={this.onSelectToggle}>
<DropdownToggle className="btn btn-secondary operation-item"> <i className="fa fa-plus-square text-secondary mr-1"></i>{gettext('Publish a Library')}
<i className="fa fa-plus-square text-secondary mr-1"></i>{gettext('Add Wiki')} </Button>
</DropdownToggle>
<DropdownMenu>
<DropdownItem onClick={this.onCreateToggle}>{gettext('New Wiki')}</DropdownItem>
<DropdownItem onClick={this.onSelectToggle}>{gettext('Choose a library as Wiki')}</DropdownItem>
</DropdownMenu>
</Dropdown>
</div> </div>
</div> </div>
<CommonToolbar onSearchedClick={this.props.onSearchedClick} /> <CommonToolbar onSearchedClick={this.props.onSearchedClick} />
@@ -140,7 +134,7 @@ class Wikis extends Component {
<div className="cur-view-container" id="wikis"> <div className="cur-view-container" id="wikis">
<div className="cur-view-path"> <div className="cur-view-path">
<div className="path-container"> <div className="path-container">
<h3 className="sf-heading">{gettext('Wikis')}</h3> <h3 className="sf-heading">{gettext('Published Libraries')}</h3>
</div> </div>
</div> </div>
<div className="cur-view-content"> <div className="cur-view-content">

View File

@@ -56,6 +56,8 @@ export const permission = window.wiki ? window.wiki.config.permission === 'True'
export const isDir = window.wiki ? window.wiki.config.isDir : ''; export const isDir = window.wiki ? window.wiki.config.isDir : '';
export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : ''; export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : '';
export const isPublicWiki = window.wiki ? window.wiki.config.isPublicWiki === 'True': ''; export const isPublicWiki = window.wiki ? window.wiki.config.isPublicWiki === 'True': '';
export const sharedToken = window.wiki ? window.wiki.config.sharedToken : '';
export const sharedType = window.wiki ? window.wiki.config.sharedType : '';
// file history // file history
export const PER_PAGE = 25; export const PER_PAGE = 25;

View File

@@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import moment from 'moment'; import moment from 'moment';
import { slug, repoID, siteRoot, initialPath, isDir } from './utils/constants'; import { slug, repoID, siteRoot, initialPath, isDir, sharedToken } from './utils/constants';
import { Utils } from './utils/utils'; import { Utils } from './utils/utils';
import { seafileAPI } from './utils/seafile-api'; import { seafileAPI } from './utils/seafile-api';
import Dirent from './models/dirent'; import Dirent from './models/dirent';
@@ -270,7 +270,7 @@ class Wiki extends Component {
if (Utils.isMarkdownFile(path)) { if (Utils.isMarkdownFile(path)) {
this.showFile(path); this.showFile(path);
} else { } else {
let url = siteRoot + 'lib/' + item.repo_id + '/file' + Utils.encodePath(path); let url = siteRoot + 'd/' + sharedToken + '/files/?p=' + Utils.encodePath(path);
let newWindow = window.open('about:blank'); let newWindow = window.open('about:blank');
newWindow.location.href = url; newWindow.location.href = url;
} }
@@ -285,7 +285,6 @@ class Wiki extends Component {
let tree = this.state.treeData.clone(); let tree = this.state.treeData.clone();
let node = tree.getNodeByPath(nodePath); let node = tree.getNodeByPath(nodePath);
tree.expandNode(node); tree.expandNode(node);
this.setState({treeData: tree, currentNode: node}); this.setState({treeData: tree, currentNode: node});
this.showDir(node.path); this.showDir(node.path);
} }
@@ -300,7 +299,7 @@ class Wiki extends Component {
this.showFile(direntPath); this.showFile(direntPath);
} else { } else {
const w=window.open('about:blank'); const w=window.open('about:blank');
const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath); const url = siteRoot + 'd/' + sharedToken + '/files/?p=' + Utils.encodePath(direntPath);
w.location.href = url; w.location.href = url;
} }
} }
@@ -351,7 +350,7 @@ class Wiki extends Component {
} }
} else { } else {
const w = window.open('about:blank'); const w = window.open('about:blank');
const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path); const url = siteRoot + 'd/' + sharedToken + '/files/?p=' + Utils.encodePath(node.path);
w.location.href = url; w.location.href = url;
} }
} }

View File

@@ -38,6 +38,7 @@ from seahub.settings import SHARE_LINK_EXPIRE_DAYS_MAX, \
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_LOGIN_REQUIRED, \ SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_LOGIN_REQUIRED, \
ENABLE_SHARE_LINK_AUDIT, ENABLE_VIDEO_THUMBNAIL, \ ENABLE_SHARE_LINK_AUDIT, ENABLE_VIDEO_THUMBNAIL, \
THUMBNAIL_ROOT THUMBNAIL_ROOT
from seahub.wiki.models import Wiki
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -366,11 +367,23 @@ class ShareLink(APIView):
except FileShare.DoesNotExist: except FileShare.DoesNotExist:
return Response({'success': True}) return Response({'success': True})
has_published_library = False
if fs.path == '/':
try:
Wiki.objects.get(repo_id=fs.repo_id)
has_published_library = True
except Wiki.DoesNotExist:
pass
username = request.user.username username = request.user.username
if not fs.is_owner(username): if not fs.is_owner(username):
error_msg = 'Permission denied.' error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg) return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if has_published_library:
error_msg = 'This is an associated published library.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try: try:
fs.delete() fs.delete()
except Exception as e: except Exception as e:

View File

@@ -9,7 +9,6 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from seaserv import seafile_api, edit_repo from seaserv import seafile_api, edit_repo
from pysearpc import SearpcError from pysearpc import SearpcError
from django.core.urlresolvers import reverse
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import Count from django.db.models import Count
from django.http import HttpResponse from django.http import HttpResponse
@@ -24,6 +23,7 @@ from seahub.utils import is_org_context, get_user_repos
from seahub.utils.repo import is_group_repo_staff, is_repo_owner from seahub.utils.repo import is_group_repo_staff, is_repo_owner
from seahub.views import check_folder_permission from seahub.views import check_folder_permission
from seahub.share.utils import is_repo_admin from seahub.share.utils import is_repo_admin
from seahub.share.models import FileShare
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -79,49 +79,12 @@ class WikisView(APIView):
def post(self, request, format=None): def post(self, request, format=None):
"""Add a new wiki. """Add a new wiki.
""" """
use_exist_repo = request.POST.get('use_exist_repo', '')
if not use_exist_repo:
msg = 'Use exist repo is invalid'
return api_error(status.HTTP_400_BAD_REQUEST, msg)
name = request.POST.get('name', '')
if not name:
msg = 'Name is invalid'
return api_error(status.HTTP_400_BAD_REQUEST, msg)
if not is_valid_wiki_name(name):
msg = _('Name can only contain letters, numbers, blank, hyphen or underscore.')
return api_error(status.HTTP_400_BAD_REQUEST, msg)
username = request.user.username username = request.user.username
org_id = -1 org_id = -1
if is_org_context(request): if is_org_context(request):
org_id = request.user.org.org_id org_id = request.user.org.org_id
if use_exist_repo == 'false':
try:
wiki = Wiki.objects.add(name, username, org_id=org_id)
except DuplicateWikiNameError:
msg = _('%s is taken by others, please try another name.') % name
return api_error(status.HTTP_400_BAD_REQUEST, msg)
except IntegrityError:
msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg)
# create home page
page_name = "home.md"
try:
seafile_api.post_empty_file(wiki.repo_id, '/',
page_name, request.user.username)
except SearpcError as e:
logger.error(e)
msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg)
return Response(wiki.to_dict())
if use_exist_repo == 'true':
repo_id = request.POST.get('repo_id', '') repo_id = request.POST.get('repo_id', '')
if not repo_id: if not repo_id:
msg = 'Repo id is invalid.' msg = 'Repo id is invalid.'
@@ -147,7 +110,7 @@ class WikisView(APIView):
try: try:
wiki = Wiki.objects.add(wiki_name=repo.repo_name, username=username, wiki = Wiki.objects.add(wiki_name=repo.repo_name, username=username,
repo_id=repo.repo_id, org_id=org_id) repo_id=repo.repo_id, org_id=org_id, permission='public')
except DuplicateWikiNameError: except DuplicateWikiNameError:
msg = _('%s is taken by others, please try another name.') % repo.repo_name msg = _('%s is taken by others, please try another name.') % repo.repo_name
return api_error(status.HTTP_400_BAD_REQUEST, msg) return api_error(status.HTTP_400_BAD_REQUEST, msg)
@@ -165,6 +128,10 @@ class WikisView(APIView):
msg = 'Internal Server Error' msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg) return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg)
fs = FileShare.objects.get_dir_link_by_path(username, repo_id, '/')
if not fs:
fs = FileShare.objects.create_dir_link(username, repo_id, '/',
permission='view_download', org_id=org_id)
return Response(wiki.to_dict()) return Response(wiki.to_dict())

View File

@@ -10,6 +10,8 @@
config: { config: {
slug: "{{ wiki.slug }}", slug: "{{ wiki.slug }}",
repoId: "{{ wiki.repo_id }}", repoId: "{{ wiki.repo_id }}",
sharedToken: "{{ shared_token }}",
sharedType: "{{ shared_type }}",
initial_path: "{{ file_path|escapejs }}", initial_path: "{{ file_path|escapejs }}",
permission: "{{ user_can_write }}", permission: "{{ user_can_write }}",
isPublicWiki: "{{ is_public_wiki }}", isPublicWiki: "{{ is_public_wiki }}",

View File

@@ -160,3 +160,9 @@ def remove_personal_wiki(sender, **kwargs):
repo_id = kwargs['repo_id'] repo_id = kwargs['repo_id']
PersonalWiki.objects.filter(username=repo_owner, repo_id=repo_id).delete() PersonalWiki.objects.filter(username=repo_owner, repo_id=repo_id).delete()
@receiver(repo_deleted)
def remove_wiki(sender, **kwargs):
repo_id = kwargs['repo_id']
Wiki.objects.filter(repo_id=repo_id).delete()

View File

@@ -12,6 +12,7 @@ from django.utils.translation import ugettext as _
from seahub.auth.decorators import login_required from seahub.auth.decorators import login_required
from seahub.base.decorators import user_mods_check from seahub.base.decorators import user_mods_check
from seahub.share.models import FileShare
from seahub.wiki.models import Wiki from seahub.wiki.models import Wiki
from seahub.views import check_folder_permission from seahub.views import check_folder_permission
from seahub.utils import get_service_url, get_file_type_and_ext, render_permission_error from seahub.utils import get_service_url, get_file_type_and_ext, render_permission_error
@@ -88,9 +89,13 @@ def slug(request, slug, file_path="home.md"):
if wiki.permission == 'public': if wiki.permission == 'public':
is_public_wiki = True is_public_wiki = True
fs = FileShare.objects.get(repo_id=wiki.repo_id, path='/')
return render(request, "wiki/wiki.html", { return render(request, "wiki/wiki.html", {
"wiki": wiki, "wiki": wiki,
"page_name": file_path, "page_name": file_path,
"shared_token": fs.token,
"shared_type": fs.s_type,
"user_can_write": user_can_write, "user_can_write": user_can_write,
"file_path": file_path, "file_path": file_path,
"repo_id": wiki.repo_id, "repo_id": wiki.repo_id,