1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-09 13:14:14 +00:00
seahub/frontend/src/markdown-editor.js

490 lines
14 KiB
JavaScript
Raw Normal View History

2018-05-02 06:09:58 +00:00
import React from 'react';
import SeafileEditor from '@seafile/seafile-editor';
2018-05-02 06:09:58 +00:00
import 'whatwg-fetch';
2019-02-14 02:41:59 +00:00
import { seafileAPI } from './utils/seafile-api';
import { Utils } from './utils/utils';
import ModalPortal from './components/modal-portal';
import EditFileTagDialog from './components/dialog/edit-filetag-dialog';
import ListRelatedFileDialog from './components/dialog/list-related-file-dialog';
import AddRelatedFileDialog from './components/dialog/add-related-file-dialog';
2019-02-25 10:17:04 +00:00
import ShareDialog from './components/dialog/share-dialog';
2018-05-02 06:09:58 +00:00
2019-02-25 10:17:04 +00:00
const { repoID, repoName, filePath, fileName, mode, draftID, reviewID, reviewStatus, draftFilePath, isDraft, hasDraft, shareLinkExpireDaysMin, shareLinkExpireDaysMax } = window.app.pageOptions;
const { siteRoot, serviceUrl, seafileCollabServer } = window.app.config;
const userInfo = window.app.userInfo;
2019-02-25 10:17:04 +00:00
const userName = userInfo.username;
let dirPath = '/';
2018-05-02 06:09:58 +00:00
function getImageFileNameWithTimestamp() {
var d = Date.now();
return 'image-' + d.toString() + '.png';
2018-05-02 06:09:58 +00:00
}
2018-10-15 07:51:29 +00:00
2018-05-02 06:09:58 +00:00
class EditorUtilities {
constructor () {
this.repoID = repoID;
this.filePath = filePath;
this.serviceUrl = serviceUrl;
this.name = userName;
this.contact_email = userInfo.contact_email;
2019-02-27 04:22:13 +00:00
this.fileName = fileName;
this.userName = userName;
}
2018-05-02 06:09:58 +00:00
saveContent(content) {
return (
seafileAPI.getUpdateLink(repoID, dirPath).then((res) => {
const uploadLink = res.data;
return seafileAPI.updateFile(uploadLink, filePath, fileName, content);
2018-05-02 06:09:58 +00:00
})
);
}
2019-02-18 12:26:55 +00:00
unStarItem () {
return (
2019-02-18 12:26:55 +00:00
seafileAPI.unStarItem(this.repoID, this.filePath)
);
2018-05-02 06:09:58 +00:00
}
2019-02-18 12:26:55 +00:00
starItem() {
return (
2019-02-18 12:26:55 +00:00
seafileAPI.starItem(this.repoID, this.filePath)
);
}
getParentDectionaryUrl() {
let parentPath = this.filePath.substring(0, this.filePath.lastIndexOf('/'));
let libName = encodeURIComponent(repoName);
let path = Utils.encodePath(parentPath);
2019-02-11 07:10:57 +00:00
return this.serviceUrl + '/library/' + this.repoID + '/' + libName + path;
}
2018-05-02 06:09:58 +00:00
_getImageURL(fileName) {
const url = this.serviceUrl + '/lib/' + repoID + '/file/images/auto-upload/' + fileName + '?raw=1';
2018-05-02 06:09:58 +00:00
return url;
}
uploadImage = (imageFile) => {
return (
seafileAPI.getUploadLink(repoID, dirPath).then((res) => {
let uploadLinkComponent = res.data;
const uploadLink = uploadLinkComponent + '?ret-json=1';
2018-05-02 06:09:58 +00:00
const name = getImageFileNameWithTimestamp();
const blob = imageFile.slice(0, -1, 'image/png');
const newFile = new File([blob], name, {type: 'image/png'});
const formData = new FormData();
formData.append('parent_dir', '/');
formData.append('relative_path', 'images/auto-upload');
formData.append('file', newFile);
return {uploadLink, formData};
}).then(({ uploadLink, formData}) => {
return seafileAPI.uploadImage(uploadLink, formData);
}).then ((res) => {
let resArr = res.data[0];
let filename = resArr.name;
2018-05-02 06:09:58 +00:00
return this._getImageURL(filename);
})
);
}
2018-05-02 06:09:58 +00:00
2018-11-05 14:03:13 +00:00
uploadLocalImage = (imageFile) => {
return (
seafileAPI.getUploadLink(repoID, dirPath).then((res) => {
const uploadLink = res.data + '?ret-json=1';
const newFile = new File([imageFile], imageFile.name, {type: imageFile.type});
const formData = new FormData();
formData.append('parent_dir', '/');
formData.append('relative_path', 'images/auto-upload');
2018-11-05 14:03:13 +00:00
formData.append('file', newFile);
return seafileAPI.uploadImage(uploadLink, formData);
}).then ((res) => {
return this._getImageURL(res.data[0].name);
})
);
}
2018-05-02 06:09:58 +00:00
getFileURL(fileNode) {
var url;
2018-08-28 03:34:03 +00:00
if (fileNode.type === 'file') {
if (fileNode.isImage()) {
2019-01-11 02:58:10 +00:00
url = serviceUrl + '/lib/' + repoID + '/file' + Utils.encodePath(fileNode.path()) + '?raw=1';
2018-08-28 03:34:03 +00:00
} else {
2019-01-11 02:58:10 +00:00
url = serviceUrl + '/lib/' + repoID + '/file' + Utils.encodePath(fileNode.path());
2018-08-28 03:34:03 +00:00
}
2018-05-02 06:09:58 +00:00
} else {
2019-01-11 02:58:10 +00:00
url = serviceUrl + '/library/' + repoID + '/' + encodeURIComponent(repoName) + Utils.encodePath(fileNode.path());
2018-05-02 06:09:58 +00:00
}
return url;
}
isInternalFileLink(url) {
2019-01-11 02:58:10 +00:00
var re = new RegExp(this.serviceUrl + '/lib/[0-9a-f-]{36}/file.*');
2018-05-02 06:09:58 +00:00
return re.test(url);
}
2018-08-28 03:34:03 +00:00
isInternalDirLink(url) {
2019-01-11 02:58:10 +00:00
var re = new RegExp(serviceUrl + '/library/' + '[0-9a-f\-]{36}.*');
2018-08-28 03:34:03 +00:00
return re.test(url);
}
2018-05-02 06:09:58 +00:00
getFiles() {
return seafileAPI.listDir(repoID, dirPath, { recursive: true} ).then((response) => {
var files = response.data.dirent_list.map((item) => {
return {
name: item.name,
type: item.type === 'dir' ? 'dir' : 'file',
parent_path: item.parent_dir
};
});
return files;
});
2018-05-02 06:09:58 +00:00
}
2018-08-06 10:29:12 +00:00
getFileHistory() {
return (
seafileAPI.getFileHistory(repoID, filePath)
);
2018-08-06 10:29:12 +00:00
}
getFileInfo() {
return (
seafileAPI.getFileInfo(repoID, filePath)
);
2018-08-06 10:29:12 +00:00
}
2018-12-28 06:25:25 +00:00
getRepoInfo(newRepoID) {
return (
seafileAPI.getRepoInfo(newRepoID)
);
}
getInternalLink() {
return seafileAPI.getInternalLink(repoID, filePath);
}
getShareLink() {
return seafileAPI.getShareLink(repoID, filePath);
}
2018-12-19 02:17:07 +00:00
createShareLink (repoID, filePath, userPassword, userValidDays, permissions) {
return seafileAPI.createShareLink(repoID, filePath, userPassword, userValidDays, permissions);
}
deleteShareLink(token){
return seafileAPI.deleteShareLink(token);
}
2018-09-19 02:47:56 +00:00
getDraftKey() {
return (repoID + filePath);
}
2018-09-27 04:01:56 +00:00
getFileContent(url) {
return seafileAPI.getFileContent(url);
}
listFileHistoryRecords(page, perPage) {
return (
seafileAPI.listFileHistoryRecords(repoID, filePath, page, perPage)
);
2018-09-27 04:01:56 +00:00
}
getFileHistoryVersion(commitID) {
return seafileAPI.getFileRevision(repoID, commitID, filePath);
}
2018-10-15 07:51:29 +00:00
createDraftReview() {
2018-10-16 10:19:51 +00:00
return seafileAPI.createDraftReview(draftID).then(res => {
let url = serviceUrl + '/drafts/review/' + res.data.id;
return url;
});
2018-10-15 07:51:29 +00:00
}
2018-10-30 03:07:01 +00:00
goReviewPage() {
2018-11-22 09:00:23 +00:00
window.location.href = serviceUrl + '/drafts/review/' + reviewID;
2018-10-30 03:07:01 +00:00
}
getCommentsNumber() {
2019-02-27 04:22:13 +00:00
return seafileAPI.getCommentsNumber(this.repoID, filePath);
2018-10-30 03:07:01 +00:00
}
2018-11-16 09:57:35 +00:00
postComment(comment, detail) {
return seafileAPI.postComment(this.repoID, this.filePath, comment, detail);
2018-10-30 03:07:01 +00:00
}
listComments() {
return seafileAPI.listComments(this.repoID, this.filePath);
}
updateComment(commentID, resolved, detail) {
return seafileAPI.updateComment(this.repoID, commentID, resolved, detail);
}
deleteComment(commentID) {
return seafileAPI.deleteComment(this.repoID, commentID);
}
getUserAvatar(size) {
return seafileAPI.getUserAvatar(userName, size);
}
2018-11-01 09:52:59 +00:00
goDraftPage() {
2018-11-22 09:00:23 +00:00
window.location.href = serviceUrl + '/lib/' + repoID + '/file' + draftFilePath + '?mode=edit';
2018-11-01 09:52:59 +00:00
}
2018-11-02 03:03:10 +00:00
createDraftFile() {
return seafileAPI.createDraft(repoID, filePath).then(res => {
2018-11-22 09:00:23 +00:00
window.location.href = serviceUrl + '/lib/' + res.data.origin_repo_id + '/file' + res.data.draft_file_path + '?mode=edit';
});
2018-11-02 03:03:10 +00:00
}
2018-05-02 06:09:58 +00:00
createFileReview() {
return seafileAPI.createFileReview(repoID, filePath).then(res => {
window.location.href = serviceUrl + '/drafts/review/' + res.data.id;
2018-11-22 09:00:23 +00:00
});
}
fileMetaData() {
return seafileAPI.fileMetaData(repoID, filePath);
}
listFileTags = () => {
return seafileAPI.listFileTags(repoID, filePath);
}
listRepoTags = () => {
return seafileAPI.listRepoTags(repoID);
}
}
2018-05-02 06:09:58 +00:00
const editorUtilities = new EditorUtilities();
2018-09-21 06:16:15 +00:00
class MarkdownEditor extends React.Component {
2018-05-02 06:09:58 +00:00
constructor(props) {
super(props);
this.state = {
markdownContent: '',
loading: true,
mode: 'editor',
fileInfo: {
repoID: repoID,
name: fileName,
path: filePath,
mtime: null,
size: 0,
starred: false,
permission: '',
lastModifier: '',
id: '',
},
collabServer: seafileCollabServer ? seafileCollabServer : null,
2018-12-28 06:25:25 +00:00
relatedFiles: [],
fileTagList: [],
showRelatedFileDialog: false,
showEditFileTagDialog: false,
showAddRelatedFileDialog: false,
showMarkdownEditorDialog: false,
2019-02-25 10:17:04 +00:00
showShareLinkDialog: false,
};
}
2018-05-02 06:09:58 +00:00
toggleCancel = () => {
this.setState({
showRelatedFileDialog: false,
showEditFileTagDialog: false,
showAddRelatedFileDialog: false,
showMarkdownEditorDialog: false,
2019-02-25 10:17:04 +00:00
showShareLinkDialog: false,
});
}
closeAddRelatedFileDialog = () => {
this.setState({
showAddRelatedFileDialog: false,
showRelatedFileDialog: true,
});
}
addRelatedFileToggle = () => {
this.setState({
showRelatedFileDialog: false,
showAddRelatedFileDialog: true,
});
}
openDialogs = (option) => {
switch(option)
{
case 'related_files':
this.setState({
showRelatedFileDialog: true,
showMarkdownEditorDialog: true,
});
break;
case 'tags':
this.setState({
showEditFileTagDialog: true,
showMarkdownEditorDialog: true,
});
break;
2019-02-25 10:17:04 +00:00
case 'share_link':
this.setState({
showMarkdownEditorDialog: true,
showShareLinkDialog: true,
});
break;
default:
return;
}
}
2018-05-02 06:09:58 +00:00
componentDidMount() {
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
let { mtime, size, starred, permission, last_modifier_name, id } = res.data;
let lastModifier = last_modifier_name;
2018-08-06 10:29:12 +00:00
this.setState((prevState, props) => ({
fileInfo: {
...prevState.fileInfo,
mtime,
size,
2018-08-06 10:29:12 +00:00
starred,
permission,
lastModifier,
id
}
}));
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
const downLoadUrl = res.data;
seafileAPI.getFileContent(downLoadUrl).then((res) => {
this.setState({
markdownContent: res.data,
loading: false
});
});
});
});
this.listRelatedFiles();
this.listFileTags();
}
2018-12-28 06:25:25 +00:00
listRelatedFiles = () => {
2018-12-28 06:25:25 +00:00
seafileAPI.listRelatedFiles(repoID, filePath).then(res => {
this.setState({
relatedFiles: res.data.related_files
});
});
}
listFileTags = () => {
seafileAPI.listFileTags(repoID, filePath).then(res => {
let fileTagList = res.data.file_tags;
for (let i = 0, length = fileTagList.length; i < length; i++) {
fileTagList[i].id = fileTagList[i].file_tag_id;
}
this.setState({
fileTagList: fileTagList
});
2018-12-28 06:25:25 +00:00
});
2018-05-02 06:09:58 +00:00
}
onRelatedFileChange = () => {
this.listRelatedFiles();
}
onFileTagChanged = () => {
this.listFileTags();
}
2018-05-02 06:09:58 +00:00
render() {
if (this.state.loading) {
return (
<div className="empty-loading-page">
<div className="lds-ripple page-centered"><div></div><div></div></div>
</div>
);
} else if (this.state.mode === 'editor') {
2018-05-02 06:09:58 +00:00
return (
<React.Fragment>
<SeafileEditor
fileInfo={this.state.fileInfo}
markdownContent={this.state.markdownContent}
editorUtilities={editorUtilities}
userInfo={this.state.collabServer ? userInfo : null}
collabServer={this.state.collabServer}
showFileHistory={true}
mode={mode}
draftID={draftID}
reviewID={reviewID}
reviewStatus={reviewStatus}
isDraft={isDraft}
hasDraft={hasDraft}
shareLinkExpireDaysMin={shareLinkExpireDaysMin}
shareLinkExpireDaysMax={shareLinkExpireDaysMax}
relatedFiles={this.state.relatedFiles}
siteRoot={siteRoot}
openDialogs={this.openDialogs}
fileTagList={this.state.fileTagList}
/>
{this.state.showMarkdownEditorDialog && (
2019-02-25 10:17:04 +00:00
<React.Fragment>
{this.state.showRelatedFileDialog &&
<ModalPortal>
<ListRelatedFileDialog
repoID={repoID}
filePath={filePath}
relatedFiles={this.state.relatedFiles}
toggleCancel={this.toggleCancel}
addRelatedFileToggle={this.addRelatedFileToggle}
onRelatedFileChange={this.onRelatedFileChange}
/>
2019-02-25 10:17:04 +00:00
</ModalPortal>
}
{this.state.showEditFileTagDialog &&
<ModalPortal>
<EditFileTagDialog
repoID={repoID}
filePath={filePath}
fileTagList={this.state.fileTagList}
toggleCancel={this.toggleCancel}
onFileTagChanged={this.onFileTagChanged}
/>
2019-02-25 10:17:04 +00:00
</ModalPortal>
}
{this.state.showAddRelatedFileDialog &&
<ModalPortal>
<AddRelatedFileDialog
repoID={repoID}
filePath={filePath}
toggleCancel={this.closeAddRelatedFileDialog}
2019-01-21 08:55:35 +00:00
dirent={this.state.fileInfo}
onRelatedFileChange={this.onRelatedFileChange}
/>
2019-02-25 10:17:04 +00:00
</ModalPortal>
}
{this.state.showShareLinkDialog &&
<ModalPortal>
<ShareDialog
itemType="file"
itemName={this.state.fileInfo.name}
itemPath={filePath}
repoID={repoID}
toggleDialog={this.toggleCancel}
isGroupOwnedRepo={false}
repoEncrypted={false}
/>
</ModalPortal>
}
</React.Fragment>
)}
</React.Fragment>
2018-05-02 06:09:58 +00:00
);
2018-06-07 07:01:41 +00:00
}
2018-05-02 06:09:58 +00:00
}
}
2018-09-21 06:16:15 +00:00
export default MarkdownEditor;