1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-02 15:57:31 +00:00

update wiki mode

This commit is contained in:
ilearnit 2018-12-12 02:34:58 +00:00
parent ce5e3a0cc7
commit 1fc0882b10
8 changed files with 141 additions and 16 deletions

View File

@ -13,7 +13,18 @@ const viewerPropTypes = {
activeTitleIndex: PropTypes.number
};
const contentClass = 'markdown-content';
class MarkdownContentViewer extends React.Component {
componentDidUpdate () {
var links = document.querySelectorAll(`.${contentClass} a`);
links.forEach((li) => {li.addEventListener('click', this.onLinkClick); });
}
onLinkClick = (event) => {
event.preventDefault();
this.props.onLinkClick(event);
}
render() {
if (this.props.isFileLoading) {

View File

@ -2,6 +2,7 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import ModalPortal from '../modal-portal';
import CreateFolder from '../../components/dialog/create-folder-dialog';
import CreateFile from '../../components/dialog/create-file-dialog';
@ -16,6 +17,11 @@ const propTypes = {
onAddFolder: PropTypes.func.isRequired,
onUploadFile: PropTypes.func.isRequired,
onUploadFolder: PropTypes.func.isRequired,
isDraft: PropTypes.bool,
hasDraft: PropTypes.bool,
reviewStatus: PropTypes.any,
goDraftPage: PropTypes.func,
goReviewPage: PropTypes.func,
};
class DirOperationToolbar extends React.Component {
@ -63,6 +69,14 @@ class DirOperationToolbar extends React.Component {
window.location.href= siteRoot + 'lib/' + repoID + '/file' + path + '?mode=edit';
}
onNewDraft = (e) => {
e.preventDefault();
let { path, repoID } = this.props;
seafileAPI.createDraft(repoID, path).then(res => {
window.location.href = siteRoot + 'lib/' + res.data.origin_repo_id + '/file' + res.data.draft_file_path + '?mode=edit';
});
}
onUploadClick = (e) => {
this.toggleOperationMenu(e);
this.setState({
@ -115,14 +129,43 @@ class DirOperationToolbar extends React.Component {
this.props.onAddFolder(dirPath);
}
onViewReview = () => {
this.props.goReviewPage();
}
onViewDraft = () => {
this.props.goDraftPage();
}
render() {
let dirName = this.props.path.replace('\/','');
let isFile = this.props.isViewFile;
let itemName;
if (this.props.isViewFile) {
itemName = Utils.getFileName(this.props.path)
} else {
itemName = this.props.path.replace('\/','');
}
return (
<Fragment>
<div className="operation">
{(this.props.isViewFile && this.props.permission === 'rw') && (
<Fragment>
<button className="btn btn-secondary operation-item" title={gettext('Edit File')} onClick={this.onEditClick}>{gettext('Edit')}</button>
<button className="btn btn-secondary operation-item" title={gettext('Share')} onClick={this.onShareClick}>{gettext('Share')}</button>
</Fragment>
)}
{(this.props.isViewFile && this.props.permission !== 'None' && !this.props.isDraft && !this.props.hasDraft) && (
<button className="btn btn-secondary operation-item" title={gettext('New Draft')} onClick={this.onNewDraft}>{gettext('New Draft')}</button>
)}
{(this.props.reviewStatus === 'open') &&
<button className="btn btn-secondary operation-item" title={gettext('View Review')} onClick={this.onViewReview}>{gettext('View Review')}</button>
}
{(!this.props.isDraft && this.props.hasDraft) &&
<button className="btn btn-secondary operation-item" title={gettext('View Draft')} onClick={this.onViewDraft}>{gettext('View Draft')}</button>
}
{!this.props.isViewFile && (
<Fragment>
{Utils.isSupportUploadFolder() ?
@ -170,8 +213,8 @@ class DirOperationToolbar extends React.Component {
{this.state.isShareDialogShow &&
<ModalPortal>
<ShareDialog
isDir={true}
itemName={dirName}
isDir={!isFile}
itemName={itemName}
itemPath={this.props.path}
repoID={this.props.repoID}
toggleDialog={this.onShareClick}

View File

@ -177,3 +177,7 @@ img[src=""] {
.wiki-md-viewer-rendered-content.article h1 {
margin-top: 0;
}
.markdown-content a {
cursor: pointer;
}

View File

@ -117,22 +117,22 @@ class EditorUtilities {
if (fileNode.isImage()) {
url = serviceUrl + '/lib/' + repoID + '/file' + encodeURIComponent(fileNode.path()) + '?raw=1';
} else {
url = serviceUrl + '/lib/' + repoID + '/file' + encodeURIComponent(fileNode.path());
url = serviceUrl + '/wiki/lib/' + repoID + '/file' + encodeURIComponent(fileNode.path());
}
} else {
url = serviceUrl + '/#common/lib/' + repoID + encodeURIComponent(fileNode.path());
url = serviceUrl + '/wiki/lib/' + repoID + encodeURIComponent(fileNode.path());
}
return url;
}
isInternalFileLink(url) {
var re = new RegExp(this.serviceUrl + '/lib/[0-9a-f-]{36}/file.*');
var re = new RegExp(this.serviceUrl + '/wiki/lib/[0-9a-f-]{36}/file.*');
return re.test(url);
}
isInternalDirLink(url) {
var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + '[0-9a-f\-]{36}.*');
var re = new RegExp(serviceUrl + '/wiki/lib/' + '[0-9a-f\-]{36}.*');
return re.test(url);
}

View File

@ -48,6 +48,12 @@ const propTypes = {
onItemsMove: PropTypes.func.isRequired,
onItemsCopy: PropTypes.func.isRequired,
onItemsDelete: PropTypes.func.isRequired,
onLinkClick: PropTypes.func.isRequired,
isDraft: PropTypes.bool,
hasDraft: PropTypes.bool,
reviewStatus: PropTypes.any,
goReviewPage: PropTypes.func,
goDraftPage: PropTypes.func,
};
class MainPanel extends Component {
@ -185,6 +191,11 @@ class MainPanel extends Component {
<DirOperationToolBar
path={this.props.path}
repoID={repoID}
isDraft={this.props.isDraft}
hasDraft={this.props.hasDraft}
reviewStatus={this.props.reviewStatus}
goDraftPage={this.props.goDraftPage}
goReviewPage={this.props.goReviewPage}
permission={this.props.permission}
isViewFile={this.props.isViewFile}
onAddFile={this.props.onAddFile}
@ -224,6 +235,7 @@ class MainPanel extends Component {
isFileLoading={this.props.isFileLoading}
activeTitleIndex={this.state.activeTitleIndex}
onContentRendered={this.onContentRendered}
onLinkClick={this.props.onLinkClick}
/> :
<Fragment>
<DirentListView

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import moment from 'moment';
import { gettext, repoID, siteRoot, initialPath, isDir } from './utils/constants';
import { gettext, repoID, siteRoot, initialPath, isDir, serviceUrl } from './utils/constants';
import { seafileAPI } from './utils/seafile-api';
import { Utils } from './utils/utils';
import SidePanel from './pages/repo-wiki-mode/side-panel';
@ -42,7 +42,12 @@ class Wiki extends Component {
isDirentSelected: false,
isAllDirentSelected: false,
selectedDirentList: [],
libNeedDecrypt: false
libNeedDecrypt: false,
isDraft: false,
hasDraft: false,
reviewStatus: '',
reviewID: '',
draftFilePath: '',
};
window.onpopstate = this.onpopstate;
this.hash = '';
@ -226,7 +231,8 @@ class Wiki extends Component {
this.setState({isFileLoading: true});
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
let { mtime, permission, last_modifier_name } = res.data;
let { mtime, permission, last_modifier_name, is_draft, has_draft,
review_status, review_id, draft_file_path } = res.data;
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
seafileAPI.getFileContent(res.data).then((res) => {
this.setState({
@ -235,6 +241,11 @@ class Wiki extends Component {
latestContributor: last_modifier_name,
lastModified: moment.unix(mtime).fromNow(),
isFileLoading: false,
isDraft: is_draft,
hasDraft: has_draft,
reviewStatus: review_status,
reviewID: review_id,
draftFilePath: draft_file_path
});
});
});
@ -271,6 +282,18 @@ class Wiki extends Component {
});
}
onLinkClick = (event) => {
const url = event.path[2].href;
if (this.isInternalMarkdownLink(url)) {
let path = this.getPathFromInternalMarkdownLink(url);
this.showFile(path);
} else if (this.isInternalDirLink(url)) {
let path = this.getPathFromInternalDirLink(url);
this.showDir(path);
}
}
updateDirent = (dirent, paramKey, paramValue) => {
let newDirentList = this.state.direntList.map(item => {
if (item.name === dirent.name) {
@ -705,24 +728,24 @@ class Wiki extends Component {
}
isInternalMarkdownLink(url) {
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$');
var re = new RegExp(serviceUrl + '/wiki/lib/' + repoID + '/file' + '.*\.md$');
return re.test(url);
}
isInternalDirLink(url) {
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*');
var re = new RegExp(serviceUrl + '/wiki/lib/' + repoID + '/.*');
return re.test(url);
}
getPathFromInternalMarkdownLink(url) {
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)');
var re = new RegExp(serviceUrl + '/wiki/lib/' + repoID + '/file' + '(.*\.md)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
return path;
}
getPathFromInternalDirLink(url) {
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)');
var re = new RegExp(serviceUrl + '/wiki/lib/' + repoID + '(/.*)');
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
@ -776,6 +799,14 @@ class Wiki extends Component {
this.loadSidePanel(initialPath);
}
goReviewPage = () => {
window.location.href = siteRoot + 'drafts/review/' + this.state.reviewID;
}
goDraftPage = () => {
window.location.href = siteRoot + 'lib/' + repoID + '/file' + this.state.draftFilePath + '?mode=edit';
}
render() {
let { libNeedDecrypt } = this.state;
if (libNeedDecrypt) {
@ -823,6 +854,7 @@ class Wiki extends Component {
onItemSelected={this.onDirentSelected}
onItemDelete={this.onMainPanelItemDelete}
onItemRename={this.onMainPanelItemRename}
onLinkClick={this.onLinkClick}
onItemMove={this.onMoveItem}
onItemCopy={this.onCopyItem}
onAddFile={this.onAddFile}
@ -835,6 +867,11 @@ class Wiki extends Component {
onItemsCopy={this.onCopyItems}
onItemsDelete={this.onDeleteItems}
hash={this.hash}
isDraft={this.state.isDraft}
hasDraft={this.state.hasDraft}
reviewStatus={this.state.reviewStatus}
goDraftPage={this.goDraftPage}
goReviewPage={this.goReviewPage}
/>
</div>
);

View File

@ -36,6 +36,7 @@ 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

@ -58,6 +58,7 @@ from seahub.notifications.models import UserNotification
from seahub.options.models import UserOptions
from seahub.profile.models import Profile, DetailedProfile
from seahub.drafts.models import Draft
from seahub.drafts.utils import is_draft_file, has_draft_file
from seahub.signals import (repo_created, repo_deleted)
from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
@ -78,7 +79,7 @@ from seahub.utils.repo import get_repo_owner, get_library_storages, \
parse_repo_perm
from seahub.utils.star import star_file, unstar_file, get_dir_starred_files
from seahub.utils.file_tags import get_files_tags_in_dir
from seahub.utils.file_types import DOCUMENT
from seahub.utils.file_types import DOCUMENT, MARKDOWN
from seahub.utils.file_size import get_file_size_unit
from seahub.utils.file_op import check_file_lock
from seahub.utils.timeutils import utc_to_local, \
@ -3028,12 +3029,28 @@ class FileDetailView(APIView):
real_path = path
real_repo_id = repo_id
file_name = os.path.basename(path)
entry = {}
entry["type"] = "file"
entry["id"] = obj_id
entry["name"] = os.path.basename(path)
entry["name"] = file_name
entry["permission"] = permission
file_type, file_ext = get_file_type_and_ext(file_name)
if file_type == MARKDOWN:
is_draft, review_id, draft_id, review_status = is_draft_file(repo_id, path)
has_draft = False
draft_file_path = ''
if not is_draft:
has_draft, draft_file_path, draft_id, review_id, review_status = has_draft_file(repo_id, path)
entry['review_id'] = review_id
entry['review_status'] = review_status
entry['is_draft'] = is_draft
entry['has_draft'] = has_draft
entry['draft_file_path'] = draft_file_path
# fetch file contributors and latest contributor
try:
# get real path for sub repo