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:
parent
ce5e3a0cc7
commit
1fc0882b10
@ -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) {
|
||||
|
@ -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}
|
||||
|
@ -177,3 +177,7 @@ img[src=""] {
|
||||
.wiki-md-viewer-rendered-content.article h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown-content a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user