Commit 0be0261c authored by Matija Obreza's avatar Matija Obreza
Browse files

Markdown: Pasted graphics get uploaded to the repository

parent 235f2a22
......@@ -75,6 +75,10 @@ module.exports = {
p = `${p}&clientId=${CLIENT_ID}`;
}
if (p.startsWith('/uploads')) {
p = p.replace(/^\/uploads/, '/api/v0/repository/download');
}
// If authorization header is not provided, use access_token from cookie
// console.log('Cookies', req.headers.cookie, req);
if (! req.headers.authorization && req.headers.cookie) {
......
......@@ -10,6 +10,9 @@ const httpProxy = proxy(config.apiUrl, {
// console.log('Will proxy /oauth');
return true;
}
if (req.url.startsWith('/uploads')) {
req.url = req.url.replace(/^\/uploads/, '/api/v0/repository/download');
}
if (req.url.startsWith('/api')) {
// If authorization header is not provided, use access_token from cookie
if (!req.headers.authorization && req.headers.cookie) {
......
import { RepositoryFile } from 'model/repositoryFile.model';
import { RepositoryFileService } from 'service/RepositoryFileService';
import {log} from 'utilities/debug';
export const uploadMarkdownAttachment = (file: File) => (dispatch, getState) => {
const token = getState().login.access_token;
return RepositoryFileService.uploadMarkdownAttachment(token, file)
.then((repoFile: RepositoryFile) => {
return repoFile;
}).catch((error) => {
log('Upload error', error);
});
};
......@@ -65,6 +65,11 @@ export const UPDATE_REPOSITORY_FILE_URL = '/files/update';
export const LIST_REPOSITORY_FILE_URL = '/files/list';
export const REMOVE_REPOSITORY_FILE_URL = '/files/delete';
const REPOSITORY_API_URL = `${API_BASE_URL}/repository`;
export const REPOSITORY_ADDFILE_URL = `${REPOSITORY_API_URL}/add`;
export const REPOSITORY_GETFILE_URL = `${REPOSITORY_API_URL}`;
export const REPOSITORY_DOWNLOADFILE_URL = `/proxy/uploads`;
// Location API. Note: starts from 'GET_DATASET_URL'
// FIXME base on DATASET_API
export const GET_LOCATION_URL = '/location';
......
......@@ -8,8 +8,11 @@ import {
UPDATE_REPOSITORY_FILE_URL,
REMOVE_REPOSITORY_FILE_URL,
GET_DATASET_URL,
REPOSITORY_ADDFILE_URL,
} from 'constants/apiURLS';
import * as UUIDv4 from 'uuid/v4';
export class RepositoryFileService {
public static listRepositoryFiles(token: string, datasetUUID: string): Promise<Page<RepositoryFile>> {
......@@ -52,4 +55,26 @@ export class RepositoryFileService {
method: 'DELETE',
}).then(({data}) => new Dataset(data));
}
public static uploadMarkdownAttachment(token: string, file: File): Promise<RepositoryFile> {
const metadata: RepositoryFile = new RepositoryFile();
metadata.path = '/content/markdown';
const dummy: string = UUIDv4().replace(/\-/g, '');
metadata.originalFilename = `${dummy}_${file.name}`;
metadata.contentType = file.type;
console.log(`Uploading Markdown`, metadata);
const data = new FormData();
data.append('file', file);
data.append('metadata', JSON.stringify(metadata));
// data.append('metadata', new Blob([ JSON.stringify(metadata) ], { type : 'application/json' }));
return authenticatedRequest(token, {
url: `${REPOSITORY_ADDFILE_URL}`,
method: 'POST',
data,
headers: {'Content-Type': 'multipart/form-data'},
}).then(({data}) => new RepositoryFile(data));
}
}
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { REPOSITORY_DOWNLOADFILE_URL } from 'constants/apiURLS';
import { uploadMarkdownAttachment } from 'actions/repository';
import { insertAtCaret } from 'utilities';
import Markdown from 'ui/common/markdown';
import OriginalMarkdownField from 'ui/common/markdown/MarkdownField';
import FormControl from 'ui/common/forms/FormControl';
import Input from 'material-ui/Input';
class MarkdownField extends OriginalMarkdownField {
public constructor(props: any) {
super(props);
}
// private dataDropped = (e) => {
// e.preventDefault();
// console.log('Dropped', e.dataTransfer);
// const { dataTransfer: { items, files }, target } = e;
// const { uploadMarkdownAttachment } = this.props;
//
// console.log('Dropped', e.dataTransfer, items, target);
//
// const list = items.filter((i) => i.kind === 'file').map((i) => i.getAsFile()) || files;
// for (const f of list) {
// console.log('Uploading file', f);
// uploadMarkdownAttachment(f)
// .then((r) => {
// // JSON.stringify(r));
// if (r.contentType.startsWith('image/')) {
// insertAtCaret(target, `![${f.name}](${REPOSITORY_DOWNLOADFILE_URL}/${r.uuid})`);
// } else {
// insertAtCaret(target, `[${f.name}](${REPOSITORY_DOWNLOADFILE_URL}/${r.uuid})`);
// }
// });
// }
// }
private dataPasted = (e) => {
const { clipboardData: { types, files, items }, target } = e;
const { uploadMarkdownAttachment } = this.props;
console.log('Pasted', e.clipboardData, types, files, items);
if (files.length) {
e.preventDefault();
console.log('Uploading files', files);
for (const f of files) {
uploadMarkdownAttachment(f).then((r) => {
// JSON.stringify(r));
if (r.contentType.startsWith('image/')) {
insertAtCaret(target, `![${f.name}](${REPOSITORY_DOWNLOADFILE_URL}/${r.uuid})`);
} else {
insertAtCaret(target, `[${f.name}](${REPOSITORY_DOWNLOADFILE_URL}/${r.uuid})`);
}
});
}
} else {
// NOOP
}
// console.log(e.clipboardData.getData('text/plain'));
}
public render() {
const {basicMarkdown, input, label, required, meta, meta: {touched, error}, ...custom} = this.props;
const basic: boolean = basicMarkdown === undefined || null ? false : basicMarkdown;
return (
<div>
{ (basic || !this.state.previewMode) ?
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ touched && error } onPaste={ this.dataPasted } multiline={ !basic } { ...input } { ...custom } />
<h6>
{ ! basic && <a onClick={ this.onChangePreviewMode }>Preview Markdown</a> }
<span> { basic ? 'Basic markdown supported: * **' : 'Full markdown supported' }</span>
</h6>
</FormControl>
:
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Markdown style={ { marginTop: '2rem' } } basic={ basic } source={ input.value } />
<h6><a onClick={ this.onChangePreviewMode }>Edit Markdown</a></h6>
</FormControl>
}
</div>
);
}
}
const mapDispatchToProps = (dispatch) => bindActionCreators({
uploadMarkdownAttachment,
}, dispatch);
const _markdownField = connect(null, mapDispatchToProps)(MarkdownField);
export { Markdown as default, _markdownField as MarkdownField };
import * as React from 'react';
import Input from 'material-ui/Input';
import { withStyles } from 'material-ui/styles';
import compose from 'recompose/compose';
import FormControl from 'ui/common/forms/FormControl';
import Markdown from 'ui/common/markdown';
const styles = {
markdown: {
marginTop: '22px',
},
};
class MarkdownField extends React.Component<any, any> {
public constructor(props: any) {
......@@ -21,27 +13,27 @@ class MarkdownField extends React.Component<any, any> {
};
}
private onChangePreviewMode = () => {
protected onChangePreviewMode = () => {
this.setState({ previewMode: !this.state.previewMode });
}
public render() {
const {classes, basicMarkdown, input, label, required, meta, meta: {touched, error}, ...custom} = this.props;
const {basicMarkdown, input, label, required, meta, meta: {touched, error}, ...custom} = this.props;
const basic: boolean = basicMarkdown === undefined || null ? false : basicMarkdown;
return (
<div>
{ !this.state.previewMode ?
{ (basic || !this.state.previewMode) ?
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Input error={ touched && error } multiline={ !basic } { ...input } { ...custom } />
<h6>
<a onClick={ this.onChangePreviewMode }>Preview Markdown</a>
{ ! basic && <a onClick={ this.onChangePreviewMode }>Preview Markdown</a> }
<span> { basic ? 'Basic markdown supported: * **' : 'Full markdown supported' }</span>
</h6>
</FormControl>
:
<FormControl fullWidth required={ required } meta={ meta } label={ label }>
<Markdown className={ classes.markdown } basic={ basic } source={ input.value } />
<Markdown style={ { marginTop: '2rem' } } basic={ basic } source={ input.value } />
<h6><a onClick={ this.onChangePreviewMode }>Edit Markdown</a></h6>
</FormControl>
}
......@@ -50,4 +42,4 @@ class MarkdownField extends React.Component<any, any> {
}
}
export default compose(withStyles(styles))(MarkdownField);
export default MarkdownField;
......@@ -117,3 +117,30 @@ export function dereferenceReferences(content: any[], prop: string, conv: (o: an
.forEach((entry) => entry[prop] = refs[entry[prop]]);
}
export function insertAtCaret(element, myValue) {
const doc = document as any;
if (doc.selection) {
// For browsers like Internet Explorer
element.focus();
const sel = doc.selection.createRange();
sel.text = myValue;
element.focus();
} else if (element.selectionStart || element.selectionStart === '0') {
// For browsers like Firefox and Webkit based
const startPos = element.selectionStart;
const endPos = element.selectionEnd;
const scrollTop = element.scrollTop;
element.value = element.value.substring(0, startPos) + myValue + element.value.substring(endPos, element.value.length);
element.focus();
element.selectionStart = startPos + myValue.length;
element.selectionEnd = startPos + myValue.length;
element.scrollTop = scrollTop;
} else {
element.value += myValue;
element.focus();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment